pixelkiln 0.2.0 → 0.3.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/CONTRIBUTING.md +48 -0
- package/NAMING.md +15 -15
- package/PROVIDERS.md +13 -13
- package/README.md +22 -9
- package/dist/cli.d.ts +18 -1
- package/dist/cli.js +588 -142
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +291 -21
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +168 -1
- package/dist/index.d.ts +168 -1
- package/dist/index.js +280 -21
- package/dist/index.js.map +1 -1
- package/docs/AGENTS.md +1 -1
- package/docs/ARCHITECTURE.md +4 -2
- package/docs/CLI.md +47 -0
- package/docs/ENDPOINTS.md +39 -38
- package/docs/GENERATORS.md +1 -1
- package/docs/RECOVERY.md +44 -0
- package/docs/TILES.md +1 -1
- package/examples/minimal/README.md +2 -2
- package/package.json +1 -1
- package/schema/workspace.schema.json +54 -0
package/dist/index.js
CHANGED
|
@@ -409,11 +409,11 @@ var PixelLabClient = class {
|
|
|
409
409
|
* common than the failure mode of retrying (a duplicate object), and a
|
|
410
410
|
* duplicate is visible and free to delete whereas a silent gap is neither.
|
|
411
411
|
*/
|
|
412
|
-
async request(
|
|
412
|
+
async request(path17, init, attempt = 0) {
|
|
413
413
|
const auth = this.apiKey.startsWith("Bearer ") ? this.apiKey : `Bearer ${this.apiKey}`;
|
|
414
414
|
let res;
|
|
415
415
|
try {
|
|
416
|
-
res = await fetch(`${BASE}${
|
|
416
|
+
res = await fetch(`${BASE}${path17}`, {
|
|
417
417
|
...init,
|
|
418
418
|
signal: init?.signal ?? AbortSignal.timeout(this.timeoutMs),
|
|
419
419
|
headers: {
|
|
@@ -425,24 +425,24 @@ var PixelLabClient = class {
|
|
|
425
425
|
} catch (err) {
|
|
426
426
|
if (attempt < MAX_RETRIES) {
|
|
427
427
|
await sleep(backoffMs(attempt));
|
|
428
|
-
return this.request(
|
|
428
|
+
return this.request(path17, init, attempt + 1);
|
|
429
429
|
}
|
|
430
430
|
throw err;
|
|
431
431
|
}
|
|
432
432
|
if (!res.ok && shouldRetry(res.status) && attempt < MAX_RETRIES) {
|
|
433
433
|
const waitMs = retryAfterMs(res.headers.get("retry-after")) ?? backoffMs(attempt);
|
|
434
434
|
await sleep(waitMs);
|
|
435
|
-
return this.request(
|
|
435
|
+
return this.request(path17, init, attempt + 1);
|
|
436
436
|
}
|
|
437
437
|
const text = await res.text();
|
|
438
438
|
if (!res.ok) {
|
|
439
|
-
throw new PixelLabError(`${init?.method ?? "GET"} ${
|
|
439
|
+
throw new PixelLabError(`${init?.method ?? "GET"} ${path17} \u2192 ${res.status}`, res.status, text);
|
|
440
440
|
}
|
|
441
441
|
if (!text) return {};
|
|
442
442
|
try {
|
|
443
443
|
return JSON.parse(text);
|
|
444
444
|
} catch {
|
|
445
|
-
throw new Error(`${init?.method ?? "GET"} ${
|
|
445
|
+
throw new Error(`${init?.method ?? "GET"} ${path17} returned invalid JSON`);
|
|
446
446
|
}
|
|
447
447
|
}
|
|
448
448
|
async balance() {
|
|
@@ -1460,8 +1460,8 @@ import { readFile } from "fs/promises";
|
|
|
1460
1460
|
function sha256(data) {
|
|
1461
1461
|
return createHash2("sha256").update(data).digest("hex");
|
|
1462
1462
|
}
|
|
1463
|
-
async function sha256File(
|
|
1464
|
-
return sha256(await readFile(
|
|
1463
|
+
async function sha256File(path17) {
|
|
1464
|
+
return sha256(await readFile(path17));
|
|
1465
1465
|
}
|
|
1466
1466
|
function specHash(spec, styleImageHashes) {
|
|
1467
1467
|
return sha256(
|
|
@@ -2705,10 +2705,10 @@ function isSha256Hash(value) {
|
|
|
2705
2705
|
function parseCache(value) {
|
|
2706
2706
|
return HashCacheSchema.parse(value);
|
|
2707
2707
|
}
|
|
2708
|
-
async function loadCache(
|
|
2709
|
-
if (!existsSync6(
|
|
2708
|
+
async function loadCache(path17) {
|
|
2709
|
+
if (!existsSync6(path17)) return { version: 1, hashes: {} };
|
|
2710
2710
|
try {
|
|
2711
|
-
const parsed = HashCacheSchema.safeParse(JSON.parse(await readFile6(
|
|
2711
|
+
const parsed = HashCacheSchema.safeParse(JSON.parse(await readFile6(path17, "utf8")));
|
|
2712
2712
|
if (!parsed.success) return { version: 1, hashes: {} };
|
|
2713
2713
|
return {
|
|
2714
2714
|
version: 1,
|
|
@@ -2720,18 +2720,18 @@ async function loadCache(path15) {
|
|
|
2720
2720
|
return { version: 1, hashes: {} };
|
|
2721
2721
|
}
|
|
2722
2722
|
}
|
|
2723
|
-
async function saveCache(
|
|
2723
|
+
async function saveCache(path17, cache) {
|
|
2724
2724
|
const sorted = {};
|
|
2725
2725
|
for (const key of Object.keys(cache.hashes).sort()) {
|
|
2726
2726
|
const hash = cache.hashes[key];
|
|
2727
2727
|
if (!isSha256Hash(hash)) throw new Error(`Refusing to cache invalid SHA-256 for ${key}`);
|
|
2728
2728
|
sorted[key] = hash;
|
|
2729
2729
|
}
|
|
2730
|
-
await mkdir3(pathModule.dirname(pathModule.resolve(
|
|
2731
|
-
const tmp = `${
|
|
2730
|
+
await mkdir3(pathModule.dirname(pathModule.resolve(path17)), { recursive: true });
|
|
2731
|
+
const tmp = `${path17}.${process.pid}.${Math.random().toString(36).slice(2)}.tmp`;
|
|
2732
2732
|
try {
|
|
2733
2733
|
await writeFile3(tmp, JSON.stringify({ version: 1, hashes: sorted }, null, 2) + "\n");
|
|
2734
|
-
await rename3(tmp,
|
|
2734
|
+
await rename3(tmp, path17);
|
|
2735
2735
|
} finally {
|
|
2736
2736
|
await rm3(tmp, { force: true });
|
|
2737
2737
|
}
|
|
@@ -4464,6 +4464,7 @@ function mode(values) {
|
|
|
4464
4464
|
// src/pipeline/salvage.ts
|
|
4465
4465
|
import { readFile as readFile11 } from "fs/promises";
|
|
4466
4466
|
import { existsSync as existsSync12 } from "fs";
|
|
4467
|
+
import path14 from "path";
|
|
4467
4468
|
async function loadClaims(lockPaths) {
|
|
4468
4469
|
const claimed = /* @__PURE__ */ new Set();
|
|
4469
4470
|
for (const p of lockPaths) {
|
|
@@ -4516,6 +4517,26 @@ function matchOrphanStyle(prompt, manifest) {
|
|
|
4516
4517
|
if (styleIds.length <= 1) return styleIds[0] ?? null;
|
|
4517
4518
|
return matchStyleByPattern(prompt, manifest);
|
|
4518
4519
|
}
|
|
4520
|
+
async function loadSiblingManifests(ownManifestPath, workspaceManifestPaths, claimPaths) {
|
|
4521
|
+
const own = path14.resolve(ownManifestPath);
|
|
4522
|
+
const siblingManifestPaths = [
|
|
4523
|
+
.../* @__PURE__ */ new Set([
|
|
4524
|
+
...workspaceManifestPaths,
|
|
4525
|
+
...claimPaths.map((c) => path14.join(path14.dirname(path14.resolve(c)), "pixelkiln.manifest.json"))
|
|
4526
|
+
])
|
|
4527
|
+
];
|
|
4528
|
+
const siblings = [];
|
|
4529
|
+
for (const siblingManifestPath of siblingManifestPaths) {
|
|
4530
|
+
if (path14.resolve(siblingManifestPath) === own) continue;
|
|
4531
|
+
if (!existsSync12(siblingManifestPath)) continue;
|
|
4532
|
+
try {
|
|
4533
|
+
const { manifest } = await loadManifest(siblingManifestPath);
|
|
4534
|
+
siblings.push({ label: path14.basename(path14.dirname(siblingManifestPath)), manifest });
|
|
4535
|
+
} catch {
|
|
4536
|
+
}
|
|
4537
|
+
}
|
|
4538
|
+
return siblings;
|
|
4539
|
+
}
|
|
4519
4540
|
function groupOrphansByStyle(orphans, manifest, siblings = []) {
|
|
4520
4541
|
const matched = /* @__PURE__ */ new Map();
|
|
4521
4542
|
const elsewhere = /* @__PURE__ */ new Map();
|
|
@@ -4627,7 +4648,7 @@ async function applyTags(provider, decisions, existing, opts = {}) {
|
|
|
4627
4648
|
|
|
4628
4649
|
// src/pick/salvage-server.ts
|
|
4629
4650
|
import { mkdir as mkdir5, writeFile as writeFile7, readFile as readFile12 } from "fs/promises";
|
|
4630
|
-
import
|
|
4651
|
+
import path15 from "path";
|
|
4631
4652
|
|
|
4632
4653
|
// src/pick/salvage-sheet.ts
|
|
4633
4654
|
var escapeHtml2 = (s) => s.replace(/[&<>"']/g, (c) => ({ "&": "&", "<": "<", ">": ">", '"': """, "'": "'" })[c]);
|
|
@@ -4792,7 +4813,7 @@ async function runSalvage(provider, orphans, ctx, opts = {}) {
|
|
|
4792
4813
|
});
|
|
4793
4814
|
const html = renderSalvageSheet(orphans, {
|
|
4794
4815
|
styleId: ctx.styleId,
|
|
4795
|
-
importDir:
|
|
4816
|
+
importDir: path15.relative(process.cwd(), ctx.importDir) || "."
|
|
4796
4817
|
});
|
|
4797
4818
|
const byId = new Map(orphans.map((o) => [o.id, o]));
|
|
4798
4819
|
const existingTags = new Map(orphans.map((o) => [o.id, o.tags]));
|
|
@@ -4821,9 +4842,9 @@ async function runSalvage(provider, orphans, ctx, opts = {}) {
|
|
|
4821
4842
|
if (!buf.subarray(0, 8).equals(PNG_SIGNATURE3)) throw new Error("not a PNG");
|
|
4822
4843
|
decodePng(buf);
|
|
4823
4844
|
const assetId = idFromPrompt(orphan.prompt, taken);
|
|
4824
|
-
const rel =
|
|
4825
|
-
const outFile =
|
|
4826
|
-
await mkdir5(
|
|
4845
|
+
const rel = path15.join("_salvaged", `${assetId}.png`);
|
|
4846
|
+
const outFile = path15.resolve(ctx.importDir, rel);
|
|
4847
|
+
await mkdir5(path15.dirname(outFile), { recursive: true });
|
|
4827
4848
|
await writeFile7(outFile, buf);
|
|
4828
4849
|
ctx.manifest.assets[assetId] = {
|
|
4829
4850
|
prompt: orphan.prompt,
|
|
@@ -4850,7 +4871,7 @@ async function runSalvage(provider, orphans, ctx, opts = {}) {
|
|
|
4850
4871
|
error: null,
|
|
4851
4872
|
sourceUrl: orphan.previewUrl,
|
|
4852
4873
|
outputs: [{
|
|
4853
|
-
path: portableOutputPath(outFile,
|
|
4874
|
+
path: portableOutputPath(outFile, path15.dirname(ctx.manifestPath)),
|
|
4854
4875
|
sha256: sha256(buf)
|
|
4855
4876
|
}],
|
|
4856
4877
|
submittedAt: orphan.createdAt,
|
|
@@ -4882,6 +4903,233 @@ async function runSalvage(provider, orphans, ctx, opts = {}) {
|
|
|
4882
4903
|
}
|
|
4883
4904
|
});
|
|
4884
4905
|
}
|
|
4906
|
+
|
|
4907
|
+
// src/workspace.ts
|
|
4908
|
+
import { mkdir as mkdir6, readFile as readFile13, rename as rename5, rm as rm6, writeFile as writeFile8 } from "fs/promises";
|
|
4909
|
+
import { existsSync as existsSync13 } from "fs";
|
|
4910
|
+
import path16 from "path";
|
|
4911
|
+
import { z as z4 } from "zod";
|
|
4912
|
+
var WorkspaceProjectSchema = z4.object({
|
|
4913
|
+
id: z4.string().min(1),
|
|
4914
|
+
/** Manifest path, relative to the catalog file's own directory. */
|
|
4915
|
+
manifest: z4.string().min(1),
|
|
4916
|
+
/** Lockfile path, relative to the catalog file's own directory. */
|
|
4917
|
+
lock: z4.string().min(1),
|
|
4918
|
+
provider: z4.string().min(1).default("pixellab"),
|
|
4919
|
+
/** Free-form label for a shared account, e.g. distinguishing sandboxes. */
|
|
4920
|
+
account: z4.string().optional()
|
|
4921
|
+
}).strict();
|
|
4922
|
+
var WorkspaceSchema = z4.object({
|
|
4923
|
+
version: z4.literal(1),
|
|
4924
|
+
projects: z4.array(WorkspaceProjectSchema).default([])
|
|
4925
|
+
}).strict();
|
|
4926
|
+
function parseWorkspace(raw) {
|
|
4927
|
+
const parsed = WorkspaceSchema.safeParse(raw);
|
|
4928
|
+
if (parsed.success) return parsed.data;
|
|
4929
|
+
throw new Error(
|
|
4930
|
+
`Workspace catalog is not valid v1:
|
|
4931
|
+
${parsed.error.issues.slice(0, 5).map((i) => ` ${i.path.join(".")}: ${i.message}`).join("\n")}`
|
|
4932
|
+
);
|
|
4933
|
+
}
|
|
4934
|
+
async function loadWorkspace(workspacePath) {
|
|
4935
|
+
if (!existsSync13(workspacePath)) return { version: 1, projects: [] };
|
|
4936
|
+
let raw;
|
|
4937
|
+
try {
|
|
4938
|
+
raw = JSON.parse(await readFile13(workspacePath, "utf8"));
|
|
4939
|
+
} catch (err) {
|
|
4940
|
+
throw new Error(
|
|
4941
|
+
`Workspace catalog at ${workspacePath} is malformed:
|
|
4942
|
+
${err instanceof Error ? err.message : String(err)}`
|
|
4943
|
+
);
|
|
4944
|
+
}
|
|
4945
|
+
return parseWorkspace(raw);
|
|
4946
|
+
}
|
|
4947
|
+
async function saveWorkspace(workspacePath, ws) {
|
|
4948
|
+
const sorted = {
|
|
4949
|
+
version: 1,
|
|
4950
|
+
projects: [...ws.projects].sort((a, b) => a.id.localeCompare(b.id))
|
|
4951
|
+
};
|
|
4952
|
+
await mkdir6(path16.dirname(path16.resolve(workspacePath)), { recursive: true });
|
|
4953
|
+
const tmp = `${workspacePath}.${process.pid}.${Math.random().toString(36).slice(2)}.tmp`;
|
|
4954
|
+
try {
|
|
4955
|
+
await writeFile8(tmp, JSON.stringify(sorted, null, 2) + "\n");
|
|
4956
|
+
await rename5(tmp, workspacePath);
|
|
4957
|
+
} finally {
|
|
4958
|
+
await rm6(tmp, { force: true });
|
|
4959
|
+
}
|
|
4960
|
+
}
|
|
4961
|
+
function toPortablePath(dir, absolute) {
|
|
4962
|
+
return path16.relative(dir, absolute).split(path16.sep).join("/");
|
|
4963
|
+
}
|
|
4964
|
+
function resolveProject(dir, project) {
|
|
4965
|
+
return {
|
|
4966
|
+
manifestPath: path16.resolve(dir, project.manifest.split("/").join(path16.sep)),
|
|
4967
|
+
lockPath: path16.resolve(dir, project.lock.split("/").join(path16.sep))
|
|
4968
|
+
};
|
|
4969
|
+
}
|
|
4970
|
+
function validateWorkspace(ws, dir) {
|
|
4971
|
+
const diagnostics = [];
|
|
4972
|
+
const idCounts = /* @__PURE__ */ new Map();
|
|
4973
|
+
const lockOwners = /* @__PURE__ */ new Map();
|
|
4974
|
+
const manifestOwners = /* @__PURE__ */ new Map();
|
|
4975
|
+
for (const project of ws.projects) {
|
|
4976
|
+
idCounts.set(project.id, (idCounts.get(project.id) ?? 0) + 1);
|
|
4977
|
+
const { manifestPath, lockPath } = resolveProject(dir, project);
|
|
4978
|
+
lockOwners.set(lockPath, [...lockOwners.get(lockPath) ?? [], project.id]);
|
|
4979
|
+
manifestOwners.set(manifestPath, [...manifestOwners.get(manifestPath) ?? [], project.id]);
|
|
4980
|
+
if (path16.isAbsolute(project.manifest) || path16.isAbsolute(project.lock)) {
|
|
4981
|
+
diagnostics.push({
|
|
4982
|
+
id: "absolute-path",
|
|
4983
|
+
level: "warning",
|
|
4984
|
+
message: `project "${project.id}" stores an absolute path \u2014 the catalog will not resolve correctly if this tree is cloned or moved elsewhere`
|
|
4985
|
+
});
|
|
4986
|
+
}
|
|
4987
|
+
if (!existsSync13(manifestPath)) {
|
|
4988
|
+
diagnostics.push({
|
|
4989
|
+
id: "missing-manifest",
|
|
4990
|
+
level: "error",
|
|
4991
|
+
message: `project "${project.id}" manifest not found: ${manifestPath}`
|
|
4992
|
+
});
|
|
4993
|
+
}
|
|
4994
|
+
if (!existsSync13(lockPath)) {
|
|
4995
|
+
diagnostics.push({
|
|
4996
|
+
id: "missing-lock",
|
|
4997
|
+
level: "error",
|
|
4998
|
+
message: `project "${project.id}" lockfile not found: ${lockPath}`
|
|
4999
|
+
});
|
|
5000
|
+
}
|
|
5001
|
+
}
|
|
5002
|
+
for (const [id, count] of idCounts) {
|
|
5003
|
+
if (count > 1) {
|
|
5004
|
+
diagnostics.push({
|
|
5005
|
+
id: "duplicate-id",
|
|
5006
|
+
level: "error",
|
|
5007
|
+
message: `project id "${id}" is registered ${count} times`
|
|
5008
|
+
});
|
|
5009
|
+
}
|
|
5010
|
+
}
|
|
5011
|
+
for (const [lockPath, ids] of lockOwners) {
|
|
5012
|
+
if (ids.length > 1) {
|
|
5013
|
+
diagnostics.push({
|
|
5014
|
+
id: "duplicate-lock",
|
|
5015
|
+
level: "error",
|
|
5016
|
+
message: `${ids.join(", ")} all register the same lockfile: ${lockPath}`
|
|
5017
|
+
});
|
|
5018
|
+
}
|
|
5019
|
+
}
|
|
5020
|
+
for (const [manifestPath, ids] of manifestOwners) {
|
|
5021
|
+
if (ids.length > 1) {
|
|
5022
|
+
diagnostics.push({
|
|
5023
|
+
id: "duplicate-manifest",
|
|
5024
|
+
level: "warning",
|
|
5025
|
+
message: `${ids.join(", ")} share manifest ${manifestPath} \u2014 expected only when they are variant lockfiles beside one manifest`
|
|
5026
|
+
});
|
|
5027
|
+
}
|
|
5028
|
+
}
|
|
5029
|
+
const providers = new Set(ws.projects.map((p) => p.provider));
|
|
5030
|
+
if (providers.size > 1) {
|
|
5031
|
+
diagnostics.push({
|
|
5032
|
+
id: "mixed-provider",
|
|
5033
|
+
level: "warning",
|
|
5034
|
+
message: `registered projects use different providers: ${[...providers].sort().join(", ")} \u2014 spend totals are kept separate per unit, but confirm this is intentional`
|
|
5035
|
+
});
|
|
5036
|
+
}
|
|
5037
|
+
return diagnostics;
|
|
5038
|
+
}
|
|
5039
|
+
|
|
5040
|
+
// src/pipeline/workspace.ts
|
|
5041
|
+
async function workspaceClaims(ws, dir) {
|
|
5042
|
+
const lockPaths = [];
|
|
5043
|
+
const byProject = {};
|
|
5044
|
+
const claimed = /* @__PURE__ */ new Set();
|
|
5045
|
+
for (const project of ws.projects) {
|
|
5046
|
+
const { lockPath } = resolveProject(dir, project);
|
|
5047
|
+
lockPaths.push(lockPath);
|
|
5048
|
+
let projectClaims;
|
|
5049
|
+
try {
|
|
5050
|
+
projectClaims = await loadClaims([lockPath]);
|
|
5051
|
+
} catch (err) {
|
|
5052
|
+
throw new Error(
|
|
5053
|
+
`Project "${project.id}" lockfile is unreadable: ${err instanceof Error ? err.message : String(err)}`
|
|
5054
|
+
);
|
|
5055
|
+
}
|
|
5056
|
+
byProject[project.id] = projectClaims.size;
|
|
5057
|
+
for (const id of projectClaims) claimed.add(id);
|
|
5058
|
+
}
|
|
5059
|
+
return { claimed, byProject, lockPaths };
|
|
5060
|
+
}
|
|
5061
|
+
function emptyStateCounts() {
|
|
5062
|
+
return {
|
|
5063
|
+
ok: 0,
|
|
5064
|
+
missing: 0,
|
|
5065
|
+
untracked: 0,
|
|
5066
|
+
stale: 0,
|
|
5067
|
+
orphaned: 0,
|
|
5068
|
+
"in-flight": 0,
|
|
5069
|
+
recoverable: 0,
|
|
5070
|
+
failed: 0
|
|
5071
|
+
};
|
|
5072
|
+
}
|
|
5073
|
+
async function workspaceStatus(ws, dir) {
|
|
5074
|
+
const diagnostics = validateWorkspace(ws, dir);
|
|
5075
|
+
const provider = PixelLabProvider.forOffline();
|
|
5076
|
+
const projects = [];
|
|
5077
|
+
const totalsByState = emptyStateCounts();
|
|
5078
|
+
const totalsSpend = { generations: 0, usd: 0, free: 0 };
|
|
5079
|
+
for (const project of ws.projects) {
|
|
5080
|
+
const { manifestPath, lockPath } = resolveProject(dir, project);
|
|
5081
|
+
const base = {
|
|
5082
|
+
id: project.id,
|
|
5083
|
+
provider: project.provider,
|
|
5084
|
+
account: project.account ?? null,
|
|
5085
|
+
manifest: manifestPath,
|
|
5086
|
+
lock: lockPath
|
|
5087
|
+
};
|
|
5088
|
+
try {
|
|
5089
|
+
const loaded = await loadManifest(manifestPath);
|
|
5090
|
+
const specs = await resolveSpecs(loaded, { provider });
|
|
5091
|
+
const lock = await loadLock(lockPath);
|
|
5092
|
+
normalizeLockOutputPaths(lock, specs);
|
|
5093
|
+
const plan = await buildPlan(specs, lock);
|
|
5094
|
+
const byState = summarize(plan);
|
|
5095
|
+
const spend = spendByUnit(lock);
|
|
5096
|
+
for (const state of Object.keys(byState)) {
|
|
5097
|
+
totalsByState[state] += byState[state];
|
|
5098
|
+
}
|
|
5099
|
+
for (const unit of Object.keys(spend)) {
|
|
5100
|
+
totalsSpend[unit] += spend[unit];
|
|
5101
|
+
}
|
|
5102
|
+
projects.push({
|
|
5103
|
+
...base,
|
|
5104
|
+
entries: Object.keys(lock.entries).length,
|
|
5105
|
+
byState,
|
|
5106
|
+
spendByUnit: spend,
|
|
5107
|
+
error: null
|
|
5108
|
+
});
|
|
5109
|
+
} catch (err) {
|
|
5110
|
+
projects.push({
|
|
5111
|
+
...base,
|
|
5112
|
+
entries: 0,
|
|
5113
|
+
byState: emptyStateCounts(),
|
|
5114
|
+
spendByUnit: { generations: 0, usd: 0, free: 0 },
|
|
5115
|
+
error: err instanceof Error ? err.message : String(err)
|
|
5116
|
+
});
|
|
5117
|
+
}
|
|
5118
|
+
}
|
|
5119
|
+
let claims = 0;
|
|
5120
|
+
try {
|
|
5121
|
+
claims = (await workspaceClaims(ws, dir)).claimed.size;
|
|
5122
|
+
} catch {
|
|
5123
|
+
}
|
|
5124
|
+
return {
|
|
5125
|
+
version: 1,
|
|
5126
|
+
safe: !diagnostics.some((d) => d.level === "error") && projects.every((p) => !p.error),
|
|
5127
|
+
dir,
|
|
5128
|
+
projects,
|
|
5129
|
+
totals: { byState: totalsByState, spendByUnit: totalsSpend, claims },
|
|
5130
|
+
diagnostics
|
|
5131
|
+
};
|
|
5132
|
+
}
|
|
4885
5133
|
export {
|
|
4886
5134
|
AssetSchema,
|
|
4887
5135
|
DEFAULT_RATE_LIMIT,
|
|
@@ -4898,6 +5146,8 @@ export {
|
|
|
4898
5146
|
PixelLabProvider,
|
|
4899
5147
|
StyleSchema,
|
|
4900
5148
|
UnsupportedCapabilityError,
|
|
5149
|
+
WorkspaceProjectSchema,
|
|
5150
|
+
WorkspaceSchema,
|
|
4901
5151
|
adopt,
|
|
4902
5152
|
applyTags,
|
|
4903
5153
|
auditStyle,
|
|
@@ -4928,6 +5178,8 @@ export {
|
|
|
4928
5178
|
loadClaims,
|
|
4929
5179
|
loadLock,
|
|
4930
5180
|
loadManifest,
|
|
5181
|
+
loadSiblingManifests,
|
|
5182
|
+
loadWorkspace,
|
|
4931
5183
|
lockKey,
|
|
4932
5184
|
matchOrphanStyle,
|
|
4933
5185
|
measureBalanceChange,
|
|
@@ -4942,6 +5194,7 @@ export {
|
|
|
4942
5194
|
packStyle,
|
|
4943
5195
|
paletteDistance,
|
|
4944
5196
|
parseLock,
|
|
5197
|
+
parseWorkspace,
|
|
4945
5198
|
pngSize,
|
|
4946
5199
|
poll,
|
|
4947
5200
|
portableOutputPath,
|
|
@@ -4955,6 +5208,7 @@ export {
|
|
|
4955
5208
|
resolveEntryOutputs,
|
|
4956
5209
|
resolveOutputPath,
|
|
4957
5210
|
resolvePackInputs,
|
|
5211
|
+
resolveProject,
|
|
4958
5212
|
resolveSpecEntryOutputs,
|
|
4959
5213
|
resolveSpecOutputs,
|
|
4960
5214
|
resolveSpecs,
|
|
@@ -4964,6 +5218,7 @@ export {
|
|
|
4964
5218
|
runPicker,
|
|
4965
5219
|
runSalvage,
|
|
4966
5220
|
saveLock,
|
|
5221
|
+
saveWorkspace,
|
|
4967
5222
|
scanAssets,
|
|
4968
5223
|
selectEntryOutput,
|
|
4969
5224
|
sha256,
|
|
@@ -4979,11 +5234,15 @@ export {
|
|
|
4979
5234
|
tileFeatureOutputCount,
|
|
4980
5235
|
tileVariationCount,
|
|
4981
5236
|
tilesCost,
|
|
5237
|
+
toPortablePath,
|
|
4982
5238
|
totalSpend,
|
|
4983
5239
|
upsert,
|
|
4984
5240
|
validateCostEstimate,
|
|
5241
|
+
validateWorkspace,
|
|
4985
5242
|
verifyArtifactBundle,
|
|
4986
5243
|
withArtifactManifest,
|
|
5244
|
+
workspaceClaims,
|
|
5245
|
+
workspaceStatus,
|
|
4987
5246
|
writeArtifactBundle,
|
|
4988
5247
|
writeManagedArtifactBundle
|
|
4989
5248
|
};
|