pixelkiln 0.69.0 → 0.70.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +582 -422
- package/dist/cli.js.map +1 -1
- package/dist/client/gallery.css +5 -0
- package/dist/client/gallery.js +223 -1
- package/dist/index.cjs +261 -139
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +170 -74
- package/dist/index.d.ts +170 -74
- package/dist/index.js +256 -134
- package/dist/index.js.map +1 -1
- package/docs/REVISIONS.md +11 -0
- package/package.json +1 -1
- package/skills/pixelkiln/references/revisions.md +7 -4
package/dist/index.cjs
CHANGED
|
@@ -2055,9 +2055,9 @@ var PixelLabClient = class {
|
|
|
2055
2055
|
apiKey;
|
|
2056
2056
|
http;
|
|
2057
2057
|
/** Retries transport failures, 429, and 5xx; see http.ts for the policy. */
|
|
2058
|
-
async request(
|
|
2058
|
+
async request(path35, init) {
|
|
2059
2059
|
const auth = this.apiKey.startsWith("Bearer ") ? this.apiKey : `Bearer ${this.apiKey}`;
|
|
2060
|
-
const res = await this.http(`${BASE}${
|
|
2060
|
+
const res = await this.http(`${BASE}${path35}`, {
|
|
2061
2061
|
...init,
|
|
2062
2062
|
headers: {
|
|
2063
2063
|
Authorization: auth,
|
|
@@ -2067,13 +2067,13 @@ var PixelLabClient = class {
|
|
|
2067
2067
|
});
|
|
2068
2068
|
const text = await res.text();
|
|
2069
2069
|
if (!res.ok) {
|
|
2070
|
-
throw new PixelLabError(`${init?.method ?? "GET"} ${
|
|
2070
|
+
throw new PixelLabError(`${init?.method ?? "GET"} ${path35} \u2192 ${res.status}`, res.status, text);
|
|
2071
2071
|
}
|
|
2072
2072
|
if (!text) return {};
|
|
2073
2073
|
try {
|
|
2074
2074
|
return JSON.parse(text);
|
|
2075
2075
|
} catch {
|
|
2076
|
-
throw new Error(`${init?.method ?? "GET"} ${
|
|
2076
|
+
throw new Error(`${init?.method ?? "GET"} ${path35} returned invalid JSON`);
|
|
2077
2077
|
}
|
|
2078
2078
|
}
|
|
2079
2079
|
async balance() {
|
|
@@ -2465,10 +2465,10 @@ var PixelLabClient = class {
|
|
|
2465
2465
|
const palette = args.paletteSwatchBase64 ? { color_image: { type: "base64", base64: args.paletteSwatchBase64, format: "png" }, force_colors: true } : {};
|
|
2466
2466
|
const encode = (image) => ({ type: "base64", base64: image.base64, format: image.format });
|
|
2467
2467
|
const south = args.reference?.south;
|
|
2468
|
-
let
|
|
2468
|
+
let path35;
|
|
2469
2469
|
let body;
|
|
2470
2470
|
if (args.mode === "v3") {
|
|
2471
|
-
|
|
2471
|
+
path35 = "/create-character-v3";
|
|
2472
2472
|
body = {
|
|
2473
2473
|
description: args.description,
|
|
2474
2474
|
image_size: imageSize,
|
|
@@ -2482,7 +2482,7 @@ var PixelLabClient = class {
|
|
|
2482
2482
|
...args.enhancePrompt ? { enhance_prompt: true } : {}
|
|
2483
2483
|
};
|
|
2484
2484
|
} else if (args.mode === "pro-flash") {
|
|
2485
|
-
|
|
2485
|
+
path35 = "/create-character-pro-flash";
|
|
2486
2486
|
const traits = args.styleTraits;
|
|
2487
2487
|
body = {
|
|
2488
2488
|
description: args.description,
|
|
@@ -2506,7 +2506,7 @@ var PixelLabClient = class {
|
|
|
2506
2506
|
} : {}
|
|
2507
2507
|
};
|
|
2508
2508
|
} else if (args.mode === "pro") {
|
|
2509
|
-
|
|
2509
|
+
path35 = "/create-character-pro";
|
|
2510
2510
|
body = {
|
|
2511
2511
|
description: args.description,
|
|
2512
2512
|
image_size: imageSize,
|
|
@@ -2520,7 +2520,7 @@ var PixelLabClient = class {
|
|
|
2520
2520
|
...!south && args.styleCharacterId ? { style_character_id: args.styleCharacterId } : {}
|
|
2521
2521
|
};
|
|
2522
2522
|
} else {
|
|
2523
|
-
|
|
2523
|
+
path35 = args.directions === 4 ? "/create-character-with-4-directions" : "/create-character-with-8-directions";
|
|
2524
2524
|
body = {
|
|
2525
2525
|
description: args.description,
|
|
2526
2526
|
image_size: imageSize,
|
|
@@ -2537,8 +2537,8 @@ var PixelLabClient = class {
|
|
|
2537
2537
|
...palette
|
|
2538
2538
|
};
|
|
2539
2539
|
}
|
|
2540
|
-
const raw = await this.request(
|
|
2541
|
-
return validateResponse(CharacterSubmitSchema, raw,
|
|
2540
|
+
const raw = await this.request(path35, { method: "POST", body: JSON.stringify(body) });
|
|
2541
|
+
return validateResponse(CharacterSubmitSchema, raw, path35.slice(1));
|
|
2542
2542
|
}
|
|
2543
2543
|
/** A text edit of an existing character, applied to every direction and stored as a sibling. */
|
|
2544
2544
|
async createCharacterState(args) {
|
|
@@ -3716,10 +3716,10 @@ var import_node_fs = require("fs");
|
|
|
3716
3716
|
function sha256(data) {
|
|
3717
3717
|
return (0, import_node_crypto.createHash)("sha256").update(data).digest("hex");
|
|
3718
3718
|
}
|
|
3719
|
-
async function sha256File(
|
|
3719
|
+
async function sha256File(path35) {
|
|
3720
3720
|
return new Promise((resolve, reject) => {
|
|
3721
3721
|
const hash = (0, import_node_crypto.createHash)("sha256");
|
|
3722
|
-
const stream = (0, import_node_fs.createReadStream)(
|
|
3722
|
+
const stream = (0, import_node_fs.createReadStream)(path35);
|
|
3723
3723
|
stream.on("data", (chunk2) => hash.update(chunk2));
|
|
3724
3724
|
stream.on("error", reject);
|
|
3725
3725
|
stream.on("end", () => resolve(hash.digest("hex")));
|
|
@@ -6082,9 +6082,9 @@ var RetroDiffusionClient = class {
|
|
|
6082
6082
|
if (!response.ok) throw new ProviderError("retrodiffusion", `Retro Diffusion download failed (${response.status})`, { status: response.status });
|
|
6083
6083
|
return Buffer.from(await response.arrayBuffer());
|
|
6084
6084
|
}
|
|
6085
|
-
async call(
|
|
6085
|
+
async call(path35, init = {}) {
|
|
6086
6086
|
if (!this.token) throw new Error("RD_API_KEY is not set");
|
|
6087
|
-
const response = await this.request(`${this.baseUrl}${
|
|
6087
|
+
const response = await this.request(`${this.baseUrl}${path35}`, {
|
|
6088
6088
|
...init,
|
|
6089
6089
|
headers: {
|
|
6090
6090
|
"Content-Type": "application/json",
|
|
@@ -12373,10 +12373,10 @@ function isSha256Hash(value) {
|
|
|
12373
12373
|
function parseCache(value) {
|
|
12374
12374
|
return HashCacheSchema.parse(value);
|
|
12375
12375
|
}
|
|
12376
|
-
async function loadCache(
|
|
12377
|
-
if (!(0, import_node_fs14.existsSync)(
|
|
12376
|
+
async function loadCache(path35) {
|
|
12377
|
+
if (!(0, import_node_fs14.existsSync)(path35)) return { version: 1, hashes: {} };
|
|
12378
12378
|
try {
|
|
12379
|
-
const parsed = HashCacheSchema.safeParse(JSON.parse(await (0, import_promises11.readFile)(
|
|
12379
|
+
const parsed = HashCacheSchema.safeParse(JSON.parse(await (0, import_promises11.readFile)(path35, "utf8")));
|
|
12380
12380
|
if (!parsed.success) return { version: 1, hashes: {} };
|
|
12381
12381
|
return {
|
|
12382
12382
|
version: 1,
|
|
@@ -12388,18 +12388,18 @@ async function loadCache(path34) {
|
|
|
12388
12388
|
return { version: 1, hashes: {} };
|
|
12389
12389
|
}
|
|
12390
12390
|
}
|
|
12391
|
-
async function saveCache(
|
|
12391
|
+
async function saveCache(path35, cache) {
|
|
12392
12392
|
const sorted = {};
|
|
12393
12393
|
for (const key of Object.keys(cache.hashes).sort()) {
|
|
12394
12394
|
const hash = cache.hashes[key];
|
|
12395
12395
|
if (!isSha256Hash(hash)) throw new Error(`Refusing to cache invalid SHA-256 for ${key}`);
|
|
12396
12396
|
sorted[key] = hash;
|
|
12397
12397
|
}
|
|
12398
|
-
await (0, import_promises11.mkdir)(import_node_path18.default.dirname(import_node_path18.default.resolve(
|
|
12399
|
-
const tmp = `${
|
|
12398
|
+
await (0, import_promises11.mkdir)(import_node_path18.default.dirname(import_node_path18.default.resolve(path35)), { recursive: true });
|
|
12399
|
+
const tmp = `${path35}.${process.pid}.${Math.random().toString(36).slice(2)}.tmp`;
|
|
12400
12400
|
try {
|
|
12401
12401
|
await (0, import_promises11.writeFile)(tmp, JSON.stringify({ version: 1, hashes: sorted }, null, 2) + "\n");
|
|
12402
|
-
await (0, import_promises11.rename)(tmp,
|
|
12402
|
+
await (0, import_promises11.rename)(tmp, path35);
|
|
12403
12403
|
} finally {
|
|
12404
12404
|
await (0, import_promises11.rm)(tmp, { force: true });
|
|
12405
12405
|
}
|
|
@@ -15204,9 +15204,9 @@ async function runPicker(provider, lock, lockPath, opts = {}) {
|
|
|
15204
15204
|
|
|
15205
15205
|
// src/gallery/snapshot.ts
|
|
15206
15206
|
var import_node_crypto6 = require("crypto");
|
|
15207
|
-
var
|
|
15208
|
-
var
|
|
15209
|
-
var
|
|
15207
|
+
var import_node_fs26 = require("fs");
|
|
15208
|
+
var import_promises23 = require("fs/promises");
|
|
15209
|
+
var import_node_path31 = __toESM(require("path"), 1);
|
|
15210
15210
|
|
|
15211
15211
|
// src/workspace.ts
|
|
15212
15212
|
var import_promises19 = require("fs/promises");
|
|
@@ -15342,8 +15342,8 @@ function validateWorkspace(ws, dir) {
|
|
|
15342
15342
|
}
|
|
15343
15343
|
|
|
15344
15344
|
// src/gallery/edit.ts
|
|
15345
|
-
var
|
|
15346
|
-
var
|
|
15345
|
+
var import_node_path30 = __toESM(require("path"), 1);
|
|
15346
|
+
var import_zod11 = require("zod");
|
|
15347
15347
|
|
|
15348
15348
|
// src/manifest-edit.ts
|
|
15349
15349
|
var import_promises20 = require("fs/promises");
|
|
@@ -15391,15 +15391,27 @@ var NewAssetSchema = import_zod8.z.object({
|
|
|
15391
15391
|
/**
|
|
15392
15392
|
* A controlled regeneration of another asset in the same manifest,
|
|
15393
15393
|
* instead of a fresh generation. `inpaint` needs a mask upload the
|
|
15394
|
-
* gallery does not offer yet, so only `image-to-image`
|
|
15395
|
-
*
|
|
15394
|
+
* gallery does not offer yet, so only `image-to-image` and
|
|
15395
|
+
* `animate-skeleton` (whose keypoints file the gallery writes first) can
|
|
15396
|
+
* be created here; hand-edit the manifest for the other modes.
|
|
15396
15397
|
*/
|
|
15397
|
-
revision: import_zod8.z.
|
|
15398
|
-
|
|
15399
|
-
|
|
15400
|
-
|
|
15401
|
-
|
|
15402
|
-
|
|
15398
|
+
revision: import_zod8.z.discriminatedUnion("mode", [
|
|
15399
|
+
import_zod8.z.object({
|
|
15400
|
+
mode: import_zod8.z.literal("image-to-image"),
|
|
15401
|
+
/** Asset id of the parent this revises; must already exist. */
|
|
15402
|
+
from: import_zod8.z.string().min(1),
|
|
15403
|
+
strength: import_zod8.z.number().min(0).max(1).optional()
|
|
15404
|
+
}).strict(),
|
|
15405
|
+
import_zod8.z.object({
|
|
15406
|
+
mode: import_zod8.z.literal("animate-skeleton"),
|
|
15407
|
+
from: import_zod8.z.string().min(1),
|
|
15408
|
+
/** Manifest-relative; checked when the manifest is loaded, like every revision input. */
|
|
15409
|
+
keypointsFile: import_zod8.z.string().min(1),
|
|
15410
|
+
direction: CharacterDirectionSchema,
|
|
15411
|
+
description: import_zod8.z.string().min(1).optional(),
|
|
15412
|
+
skeletonTemplate: import_zod8.z.string().min(1).optional()
|
|
15413
|
+
}).strict()
|
|
15414
|
+
]).optional(),
|
|
15403
15415
|
/** A pose or outfit of an existing `character`/`objectPro` base or state. */
|
|
15404
15416
|
state: NewStateSchema.optional(),
|
|
15405
15417
|
/** A loop of an existing `character`/`objectPro` base or state. */
|
|
@@ -15903,41 +15915,118 @@ function openInEditor(file, editor = process.env.PIXELKILN_EDITOR) {
|
|
|
15903
15915
|
return openExternal(file, command);
|
|
15904
15916
|
}
|
|
15905
15917
|
|
|
15918
|
+
// src/gallery/skeleton.ts
|
|
15919
|
+
var import_node_fs25 = require("fs");
|
|
15920
|
+
var import_promises22 = require("fs/promises");
|
|
15921
|
+
var import_node_path29 = __toESM(require("path"), 1);
|
|
15922
|
+
var import_zod10 = require("zod");
|
|
15923
|
+
var SkeletonEstimateRequestSchema = import_zod10.z.object({
|
|
15924
|
+
project: import_zod10.z.string().min(1).optional(),
|
|
15925
|
+
styleId: import_zod10.z.string().min(1),
|
|
15926
|
+
assetId: import_zod10.z.string().min(1),
|
|
15927
|
+
frames: import_zod10.z.number().int().min(3).max(15).optional()
|
|
15928
|
+
}).strict();
|
|
15929
|
+
var SkeletonAnimationRequestSchema = import_zod10.z.object({
|
|
15930
|
+
action: import_zod10.z.literal("create-skeleton-animation"),
|
|
15931
|
+
project: import_zod10.z.string().min(1).optional(),
|
|
15932
|
+
expectedSha256: import_zod10.z.string().regex(/^[0-9a-f]{64}$/),
|
|
15933
|
+
styleId: import_zod10.z.string().min(1),
|
|
15934
|
+
/** The sprite the animation poses. */
|
|
15935
|
+
from: import_zod10.z.string().min(1),
|
|
15936
|
+
assetId: import_zod10.z.string().min(1).regex(/^[^/\\]+$/, "asset ids cannot contain slashes"),
|
|
15937
|
+
/** The motion, sent as PixelLab's `action`. */
|
|
15938
|
+
prompt: import_zod10.z.string().min(1),
|
|
15939
|
+
direction: CharacterDirectionSchema,
|
|
15940
|
+
description: import_zod10.z.string().min(1).optional(),
|
|
15941
|
+
skeletonTemplate: import_zod10.z.string().min(1).optional(),
|
|
15942
|
+
/** Manifest-relative JSON path inside the project. */
|
|
15943
|
+
keypointsFile: import_zod10.z.string().min(1),
|
|
15944
|
+
/** Poses to write to `keypointsFile`; omitted to use a file that already exists. */
|
|
15945
|
+
set: SkeletonSetSchema.optional(),
|
|
15946
|
+
/** Replace an existing `keypointsFile` with `set`. */
|
|
15947
|
+
overwrite: import_zod10.z.boolean().optional()
|
|
15948
|
+
}).strict();
|
|
15949
|
+
async function createSkeletonAnimation(manifestPath, request) {
|
|
15950
|
+
const root = import_node_path29.default.dirname(import_node_path29.default.resolve(manifestPath));
|
|
15951
|
+
const file = import_node_path29.default.resolve(root, request.keypointsFile);
|
|
15952
|
+
const relative = import_node_path29.default.relative(root, file);
|
|
15953
|
+
if (!request.keypointsFile.endsWith(".json") || relative.startsWith("..") || import_node_path29.default.isAbsolute(relative)) {
|
|
15954
|
+
throw new ManifestEditError("the keypoints file must be a .json path inside the project");
|
|
15955
|
+
}
|
|
15956
|
+
const existed = (0, import_node_fs25.existsSync)(file);
|
|
15957
|
+
let previous = null;
|
|
15958
|
+
if (request.set) {
|
|
15959
|
+
if (existed && !request.overwrite) {
|
|
15960
|
+
throw new ManifestEditError(`${request.keypointsFile} already exists; choose another path, or replace it`);
|
|
15961
|
+
}
|
|
15962
|
+
if (existed) previous = await (0, import_promises22.readFile)(file);
|
|
15963
|
+
await (0, import_promises22.mkdir)(import_node_path29.default.dirname(file), { recursive: true });
|
|
15964
|
+
await (0, import_promises22.writeFile)(file, JSON.stringify(request.set, null, 2) + "\n");
|
|
15965
|
+
} else if (!existed) {
|
|
15966
|
+
throw new ManifestEditError(`${request.keypointsFile} does not exist; paste poses or estimate them from the sprite`);
|
|
15967
|
+
}
|
|
15968
|
+
try {
|
|
15969
|
+
await applyManifestEdit(manifestPath, {
|
|
15970
|
+
action: "add-asset",
|
|
15971
|
+
assetId: request.assetId,
|
|
15972
|
+
expectedSha256: request.expectedSha256,
|
|
15973
|
+
asset: {
|
|
15974
|
+
prompt: request.prompt,
|
|
15975
|
+
styles: [request.styleId],
|
|
15976
|
+
revision: {
|
|
15977
|
+
mode: "animate-skeleton",
|
|
15978
|
+
from: request.from,
|
|
15979
|
+
keypointsFile: relative.split(import_node_path29.default.sep).join("/"),
|
|
15980
|
+
direction: request.direction,
|
|
15981
|
+
...request.description ? { description: request.description } : {},
|
|
15982
|
+
...request.skeletonTemplate ? { skeletonTemplate: request.skeletonTemplate } : {}
|
|
15983
|
+
}
|
|
15984
|
+
}
|
|
15985
|
+
});
|
|
15986
|
+
} catch (error) {
|
|
15987
|
+
if (request.set) {
|
|
15988
|
+
if (previous) await (0, import_promises22.writeFile)(file, previous);
|
|
15989
|
+
else await (0, import_promises22.rm)(file, { force: true });
|
|
15990
|
+
}
|
|
15991
|
+
throw error;
|
|
15992
|
+
}
|
|
15993
|
+
}
|
|
15994
|
+
|
|
15906
15995
|
// src/gallery/edit.ts
|
|
15907
|
-
var Base64 =
|
|
15908
|
-
var HandEditRequestSchema =
|
|
15909
|
-
|
|
15910
|
-
action:
|
|
15911
|
-
project:
|
|
15912
|
-
assetId:
|
|
15913
|
-
styleId:
|
|
15914
|
-
expectedSha256:
|
|
15996
|
+
var Base64 = import_zod11.z.string().regex(/^[A-Za-z0-9+/]*={0,2}$/).max(Math.ceil(MAX_HAND_EDIT_BYTES / 3) * 4);
|
|
15997
|
+
var HandEditRequestSchema = import_zod11.z.discriminatedUnion("action", [
|
|
15998
|
+
import_zod11.z.object({
|
|
15999
|
+
action: import_zod11.z.literal("start-edit"),
|
|
16000
|
+
project: import_zod11.z.string().min(1).optional(),
|
|
16001
|
+
assetId: import_zod11.z.string().min(1),
|
|
16002
|
+
styleId: import_zod11.z.string().min(1),
|
|
16003
|
+
expectedSha256: import_zod11.z.string().regex(/^[0-9a-f]{64}$/),
|
|
15915
16004
|
/** Also launch the author's editor on the file. */
|
|
15916
|
-
open:
|
|
16005
|
+
open: import_zod11.z.boolean().optional()
|
|
15917
16006
|
}).strict(),
|
|
15918
|
-
|
|
15919
|
-
action:
|
|
15920
|
-
project:
|
|
15921
|
-
assetId:
|
|
15922
|
-
styleId:
|
|
15923
|
-
expectedSha256:
|
|
16007
|
+
import_zod11.z.object({
|
|
16008
|
+
action: import_zod11.z.literal("detach-edit"),
|
|
16009
|
+
project: import_zod11.z.string().min(1).optional(),
|
|
16010
|
+
assetId: import_zod11.z.string().min(1),
|
|
16011
|
+
styleId: import_zod11.z.string().min(1),
|
|
16012
|
+
expectedSha256: import_zod11.z.string().regex(/^[0-9a-f]{64}$/)
|
|
15924
16013
|
}).strict(),
|
|
15925
16014
|
/** From the in-browser editor: the page holds the bytes, the server validates and writes. */
|
|
15926
|
-
|
|
15927
|
-
action:
|
|
15928
|
-
project:
|
|
15929
|
-
assetId:
|
|
15930
|
-
styleId:
|
|
15931
|
-
expectedSha256:
|
|
16015
|
+
import_zod11.z.object({
|
|
16016
|
+
action: import_zod11.z.literal("save-edit"),
|
|
16017
|
+
project: import_zod11.z.string().min(1).optional(),
|
|
16018
|
+
assetId: import_zod11.z.string().min(1),
|
|
16019
|
+
styleId: import_zod11.z.string().min(1),
|
|
16020
|
+
expectedSha256: import_zod11.z.string().regex(/^[0-9a-f]{64}$/),
|
|
15932
16021
|
/** The flattened image; for a frame set, `frames` instead. */
|
|
15933
16022
|
png: Base64.min(1).optional(),
|
|
15934
16023
|
/** Every frame of a set, tagged with the role it was opened under (null for one added in the editor). */
|
|
15935
|
-
frames:
|
|
16024
|
+
frames: import_zod11.z.array(import_zod11.z.object({ role: import_zod11.z.string().min(1).nullable(), png: Base64.min(1) }).strict()).min(1).max(256).optional(),
|
|
15936
16025
|
/** The editor's layered project file, kept beside the edit. */
|
|
15937
16026
|
pxo: Base64.optional(),
|
|
15938
16027
|
/** Editor identity from the bridge's ready message. */
|
|
15939
|
-
editor:
|
|
15940
|
-
protocol:
|
|
16028
|
+
editor: import_zod11.z.string().min(1).max(200),
|
|
16029
|
+
protocol: import_zod11.z.number().int().positive()
|
|
15941
16030
|
}).strict()
|
|
15942
16031
|
]);
|
|
15943
16032
|
function createGalleryEditHandler(opts) {
|
|
@@ -15978,6 +16067,23 @@ function createGalleryEditHandler(opts) {
|
|
|
15978
16067
|
}
|
|
15979
16068
|
return opts.reload();
|
|
15980
16069
|
}
|
|
16070
|
+
const skeleton = SkeletonAnimationRequestSchema.safeParse(body);
|
|
16071
|
+
if (skeleton.success) {
|
|
16072
|
+
let manifestPath2;
|
|
16073
|
+
try {
|
|
16074
|
+
manifestPath2 = await opts.manifestFor(skeleton.data.project);
|
|
16075
|
+
} catch (error) {
|
|
16076
|
+
throw new ManifestEditError(error instanceof Error ? error.message : String(error));
|
|
16077
|
+
}
|
|
16078
|
+
await createSkeletonAnimation(manifestPath2, skeleton.data);
|
|
16079
|
+
log(` manifest edited: added skeleton animation ${skeleton.data.assetId} (${skeleton.data.keypointsFile})`);
|
|
16080
|
+
return opts.reload();
|
|
16081
|
+
}
|
|
16082
|
+
if (body?.action === "create-skeleton-animation") {
|
|
16083
|
+
throw new ManifestEditError(
|
|
16084
|
+
"invalid skeleton animation: " + skeleton.error.issues.slice(0, 3).map((issue) => `${issue.path.join(".")}: ${issue.message}`).join("; ")
|
|
16085
|
+
);
|
|
16086
|
+
}
|
|
15981
16087
|
const parsed = ManifestEditSchema.safeParse(body);
|
|
15982
16088
|
if (!parsed.success) {
|
|
15983
16089
|
throw new ManifestEditError(
|
|
@@ -15992,7 +16098,7 @@ function createGalleryEditHandler(opts) {
|
|
|
15992
16098
|
}
|
|
15993
16099
|
const result = await applyManifestEdit(manifestPath, parsed.data);
|
|
15994
16100
|
if (result.changed) {
|
|
15995
|
-
const relative =
|
|
16101
|
+
const relative = import_node_path30.default.relative(process.cwd(), result.manifestPath);
|
|
15996
16102
|
const shown = !relative || relative.startsWith("..") ? result.manifestPath : relative;
|
|
15997
16103
|
const subject = parsed.data.action === "patch-style" ? `style ${parsed.data.styleId}` : parsed.data.action === "set-source" || parsed.data.action === "clear-source" ? `${parsed.data.styleId}/${parsed.data.assetId} source` : parsed.data.assetId;
|
|
15998
16104
|
log(` manifest edited: ${parsed.data.action === "add-asset" ? "added" : "changed"} ${subject} in ${shown}`);
|
|
@@ -16004,7 +16110,7 @@ function createGalleryEditHandler(opts) {
|
|
|
16004
16110
|
// src/gallery/snapshot.ts
|
|
16005
16111
|
var PROJECT_FILE_TYPE = "application/octet-stream";
|
|
16006
16112
|
function galleryMediaId(absolutePath) {
|
|
16007
|
-
return (0, import_node_crypto6.createHash)("sha256").update(
|
|
16113
|
+
return (0, import_node_crypto6.createHash)("sha256").update(import_node_path31.default.resolve(absolutePath)).digest("hex").slice(0, 24);
|
|
16008
16114
|
}
|
|
16009
16115
|
function galleryMediaRoute(id) {
|
|
16010
16116
|
return `/media/${id}`;
|
|
@@ -16023,9 +16129,9 @@ function describeHistory(media, entry, cacheDir) {
|
|
|
16023
16129
|
return (entry.history ?? []).map((generation, i) => {
|
|
16024
16130
|
const outputs = generation.outputs.map((output) => {
|
|
16025
16131
|
const mediaType = output.mediaType ?? MediaType.PNG;
|
|
16026
|
-
const file =
|
|
16132
|
+
const file = import_node_path31.default.join(cacheDir, cacheFileName(output.sha256, mediaType));
|
|
16027
16133
|
let url = null;
|
|
16028
|
-
if ((0,
|
|
16134
|
+
if ((0, import_node_fs26.existsSync)(file)) {
|
|
16029
16135
|
const id = galleryMediaId(file);
|
|
16030
16136
|
media.set(id, { path: file, contentType: mediaType });
|
|
16031
16137
|
url = `${galleryMediaRoute(id)}?v=${output.sha256.slice(0, 24)}`;
|
|
@@ -16057,7 +16163,7 @@ function describeHistory(media, entry, cacheDir) {
|
|
|
16057
16163
|
var MAX_COMPARED_BYTES = 8 * 1024 * 1024;
|
|
16058
16164
|
async function samePixels(a, b) {
|
|
16059
16165
|
try {
|
|
16060
|
-
const [left, right] = await Promise.all([(0,
|
|
16166
|
+
const [left, right] = await Promise.all([(0, import_promises23.readFile)(a), (0, import_promises23.readFile)(b)]);
|
|
16061
16167
|
if (left.length > MAX_COMPARED_BYTES || right.length > MAX_COMPARED_BYTES) return false;
|
|
16062
16168
|
const x = decodePng(left);
|
|
16063
16169
|
const y = decodePng(right);
|
|
@@ -16123,7 +16229,7 @@ function describeCharacter(spec, entry) {
|
|
|
16123
16229
|
}
|
|
16124
16230
|
async function fileInfo(absolutePath) {
|
|
16125
16231
|
try {
|
|
16126
|
-
const info = await (0,
|
|
16232
|
+
const info = await (0, import_promises23.stat)(absolutePath);
|
|
16127
16233
|
return info.isFile() ? { bytes: info.size, modifiedAt: info.mtime.toISOString() } : null;
|
|
16128
16234
|
} catch {
|
|
16129
16235
|
return null;
|
|
@@ -16155,10 +16261,10 @@ async function describeOutput(media, root, absolutePath, recorded = {}) {
|
|
|
16155
16261
|
async function describeQuality(media, root, inspection) {
|
|
16156
16262
|
const outputPaths = inspection.outputs ?? [inspection.output];
|
|
16157
16263
|
const outputs = await Promise.all(
|
|
16158
|
-
outputPaths.map((file) => describeOutput(media, root,
|
|
16264
|
+
outputPaths.map((file) => describeOutput(media, root, import_node_path31.default.resolve(root, file)))
|
|
16159
16265
|
);
|
|
16160
|
-
const recordPath =
|
|
16161
|
-
const recordExists = (0,
|
|
16266
|
+
const recordPath = import_node_path31.default.resolve(root, inspection.record);
|
|
16267
|
+
const recordExists = (0, import_node_fs26.existsSync)(recordPath);
|
|
16162
16268
|
let options = null;
|
|
16163
16269
|
let check = null;
|
|
16164
16270
|
if (recordExists) {
|
|
@@ -16177,7 +16283,7 @@ async function describeQuality(media, root, inspection) {
|
|
|
16177
16283
|
return {
|
|
16178
16284
|
state: inspection.state,
|
|
16179
16285
|
reason: inspection.reason,
|
|
16180
|
-
output: portableOutputPath(
|
|
16286
|
+
output: portableOutputPath(import_node_path31.default.resolve(root, inspection.output), root),
|
|
16181
16287
|
record: portableOutputPath(recordPath, root),
|
|
16182
16288
|
recordExists,
|
|
16183
16289
|
outputs,
|
|
@@ -16215,9 +16321,9 @@ async function buildGallerySnapshot(opts) {
|
|
|
16215
16321
|
const media = /* @__PURE__ */ new Map();
|
|
16216
16322
|
const plan = await buildPlan(specs, lock);
|
|
16217
16323
|
const items = [];
|
|
16218
|
-
const manifestText = await (0,
|
|
16324
|
+
const manifestText = await (0, import_promises23.readFile)(loaded.path, "utf8");
|
|
16219
16325
|
const manifestSha256 = sha256(manifestText);
|
|
16220
|
-
const cacheDir =
|
|
16326
|
+
const cacheDir = import_node_path31.default.resolve(import_node_path31.default.dirname(opts.lockPath), ".pixelkiln", "cache");
|
|
16221
16327
|
let rawStyles = {};
|
|
16222
16328
|
try {
|
|
16223
16329
|
const raw = JSON.parse(manifestText);
|
|
@@ -16241,12 +16347,12 @@ async function buildGallerySnapshot(opts) {
|
|
|
16241
16347
|
)
|
|
16242
16348
|
);
|
|
16243
16349
|
if (spec.source) {
|
|
16244
|
-
const editPath =
|
|
16350
|
+
const editPath = import_node_path31.default.resolve(root, spec.source);
|
|
16245
16351
|
const memberCount = sourceIsStem(spec.source, entry, root) ? entry.outputs.length : 1;
|
|
16246
16352
|
const editShas = await Promise.all(
|
|
16247
16353
|
Array.from({ length: memberCount }, async (_, index) => {
|
|
16248
16354
|
const file = sourceOutputPath(spec.source, entry, index, root);
|
|
16249
|
-
return { file, sha256: (0,
|
|
16355
|
+
return { file, sha256: (0, import_node_fs26.existsSync)(file) ? await sha256File(file) : null };
|
|
16250
16356
|
})
|
|
16251
16357
|
);
|
|
16252
16358
|
edits = await Promise.all(editShas.map(({ file, sha256: editSha }) => describeOutput(media, root, file, { sha256: editSha })));
|
|
@@ -16254,7 +16360,7 @@ async function buildGallerySnapshot(opts) {
|
|
|
16254
16360
|
const companion = await readHandEditCompanion(editPath);
|
|
16255
16361
|
if (companion) {
|
|
16256
16362
|
const projectPath = handEditProjectPath(editPath);
|
|
16257
|
-
const project = companion.project && (0,
|
|
16363
|
+
const project = companion.project && (0, import_node_fs26.existsSync)(projectPath) ? await fileInfo(projectPath) : null;
|
|
16258
16364
|
let projectUrl = null;
|
|
16259
16365
|
if (project) {
|
|
16260
16366
|
const id = galleryMediaId(projectPath);
|
|
@@ -16290,8 +16396,8 @@ async function buildGallerySnapshot(opts) {
|
|
|
16290
16396
|
editStatus = edits.some((member) => !member.exists) ? "missing" : editChanged.every((changed) => !changed) ? "same" : regenerated ? "regenerated-since" : "edited";
|
|
16291
16397
|
}
|
|
16292
16398
|
} else if (spec.source) {
|
|
16293
|
-
outputs = [await describeOutput(media, root,
|
|
16294
|
-
} else if (!entry && (0,
|
|
16399
|
+
outputs = [await describeOutput(media, root, import_node_path31.default.resolve(root, spec.source))];
|
|
16400
|
+
} else if (!entry && (0, import_node_fs26.existsSync)(spec.outFile)) {
|
|
16295
16401
|
outputs = [await describeOutput(media, root, spec.outFile)];
|
|
16296
16402
|
} else {
|
|
16297
16403
|
outputs = [];
|
|
@@ -16332,6 +16438,10 @@ async function buildGallerySnapshot(opts) {
|
|
|
16332
16438
|
revision: entry?.revision ?? null,
|
|
16333
16439
|
revisionParentKey: spec.revision ? lockKey(spec.styleId, spec.revision.sourceAssetId) : entry?.revision ? lockKey(spec.styleId, entry.revision.sourceAssetId) : null,
|
|
16334
16440
|
mirrorOfKey: spec.mirror ? lockKey(spec.styleId, spec.mirror.sourceAssetId) : entry?.mirror ? lockKey(spec.styleId, entry.mirror.sourceAssetId) : null,
|
|
16441
|
+
skeleton: spec.revision?.mode === "animate-skeleton" && spec.revision.keypointsFile ? {
|
|
16442
|
+
keypointsFile: portableOutputPath(spec.revision.keypointsFile, root),
|
|
16443
|
+
set: spec.revision.skeleton ?? null
|
|
16444
|
+
} : null,
|
|
16335
16445
|
character: describeCharacter(spec, entry),
|
|
16336
16446
|
outputs,
|
|
16337
16447
|
quality,
|
|
@@ -16396,6 +16506,7 @@ async function buildGallerySnapshot(opts) {
|
|
|
16396
16506
|
revision: entry.revision,
|
|
16397
16507
|
revisionParentKey: entry.revision ? lockKey(entry.styleId, entry.revision.sourceAssetId) : null,
|
|
16398
16508
|
mirrorOfKey: entry.mirror ? lockKey(entry.styleId, entry.mirror.sourceAssetId) : null,
|
|
16509
|
+
skeleton: null,
|
|
16399
16510
|
character: describeCharacter(void 0, entry),
|
|
16400
16511
|
outputs,
|
|
16401
16512
|
quality: null,
|
|
@@ -16486,7 +16597,7 @@ async function buildGallerySnapshot(opts) {
|
|
|
16486
16597
|
id: loaded.manifest.name,
|
|
16487
16598
|
name: loaded.manifest.name,
|
|
16488
16599
|
manifest: loaded.path,
|
|
16489
|
-
lock:
|
|
16600
|
+
lock: import_node_path31.default.resolve(opts.lockPath),
|
|
16490
16601
|
root,
|
|
16491
16602
|
provider: loaded.manifest.provider,
|
|
16492
16603
|
account: null,
|
|
@@ -16512,7 +16623,7 @@ async function buildGallerySnapshot(opts) {
|
|
|
16512
16623
|
return { snapshot, media };
|
|
16513
16624
|
}
|
|
16514
16625
|
async function buildWorkspaceGallerySnapshot(opts) {
|
|
16515
|
-
const dir =
|
|
16626
|
+
const dir = import_node_path31.default.dirname(import_node_path31.default.resolve(opts.workspacePath));
|
|
16516
16627
|
const media = /* @__PURE__ */ new Map();
|
|
16517
16628
|
const projects = [];
|
|
16518
16629
|
const items = [];
|
|
@@ -16555,11 +16666,11 @@ async function buildWorkspaceGallerySnapshot(opts) {
|
|
|
16555
16666
|
id: project.id,
|
|
16556
16667
|
name: loaded.manifest.name,
|
|
16557
16668
|
manifest: manifestPath,
|
|
16558
|
-
lock:
|
|
16669
|
+
lock: import_node_path31.default.resolve(lockPath),
|
|
16559
16670
|
root: loaded.root,
|
|
16560
16671
|
provider: loaded.manifest.provider,
|
|
16561
16672
|
account: project.account ?? null,
|
|
16562
|
-
manifestSha256: manifestSha256 ?? sha256(await (0,
|
|
16673
|
+
manifestSha256: manifestSha256 ?? sha256(await (0, import_promises23.readFile)(manifestPath, "utf8")),
|
|
16563
16674
|
historyLimit: historyLimit(loaded.manifest),
|
|
16564
16675
|
entries: Object.keys(lock.entries).length,
|
|
16565
16676
|
items: projectItems.length,
|
|
@@ -16571,8 +16682,8 @@ async function buildWorkspaceGallerySnapshot(opts) {
|
|
|
16571
16682
|
id: project.id,
|
|
16572
16683
|
name: project.id,
|
|
16573
16684
|
manifest: manifestPath,
|
|
16574
|
-
lock:
|
|
16575
|
-
root:
|
|
16685
|
+
lock: import_node_path31.default.resolve(lockPath),
|
|
16686
|
+
root: import_node_path31.default.dirname(manifestPath),
|
|
16576
16687
|
provider: project.provider,
|
|
16577
16688
|
account: project.account ?? null,
|
|
16578
16689
|
manifestSha256: null,
|
|
@@ -16599,7 +16710,7 @@ async function buildWorkspaceGallerySnapshot(opts) {
|
|
|
16599
16710
|
version: 1,
|
|
16600
16711
|
generatedAt: (opts.now ?? (() => /* @__PURE__ */ new Date()))().toISOString(),
|
|
16601
16712
|
project: null,
|
|
16602
|
-
workspace: { path:
|
|
16713
|
+
workspace: { path: import_node_path31.default.resolve(opts.workspacePath), dir, projects },
|
|
16603
16714
|
filter,
|
|
16604
16715
|
totals: {
|
|
16605
16716
|
entries: projects.reduce((sum, project) => sum + project.entries, 0),
|
|
@@ -16616,13 +16727,13 @@ async function buildWorkspaceGallerySnapshot(opts) {
|
|
|
16616
16727
|
}
|
|
16617
16728
|
|
|
16618
16729
|
// src/gallery/page.ts
|
|
16619
|
-
var
|
|
16730
|
+
var import_node_fs27 = require("fs");
|
|
16620
16731
|
var CLIENT_DIR = new URL("./client/", importMetaUrl);
|
|
16621
16732
|
var client;
|
|
16622
16733
|
function clientAssets() {
|
|
16623
16734
|
client ??= {
|
|
16624
|
-
css: (0,
|
|
16625
|
-
js: (0,
|
|
16735
|
+
css: (0, import_node_fs27.readFileSync)(new URL("gallery.css", CLIENT_DIR), "utf8"),
|
|
16736
|
+
js: (0, import_node_fs27.readFileSync)(new URL("gallery.js", CLIENT_DIR), "utf8")
|
|
16626
16737
|
};
|
|
16627
16738
|
return client;
|
|
16628
16739
|
}
|
|
@@ -16748,10 +16859,10 @@ var EDITOR_RELEASE_BASE = "https://github.com/gfargo/pixelkiln/releases/download
|
|
|
16748
16859
|
|
|
16749
16860
|
// src/editor/install.ts
|
|
16750
16861
|
var import_node_crypto7 = require("crypto");
|
|
16751
|
-
var
|
|
16752
|
-
var
|
|
16862
|
+
var import_node_fs28 = require("fs");
|
|
16863
|
+
var import_promises24 = require("fs/promises");
|
|
16753
16864
|
var import_node_os3 = __toESM(require("os"), 1);
|
|
16754
|
-
var
|
|
16865
|
+
var import_node_path32 = __toESM(require("path"), 1);
|
|
16755
16866
|
var EditorInstallError = class extends Error {
|
|
16756
16867
|
constructor(message6, file) {
|
|
16757
16868
|
super(message6);
|
|
@@ -16762,15 +16873,15 @@ var EditorInstallError = class extends Error {
|
|
|
16762
16873
|
};
|
|
16763
16874
|
function editorToolsRoot() {
|
|
16764
16875
|
const override = process.env.PIXELKILN_TOOLS_DIR?.trim();
|
|
16765
|
-
if (override) return
|
|
16766
|
-
if (process.platform === "darwin") return
|
|
16876
|
+
if (override) return import_node_path32.default.resolve(override);
|
|
16877
|
+
if (process.platform === "darwin") return import_node_path32.default.join(import_node_os3.default.homedir(), "Library", "Caches", "pixelkiln", "tools");
|
|
16767
16878
|
if (process.platform === "win32") {
|
|
16768
|
-
return
|
|
16879
|
+
return import_node_path32.default.join(process.env.LOCALAPPDATA ?? import_node_path32.default.join(import_node_os3.default.homedir(), "AppData", "Local"), "pixelkiln", "tools");
|
|
16769
16880
|
}
|
|
16770
|
-
return
|
|
16881
|
+
return import_node_path32.default.join(process.env.XDG_CACHE_HOME ?? import_node_path32.default.join(import_node_os3.default.homedir(), ".cache"), "pixelkiln", "tools");
|
|
16771
16882
|
}
|
|
16772
16883
|
function editorDir(pin = EDITOR_PIN, root = editorToolsRoot()) {
|
|
16773
|
-
return
|
|
16884
|
+
return import_node_path32.default.join(root, "pixelorama", pin.release ?? "unpublished");
|
|
16774
16885
|
}
|
|
16775
16886
|
function editorBaseUrl(pin = EDITOR_PIN) {
|
|
16776
16887
|
const override = process.env.PIXELKILN_EDITOR_URL?.trim();
|
|
@@ -16780,7 +16891,7 @@ function editorBaseUrl(pin = EDITOR_PIN) {
|
|
|
16780
16891
|
}
|
|
16781
16892
|
async function sha256Of(file) {
|
|
16782
16893
|
try {
|
|
16783
|
-
return (0, import_node_crypto7.createHash)("sha256").update(await (0,
|
|
16894
|
+
return (0, import_node_crypto7.createHash)("sha256").update(await (0, import_promises24.readFile)(file)).digest("hex");
|
|
16784
16895
|
} catch {
|
|
16785
16896
|
return null;
|
|
16786
16897
|
}
|
|
@@ -16793,8 +16904,8 @@ async function editorStatus(opts = {}) {
|
|
|
16793
16904
|
let totalBytes = 0;
|
|
16794
16905
|
for (const [name, expected] of Object.entries(pin.files)) {
|
|
16795
16906
|
totalBytes += expected.bytes;
|
|
16796
|
-
const file =
|
|
16797
|
-
const info = (0,
|
|
16907
|
+
const file = import_node_path32.default.join(dir, name);
|
|
16908
|
+
const info = (0, import_node_fs28.existsSync)(file) ? await (0, import_promises24.stat)(file).catch(() => null) : null;
|
|
16798
16909
|
if (info && info.size === expected.bytes && await sha256Of(file) === expected.sha256) {
|
|
16799
16910
|
installedBytes += expected.bytes;
|
|
16800
16911
|
} else {
|
|
@@ -16823,7 +16934,7 @@ async function installEditor(opts = {}) {
|
|
|
16823
16934
|
);
|
|
16824
16935
|
}
|
|
16825
16936
|
if (!baseUrl) throw new EditorInstallError("no download location for the editor build");
|
|
16826
|
-
await (0,
|
|
16937
|
+
await (0, import_promises24.mkdir)(dir, { recursive: true });
|
|
16827
16938
|
const downloaded = [];
|
|
16828
16939
|
const skipped = Object.keys(pin.files).filter((name) => !before.missing.includes(name));
|
|
16829
16940
|
let fetchedBytes = before.installedBytes;
|
|
@@ -16851,13 +16962,13 @@ async function installEditor(opts = {}) {
|
|
|
16851
16962
|
if (digest2 !== expected.sha256) {
|
|
16852
16963
|
throw new EditorInstallError(`${name}: sha256 ${digest2.slice(0, 12)}\u2026 does not match the pinned ${expected.sha256.slice(0, 12)}\u2026`, name);
|
|
16853
16964
|
}
|
|
16854
|
-
const final =
|
|
16965
|
+
const final = import_node_path32.default.join(dir, name);
|
|
16855
16966
|
const tmp = `${final}.${process.pid}.part`;
|
|
16856
16967
|
try {
|
|
16857
|
-
await (0,
|
|
16858
|
-
await (0,
|
|
16968
|
+
await (0, import_promises24.writeFile)(tmp, bytes);
|
|
16969
|
+
await (0, import_promises24.rename)(tmp, final);
|
|
16859
16970
|
} finally {
|
|
16860
|
-
await (0,
|
|
16971
|
+
await (0, import_promises24.rm)(tmp, { force: true });
|
|
16861
16972
|
}
|
|
16862
16973
|
fetchedBytes += bytes.length;
|
|
16863
16974
|
downloaded.push(name);
|
|
@@ -16897,12 +17008,12 @@ var EDITOR_CONTENT_TYPES = {
|
|
|
16897
17008
|
".json": "application/json; charset=utf-8"
|
|
16898
17009
|
};
|
|
16899
17010
|
function editorContentType(name) {
|
|
16900
|
-
return EDITOR_CONTENT_TYPES[
|
|
17011
|
+
return EDITOR_CONTENT_TYPES[import_node_path32.default.extname(name).toLowerCase()] ?? null;
|
|
16901
17012
|
}
|
|
16902
17013
|
|
|
16903
17014
|
// src/gallery/editor.ts
|
|
16904
|
-
var
|
|
16905
|
-
var
|
|
17015
|
+
var import_promises25 = require("fs/promises");
|
|
17016
|
+
var import_node_path33 = __toESM(require("path"), 1);
|
|
16906
17017
|
function editorRoute(release, name = "index.html") {
|
|
16907
17018
|
return `/editor/${encodeURIComponent(release)}/${encodeURIComponent(name)}`;
|
|
16908
17019
|
}
|
|
@@ -16975,7 +17086,7 @@ function createGalleryEditorHandlers(opts = {}) {
|
|
|
16975
17086
|
if (!status.installed) return null;
|
|
16976
17087
|
let bytes;
|
|
16977
17088
|
try {
|
|
16978
|
-
bytes = await (0,
|
|
17089
|
+
bytes = await (0, import_promises25.readFile)(import_node_path33.default.join(dir, name));
|
|
16979
17090
|
} catch {
|
|
16980
17091
|
verified = null;
|
|
16981
17092
|
return null;
|
|
@@ -16992,7 +17103,7 @@ function createGalleryEditorHandlers(opts = {}) {
|
|
|
16992
17103
|
// src/gallery/server.ts
|
|
16993
17104
|
var import_node_crypto8 = require("crypto");
|
|
16994
17105
|
var import_node_http2 = require("http");
|
|
16995
|
-
var
|
|
17106
|
+
var import_promises26 = require("fs/promises");
|
|
16996
17107
|
var MAX_EDIT_BYTES = 48 * 1024 * 1024;
|
|
16997
17108
|
var EDITOR_CSP = [
|
|
16998
17109
|
"default-src 'self'",
|
|
@@ -17020,7 +17131,7 @@ async function readJsonBody(req) {
|
|
|
17020
17131
|
async function serveGallery(opts) {
|
|
17021
17132
|
const log = opts.onProgress ?? (() => {
|
|
17022
17133
|
});
|
|
17023
|
-
const session = opts.edit || opts.generate || opts.editor ? (0, import_node_crypto8.randomBytes)(16).toString("hex") : null;
|
|
17134
|
+
const session = opts.edit || opts.generate || opts.editor || opts.skeleton ? (0, import_node_crypto8.randomBytes)(16).toString("hex") : null;
|
|
17024
17135
|
let media = /* @__PURE__ */ new Map();
|
|
17025
17136
|
let loading = null;
|
|
17026
17137
|
const reviewAssets = /* @__PURE__ */ new Map();
|
|
@@ -17095,6 +17206,17 @@ async function serveGallery(opts) {
|
|
|
17095
17206
|
}
|
|
17096
17207
|
return;
|
|
17097
17208
|
}
|
|
17209
|
+
if (req.method === "POST" && url2.pathname === "/api/skeleton/estimate") {
|
|
17210
|
+
if (!opts.skeleton || !session) return fail(405, "skeleton estimates need the gallery started with --edit");
|
|
17211
|
+
try {
|
|
17212
|
+
const body = await guardedBody("skeleton estimates");
|
|
17213
|
+
if (body === void 0) return;
|
|
17214
|
+
json(200, await opts.skeleton.estimate(body));
|
|
17215
|
+
} catch (err) {
|
|
17216
|
+
reportError("skeleton estimate", err);
|
|
17217
|
+
}
|
|
17218
|
+
return;
|
|
17219
|
+
}
|
|
17098
17220
|
if (req.method === "POST" && url2.pathname === "/api/editor/install") {
|
|
17099
17221
|
if (!opts.editor || !session) return fail(405, "the in-browser editor is off for this gallery");
|
|
17100
17222
|
try {
|
|
@@ -17151,7 +17273,7 @@ async function serveGallery(opts) {
|
|
|
17151
17273
|
const asset = reviewAssets.get(jobId)?.get(url2.pathname);
|
|
17152
17274
|
if (!asset) return fail(404, "no such review asset");
|
|
17153
17275
|
try {
|
|
17154
|
-
const bytes = await (0,
|
|
17276
|
+
const bytes = await (0, import_promises26.readFile)(asset.path);
|
|
17155
17277
|
res.writeHead(200, {
|
|
17156
17278
|
"Content-Type": asset.contentType,
|
|
17157
17279
|
"Cache-Control": "no-store",
|
|
@@ -17227,7 +17349,7 @@ async function serveGallery(opts) {
|
|
|
17227
17349
|
if (!asset) return fail(404, "no such gallery media");
|
|
17228
17350
|
let bytes;
|
|
17229
17351
|
try {
|
|
17230
|
-
bytes = await (0,
|
|
17352
|
+
bytes = await (0, import_promises26.readFile)(asset.path);
|
|
17231
17353
|
} catch {
|
|
17232
17354
|
return fail(404, "gallery media is no longer on disk; refresh the page");
|
|
17233
17355
|
}
|
|
@@ -17271,27 +17393,27 @@ async function serveGallery(opts) {
|
|
|
17271
17393
|
|
|
17272
17394
|
// src/gallery/generate.ts
|
|
17273
17395
|
var import_node_crypto9 = require("crypto");
|
|
17274
|
-
var
|
|
17275
|
-
var GenerateRequestSchema =
|
|
17276
|
-
project:
|
|
17277
|
-
keys:
|
|
17396
|
+
var import_zod12 = require("zod");
|
|
17397
|
+
var GenerateRequestSchema = import_zod12.z.object({
|
|
17398
|
+
project: import_zod12.z.string().min(1).optional(),
|
|
17399
|
+
keys: import_zod12.z.array(import_zod12.z.string().min(1)).min(1).max(500),
|
|
17278
17400
|
/**
|
|
17279
17401
|
* Regenerate work that is up to date, as `gen --force` does, and let the
|
|
17280
17402
|
* fetch replace a locally modified or untracked file. With `restore`, put
|
|
17281
17403
|
* the recorded bytes back over a modified file.
|
|
17282
17404
|
*/
|
|
17283
|
-
force:
|
|
17405
|
+
force: import_zod12.z.boolean().optional(),
|
|
17284
17406
|
/** Advance in-flight, review, or selected work without submitting anything. */
|
|
17285
|
-
resume:
|
|
17407
|
+
resume: import_zod12.z.boolean().optional(),
|
|
17286
17408
|
/** Re-download downloaded outputs and replace files whose object changed upstream. */
|
|
17287
|
-
refresh:
|
|
17409
|
+
refresh: import_zod12.z.boolean().optional(),
|
|
17288
17410
|
/** Put recorded outputs back on disk from the cache or the provider, without generating. */
|
|
17289
|
-
restore:
|
|
17411
|
+
restore: import_zod12.z.boolean().optional(),
|
|
17290
17412
|
/**
|
|
17291
17413
|
* Bring a replaced generation back: 1-based position in the record's
|
|
17292
17414
|
* history (newest first) or an output hash prefix. One key at a time.
|
|
17293
17415
|
*/
|
|
17294
|
-
revert:
|
|
17416
|
+
revert: import_zod12.z.string().min(1).max(64).optional()
|
|
17295
17417
|
}).strict().refine((request) => [request.resume, request.refresh, request.restore, request.revert !== void 0].filter(Boolean).length <= 1, {
|
|
17296
17418
|
message: "resume, refresh, restore, and revert are exclusive"
|
|
17297
17419
|
}).refine((request) => request.revert === void 0 || request.keys.length === 1, { message: "revert takes one key" });
|
|
@@ -17601,19 +17723,19 @@ function createGenerateHandlers(opts) {
|
|
|
17601
17723
|
}
|
|
17602
17724
|
|
|
17603
17725
|
// src/pipeline/salvage.ts
|
|
17604
|
-
var
|
|
17605
|
-
var
|
|
17606
|
-
var
|
|
17726
|
+
var import_promises27 = require("fs/promises");
|
|
17727
|
+
var import_node_fs29 = require("fs");
|
|
17728
|
+
var import_node_path34 = __toESM(require("path"), 1);
|
|
17607
17729
|
function providerClaimId(provider, objectId) {
|
|
17608
17730
|
return `${encodeURIComponent(provider)}:${encodeURIComponent(objectId)}`;
|
|
17609
17731
|
}
|
|
17610
17732
|
async function loadClaims(lockPaths, opts = {}) {
|
|
17611
17733
|
const claimed = /* @__PURE__ */ new Set();
|
|
17612
17734
|
for (const p of lockPaths) {
|
|
17613
|
-
if (!(0,
|
|
17735
|
+
if (!(0, import_node_fs29.existsSync)(p)) throw new Error(`Claim lockfile not found: ${p}`);
|
|
17614
17736
|
let parsed;
|
|
17615
17737
|
try {
|
|
17616
|
-
parsed = parseLock(JSON.parse(await (0,
|
|
17738
|
+
parsed = parseLock(JSON.parse(await (0, import_promises27.readFile)(p, "utf8")));
|
|
17617
17739
|
} catch {
|
|
17618
17740
|
throw new Error(`Claim lockfile is malformed: ${p}`);
|
|
17619
17741
|
}
|
|
@@ -17717,20 +17839,20 @@ function matchOrphanStyle(prompt, manifest) {
|
|
|
17717
17839
|
return matchStyleByPattern(prompt, manifest);
|
|
17718
17840
|
}
|
|
17719
17841
|
async function loadSiblingManifests(ownManifestPath, workspaceManifestPaths, claimPaths) {
|
|
17720
|
-
const own =
|
|
17842
|
+
const own = import_node_path34.default.resolve(ownManifestPath);
|
|
17721
17843
|
const siblingManifestPaths = [
|
|
17722
17844
|
.../* @__PURE__ */ new Set([
|
|
17723
17845
|
...workspaceManifestPaths,
|
|
17724
|
-
...claimPaths.map((c) =>
|
|
17846
|
+
...claimPaths.map((c) => import_node_path34.default.join(import_node_path34.default.dirname(import_node_path34.default.resolve(c)), "pixelkiln.manifest.json"))
|
|
17725
17847
|
])
|
|
17726
17848
|
];
|
|
17727
17849
|
const siblings = [];
|
|
17728
17850
|
for (const siblingManifestPath of siblingManifestPaths) {
|
|
17729
|
-
if (
|
|
17730
|
-
if (!(0,
|
|
17851
|
+
if (import_node_path34.default.resolve(siblingManifestPath) === own) continue;
|
|
17852
|
+
if (!(0, import_node_fs29.existsSync)(siblingManifestPath)) continue;
|
|
17731
17853
|
try {
|
|
17732
17854
|
const { manifest } = await loadManifest(siblingManifestPath);
|
|
17733
|
-
siblings.push({ label:
|
|
17855
|
+
siblings.push({ label: import_node_path34.default.basename(import_node_path34.default.dirname(siblingManifestPath)), manifest });
|
|
17734
17856
|
} catch {
|
|
17735
17857
|
}
|
|
17736
17858
|
}
|
|
@@ -17850,8 +17972,8 @@ async function applyTags(provider, decisions, existing, opts = {}) {
|
|
|
17850
17972
|
}
|
|
17851
17973
|
|
|
17852
17974
|
// src/pick/salvage-server.ts
|
|
17853
|
-
var
|
|
17854
|
-
var
|
|
17975
|
+
var import_promises28 = require("fs/promises");
|
|
17976
|
+
var import_node_path35 = __toESM(require("path"), 1);
|
|
17855
17977
|
|
|
17856
17978
|
// src/pick/salvage-sheet.ts
|
|
17857
17979
|
var escapeHtml3 = (s) => s.replace(/[&<>"']/g, (c) => ({ "&": "&", "<": "<", ">": ">", '"': """, "'": "'" })[c]);
|
|
@@ -18017,7 +18139,7 @@ async function runSalvage(provider, orphans, ctx, opts = {}) {
|
|
|
18017
18139
|
});
|
|
18018
18140
|
const html = renderSalvageSheet(orphans, {
|
|
18019
18141
|
styleId: ctx.styleId,
|
|
18020
|
-
importDir:
|
|
18142
|
+
importDir: import_node_path35.default.relative(process.cwd(), ctx.importDir) || "."
|
|
18021
18143
|
});
|
|
18022
18144
|
const byId = new Map(orphans.map((o) => [o.id, o]));
|
|
18023
18145
|
const existingTags = new Map(orphans.map((o) => [o.id, o.tags]));
|
|
@@ -18073,10 +18195,10 @@ async function runSalvage(provider, orphans, ctx, opts = {}) {
|
|
|
18073
18195
|
if (!buf.subarray(0, 8).equals(PNG_SIGNATURE2)) throw new Error("not a PNG");
|
|
18074
18196
|
decodePng(buf);
|
|
18075
18197
|
const assetId = idFromPrompt(orphan.prompt, taken);
|
|
18076
|
-
const rel =
|
|
18077
|
-
const outFile =
|
|
18078
|
-
await (0,
|
|
18079
|
-
await (0,
|
|
18198
|
+
const rel = import_node_path35.default.join("_salvaged", `${assetId}.png`);
|
|
18199
|
+
const outFile = import_node_path35.default.resolve(ctx.importDir, rel);
|
|
18200
|
+
await (0, import_promises28.mkdir)(import_node_path35.default.dirname(outFile), { recursive: true });
|
|
18201
|
+
await (0, import_promises28.writeFile)(outFile, buf);
|
|
18080
18202
|
ctx.manifest.assets[assetId] = {
|
|
18081
18203
|
prompt: orphan.prompt,
|
|
18082
18204
|
promptByStyle: {},
|
|
@@ -18108,7 +18230,7 @@ async function runSalvage(provider, orphans, ctx, opts = {}) {
|
|
|
18108
18230
|
sourceUrl: durableSource,
|
|
18109
18231
|
sourceUrls: durableSource ? [{ url: durableSource }] : [],
|
|
18110
18232
|
outputs: [{
|
|
18111
|
-
path: portableOutputPath(outFile,
|
|
18233
|
+
path: portableOutputPath(outFile, import_node_path35.default.dirname(ctx.manifestPath)),
|
|
18112
18234
|
sha256: sha256(buf)
|
|
18113
18235
|
}],
|
|
18114
18236
|
submittedAt: orphan.createdAt,
|
|
@@ -18132,9 +18254,9 @@ async function runSalvage(provider, orphans, ctx, opts = {}) {
|
|
|
18132
18254
|
}
|
|
18133
18255
|
}
|
|
18134
18256
|
await applyTags(provider, decisions, existingTags, { onProgress: log, characters: characterMembers });
|
|
18135
|
-
const raw = JSON.parse(await (0,
|
|
18257
|
+
const raw = JSON.parse(await (0, import_promises28.readFile)(ctx.manifestPath, "utf8"));
|
|
18136
18258
|
for (const id of importedAssetIds) raw.assets[id] = ctx.manifest.assets[id];
|
|
18137
|
-
await (0,
|
|
18259
|
+
await (0, import_promises28.writeFile)(ctx.manifestPath, JSON.stringify(raw, null, 2) + "\n");
|
|
18138
18260
|
await saveLock(ctx.lockPath, ctx.lock);
|
|
18139
18261
|
if (characterAssetIds.length) result.characterAssetIds = characterAssetIds;
|
|
18140
18262
|
return result;
|