pixelkiln 0.70.0 → 0.70.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +122 -53
- package/dist/cli.js.map +1 -1
- package/dist/client/gallery.css +1 -1
- package/dist/client/gallery.js +2979 -2588
- package/dist/index.cjs +49 -4
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +27 -2
- package/dist/index.d.ts +27 -2
- package/dist/index.js +54 -10
- package/dist/index.js.map +1 -1
- package/docs/CLI.md +11 -0
- package/docs/REVISIONS.md +4 -3
- package/package.json +3 -2
package/dist/cli.js
CHANGED
|
@@ -67,7 +67,7 @@ function exitCodeFor(err) {
|
|
|
67
67
|
}
|
|
68
68
|
|
|
69
69
|
// src/cli/main.ts
|
|
70
|
-
import { existsSync as
|
|
70
|
+
import { existsSync as existsSync36 } from "fs";
|
|
71
71
|
import { readFile as readFile32 } from "fs/promises";
|
|
72
72
|
|
|
73
73
|
// src/cli/args.ts
|
|
@@ -108,7 +108,8 @@ var VALUE_FLAGS = [
|
|
|
108
108
|
"--description",
|
|
109
109
|
"--weight",
|
|
110
110
|
"--glyph-px",
|
|
111
|
-
"--frames"
|
|
111
|
+
"--frames",
|
|
112
|
+
"--estimate-limit"
|
|
112
113
|
];
|
|
113
114
|
var BOOL_FLAGS = [
|
|
114
115
|
"--force",
|
|
@@ -430,7 +431,8 @@ function parseArgs(argv) {
|
|
|
430
431
|
description: get("--description"),
|
|
431
432
|
weight: get("--weight"),
|
|
432
433
|
glyphPx: numberOption("--glyph-px", { min: 8, max: 64, integer: true }),
|
|
433
|
-
frames: numberOption("--frames", { min: 3, max: 15, integer: true })
|
|
434
|
+
frames: numberOption("--frames", { min: 3, max: 15, integer: true }),
|
|
435
|
+
estimateLimit: numberOption("--estimate-limit", { min: 0, max: 1e3, integer: true })
|
|
434
436
|
};
|
|
435
437
|
}
|
|
436
438
|
|
|
@@ -543,6 +545,7 @@ Options
|
|
|
543
545
|
--no-open Do not auto-open the browser (pick, salvage, gallery) or editor (edit)
|
|
544
546
|
--edit gallery: allow manifest edits from the page (never spends)
|
|
545
547
|
--no-editor gallery: do not offer or serve the in-browser editor
|
|
548
|
+
--estimate-limit <n> gallery: PixelLab skeleton estimates the page may make (default 10)
|
|
546
549
|
--tag Also push tags upstream after fetch
|
|
547
550
|
--refresh fetch: re-download and replace files whose object changed
|
|
548
551
|
upstream (e.g. edited in PixelLab's editor); no generation
|
|
@@ -14373,7 +14376,7 @@ async function runEdit(args) {
|
|
|
14373
14376
|
|
|
14374
14377
|
// src/cli/commands/gallery.ts
|
|
14375
14378
|
import path33 from "path";
|
|
14376
|
-
import { existsSync as
|
|
14379
|
+
import { existsSync as existsSync25 } from "fs";
|
|
14377
14380
|
|
|
14378
14381
|
// src/gallery/snapshot.ts
|
|
14379
14382
|
import { createHash as createHash3 } from "crypto";
|
|
@@ -14416,6 +14419,7 @@ var SkeletonAnimationRequestSchema = z8.object({
|
|
|
14416
14419
|
/** Replace an existing `keypointsFile` with `set`. */
|
|
14417
14420
|
overwrite: z8.boolean().optional()
|
|
14418
14421
|
}).strict();
|
|
14422
|
+
var DEFAULT_ESTIMATE_LIMIT = 10;
|
|
14419
14423
|
function skeletonSourceFile(spec) {
|
|
14420
14424
|
if (spec.source) return path26.resolve(spec.root, spec.source);
|
|
14421
14425
|
if (spec.quality && existsSync18(spec.quality.outFile)) return spec.quality.outFile;
|
|
@@ -14424,7 +14428,12 @@ function skeletonSourceFile(spec) {
|
|
|
14424
14428
|
function createGallerySkeletonHandlers(opts) {
|
|
14425
14429
|
const log2 = opts.onProgress ?? (() => {
|
|
14426
14430
|
});
|
|
14431
|
+
const limit = opts.limit ?? DEFAULT_ESTIMATE_LIMIT;
|
|
14432
|
+
let used = 0;
|
|
14433
|
+
const spent = {};
|
|
14434
|
+
const status = () => ({ used, limit, spent: { ...spent } });
|
|
14427
14435
|
return {
|
|
14436
|
+
status,
|
|
14428
14437
|
async estimate(body) {
|
|
14429
14438
|
const parsed = SkeletonEstimateRequestSchema.safeParse(body);
|
|
14430
14439
|
if (!parsed.success) throw new ManifestEditError(`invalid estimate request: ${parsed.error.issues[0]?.message}`);
|
|
@@ -14440,16 +14449,33 @@ function createGallerySkeletonHandlers(opts) {
|
|
|
14440
14449
|
if (!metadata) throw new ManifestEditError(`${path26.basename(file)} is not a readable PNG or JPEG`);
|
|
14441
14450
|
const problem = estimateSkeletonSizeProblem(metadata.width, metadata.height);
|
|
14442
14451
|
if (problem) throw new ManifestEditError(`${key} is ${problem}`);
|
|
14452
|
+
if (used >= limit) {
|
|
14453
|
+
throw Object.assign(
|
|
14454
|
+
new Error(`this gallery has made its ${limit} skeleton estimate${limit === 1 ? "" : "s"}; restart it with --estimate-limit to allow more`),
|
|
14455
|
+
{ status: 429 }
|
|
14456
|
+
);
|
|
14457
|
+
}
|
|
14443
14458
|
const client2 = (opts.client ?? clientFromEnv)();
|
|
14459
|
+
used++;
|
|
14444
14460
|
const res = await client2.estimateSkeleton({ image: { base64: bytes.toString("base64"), format: metadata.format } });
|
|
14445
|
-
|
|
14461
|
+
const billed = billedAmount(res.usage);
|
|
14462
|
+
if (billed) spent[billed.unit] = (spent[billed.unit] ?? 0) + billed.amount;
|
|
14463
|
+
log2(` estimated a skeleton for ${key} (PixelLab estimate-skeleton, ${used}/${limit} this session${billed ? `, billed ${billed.amount} ${billed.unit}` : ""})`);
|
|
14446
14464
|
return {
|
|
14447
14465
|
set: scaffoldSkeletonSet(res.keypoints, request.frames ?? DEFAULT_SCAFFOLD_FRAMES),
|
|
14448
|
-
source: path26.relative(ctx.loaded.root, file).split(path26.sep).join("/")
|
|
14466
|
+
source: path26.relative(ctx.loaded.root, file).split(path26.sep).join("/"),
|
|
14467
|
+
session: status()
|
|
14449
14468
|
};
|
|
14450
14469
|
}
|
|
14451
14470
|
};
|
|
14452
14471
|
}
|
|
14472
|
+
function billedAmount(usage) {
|
|
14473
|
+
if (!usage || typeof usage !== "object") return null;
|
|
14474
|
+
const { type } = usage;
|
|
14475
|
+
if (type !== "usd" && type !== "generations") return null;
|
|
14476
|
+
const amount = usage[type];
|
|
14477
|
+
return typeof amount === "number" && Number.isFinite(amount) ? { unit: type, amount } : null;
|
|
14478
|
+
}
|
|
14453
14479
|
async function createSkeletonAnimation(manifestPath, request) {
|
|
14454
14480
|
const root = path26.dirname(path26.resolve(manifestPath));
|
|
14455
14481
|
const file = path26.resolve(root, request.keypointsFile);
|
|
@@ -15378,13 +15404,48 @@ import { createServer as createServer2 } from "http";
|
|
|
15378
15404
|
import { readFile as readFile19 } from "fs/promises";
|
|
15379
15405
|
|
|
15380
15406
|
// src/gallery/page.ts
|
|
15381
|
-
import { readFileSync as readFileSync4 } from "fs";
|
|
15407
|
+
import { existsSync as existsSync21, readFileSync as readFileSync4 } from "fs";
|
|
15408
|
+
|
|
15409
|
+
// src/gallery/client-bundle.ts
|
|
15410
|
+
import { createRequire } from "module";
|
|
15411
|
+
import { fileURLToPath } from "url";
|
|
15412
|
+
function bundleGalleryClient(entry = fileURLToPath(new URL("./client/src/main.ts", import.meta.url))) {
|
|
15413
|
+
const esbuild = createRequire(import.meta.url)("esbuild");
|
|
15414
|
+
const result = esbuild.buildSync({
|
|
15415
|
+
entryPoints: [entry],
|
|
15416
|
+
bundle: true,
|
|
15417
|
+
format: "iife",
|
|
15418
|
+
target: "es2020",
|
|
15419
|
+
platform: "browser",
|
|
15420
|
+
write: false,
|
|
15421
|
+
legalComments: "none",
|
|
15422
|
+
charset: "utf8"
|
|
15423
|
+
});
|
|
15424
|
+
return result.outputFiles[0].text;
|
|
15425
|
+
}
|
|
15426
|
+
|
|
15427
|
+
// src/gallery/page.ts
|
|
15428
|
+
function galleryContentSecurityPolicy(nonce) {
|
|
15429
|
+
return [
|
|
15430
|
+
"default-src 'self'",
|
|
15431
|
+
`script-src 'nonce-${nonce}'`,
|
|
15432
|
+
`style-src 'nonce-${nonce}'`,
|
|
15433
|
+
"img-src 'self' data: blob:",
|
|
15434
|
+
"frame-src 'self'",
|
|
15435
|
+
"connect-src 'self'",
|
|
15436
|
+
"object-src 'none'",
|
|
15437
|
+
"base-uri 'none'",
|
|
15438
|
+
"form-action 'none'",
|
|
15439
|
+
"frame-ancestors 'none'"
|
|
15440
|
+
].join("; ");
|
|
15441
|
+
}
|
|
15382
15442
|
var CLIENT_DIR = new URL("./client/", import.meta.url);
|
|
15383
15443
|
var client;
|
|
15384
15444
|
function clientAssets() {
|
|
15445
|
+
const prebuilt = new URL("gallery.js", CLIENT_DIR);
|
|
15385
15446
|
client ??= {
|
|
15386
15447
|
css: readFileSync4(new URL("gallery.css", CLIENT_DIR), "utf8"),
|
|
15387
|
-
js:
|
|
15448
|
+
js: existsSync21(prebuilt) ? readFileSync4(prebuilt, "utf8") : bundleGalleryClient()
|
|
15388
15449
|
};
|
|
15389
15450
|
return client;
|
|
15390
15451
|
}
|
|
@@ -15396,6 +15457,7 @@ function renderGallery(snapshot, opts = {}) {
|
|
|
15396
15457
|
const editor = JSON.stringify(Boolean(opts.editor && opts.session));
|
|
15397
15458
|
const title = `${snapshot.project?.name ?? "workspace"} | pixelkiln`;
|
|
15398
15459
|
const { css, js } = clientAssets();
|
|
15460
|
+
const nonce = opts.nonce ? ` nonce="${escapeHtml2(opts.nonce)}"` : "";
|
|
15399
15461
|
return `<!doctype html>
|
|
15400
15462
|
<html lang="en">
|
|
15401
15463
|
<head>
|
|
@@ -15403,7 +15465,7 @@ function renderGallery(snapshot, opts = {}) {
|
|
|
15403
15465
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
15404
15466
|
<title>${escapeHtml2(title)}</title>
|
|
15405
15467
|
<link rel="icon" href="data:,">
|
|
15406
|
-
<style>
|
|
15468
|
+
<style${nonce}>
|
|
15407
15469
|
${css}</style>
|
|
15408
15470
|
</head>
|
|
15409
15471
|
<body>
|
|
@@ -15445,7 +15507,7 @@ ${css}</style>
|
|
|
15445
15507
|
<div id="drawer-host"></div>
|
|
15446
15508
|
<div id="dialog-host"></div>
|
|
15447
15509
|
<datalist id="view-options"><option value="low top-down"><option value="high top-down"><option value="side"><option value="sidescroller"></datalist>
|
|
15448
|
-
<script>
|
|
15510
|
+
<script${nonce}>
|
|
15449
15511
|
const INITIAL = ${data};
|
|
15450
15512
|
const SESSION = ${session};
|
|
15451
15513
|
const EDITABLE = ${editable};
|
|
@@ -15641,6 +15703,10 @@ async function serveGallery(opts) {
|
|
|
15641
15703
|
}
|
|
15642
15704
|
}
|
|
15643
15705
|
if (req.method !== "GET" && req.method !== "HEAD") return fail(405, "the gallery is read-only");
|
|
15706
|
+
if (url2.pathname === "/api/skeleton") {
|
|
15707
|
+
if (!opts.skeleton) return fail(404, "skeleton estimates need the gallery started with --edit");
|
|
15708
|
+
return json(200, opts.skeleton.status());
|
|
15709
|
+
}
|
|
15644
15710
|
if (url2.pathname === "/api/jobs") {
|
|
15645
15711
|
if (!opts.generate) return fail(404, "generation is off");
|
|
15646
15712
|
return json(200, opts.generate.status());
|
|
@@ -15674,12 +15740,15 @@ async function serveGallery(opts) {
|
|
|
15674
15740
|
try {
|
|
15675
15741
|
if (url2.pathname === "/") {
|
|
15676
15742
|
const { snapshot } = await load();
|
|
15743
|
+
const nonce = randomBytes(16).toString("base64");
|
|
15677
15744
|
res.writeHead(200, {
|
|
15678
15745
|
"Content-Type": "text/html; charset=utf-8",
|
|
15679
15746
|
"Cache-Control": "no-store",
|
|
15680
|
-
"X-Content-Type-Options": "nosniff"
|
|
15747
|
+
"X-Content-Type-Options": "nosniff",
|
|
15748
|
+
"Content-Security-Policy": galleryContentSecurityPolicy(nonce)
|
|
15681
15749
|
});
|
|
15682
15750
|
res.end(renderGallery(snapshot, {
|
|
15751
|
+
nonce,
|
|
15683
15752
|
...session ? { session } : {},
|
|
15684
15753
|
editable: Boolean(opts.edit),
|
|
15685
15754
|
generation: Boolean(opts.generate),
|
|
@@ -16158,12 +16227,12 @@ function ownedReferenceImageId(spec, lock, providerId) {
|
|
|
16158
16227
|
}
|
|
16159
16228
|
|
|
16160
16229
|
// src/pick/server.ts
|
|
16161
|
-
import { existsSync as
|
|
16230
|
+
import { existsSync as existsSync23 } from "fs";
|
|
16162
16231
|
import { readFile as readFile21 } from "fs/promises";
|
|
16163
16232
|
|
|
16164
16233
|
// src/pipeline/init.ts
|
|
16165
16234
|
import { readdir, readFile as readFile20, writeFile as writeFile13 } from "fs/promises";
|
|
16166
|
-
import { existsSync as
|
|
16235
|
+
import { existsSync as existsSync22 } from "fs";
|
|
16167
16236
|
import path30 from "path";
|
|
16168
16237
|
function pngSize(buf) {
|
|
16169
16238
|
if (buf.length < 24) return null;
|
|
@@ -16257,7 +16326,7 @@ function mode(values) {
|
|
|
16257
16326
|
return [...counts.entries()].sort((a, b) => b[1] - a[1])[0]?.[0] ?? 64;
|
|
16258
16327
|
}
|
|
16259
16328
|
async function writeManifestFile(target, manifest) {
|
|
16260
|
-
if (
|
|
16329
|
+
if (existsSync22(target)) {
|
|
16261
16330
|
throw new Error(`${target} already exists; refusing to overwrite an existing manifest.`);
|
|
16262
16331
|
}
|
|
16263
16332
|
await writeFile13(target, JSON.stringify(manifest, null, 2) + "\n");
|
|
@@ -16673,7 +16742,7 @@ async function currentArt(entry, spec) {
|
|
|
16673
16742
|
const previous = primaryOutput({ ...entry, outputs: entry.supersededOutputs });
|
|
16674
16743
|
if (!previous) return null;
|
|
16675
16744
|
const file = resolveOutputPath(previous.path, spec.root);
|
|
16676
|
-
if (!
|
|
16745
|
+
if (!existsSync23(file)) return null;
|
|
16677
16746
|
const contentType = previous.mediaType ?? mediaTypeFromExtension(file);
|
|
16678
16747
|
if (!contentType) return null;
|
|
16679
16748
|
let width = entry.width;
|
|
@@ -17181,7 +17250,7 @@ import path32 from "path";
|
|
|
17181
17250
|
|
|
17182
17251
|
// src/editor/install.ts
|
|
17183
17252
|
import { createHash as createHash4 } from "crypto";
|
|
17184
|
-
import { existsSync as
|
|
17253
|
+
import { existsSync as existsSync24 } from "fs";
|
|
17185
17254
|
import { mkdir as mkdir11, readFile as readFile22, rename as rename9, rm as rm10, stat as stat3, writeFile as writeFile14 } from "fs/promises";
|
|
17186
17255
|
import os2 from "os";
|
|
17187
17256
|
import path31 from "path";
|
|
@@ -17280,7 +17349,7 @@ async function editorStatus(opts = {}) {
|
|
|
17280
17349
|
for (const [name, expected] of Object.entries(pin.files)) {
|
|
17281
17350
|
totalBytes += expected.bytes;
|
|
17282
17351
|
const file = path31.join(dir, name);
|
|
17283
|
-
const info =
|
|
17352
|
+
const info = existsSync24(file) ? await stat3(file).catch(() => null) : null;
|
|
17284
17353
|
if (info && info.size === expected.bytes && await sha256Of(file) === expected.sha256) {
|
|
17285
17354
|
installedBytes += expected.bytes;
|
|
17286
17355
|
} else {
|
|
@@ -17534,7 +17603,7 @@ async function serveUntilStopped(initial, reload, args, access2) {
|
|
|
17534
17603
|
...args.edit ? { edit: createGalleryEditHandler({ manifestFor: access2.manifestFor, loadProject: access2.loadProject, reload, onProgress: log }) } : {},
|
|
17535
17604
|
...budget ? { generate: createGenerateHandlers({ loadProject, providerFor, budget, reload, onProgress: log }) } : {},
|
|
17536
17605
|
// Estimating acts on the author's behalf and feeds a manifest edit, so it follows the write gate.
|
|
17537
|
-
...args.edit ? { skeleton: createGallerySkeletonHandlers({ loadProject, onProgress: log }) } : {},
|
|
17606
|
+
...args.edit ? { skeleton: createGallerySkeletonHandlers({ loadProject, limit: args.estimateLimit, onProgress: log }) } : {},
|
|
17538
17607
|
// The editor exists to write hand edits back, so it follows the write gate.
|
|
17539
17608
|
...args.edit && !args.noEditor ? { editor: createGalleryEditorHandlers({ onProgress: log }) } : {}
|
|
17540
17609
|
});
|
|
@@ -17553,7 +17622,7 @@ async function serveUntilStopped(initial, reload, args, access2) {
|
|
|
17553
17622
|
async function runGallery(args) {
|
|
17554
17623
|
if (args.workspace) {
|
|
17555
17624
|
const workspacePath = path33.resolve(args.workspace);
|
|
17556
|
-
if (!
|
|
17625
|
+
if (!existsSync25(workspacePath)) {
|
|
17557
17626
|
throw new Error(`Workspace catalog not found: ${workspacePath}`);
|
|
17558
17627
|
}
|
|
17559
17628
|
const filter2 = { styles: args.styles, assets: args.assets };
|
|
@@ -17907,14 +17976,14 @@ async function runRestoreGeneration(args) {
|
|
|
17907
17976
|
}
|
|
17908
17977
|
|
|
17909
17978
|
// src/cli/commands/housekeeping.ts
|
|
17910
|
-
import { existsSync as
|
|
17979
|
+
import { existsSync as existsSync27 } from "fs";
|
|
17911
17980
|
|
|
17912
17981
|
// src/pipeline/cache-health.ts
|
|
17913
|
-
import { existsSync as
|
|
17982
|
+
import { existsSync as existsSync26 } from "fs";
|
|
17914
17983
|
import { readFile as readFile24, readdir as readdir2, rm as rm11 } from "fs/promises";
|
|
17915
17984
|
import path34 from "path";
|
|
17916
17985
|
async function inspectCaches(lock, lockPath, options = {}) {
|
|
17917
|
-
if (options.prune && !
|
|
17986
|
+
if (options.prune && !existsSync26(lockPath)) {
|
|
17918
17987
|
throw new Error(`Refusing to prune without an existing lockfile at ${path34.resolve(lockPath)}`);
|
|
17919
17988
|
}
|
|
17920
17989
|
const contentDir = path34.resolve(path34.dirname(lockPath), ".pixelkiln", "cache");
|
|
@@ -17957,7 +18026,7 @@ async function inspectCaches(lock, lockPath, options = {}) {
|
|
|
17957
18026
|
async function inspectContentCache(contentDir, referenced) {
|
|
17958
18027
|
const report = {
|
|
17959
18028
|
path: contentDir,
|
|
17960
|
-
exists:
|
|
18029
|
+
exists: existsSync26(contentDir),
|
|
17961
18030
|
files: 0,
|
|
17962
18031
|
bytes: 0,
|
|
17963
18032
|
valid: 0,
|
|
@@ -18021,7 +18090,7 @@ async function inspectContentCache(contentDir, referenced) {
|
|
|
18021
18090
|
async function inspectRemoteHashCache(remotePath) {
|
|
18022
18091
|
const report = {
|
|
18023
18092
|
path: remotePath,
|
|
18024
|
-
exists:
|
|
18093
|
+
exists: existsSync26(remotePath),
|
|
18025
18094
|
entries: 0,
|
|
18026
18095
|
valid: 0,
|
|
18027
18096
|
invalidIds: [],
|
|
@@ -18094,7 +18163,7 @@ async function runAccept(args) {
|
|
|
18094
18163
|
let intact = true;
|
|
18095
18164
|
for (const output of entry.outputs) {
|
|
18096
18165
|
const file = resolveOutputPath(output.path, item.spec.root);
|
|
18097
|
-
if (!
|
|
18166
|
+
if (!existsSync27(file) || await sha256File(file) !== output.sha256) {
|
|
18098
18167
|
intact = false;
|
|
18099
18168
|
break;
|
|
18100
18169
|
}
|
|
@@ -18150,11 +18219,11 @@ This drops their provenance from the lockfile. The art on disk is`);
|
|
|
18150
18219
|
|
|
18151
18220
|
// src/cli/commands/init.ts
|
|
18152
18221
|
import path35 from "path";
|
|
18153
|
-
import { existsSync as
|
|
18222
|
+
import { existsSync as existsSync28 } from "fs";
|
|
18154
18223
|
async function runInit(args) {
|
|
18155
18224
|
if (!args.from) throw new Error("init needs --from <dir> pointing at your existing PNGs.");
|
|
18156
18225
|
const root = path35.resolve(args.from);
|
|
18157
|
-
if (!
|
|
18226
|
+
if (!existsSync28(root)) throw new Error(`No directory at ${root}`);
|
|
18158
18227
|
const generator = args.generator ?? "map";
|
|
18159
18228
|
if (generator !== "1dir" && generator !== "map") {
|
|
18160
18229
|
throw new Error(`--generator must be "1dir" or "map", got "${args.generator}".`);
|
|
@@ -18182,7 +18251,7 @@ async function runInit(args) {
|
|
|
18182
18251
|
}
|
|
18183
18252
|
|
|
18184
18253
|
// src/pipeline/doctor.ts
|
|
18185
|
-
import { constants, existsSync as
|
|
18254
|
+
import { constants, existsSync as existsSync29 } from "fs";
|
|
18186
18255
|
import { access, stat as stat4 } from "fs/promises";
|
|
18187
18256
|
import path36 from "path";
|
|
18188
18257
|
async function doctor(loaded, specs, lock, lockPath, opts = {}) {
|
|
@@ -18254,7 +18323,7 @@ async function doctor(loaded, specs, lock, lockPath, opts = {}) {
|
|
|
18254
18323
|
cacheDir,
|
|
18255
18324
|
cacheFileName(output.sha256, output.mediaType ?? MediaType.PNG)
|
|
18256
18325
|
);
|
|
18257
|
-
if (
|
|
18326
|
+
if (existsSync29(cached) && await sha256File(cached) === output.sha256) {
|
|
18258
18327
|
hasCache = true;
|
|
18259
18328
|
break;
|
|
18260
18329
|
}
|
|
@@ -18358,7 +18427,7 @@ async function nearestExistingDirectory(start) {
|
|
|
18358
18427
|
|
|
18359
18428
|
// src/pipeline/audit.ts
|
|
18360
18429
|
import { readFile as readFile25 } from "fs/promises";
|
|
18361
|
-
import { existsSync as
|
|
18430
|
+
import { existsSync as existsSync30 } from "fs";
|
|
18362
18431
|
import path37 from "path";
|
|
18363
18432
|
function paletteDistance(asset, reference) {
|
|
18364
18433
|
if (!asset.length || !reference.length) return 0;
|
|
@@ -18395,7 +18464,7 @@ async function auditStyle(loaded, specs, styleId, lock) {
|
|
|
18395
18464
|
const unreadable = [];
|
|
18396
18465
|
for (const spec of mine) {
|
|
18397
18466
|
for (const output of resolveSpecOutputs(spec, lock, loaded.root)) {
|
|
18398
|
-
if (!
|
|
18467
|
+
if (!existsSync30(output.absolutePath)) {
|
|
18399
18468
|
missing.push(output.id);
|
|
18400
18469
|
continue;
|
|
18401
18470
|
}
|
|
@@ -18425,7 +18494,7 @@ async function auditStyle(loaded, specs, styleId, lock) {
|
|
|
18425
18494
|
const refPalettes = [];
|
|
18426
18495
|
for (const img of style.styleImages) {
|
|
18427
18496
|
const abs = path37.resolve(loaded.root, img.path);
|
|
18428
|
-
if (!
|
|
18497
|
+
if (!existsSync30(abs)) continue;
|
|
18429
18498
|
try {
|
|
18430
18499
|
refPalettes.push(extractPalette(decodePng(await readFile25(abs)), 12));
|
|
18431
18500
|
} catch {
|
|
@@ -19597,7 +19666,7 @@ function sheetFormat(args) {
|
|
|
19597
19666
|
import path41 from "path";
|
|
19598
19667
|
|
|
19599
19668
|
// src/pixellab-utils.ts
|
|
19600
|
-
import { existsSync as
|
|
19669
|
+
import { existsSync as existsSync31 } from "fs";
|
|
19601
19670
|
import { mkdir as mkdir12, readFile as readFile27, writeFile as writeFile15 } from "fs/promises";
|
|
19602
19671
|
import path40 from "path";
|
|
19603
19672
|
var UNZOOM_MIN_SIDE = 256;
|
|
@@ -19609,7 +19678,7 @@ function defaultUnzoomOut(input) {
|
|
|
19609
19678
|
async function unzoomFile(client2, input, opts = {}) {
|
|
19610
19679
|
const out = path40.resolve(opts.out ?? defaultUnzoomOut(input));
|
|
19611
19680
|
if (path40.resolve(input) === out) throw new Error("unzoom will not overwrite its own input; pass a different --out");
|
|
19612
|
-
if (
|
|
19681
|
+
if (existsSync31(out) && !opts.force) throw new Error(`${out} already exists; pass --force to replace it`);
|
|
19613
19682
|
const bytes = await readFile27(input);
|
|
19614
19683
|
const metadata = imageMetadata(bytes);
|
|
19615
19684
|
if (!metadata) throw new Error(`${input} is not a readable PNG or JPEG`);
|
|
@@ -19638,7 +19707,7 @@ async function generateFont(client2, opts) {
|
|
|
19638
19707
|
const { ttf, atlas } = fontOutputPaths(opts.out);
|
|
19639
19708
|
if (!opts.force) {
|
|
19640
19709
|
for (const file of [ttf, atlas]) {
|
|
19641
|
-
if (
|
|
19710
|
+
if (existsSync31(file)) throw new Error(`${file} already exists; pass --force to replace it`);
|
|
19642
19711
|
}
|
|
19643
19712
|
}
|
|
19644
19713
|
const sleep3 = opts.sleep ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));
|
|
@@ -19752,7 +19821,7 @@ import { readFile as readFile29 } from "fs/promises";
|
|
|
19752
19821
|
|
|
19753
19822
|
// src/pipeline/quality-regression.ts
|
|
19754
19823
|
import path42 from "path";
|
|
19755
|
-
import { existsSync as
|
|
19824
|
+
import { existsSync as existsSync32 } from "fs";
|
|
19756
19825
|
import { readFile as readFile28 } from "fs/promises";
|
|
19757
19826
|
import { z as z11 } from "zod";
|
|
19758
19827
|
var SHA256_RE = /^[0-9a-f]{64}$/;
|
|
@@ -19995,7 +20064,7 @@ async function snapshotQualityBaseline(inputs, baselinePath, options = {}) {
|
|
|
19995
20064
|
cases
|
|
19996
20065
|
});
|
|
19997
20066
|
const data = Buffer.from(JSON.stringify(baseline, null, 2) + "\n");
|
|
19998
|
-
if (
|
|
20067
|
+
if (existsSync32(absolute) && !options.force) {
|
|
19999
20068
|
const current = await readFile28(absolute);
|
|
20000
20069
|
if (!current.equals(data)) {
|
|
20001
20070
|
throw new OverwriteRefusedError(
|
|
@@ -20192,9 +20261,9 @@ import path45 from "path";
|
|
|
20192
20261
|
|
|
20193
20262
|
// src/recipes.ts
|
|
20194
20263
|
import path44 from "path";
|
|
20195
|
-
import { existsSync as
|
|
20264
|
+
import { existsSync as existsSync33 } from "fs";
|
|
20196
20265
|
import { readdir as readdir3, readFile as readFile30 } from "fs/promises";
|
|
20197
|
-
import { fileURLToPath } from "url";
|
|
20266
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
20198
20267
|
import { z as z12 } from "zod";
|
|
20199
20268
|
var SHA256_RE2 = /^[0-9a-f]{64}$/;
|
|
20200
20269
|
var RECIPE_ID_RE = /^[a-z0-9][a-z0-9-]*\/[a-z0-9][a-z0-9-]*$/;
|
|
@@ -20395,7 +20464,7 @@ async function readRecipeFile(recipePath, bundled) {
|
|
|
20395
20464
|
return { recipe: parsed.data, authored: raw, path: absolute, dir: path44.dirname(absolute), bundled };
|
|
20396
20465
|
}
|
|
20397
20466
|
async function walkRecipeFiles(root) {
|
|
20398
|
-
if (!
|
|
20467
|
+
if (!existsSync33(root)) return [];
|
|
20399
20468
|
const out = [];
|
|
20400
20469
|
for (const entry of await readdir3(root, { withFileTypes: true })) {
|
|
20401
20470
|
const resolved = path44.join(root, entry.name);
|
|
@@ -20405,7 +20474,7 @@ async function walkRecipeFiles(root) {
|
|
|
20405
20474
|
return out;
|
|
20406
20475
|
}
|
|
20407
20476
|
function bundledRecipeRoot() {
|
|
20408
|
-
return
|
|
20477
|
+
return fileURLToPath2(new URL("../recipes/", import.meta.url));
|
|
20409
20478
|
}
|
|
20410
20479
|
async function listBundledRecipes(root = bundledRecipeRoot()) {
|
|
20411
20480
|
const recipes = await Promise.all(
|
|
@@ -20433,7 +20502,7 @@ function parseSelector(selector) {
|
|
|
20433
20502
|
}
|
|
20434
20503
|
async function resolveRecipe(target, bundledRoot = bundledRecipeRoot()) {
|
|
20435
20504
|
const local = path44.resolve(target);
|
|
20436
|
-
if (
|
|
20505
|
+
if (existsSync33(local)) {
|
|
20437
20506
|
const recipePath = path44.basename(local) === RECIPE_FILE ? local : path44.join(local, RECIPE_FILE);
|
|
20438
20507
|
return readRecipeFile(recipePath, false);
|
|
20439
20508
|
}
|
|
@@ -20449,7 +20518,7 @@ async function resolveRecipe(target, bundledRoot = bundledRecipeRoot()) {
|
|
|
20449
20518
|
}
|
|
20450
20519
|
async function verifyFile(root, file) {
|
|
20451
20520
|
const absolute = path44.resolve(root, ...file.path.split("/"));
|
|
20452
|
-
if (!
|
|
20521
|
+
if (!existsSync33(absolute)) {
|
|
20453
20522
|
return { path: file.path, expectedSha256: file.sha256, actualSha256: null, status: "missing" };
|
|
20454
20523
|
}
|
|
20455
20524
|
const actualSha256 = await sha256File(absolute);
|
|
@@ -20527,7 +20596,7 @@ async function installRecipe(target, options = {}) {
|
|
|
20527
20596
|
})));
|
|
20528
20597
|
if (!options.force) {
|
|
20529
20598
|
for (const file of files) {
|
|
20530
|
-
if (!
|
|
20599
|
+
if (!existsSync33(file.path)) continue;
|
|
20531
20600
|
const current = await readFile30(file.path);
|
|
20532
20601
|
if (!current.equals(file.data)) {
|
|
20533
20602
|
throw new OverwriteRefusedError(
|
|
@@ -20767,7 +20836,7 @@ async function runRefine(args) {
|
|
|
20767
20836
|
|
|
20768
20837
|
// src/cli/commands/skeleton.ts
|
|
20769
20838
|
import path47 from "path";
|
|
20770
|
-
import { existsSync as
|
|
20839
|
+
import { existsSync as existsSync34 } from "fs";
|
|
20771
20840
|
import { readFile as readFile31, writeFile as writeFile16 } from "fs/promises";
|
|
20772
20841
|
|
|
20773
20842
|
// src/skeleton-preview.ts
|
|
@@ -20879,7 +20948,7 @@ var Canvas = class {
|
|
|
20879
20948
|
async function runEstimateSkeleton(args) {
|
|
20880
20949
|
const image = args.target;
|
|
20881
20950
|
if (!image) throw new UsageError("estimate-skeleton needs an image path: pixelkiln estimate-skeleton <image> [--out keypoints.json]");
|
|
20882
|
-
if (args.out &&
|
|
20951
|
+
if (args.out && existsSync34(path47.resolve(args.out)) && !args.force) {
|
|
20883
20952
|
throw new UsageError(`${args.out} already exists; pass --force to replace it`);
|
|
20884
20953
|
}
|
|
20885
20954
|
loadEnvFiles(path47.dirname(path47.resolve(args.manifest)));
|
|
@@ -20925,7 +20994,7 @@ async function runSkeletonPreview(args) {
|
|
|
20925
20994
|
sourceFile = args.from ? path47.resolve(args.from) : spec.revision.sourceFile;
|
|
20926
20995
|
label = `${spec.styleId}/${spec.assetId}`;
|
|
20927
20996
|
}
|
|
20928
|
-
if (!
|
|
20997
|
+
if (!existsSync34(keypointsFile)) throw new UsageError(`Keypoints file does not exist yet: ${keypointsFile}`);
|
|
20929
20998
|
let json;
|
|
20930
20999
|
try {
|
|
20931
21000
|
json = JSON.parse(await readFile31(keypointsFile, "utf8"));
|
|
@@ -20935,7 +21004,7 @@ async function runSkeletonPreview(args) {
|
|
|
20935
21004
|
const set = parseSkeletonSet(json, keypointsFile);
|
|
20936
21005
|
let source;
|
|
20937
21006
|
if (sourceFile) {
|
|
20938
|
-
if (!
|
|
21007
|
+
if (!existsSync34(sourceFile)) {
|
|
20939
21008
|
log(` source image does not exist yet, drawing on a plain ground: ${sourceFile}`);
|
|
20940
21009
|
} else {
|
|
20941
21010
|
const bytes = await readFile31(sourceFile);
|
|
@@ -20993,7 +21062,7 @@ async function runTools(args) {
|
|
|
20993
21062
|
|
|
20994
21063
|
// src/cli/commands/workspace.ts
|
|
20995
21064
|
import path48 from "path";
|
|
20996
|
-
import { existsSync as
|
|
21065
|
+
import { existsSync as existsSync35 } from "fs";
|
|
20997
21066
|
async function runWorkspace(args) {
|
|
20998
21067
|
const workspacePath = path48.resolve(args.workspace ?? "pixelkiln.workspace.json");
|
|
20999
21068
|
const dir = path48.dirname(workspacePath);
|
|
@@ -21023,7 +21092,7 @@ async function runWorkspace(args) {
|
|
|
21023
21092
|
log(` registered "${id}" in ${path48.relative(process.cwd(), workspacePath)}`);
|
|
21024
21093
|
log(` manifest: ${project.manifest}`);
|
|
21025
21094
|
log(` lock: ${project.lock}`);
|
|
21026
|
-
if (!
|
|
21095
|
+
if (!existsSync35(lockPath)) {
|
|
21027
21096
|
log(
|
|
21028
21097
|
` warning: no lockfile there yet; this project contributes no claims until one is generated`
|
|
21029
21098
|
);
|
|
@@ -21047,7 +21116,7 @@ async function runWorkspace(args) {
|
|
|
21047
21116
|
return;
|
|
21048
21117
|
}
|
|
21049
21118
|
if (args.subcommand === "list") {
|
|
21050
|
-
if (!
|
|
21119
|
+
if (!existsSync35(workspacePath)) {
|
|
21051
21120
|
throw new Error(`Workspace catalog not found: ${workspacePath}`);
|
|
21052
21121
|
}
|
|
21053
21122
|
const ws = await loadWorkspace(workspacePath);
|
|
@@ -21067,7 +21136,7 @@ async function runWorkspace(args) {
|
|
|
21067
21136
|
return;
|
|
21068
21137
|
}
|
|
21069
21138
|
if (args.subcommand === "status") {
|
|
21070
|
-
if (!
|
|
21139
|
+
if (!existsSync35(workspacePath)) {
|
|
21071
21140
|
throw new Error(`Workspace catalog not found: ${workspacePath}`);
|
|
21072
21141
|
}
|
|
21073
21142
|
const ws = await loadWorkspace(workspacePath);
|
|
@@ -21129,7 +21198,7 @@ async function runHelp() {
|
|
|
21129
21198
|
async function runVersion() {
|
|
21130
21199
|
for (const up of ["../package.json", "../../package.json"]) {
|
|
21131
21200
|
const url = new URL(up, import.meta.url);
|
|
21132
|
-
if (!
|
|
21201
|
+
if (!existsSync36(url)) continue;
|
|
21133
21202
|
const pkg = JSON.parse(await readFile32(url, "utf8"));
|
|
21134
21203
|
if (pkg.name === "pixelkiln") {
|
|
21135
21204
|
log(`${pkg.name} ${pkg.version}`);
|