pixelkiln 0.35.0 → 0.36.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 +7 -0
- package/dist/cli.js +165 -81
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +137 -38
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +116 -52
- package/dist/index.d.ts +116 -52
- package/dist/index.js +128 -38
- package/dist/index.js.map +1 -1
- package/docs/CLI.md +49 -0
- package/docs/LIBRARY.md +32 -0
- package/package.json +3 -1
package/CONTRIBUTING.md
CHANGED
|
@@ -19,8 +19,15 @@ npm run test:security
|
|
|
19
19
|
npm test
|
|
20
20
|
npm run build
|
|
21
21
|
npm run test:package
|
|
22
|
+
npm run test:gallery
|
|
22
23
|
```
|
|
23
24
|
|
|
25
|
+
`test:gallery` serves a gallery over `FakeProvider` and drives it in headless
|
|
26
|
+
Chrome: the Generate and Regenerate buttons, the busy badge while a job runs,
|
|
27
|
+
the previous generation kept and restored for free, and zero console errors.
|
|
28
|
+
It needs a Chrome or Chromium binary (`CHROME=/path/to/chrome` when it is not
|
|
29
|
+
in a usual place) and skips without one outside CI.
|
|
30
|
+
|
|
24
31
|
The Next.js marketing/documentation site is isolated in `website/`:
|
|
25
32
|
|
|
26
33
|
```bash
|
package/dist/cli.js
CHANGED
|
@@ -1,5 +1,71 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
+
// src/errors.ts
|
|
4
|
+
var EXIT_CODES = {
|
|
5
|
+
usage: 2,
|
|
6
|
+
project: 3,
|
|
7
|
+
provider: 4,
|
|
8
|
+
"refused-overwrite": 5,
|
|
9
|
+
budget: 6,
|
|
10
|
+
capability: 7
|
|
11
|
+
};
|
|
12
|
+
var PixelKilnError = class extends Error {
|
|
13
|
+
code;
|
|
14
|
+
hint;
|
|
15
|
+
constructor(message6, code, opts = {}) {
|
|
16
|
+
super(message6, opts.cause === void 0 ? void 0 : { cause: opts.cause });
|
|
17
|
+
this.name = "PixelKilnError";
|
|
18
|
+
this.code = code;
|
|
19
|
+
if (opts.hint) this.hint = opts.hint;
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
var UsageError = class extends PixelKilnError {
|
|
23
|
+
constructor(message6, opts) {
|
|
24
|
+
super(message6, "usage", opts);
|
|
25
|
+
this.name = "UsageError";
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
var ProjectError = class extends PixelKilnError {
|
|
29
|
+
constructor(message6, opts) {
|
|
30
|
+
super(message6, "project", opts);
|
|
31
|
+
this.name = "ProjectError";
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
var ProviderError = class extends PixelKilnError {
|
|
35
|
+
/** Registry id of the provider that failed. */
|
|
36
|
+
provider;
|
|
37
|
+
/** HTTP status when the failure was a response, otherwise undefined. */
|
|
38
|
+
status;
|
|
39
|
+
constructor(provider, message6, opts = {}) {
|
|
40
|
+
super(message6, "provider", opts);
|
|
41
|
+
this.name = "ProviderError";
|
|
42
|
+
this.provider = provider;
|
|
43
|
+
if (opts.status !== void 0) this.status = opts.status;
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
var OverwriteRefusedError = class extends PixelKilnError {
|
|
47
|
+
/** The file(s) left untouched. */
|
|
48
|
+
files;
|
|
49
|
+
constructor(message6, files, opts) {
|
|
50
|
+
super(message6, "refused-overwrite", opts);
|
|
51
|
+
this.name = "OverwriteRefusedError";
|
|
52
|
+
this.files = files;
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
var BudgetError = class extends PixelKilnError {
|
|
56
|
+
constructor(message6, opts) {
|
|
57
|
+
super(message6, "budget", opts);
|
|
58
|
+
this.name = "BudgetError";
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
function errorCode(err) {
|
|
62
|
+
return err instanceof PixelKilnError ? err.code : void 0;
|
|
63
|
+
}
|
|
64
|
+
function exitCodeFor(err) {
|
|
65
|
+
const code = errorCode(err);
|
|
66
|
+
return code ? EXIT_CODES[code] : 1;
|
|
67
|
+
}
|
|
68
|
+
|
|
3
69
|
// src/cli/main.ts
|
|
4
70
|
import { existsSync as existsSync31 } from "fs";
|
|
5
71
|
import { readFile as readFile27 } from "fs/promises";
|
|
@@ -103,7 +169,7 @@ var TOOLS = ["editor"];
|
|
|
103
169
|
function parseArgs(argv) {
|
|
104
170
|
const [command = "help"] = argv;
|
|
105
171
|
if (!COMMANDS.includes(command)) {
|
|
106
|
-
throw new
|
|
172
|
+
throw new UsageError(`Unknown command "${command}". Run \`pixelkiln help\` for the list.`);
|
|
107
173
|
}
|
|
108
174
|
let rest = argv.slice(1);
|
|
109
175
|
let subcommand;
|
|
@@ -111,10 +177,10 @@ function parseArgs(argv) {
|
|
|
111
177
|
if (command === "quality") {
|
|
112
178
|
subcommand = rest[0];
|
|
113
179
|
if (subcommand === void 0 || subcommand.startsWith("-")) {
|
|
114
|
-
throw new
|
|
180
|
+
throw new UsageError(`quality needs a subcommand: ${QUALITY_SUBCOMMANDS.join(", ")}`);
|
|
115
181
|
}
|
|
116
182
|
if (!QUALITY_SUBCOMMANDS.includes(subcommand)) {
|
|
117
|
-
throw new
|
|
183
|
+
throw new UsageError(
|
|
118
184
|
`Unknown quality subcommand "${subcommand}". Known: ${QUALITY_SUBCOMMANDS.join(", ")}`
|
|
119
185
|
);
|
|
120
186
|
}
|
|
@@ -122,10 +188,10 @@ function parseArgs(argv) {
|
|
|
122
188
|
} else if (command === "recipe") {
|
|
123
189
|
subcommand = rest[0];
|
|
124
190
|
if (subcommand === void 0 || subcommand.startsWith("-")) {
|
|
125
|
-
throw new
|
|
191
|
+
throw new UsageError(`recipe needs a subcommand: ${RECIPE_SUBCOMMANDS.join(", ")}`);
|
|
126
192
|
}
|
|
127
193
|
if (!RECIPE_SUBCOMMANDS.includes(subcommand)) {
|
|
128
|
-
throw new
|
|
194
|
+
throw new UsageError(
|
|
129
195
|
`Unknown recipe subcommand "${subcommand}". Known: ${RECIPE_SUBCOMMANDS.join(", ")}`
|
|
130
196
|
);
|
|
131
197
|
}
|
|
@@ -133,17 +199,17 @@ function parseArgs(argv) {
|
|
|
133
199
|
if (subcommand !== "list") {
|
|
134
200
|
target = rest[0];
|
|
135
201
|
if (target === void 0 || target.startsWith("-")) {
|
|
136
|
-
throw new
|
|
202
|
+
throw new UsageError(`recipe ${subcommand} needs a recipe id, selector, or path.`);
|
|
137
203
|
}
|
|
138
204
|
rest = rest.slice(1);
|
|
139
205
|
}
|
|
140
206
|
} else if (command === "workspace") {
|
|
141
207
|
subcommand = rest[0];
|
|
142
208
|
if (subcommand === void 0 || subcommand.startsWith("-")) {
|
|
143
|
-
throw new
|
|
209
|
+
throw new UsageError(`workspace needs a subcommand: ${WORKSPACE_SUBCOMMANDS.join(", ")}`);
|
|
144
210
|
}
|
|
145
211
|
if (!WORKSPACE_SUBCOMMANDS.includes(subcommand)) {
|
|
146
|
-
throw new
|
|
212
|
+
throw new UsageError(
|
|
147
213
|
`Unknown workspace subcommand "${subcommand}". Known: ${WORKSPACE_SUBCOMMANDS.join(", ")}`
|
|
148
214
|
);
|
|
149
215
|
}
|
|
@@ -151,7 +217,7 @@ function parseArgs(argv) {
|
|
|
151
217
|
if (subcommand === "add" || subcommand === "remove") {
|
|
152
218
|
target = rest[0];
|
|
153
219
|
if (target === void 0 || target.startsWith("-")) {
|
|
154
|
-
throw new
|
|
220
|
+
throw new UsageError(
|
|
155
221
|
subcommand === "add" ? "workspace add needs a manifest path." : "workspace remove needs a project id or manifest path."
|
|
156
222
|
);
|
|
157
223
|
}
|
|
@@ -160,29 +226,29 @@ function parseArgs(argv) {
|
|
|
160
226
|
} else if (command === "edit") {
|
|
161
227
|
subcommand = rest[0]?.startsWith("-") || rest[0] === void 0 ? "start" : rest[0];
|
|
162
228
|
if (!EDIT_SUBCOMMANDS.includes(subcommand)) {
|
|
163
|
-
throw new
|
|
229
|
+
throw new UsageError(`Unknown edit subcommand "${subcommand}". Known: ${EDIT_SUBCOMMANDS.join(", ")}`);
|
|
164
230
|
}
|
|
165
231
|
if (rest[0] === subcommand) rest = rest.slice(1);
|
|
166
232
|
} else if (command === "tools") {
|
|
167
233
|
subcommand = rest[0]?.startsWith("-") || rest[0] === void 0 ? "status" : rest[0];
|
|
168
234
|
if (!TOOLS_SUBCOMMANDS.includes(subcommand)) {
|
|
169
|
-
throw new
|
|
235
|
+
throw new UsageError(`Unknown tools subcommand "${subcommand}". Known: ${TOOLS_SUBCOMMANDS.join(", ")}`);
|
|
170
236
|
}
|
|
171
237
|
if (rest[0] === subcommand) rest = rest.slice(1);
|
|
172
238
|
target = rest[0]?.startsWith("-") ? void 0 : rest[0];
|
|
173
239
|
if (subcommand === "install" && target === void 0) {
|
|
174
|
-
throw new
|
|
240
|
+
throw new UsageError(`tools install needs a tool name: ${TOOLS.join(", ")}`);
|
|
175
241
|
}
|
|
176
242
|
if (target !== void 0) {
|
|
177
243
|
if (!TOOLS.includes(target)) {
|
|
178
|
-
throw new
|
|
244
|
+
throw new UsageError(`Unknown tool "${target}". Known: ${TOOLS.join(", ")}`);
|
|
179
245
|
}
|
|
180
246
|
rest = rest.slice(1);
|
|
181
247
|
}
|
|
182
248
|
} else if (command === "refine") {
|
|
183
249
|
subcommand = rest[0]?.startsWith("-") || rest[0] === void 0 ? "run" : rest[0];
|
|
184
250
|
if (!REFINE_SUBCOMMANDS.includes(subcommand)) {
|
|
185
|
-
throw new
|
|
251
|
+
throw new UsageError(
|
|
186
252
|
`Unknown refine subcommand "${subcommand}". Known: ${REFINE_SUBCOMMANDS.join(", ")}`
|
|
187
253
|
);
|
|
188
254
|
}
|
|
@@ -191,19 +257,19 @@ function parseArgs(argv) {
|
|
|
191
257
|
for (let i = 0; i < rest.length; i++) {
|
|
192
258
|
const token = rest[i];
|
|
193
259
|
if (!token.startsWith("-")) {
|
|
194
|
-
throw new
|
|
260
|
+
throw new UsageError(`Unexpected argument "${token}". Options must be passed with a named flag.`);
|
|
195
261
|
}
|
|
196
262
|
if (BOOL_FLAGS.includes(token)) continue;
|
|
197
263
|
if (VALUE_FLAGS.includes(token)) {
|
|
198
264
|
const value = rest[i + 1];
|
|
199
265
|
const looksLikeFlag = value !== void 0 && value.startsWith("-") && !Number.isFinite(Number(value));
|
|
200
266
|
if (value === void 0 || looksLikeFlag) {
|
|
201
|
-
throw new
|
|
267
|
+
throw new UsageError(`${token} needs a value.`);
|
|
202
268
|
}
|
|
203
269
|
i++;
|
|
204
270
|
continue;
|
|
205
271
|
}
|
|
206
|
-
throw new
|
|
272
|
+
throw new UsageError(
|
|
207
273
|
`Unknown flag "${token}". Known flags: ${[...VALUE_FLAGS, ...BOOL_FLAGS].join(", ")}`
|
|
208
274
|
);
|
|
209
275
|
}
|
|
@@ -227,7 +293,7 @@ function parseArgs(argv) {
|
|
|
227
293
|
if (rawColumns !== void 0) {
|
|
228
294
|
columns = Number(rawColumns);
|
|
229
295
|
if (!Number.isInteger(columns) || columns < 1 || columns > 1024) {
|
|
230
|
-
throw new
|
|
296
|
+
throw new UsageError(`--columns must be a whole number between 1 and 1024, got "${rawColumns}"`);
|
|
231
297
|
}
|
|
232
298
|
}
|
|
233
299
|
const rawPort = get("--port");
|
|
@@ -235,7 +301,7 @@ function parseArgs(argv) {
|
|
|
235
301
|
if (rawPort !== void 0) {
|
|
236
302
|
port = Number(rawPort);
|
|
237
303
|
if (!Number.isInteger(port) || port < 1 || port > 65535) {
|
|
238
|
-
throw new
|
|
304
|
+
throw new UsageError(`--port must be a whole number between 1 and 65535, got "${rawPort}"`);
|
|
239
305
|
}
|
|
240
306
|
}
|
|
241
307
|
let budget;
|
|
@@ -248,30 +314,30 @@ function parseArgs(argv) {
|
|
|
248
314
|
const rawAmount = rawBudget.slice(separator + 1).trim();
|
|
249
315
|
const amount2 = Number(rawAmount);
|
|
250
316
|
if (!providerId || /[=\s]/.test(providerId) || !Number.isFinite(amount2) || amount2 < 0) {
|
|
251
|
-
throw new
|
|
317
|
+
throw new UsageError(
|
|
252
318
|
`--budget must be a non-negative number or provider=number, got "${rawBudget}".`
|
|
253
319
|
);
|
|
254
320
|
}
|
|
255
321
|
if (Object.hasOwn(providerBudgets, providerId)) {
|
|
256
|
-
throw new
|
|
322
|
+
throw new UsageError(`--budget repeats provider "${providerId}".`);
|
|
257
323
|
}
|
|
258
324
|
providerBudgets[providerId] = amount2;
|
|
259
325
|
continue;
|
|
260
326
|
}
|
|
261
327
|
const amount = Number(rawBudget);
|
|
262
328
|
if (!Number.isFinite(amount) || amount < 0) {
|
|
263
|
-
throw new
|
|
329
|
+
throw new UsageError(`--budget must be a non-negative number, got "${rawBudget}".`);
|
|
264
330
|
}
|
|
265
|
-
if (budget !== void 0) throw new
|
|
331
|
+
if (budget !== void 0) throw new UsageError("Only one unkeyed --budget may be passed.");
|
|
266
332
|
budget = amount;
|
|
267
333
|
}
|
|
268
334
|
if (budget !== void 0 && Object.keys(providerBudgets).length) {
|
|
269
|
-
throw new
|
|
335
|
+
throw new UsageError("Do not mix an unkeyed --budget with provider-keyed budgets.");
|
|
270
336
|
}
|
|
271
337
|
const manifest = get("--manifest") ?? "pixelkiln.manifest.json";
|
|
272
338
|
const rawFormat = get("--format");
|
|
273
339
|
if (rawFormat && rawFormat !== "generic" && rawFormat !== "tiled" && rawFormat !== "godot") {
|
|
274
|
-
throw new
|
|
340
|
+
throw new UsageError(`--format must be generic, tiled, or godot, got "${rawFormat}"`);
|
|
275
341
|
}
|
|
276
342
|
const numberOption = (flag, opts) => {
|
|
277
343
|
const raw = get(flag);
|
|
@@ -279,13 +345,13 @@ function parseArgs(argv) {
|
|
|
279
345
|
const value = Number(raw);
|
|
280
346
|
if (!Number.isFinite(value) || value < opts.min || opts.max !== void 0 && value > opts.max || opts.integer && !Number.isInteger(value)) {
|
|
281
347
|
const range = opts.max === void 0 ? `at least ${opts.min}` : `${opts.min} to ${opts.max}`;
|
|
282
|
-
throw new
|
|
348
|
+
throw new UsageError(`${flag} must be ${opts.integer ? "a whole number " : "a number "}${range}, got "${raw}"`);
|
|
283
349
|
}
|
|
284
350
|
return value;
|
|
285
351
|
};
|
|
286
352
|
const rawGridConfidence = get("--min-grid-confidence");
|
|
287
353
|
if (rawGridConfidence !== void 0 && rawGridConfidence !== "low" && rawGridConfidence !== "medium" && rawGridConfidence !== "high") {
|
|
288
|
-
throw new
|
|
354
|
+
throw new UsageError(
|
|
289
355
|
`--min-grid-confidence must be low, medium, or high, got "${rawGridConfidence}"`
|
|
290
356
|
);
|
|
291
357
|
}
|
|
@@ -505,10 +571,11 @@ function measureBalanceChange(before, after) {
|
|
|
505
571
|
};
|
|
506
572
|
}
|
|
507
573
|
var DEFAULT_RATE_LIMIT = { spacingMs: 2500, maxInFlight: 8 };
|
|
508
|
-
var UnsupportedCapabilityError = class extends
|
|
574
|
+
var UnsupportedCapabilityError = class extends PixelKilnError {
|
|
509
575
|
constructor(providerId, capability) {
|
|
510
576
|
super(
|
|
511
|
-
`Provider "${providerId}" does not support ${capability}. That command is unavailable with this backend
|
|
577
|
+
`Provider "${providerId}" does not support ${capability}. That command is unavailable with this backend.`,
|
|
578
|
+
"capability"
|
|
512
579
|
);
|
|
513
580
|
this.name = "UnsupportedCapabilityError";
|
|
514
581
|
}
|
|
@@ -1086,9 +1153,10 @@ async function loadLock(lockPath) {
|
|
|
1086
1153
|
try {
|
|
1087
1154
|
return parseLock(JSON.parse(await readFile(lockPath, "utf8")));
|
|
1088
1155
|
} catch (err) {
|
|
1089
|
-
throw new
|
|
1156
|
+
throw new ProjectError(
|
|
1090
1157
|
`Lockfile at ${lockPath} is malformed:
|
|
1091
|
-
${err instanceof Error ? err.message : String(err)}
|
|
1158
|
+
${err instanceof Error ? err.message : String(err)}`,
|
|
1159
|
+
{ cause: err }
|
|
1092
1160
|
);
|
|
1093
1161
|
}
|
|
1094
1162
|
}
|
|
@@ -1122,8 +1190,9 @@ async function writeLockWhileHeld(file, lock) {
|
|
|
1122
1190
|
try {
|
|
1123
1191
|
disk = parseLock(JSON.parse(await readFile(file, "utf8")));
|
|
1124
1192
|
} catch (err) {
|
|
1125
|
-
throw new
|
|
1126
|
-
`Refusing to overwrite malformed lockfile at ${file}: ${err instanceof Error ? err.message : String(err)}
|
|
1193
|
+
throw new ProjectError(
|
|
1194
|
+
`Refusing to overwrite malformed lockfile at ${file}: ${err instanceof Error ? err.message : String(err)}`,
|
|
1195
|
+
{ cause: err }
|
|
1127
1196
|
);
|
|
1128
1197
|
}
|
|
1129
1198
|
}
|
|
@@ -2051,10 +2120,11 @@ async function writeManagedArtifactBundle(manifestPath, outputs, provenance, opt
|
|
|
2051
2120
|
if (!expected || digest(current) !== expected) conflicts.push(destination);
|
|
2052
2121
|
}
|
|
2053
2122
|
if (conflicts.length) {
|
|
2054
|
-
throw new
|
|
2123
|
+
throw new OverwriteRefusedError(
|
|
2055
2124
|
`Refusing to overwrite modified or unowned artifact file(s):
|
|
2056
2125
|
` + conflicts.map((file) => ` ${file}`).join("\n") + `
|
|
2057
|
-
Pass { force: true } (CLI: --force) to take ownership and replace the complete bundle
|
|
2126
|
+
Pass { force: true } (CLI: --force) to take ownership and replace the complete bundle.`,
|
|
2127
|
+
conflicts
|
|
2058
2128
|
);
|
|
2059
2129
|
}
|
|
2060
2130
|
}
|
|
@@ -3706,8 +3776,10 @@ var ComfyUIClient = class {
|
|
|
3706
3776
|
value = null;
|
|
3707
3777
|
}
|
|
3708
3778
|
if (!response.ok) {
|
|
3709
|
-
throw new
|
|
3710
|
-
|
|
3779
|
+
throw new ProviderError(
|
|
3780
|
+
"comfyui",
|
|
3781
|
+
`ComfyUI image upload failed (${response.status})` + (apiError(value) ? `: ${apiError(value)}` : ""),
|
|
3782
|
+
{ status: response.status }
|
|
3711
3783
|
);
|
|
3712
3784
|
}
|
|
3713
3785
|
if (!isObject(value) || typeof value.name !== "string" || !value.name) {
|
|
@@ -3730,7 +3802,7 @@ var ComfyUIClient = class {
|
|
|
3730
3802
|
let target = source;
|
|
3731
3803
|
if (source.startsWith("comfyui://")) target = this.viewUrl(parseSource(source));
|
|
3732
3804
|
const response = await this.request(target);
|
|
3733
|
-
if (!response.ok) throw new
|
|
3805
|
+
if (!response.ok) throw new ProviderError("comfyui", `ComfyUI download failed (${response.status})`, { status: response.status });
|
|
3734
3806
|
return Buffer.from(await response.arrayBuffer());
|
|
3735
3807
|
}
|
|
3736
3808
|
endpoint(route) {
|
|
@@ -3755,8 +3827,10 @@ var ComfyUIClient = class {
|
|
|
3755
3827
|
}
|
|
3756
3828
|
if (!response.ok) {
|
|
3757
3829
|
const detail = apiError(value);
|
|
3758
|
-
throw new
|
|
3759
|
-
|
|
3830
|
+
throw new ProviderError(
|
|
3831
|
+
"comfyui",
|
|
3832
|
+
`ComfyUI request failed (${response.status}) at ${url.pathname}` + (detail ? `: ${detail}` : ""),
|
|
3833
|
+
{ status: response.status }
|
|
3760
3834
|
);
|
|
3761
3835
|
}
|
|
3762
3836
|
return value;
|
|
@@ -4592,14 +4666,12 @@ function validateResponse(schema, raw, operation) {
|
|
|
4592
4666
|
const issues = parsed.error.issues.slice(0, 4).map((i) => `${i.path.join(".") || "response"}: ${i.message}`).join("; ");
|
|
4593
4667
|
throw new Error(`Invalid PixelLab response for ${operation}: ${issues}`);
|
|
4594
4668
|
}
|
|
4595
|
-
var PixelLabError = class extends
|
|
4669
|
+
var PixelLabError = class extends ProviderError {
|
|
4596
4670
|
constructor(message6, status, body) {
|
|
4597
|
-
super(message6);
|
|
4598
|
-
this.status = status;
|
|
4671
|
+
super("pixellab", message6, { status });
|
|
4599
4672
|
this.body = body;
|
|
4600
4673
|
this.name = "PixelLabError";
|
|
4601
4674
|
}
|
|
4602
|
-
status;
|
|
4603
4675
|
body;
|
|
4604
4676
|
};
|
|
4605
4677
|
var PixelLabClient = class {
|
|
@@ -5180,7 +5252,7 @@ var RetroDiffusionClient = class {
|
|
|
5180
5252
|
/** Result URLs are signed; no token header, and the same retry policy as the API. */
|
|
5181
5253
|
async download(url) {
|
|
5182
5254
|
const response = await this.request(url);
|
|
5183
|
-
if (!response.ok) throw new
|
|
5255
|
+
if (!response.ok) throw new ProviderError("retrodiffusion", `Retro Diffusion download failed (${response.status})`, { status: response.status });
|
|
5184
5256
|
return Buffer.from(await response.arrayBuffer());
|
|
5185
5257
|
}
|
|
5186
5258
|
async call(path43, init = {}) {
|
|
@@ -5203,8 +5275,10 @@ var RetroDiffusionClient = class {
|
|
|
5203
5275
|
if (!response.ok) {
|
|
5204
5276
|
const retry = response.headers.get("retry-after");
|
|
5205
5277
|
const detail = retroError(value);
|
|
5206
|
-
throw new
|
|
5207
|
-
|
|
5278
|
+
throw new ProviderError(
|
|
5279
|
+
"retrodiffusion",
|
|
5280
|
+
`Retro Diffusion request failed (${response.status}): ${detail}` + (retry ? `; retry after ${retry}s` : ""),
|
|
5281
|
+
{ status: response.status }
|
|
5208
5282
|
);
|
|
5209
5283
|
}
|
|
5210
5284
|
return value;
|
|
@@ -5596,7 +5670,7 @@ var ScenarioClient = class {
|
|
|
5596
5670
|
throw new Error("Scenario downloads must use HTTP or HTTPS");
|
|
5597
5671
|
}
|
|
5598
5672
|
const response = await this.request(parsed);
|
|
5599
|
-
if (!response.ok) throw new
|
|
5673
|
+
if (!response.ok) throw new ProviderError("scenario", `Scenario download failed (${response.status})`, { status: response.status });
|
|
5600
5674
|
const declared = Number(response.headers.get("content-length"));
|
|
5601
5675
|
if (Number.isFinite(declared) && declared > MAX_DOWNLOAD_BYTES) {
|
|
5602
5676
|
throw new Error(`Scenario download exceeds the ${MAX_DOWNLOAD_BYTES}-byte safety limit`);
|
|
@@ -5643,8 +5717,10 @@ var ScenarioClient = class {
|
|
|
5643
5717
|
if (!response.ok) {
|
|
5644
5718
|
const retry = response.headers.get("retry-after");
|
|
5645
5719
|
const detail = redactScenarioError(value, [this.apiKey, this.apiSecret, auth]);
|
|
5646
|
-
throw new
|
|
5647
|
-
|
|
5720
|
+
throw new ProviderError(
|
|
5721
|
+
"scenario",
|
|
5722
|
+
`Scenario request failed (${response.status}): ${detail}` + (retry ? `; retry after ${retry}s` : ""),
|
|
5723
|
+
{ status: response.status }
|
|
5648
5724
|
);
|
|
5649
5725
|
}
|
|
5650
5726
|
return value;
|
|
@@ -6043,18 +6119,18 @@ import { existsSync as existsSync9 } from "fs";
|
|
|
6043
6119
|
import path13 from "path";
|
|
6044
6120
|
async function loadManifest(manifestPath) {
|
|
6045
6121
|
const abs = path13.resolve(manifestPath);
|
|
6046
|
-
if (!existsSync9(abs)) throw new
|
|
6122
|
+
if (!existsSync9(abs)) throw new ProjectError(`No manifest at ${abs}`);
|
|
6047
6123
|
const input = ManifestInputSchema.safeParse(JSON.parse(await readFile6(abs, "utf8")));
|
|
6048
6124
|
if (!input.success) {
|
|
6049
6125
|
const issues = formatManifestIssues(input.error.issues);
|
|
6050
|
-
throw new
|
|
6126
|
+
throw new ProjectError(`Manifest at ${abs} is invalid:
|
|
6051
6127
|
${issues}`);
|
|
6052
6128
|
}
|
|
6053
6129
|
let styles;
|
|
6054
6130
|
try {
|
|
6055
6131
|
styles = resolveStyleInheritance(input.data.styles);
|
|
6056
6132
|
} catch (error) {
|
|
6057
|
-
throw new
|
|
6133
|
+
throw new ProjectError(
|
|
6058
6134
|
`Manifest at ${abs} is invalid:
|
|
6059
6135
|
${error instanceof Error ? error.message : String(error)}`
|
|
6060
6136
|
);
|
|
@@ -6062,7 +6138,7 @@ ${issues}`);
|
|
|
6062
6138
|
const parsed = ManifestSchema.safeParse({ ...input.data, styles });
|
|
6063
6139
|
if (!parsed.success) {
|
|
6064
6140
|
const issues = formatManifestIssues(parsed.error.issues);
|
|
6065
|
-
throw new
|
|
6141
|
+
throw new ProjectError(`Manifest at ${abs} is invalid:
|
|
6066
6142
|
${issues}`);
|
|
6067
6143
|
}
|
|
6068
6144
|
const styleIds = new Set(Object.keys(parsed.data.styles));
|
|
@@ -6112,7 +6188,7 @@ ${issues}`);
|
|
|
6112
6188
|
};
|
|
6113
6189
|
for (const assetId of Object.keys(parsed.data.assets)) visitRevision(assetId, []);
|
|
6114
6190
|
if (unknownReferences.length) {
|
|
6115
|
-
throw new
|
|
6191
|
+
throw new ProjectError(`Manifest at ${abs} is invalid:
|
|
6116
6192
|
${unknownReferences.map((i) => ` ${i}`).join("\n")}`);
|
|
6117
6193
|
}
|
|
6118
6194
|
return { manifest: parsed.data, root: path13.dirname(abs), path: abs };
|
|
@@ -6191,14 +6267,14 @@ async function resolveSpecs(loaded, filter) {
|
|
|
6191
6267
|
);
|
|
6192
6268
|
for (const unknownStyle of filter?.styles ?? []) {
|
|
6193
6269
|
if (!manifest.styles[unknownStyle]) {
|
|
6194
|
-
throw new
|
|
6270
|
+
throw new UsageError(
|
|
6195
6271
|
`Unknown style "${unknownStyle}". Defined: ${Object.keys(manifest.styles).join(", ") || "(none)"}`
|
|
6196
6272
|
);
|
|
6197
6273
|
}
|
|
6198
6274
|
}
|
|
6199
6275
|
for (const unknownAsset of filter?.assets ?? []) {
|
|
6200
6276
|
if (!manifest.assets[unknownAsset]) {
|
|
6201
|
-
throw new
|
|
6277
|
+
throw new UsageError(`Unknown asset "${unknownAsset}".`);
|
|
6202
6278
|
}
|
|
6203
6279
|
}
|
|
6204
6280
|
const requestedAssetIds = new Set(filter?.assets?.length ? filter.assets : Object.keys(manifest.assets));
|
|
@@ -7232,7 +7308,7 @@ var WorkspaceSchema = z4.object({
|
|
|
7232
7308
|
function parseWorkspace(raw) {
|
|
7233
7309
|
const parsed = WorkspaceSchema.safeParse(raw);
|
|
7234
7310
|
if (parsed.success) return parsed.data;
|
|
7235
|
-
throw new
|
|
7311
|
+
throw new ProjectError(
|
|
7236
7312
|
`Workspace catalog is not valid v1:
|
|
7237
7313
|
${parsed.error.issues.slice(0, 5).map((i) => ` ${i.path.join(".")}: ${i.message}`).join("\n")}`
|
|
7238
7314
|
);
|
|
@@ -7243,7 +7319,7 @@ async function loadWorkspace(workspacePath) {
|
|
|
7243
7319
|
try {
|
|
7244
7320
|
raw = JSON.parse(await readFile11(workspacePath, "utf8"));
|
|
7245
7321
|
} catch (err) {
|
|
7246
|
-
throw new
|
|
7322
|
+
throw new ProjectError(
|
|
7247
7323
|
`Workspace catalog at ${workspacePath} is malformed:
|
|
7248
7324
|
${err instanceof Error ? err.message : String(err)}`
|
|
7249
7325
|
);
|
|
@@ -7885,14 +7961,14 @@ function accountProviderId(manifest, requested, command) {
|
|
|
7885
7961
|
const providers = manifestProviderIds(manifest);
|
|
7886
7962
|
if (requested) {
|
|
7887
7963
|
if (!providers.includes(requested)) {
|
|
7888
|
-
throw new
|
|
7964
|
+
throw new UsageError(
|
|
7889
7965
|
`Provider "${requested}" is not used by this manifest. Used: ${providers.join(", ")}.`
|
|
7890
7966
|
);
|
|
7891
7967
|
}
|
|
7892
7968
|
return requested;
|
|
7893
7969
|
}
|
|
7894
7970
|
if (providers.length > 1) {
|
|
7895
|
-
throw new
|
|
7971
|
+
throw new UsageError(
|
|
7896
7972
|
`${command} is account-scoped and this manifest uses ${providers.join(", ")}. Pass --provider <id>.`
|
|
7897
7973
|
);
|
|
7898
7974
|
}
|
|
@@ -7903,13 +7979,13 @@ function budgetsForPlan(plan, args) {
|
|
|
7903
7979
|
const keyed = Object.entries(args.providerBudgets);
|
|
7904
7980
|
for (const [provider] of keyed) {
|
|
7905
7981
|
if (!providerIds.has(provider)) {
|
|
7906
|
-
throw new
|
|
7982
|
+
throw new BudgetError(
|
|
7907
7983
|
`Budget names provider "${provider}", but this run has work only for ${[...providerIds].join(", ") || "no providers"}.`
|
|
7908
7984
|
);
|
|
7909
7985
|
}
|
|
7910
7986
|
}
|
|
7911
7987
|
if (plan.groups.length > 1 && args.budget !== void 0) {
|
|
7912
|
-
throw new
|
|
7988
|
+
throw new BudgetError(
|
|
7913
7989
|
"A mixed-provider run needs provider-keyed budgets, for example --budget pixellab=40 --budget retrodiffusion=1.25."
|
|
7914
7990
|
);
|
|
7915
7991
|
}
|
|
@@ -7917,7 +7993,7 @@ function budgetsForPlan(plan, args) {
|
|
|
7917
7993
|
for (const group of plan.groups) {
|
|
7918
7994
|
const ceiling = args.providerBudgets[group.provider];
|
|
7919
7995
|
if (plan.groups.length > 1 && group.costUnit !== "free" && group.cost > 0 && ceiling === void 0) {
|
|
7920
|
-
throw new
|
|
7996
|
+
throw new BudgetError(
|
|
7921
7997
|
`Mixed-provider run is missing --budget ${group.provider}=<amount> for ${formatCost(group.costUnit, group.cost)} of planned work.`
|
|
7922
7998
|
);
|
|
7923
7999
|
}
|
|
@@ -7927,14 +8003,14 @@ function budgetsForPlan(plan, args) {
|
|
|
7927
8003
|
}
|
|
7928
8004
|
async function requireCompleteWorkspaceClaims(workspacePath) {
|
|
7929
8005
|
if (!existsSync15(workspacePath)) {
|
|
7930
|
-
throw new
|
|
8006
|
+
throw new ProjectError(`Workspace catalog not found: ${workspacePath}`);
|
|
7931
8007
|
}
|
|
7932
8008
|
const dir = path20.dirname(path20.resolve(workspacePath));
|
|
7933
8009
|
const ws = await loadWorkspace(workspacePath);
|
|
7934
8010
|
const diagnostics = validateWorkspace(ws, dir);
|
|
7935
8011
|
const errors = diagnostics.filter((d) => d.level === "error");
|
|
7936
8012
|
if (errors.length) {
|
|
7937
|
-
throw new
|
|
8013
|
+
throw new ProjectError(
|
|
7938
8014
|
`Workspace catalog at ${workspacePath} is not safe to derive a claim set from:
|
|
7939
8015
|
` + errors.map((d) => ` ${d.id}: ${d.message}`).join("\n")
|
|
7940
8016
|
);
|
|
@@ -7953,7 +8029,7 @@ async function openAccountProject(args) {
|
|
|
7953
8029
|
}
|
|
7954
8030
|
async function open(args) {
|
|
7955
8031
|
if (!existsSync15(path20.resolve(args.manifest))) {
|
|
7956
|
-
throw new
|
|
8032
|
+
throw new ProjectError(
|
|
7957
8033
|
`No manifest at ${path20.resolve(args.manifest)}. Pass --manifest, or run \`pixelkiln init --from <dir>\`.`
|
|
7958
8034
|
);
|
|
7959
8035
|
}
|
|
@@ -7962,7 +8038,7 @@ async function open(args) {
|
|
|
7962
8038
|
let specs = project.specs;
|
|
7963
8039
|
const accountProvider = ACCOUNT_COMMANDS.has(args.command) ? accountProviderId(loaded.manifest, args.provider, args.command) : void 0;
|
|
7964
8040
|
if (args.provider && !accountProvider) {
|
|
7965
|
-
throw new
|
|
8041
|
+
throw new UsageError("--provider is only used by balance, adopt, salvage, purge, or workspace add.");
|
|
7966
8042
|
}
|
|
7967
8043
|
if (accountProvider) specs = specs.filter((spec) => spec.provider === accountProvider);
|
|
7968
8044
|
const accountManifest = accountProvider ? {
|
|
@@ -7975,7 +8051,7 @@ async function open(args) {
|
|
|
7975
8051
|
)
|
|
7976
8052
|
} : loaded.manifest;
|
|
7977
8053
|
if (accountProvider && args.styles.some((styleId) => !accountManifest.styles[styleId])) {
|
|
7978
|
-
throw new
|
|
8054
|
+
throw new UsageError(`Selected style is not assigned to provider "${accountProvider}".`);
|
|
7979
8055
|
}
|
|
7980
8056
|
return { loaded, specs, lock, accountProvider, accountManifest };
|
|
7981
8057
|
}
|
|
@@ -9010,8 +9086,9 @@ async function revertGeneration(provider, spec, lock, lockPath, opts) {
|
|
|
9010
9086
|
const file = currentEntryOutputPath(entry, spec, i);
|
|
9011
9087
|
if (!existsSync18(file)) continue;
|
|
9012
9088
|
if (await sha256File(file) !== entry.outputs[i].sha256 && !opts.force) {
|
|
9013
|
-
throw new
|
|
9014
|
-
`${path26.relative(process.cwd(), file)} was changed after download; keep it with pixelkiln edit, or pass --force to replace it
|
|
9089
|
+
throw new OverwriteRefusedError(
|
|
9090
|
+
`${path26.relative(process.cwd(), file)} was changed after download; keep it with pixelkiln edit, or pass --force to replace it`,
|
|
9091
|
+
[file]
|
|
9015
9092
|
);
|
|
9016
9093
|
}
|
|
9017
9094
|
}
|
|
@@ -10080,7 +10157,7 @@ async function submit(provider, loaded, items, lock, lockPath, opts = {}) {
|
|
|
10080
10157
|
if (opts.budget != null) {
|
|
10081
10158
|
const cost = [...estimates.values()].reduce((sum, estimate) => sum + estimate.amount, 0);
|
|
10082
10159
|
if (cost > opts.budget) {
|
|
10083
|
-
throw new
|
|
10160
|
+
throw new BudgetError(
|
|
10084
10161
|
`This run would spend ${formatCost(unit, cost)} but the budget is ${formatCost(unit, opts.budget)}. Narrow it with --only/--style, or raise --budget.`
|
|
10085
10162
|
);
|
|
10086
10163
|
}
|
|
@@ -11690,12 +11767,12 @@ async function runGenerate(args) {
|
|
|
11690
11767
|
balance: ${formatCost(balance.unit, balance.remaining)} remaining (${group.provider})`
|
|
11691
11768
|
);
|
|
11692
11769
|
if (balance.unit !== group.costUnit) {
|
|
11693
|
-
throw new
|
|
11770
|
+
throw new BudgetError(
|
|
11694
11771
|
`Provider ${group.provider} estimate unit ${group.costUnit} does not match balance unit ${balance.unit}.`
|
|
11695
11772
|
);
|
|
11696
11773
|
}
|
|
11697
11774
|
if (balance.unit !== "free" && group.cost > balance.remaining) {
|
|
11698
|
-
throw new
|
|
11775
|
+
throw new BudgetError(
|
|
11699
11776
|
`${group.provider} needs ${formatCost(balance.unit, group.cost)} but only ${formatCost(balance.unit, balance.remaining)} remain.`
|
|
11700
11777
|
);
|
|
11701
11778
|
}
|
|
@@ -11707,7 +11784,7 @@ async function runGenerate(args) {
|
|
|
11707
11784
|
}
|
|
11708
11785
|
const ceiling = budgets.get(group.provider);
|
|
11709
11786
|
if (ceiling !== void 0 && group.cost > ceiling) {
|
|
11710
|
-
throw new
|
|
11787
|
+
throw new BudgetError(
|
|
11711
11788
|
`${group.provider} would spend ${formatCost(group.costUnit, group.cost)} but its budget is ${formatCost(group.costUnit, ceiling)}.`
|
|
11712
11789
|
);
|
|
11713
11790
|
}
|
|
@@ -13522,7 +13599,10 @@ async function snapshotQualityBaseline(inputs, baselinePath, options = {}) {
|
|
|
13522
13599
|
if (existsSync28(absolute) && !options.force) {
|
|
13523
13600
|
const current = await readFile24(absolute);
|
|
13524
13601
|
if (!current.equals(data)) {
|
|
13525
|
-
throw new
|
|
13602
|
+
throw new OverwriteRefusedError(
|
|
13603
|
+
`Quality baseline already exists with different content: ${absolute}. Pass --force to replace it.`,
|
|
13604
|
+
[absolute]
|
|
13605
|
+
);
|
|
13526
13606
|
}
|
|
13527
13607
|
}
|
|
13528
13608
|
const result = await writeArtifactBundle([{ path: absolute, data }]);
|
|
@@ -14051,8 +14131,9 @@ async function installRecipe(target, options = {}) {
|
|
|
14051
14131
|
if (!existsSync29(file.path)) continue;
|
|
14052
14132
|
const current = await readFile26(file.path);
|
|
14053
14133
|
if (!current.equals(file.data)) {
|
|
14054
|
-
throw new
|
|
14055
|
-
`Recipe destination has local changes: ${file.path}. Choose another --out or pass --force to replace declared recipe files
|
|
14134
|
+
throw new OverwriteRefusedError(
|
|
14135
|
+
`Recipe destination has local changes: ${file.path}. Choose another --out or pass --force to replace declared recipe files.`,
|
|
14136
|
+
[file.path]
|
|
14056
14137
|
);
|
|
14057
14138
|
}
|
|
14058
14139
|
}
|
|
@@ -14519,9 +14600,12 @@ async function main(argv = process.argv.slice(2)) {
|
|
|
14519
14600
|
|
|
14520
14601
|
// src/cli.ts
|
|
14521
14602
|
main().catch((err) => {
|
|
14603
|
+
const message6 = err instanceof Error ? err.message : String(err);
|
|
14604
|
+
const hint = err instanceof PixelKilnError && err.hint ? ` ${err.hint}
|
|
14605
|
+
` : "";
|
|
14522
14606
|
console.error(`
|
|
14523
|
-
error: ${
|
|
14524
|
-
`);
|
|
14525
|
-
process.exit(
|
|
14607
|
+
error: ${message6}
|
|
14608
|
+
${hint}`);
|
|
14609
|
+
process.exit(exitCodeFor(err));
|
|
14526
14610
|
});
|
|
14527
14611
|
//# sourceMappingURL=cli.js.map
|