pixelkiln 0.30.0 → 0.32.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 +119 -23
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +117 -21
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +19 -3
- package/dist/index.d.ts +19 -3
- package/dist/index.js +118 -22
- package/dist/index.js.map +1 -1
- package/docs/CLI.md +18 -5
- package/docs/GETTING_STARTED.md +2 -1
- package/docs/RECOVERY.md +3 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -6431,7 +6431,7 @@ async function poll(provider, lock, lockPath, opts = {}) {
|
|
|
6431
6431
|
}
|
|
6432
6432
|
|
|
6433
6433
|
// src/pipeline/fetch.ts
|
|
6434
|
-
import { existsSync as existsSync10 } from "fs";
|
|
6434
|
+
import { existsSync as existsSync10, readFileSync as readFileSync4 } from "fs";
|
|
6435
6435
|
import { mkdir as mkdir3, readFile as readFile7, rename as rename3, rm as rm4, writeFile as writeFile3 } from "fs/promises";
|
|
6436
6436
|
import path14 from "path";
|
|
6437
6437
|
|
|
@@ -6487,7 +6487,12 @@ async function fetchAssets(provider, specs, lock, lockPath, opts = {}) {
|
|
|
6487
6487
|
if (opts.refresh) return e.outputs.length > 0 && Boolean(e.sourceUrls?.length || e.sourceUrl);
|
|
6488
6488
|
if (!opts.repair) return false;
|
|
6489
6489
|
const spec = specByKey.get(key);
|
|
6490
|
-
|
|
6490
|
+
if (e.outputs.length === 0 || !spec) return true;
|
|
6491
|
+
return e.outputs.some((output) => {
|
|
6492
|
+
const file = resolveOutputPath(output.path, spec.root);
|
|
6493
|
+
if (!existsSync10(file)) return true;
|
|
6494
|
+
return Boolean(opts.force) && sha256(readFileSync4(file)) !== output.sha256;
|
|
6495
|
+
});
|
|
6491
6496
|
});
|
|
6492
6497
|
const concurrency = Math.min(Math.max(1, opts.concurrency ?? 8), Math.max(1, pending.length));
|
|
6493
6498
|
let cursor = 0;
|
|
@@ -9364,6 +9369,7 @@ function renderGallery(snapshot, opts = {}) {
|
|
|
9364
9369
|
.sheet.editor-host { width:min(1400px, 96vw); grid-template-rows:auto 1fr; }
|
|
9365
9370
|
.sheet.editor-host .rbar b { font:650 12.5px/1.4 var(--mono); color:var(--text); }
|
|
9366
9371
|
.sheet.editor-host .rbar .acts { margin-left:auto; display:flex; gap:8px; align-items:center; }
|
|
9372
|
+
.sheet.editor-host .rbar .chip { color:var(--dim); font-size:12.5px; display:inline-flex; align-items:center; gap:5px; margin-right:6px; }
|
|
9367
9373
|
.sheet.editor-host .rbar .st { display:inline-flex; align-items:center; gap:6px; font:650 11.5px/1 var(--mono); }
|
|
9368
9374
|
.sheet.editor-host .rbar .msg { color:var(--bad); font-size:12.5px; }
|
|
9369
9375
|
.sheet.editor-host .stage { position:relative; min-height:0; }
|
|
@@ -9411,6 +9417,8 @@ function renderGallery(snapshot, opts = {}) {
|
|
|
9411
9417
|
.tray > button { padding:5px 11px; font-size:12.5px; }
|
|
9412
9418
|
@media (max-width: 900px) { .sheet.review-host, .sheet.compare-host, .sheet.editor-host { width:100vw; } }
|
|
9413
9419
|
.drawer .gen { display:flex; gap:8px; flex-wrap:wrap; margin-top:12px; }
|
|
9420
|
+
.drawer .readonly-hint { margin-top:10px; font-size:12.5px; line-height:1.5; }
|
|
9421
|
+
.drawer .readonly-hint code { font:12px var(--mono); color:var(--text); }
|
|
9414
9422
|
.pair { display:grid; grid-template-columns:1fr 1fr; gap:8px; margin:8px 0 10px; }
|
|
9415
9423
|
.pair figure { margin:0; border:1px solid var(--line); background:var(--panel-deep); background-image:var(--checker);
|
|
9416
9424
|
background-size:12px 12px; background-position:0 0,6px 6px; display:grid; place-items:center; padding:8px; min-height:96px; }
|
|
@@ -9620,7 +9628,7 @@ function renderJobs() {
|
|
|
9620
9628
|
const row = el('div', 'job');
|
|
9621
9629
|
const ph = el('span', 'ph');
|
|
9622
9630
|
ph.append(el('i', 'dot ' + PHASE_TONE[job.phase]), document.createTextNode(job.phase));
|
|
9623
|
-
const what = (job.mode === 'refresh' ? 'pull upstream for ' : job.mode === 'resume' ? 'resume ' : 'generate ') + job.keys.length + (job.keys.length === 1 ? ' asset' : ' assets') +
|
|
9631
|
+
const what = (job.mode === 'refresh' ? 'pull upstream for ' : job.mode === 'restore' ? 'restore ' : job.mode === 'resume' ? 'resume ' : 'generate ') + job.keys.length + (job.keys.length === 1 ? ' asset' : ' assets') +
|
|
9624
9632
|
(job.project ? ' in ' + job.project : '');
|
|
9625
9633
|
const last = el('span', 'last', what + (job.messages.length ? ' \u2014 ' + job.messages[job.messages.length - 1].trim() : ''));
|
|
9626
9634
|
last.title = job.keys.join('\\n');
|
|
@@ -9745,13 +9753,16 @@ function budgetLine() {
|
|
|
9745
9753
|
|
|
9746
9754
|
// The confirm step: what will be sent, what it is estimated to cost, and what
|
|
9747
9755
|
// this session may still spend. Mirrors gen's "Spend \u2026 on N asset(s)?" prompt.
|
|
9748
|
-
function generateDialog(items, { project = null, force = false, resume = false, refresh = false } = {}) {
|
|
9756
|
+
function generateDialog(items, { project = null, force = false, resume = false, refresh = false, restore = false } = {}) {
|
|
9749
9757
|
const host = $('dialog-host');
|
|
9750
9758
|
host.textContent = '';
|
|
9751
9759
|
const wrap = el('div', 'dialog');
|
|
9752
9760
|
const form = el('form');
|
|
9753
9761
|
const noun = items.length + (items.length === 1 ? ' asset' : ' assets');
|
|
9762
|
+
const free = resume || refresh || restore;
|
|
9763
|
+
const replacing = items.filter((i) => i.state === 'orphaned' && i.outputs.every((o) => o.exists) || i.state === 'untracked');
|
|
9754
9764
|
form.append(el('h3', null, refresh ? 'Pull upstream changes for ' + noun
|
|
9765
|
+
: restore ? 'Restore ' + noun
|
|
9755
9766
|
: resume ? 'Resume ' + noun
|
|
9756
9767
|
: (force ? 'Regenerate ' : 'Generate ') + noun));
|
|
9757
9768
|
const table = el('table');
|
|
@@ -9761,9 +9772,9 @@ function generateDialog(items, { project = null, force = false, resume = false,
|
|
|
9761
9772
|
for (const item of items) {
|
|
9762
9773
|
const tr = el('tr');
|
|
9763
9774
|
tr.append(el('td', 'mono', item.key), el('td', null, item.state), el('td', 'n', item.candidates ?? '\u2014'),
|
|
9764
|
-
el('td', 'n',
|
|
9775
|
+
el('td', 'n', free ? 'no cost' : fmtCost(item.costUnit, item.estimatedCost ?? 0)));
|
|
9765
9776
|
table.append(tr);
|
|
9766
|
-
if (!
|
|
9777
|
+
if (!free) {
|
|
9767
9778
|
const g = byProvider.get(item.provider) || { cost: 0, unit: item.costUnit };
|
|
9768
9779
|
g.cost += item.estimatedCost ?? 0; byProvider.set(item.provider, g);
|
|
9769
9780
|
}
|
|
@@ -9772,6 +9783,9 @@ function generateDialog(items, { project = null, force = false, resume = false,
|
|
|
9772
9783
|
const sum = el('div', 'sum');
|
|
9773
9784
|
if (refresh) {
|
|
9774
9785
|
sum.append(el('div', null, 'Re-downloads each object from the provider and replaces the local file only when the object changed upstream \u2014 for example after editing it in the provider\u2019s own editor. Unchanged objects are left alone; a file you changed locally is refused. Nothing is submitted.'));
|
|
9786
|
+
} else if (restore) {
|
|
9787
|
+
sum.append(el('div', null, 'Puts the recorded bytes back on disk from the local cache or the provider object. Nothing is generated or submitted.'));
|
|
9788
|
+
if (force && replacing.length) sum.append(el('div', 'warn', 'The file on disk was changed after PixelKiln wrote it. Restoring discards that change. To keep it instead, cancel and use Edit by hand or Edit in browser first: the edit is copied beside the generated file, and the record stays intact.'));
|
|
9775
9789
|
} else if (resume) {
|
|
9776
9790
|
sum.append(el('div', null, 'Polls, reviews, and downloads existing provider work. Nothing is submitted.'));
|
|
9777
9791
|
} else {
|
|
@@ -9784,12 +9798,13 @@ function generateDialog(items, { project = null, force = false, resume = false,
|
|
|
9784
9798
|
if (left !== null && g.cost > left) line.className = 'warn';
|
|
9785
9799
|
sum.append(line);
|
|
9786
9800
|
}
|
|
9787
|
-
if (force) sum.append(el('div', 'warn', '
|
|
9801
|
+
if (force && replacing.length) sum.append(el('div', 'warn', (replacing.length === 1 ? 'This file' : replacing.length + ' of these files') + ' changed on disk after PixelKiln wrote it (or was never recorded). Generating replaces it and that change is lost. To keep it, cancel and use Edit by hand or Edit in browser first.'));
|
|
9802
|
+
else if (force) sum.append(el('div', 'warn', 'Regenerating replaces the current art. The previous file is replaced when the new result is fetched; the provider objects it came from are not deleted.'));
|
|
9788
9803
|
sum.append(el('div', null, 'Candidate sets land in review; you choose from them here before anything is downloaded.'));
|
|
9789
9804
|
}
|
|
9790
9805
|
form.append(sum);
|
|
9791
9806
|
const actions = el('div', 'actions');
|
|
9792
|
-
const go = el('button', 'primary', refresh ? 'Pull changes' : resume ? 'Resume' : 'Generate');
|
|
9807
|
+
const go = el('button', 'primary', refresh ? 'Pull changes' : restore ? (force && replacing.length ? 'Restore and discard my change' : 'Restore') : resume ? 'Resume' : force && replacing.length ? 'Generate and replace' : 'Generate');
|
|
9793
9808
|
go.type = 'submit';
|
|
9794
9809
|
const cancel = el('button', null, 'Cancel'); cancel.type = 'button'; cancel.onclick = () => { host.textContent = ''; };
|
|
9795
9810
|
const msg = el('span', 'msg');
|
|
@@ -9804,6 +9819,7 @@ function generateDialog(items, { project = null, force = false, resume = false,
|
|
|
9804
9819
|
if (force) body.force = true;
|
|
9805
9820
|
if (resume) body.resume = true;
|
|
9806
9821
|
if (refresh) body.refresh = true;
|
|
9822
|
+
if (restore) body.restore = true;
|
|
9807
9823
|
const job = await postGenerate(body);
|
|
9808
9824
|
host.textContent = '';
|
|
9809
9825
|
GEN.jobs.unshift(job);
|
|
@@ -9892,11 +9908,16 @@ function openEditorSheet(item) {
|
|
|
9892
9908
|
const status = el('span', 'st');
|
|
9893
9909
|
const acts = el('div', 'acts');
|
|
9894
9910
|
const msg = el('span', 'msg');
|
|
9911
|
+
const compare = el('label', 'chip'); compare.hidden = true;
|
|
9912
|
+
compare.title = 'The generated art on a locked layer at 50%, for comparison; never part of the saved file';
|
|
9913
|
+
const compareBox = el('input'); compareBox.type = 'checkbox'; compareBox.checked = true;
|
|
9914
|
+
compareBox.onchange = () => showReference(compareBox.checked);
|
|
9915
|
+
compare.append(compareBox, document.createTextNode(' show generated'));
|
|
9895
9916
|
const save = el('button', 'primary', 'Save to project'); save.type = 'button';
|
|
9896
9917
|
save.onclick = () => requestEditorSave(false);
|
|
9897
9918
|
const saveClose = el('button', null, 'Save & close'); saveClose.type = 'button';
|
|
9898
9919
|
saveClose.onclick = () => requestEditorSave(true);
|
|
9899
|
-
acts.append(msg, save, saveClose);
|
|
9920
|
+
acts.append(msg, compare, save, saveClose);
|
|
9900
9921
|
bar.append(close, title, status, acts);
|
|
9901
9922
|
const stage = el('div', 'stage');
|
|
9902
9923
|
const frame = el('iframe');
|
|
@@ -9909,7 +9930,7 @@ function openEditorSheet(item) {
|
|
|
9909
9930
|
stage.append(frame, loading);
|
|
9910
9931
|
panel.append(bar, stage);
|
|
9911
9932
|
host.append(scrim, panel);
|
|
9912
|
-
SHEET = { item, frame, loading, status, msg, save, saveClose, dirty: false, opened: false, ready: null, pending: null, requests: 0, source: src, sentProject: false, roles: null };
|
|
9933
|
+
SHEET = { item, frame, loading, status, msg, save, saveClose, compare, dirty: false, opened: false, ready: null, pending: null, requests: 0, source: src, sentProject: false, sentReference: false, reference: false, roles: null };
|
|
9913
9934
|
sheetStatus('cool', 'loading');
|
|
9914
9935
|
sheetButtons();
|
|
9915
9936
|
close.focus({ preventScroll: true });
|
|
@@ -9929,7 +9950,11 @@ async function sendOpen() {
|
|
|
9929
9950
|
sheetStatus('bad', 'unsupported');
|
|
9930
9951
|
return;
|
|
9931
9952
|
}
|
|
9953
|
+
// When an edit is being opened, the generated art rides along as a
|
|
9954
|
+
// reference layer: what the editor shows through at 50% for comparison.
|
|
9955
|
+
const referenceFiles = sources.fromEdit && SHEET.ready.version >= 4 && item.outputs.every((o) => o.exists && o.url) ? item.outputs : null;
|
|
9932
9956
|
const pngs = [];
|
|
9957
|
+
const reference = [];
|
|
9933
9958
|
let pxo = null;
|
|
9934
9959
|
try {
|
|
9935
9960
|
for (const file of sources.files) {
|
|
@@ -9941,6 +9966,10 @@ async function sendOpen() {
|
|
|
9941
9966
|
const p = await fetch(projectUrl, { cache: 'no-store' });
|
|
9942
9967
|
if (p.ok) pxo = await p.arrayBuffer();
|
|
9943
9968
|
}
|
|
9969
|
+
for (const file of referenceFiles || []) {
|
|
9970
|
+
const res = await fetch(file.url, { cache: 'no-store' });
|
|
9971
|
+
if (res.ok) reference.push({ role: file.role || null, png: await res.arrayBuffer() });
|
|
9972
|
+
}
|
|
9944
9973
|
} catch (err) {
|
|
9945
9974
|
if (SHEET) { SHEET.msg.textContent = 'Could not load the image: ' + err.message; sheetStatus('bad', 'failed'); }
|
|
9946
9975
|
return;
|
|
@@ -9965,9 +9994,18 @@ async function sendOpen() {
|
|
|
9965
9994
|
transfer.push(message.png);
|
|
9966
9995
|
}
|
|
9967
9996
|
if (pxo) { message.pxo = pxo; transfer.push(pxo); }
|
|
9997
|
+
if (reference.length === (referenceFiles || []).length && reference.length) {
|
|
9998
|
+
message.reference = reference;
|
|
9999
|
+
for (const r of reference) transfer.push(r.png);
|
|
10000
|
+
}
|
|
10001
|
+
SHEET.sentReference = !!message.reference;
|
|
9968
10002
|
SHEET.frame.contentWindow.postMessage(message, location.origin, transfer);
|
|
9969
10003
|
sheetStatus('cool', 'opening');
|
|
9970
10004
|
}
|
|
10005
|
+
function showReference(visible) {
|
|
10006
|
+
if (!SHEET || !SHEET.opened || !SHEET.reference) return;
|
|
10007
|
+
SHEET.frame.contentWindow.postMessage({ type: 'pixelkiln:reference', visible }, location.origin);
|
|
10008
|
+
}
|
|
9971
10009
|
function requestEditorSave(close) {
|
|
9972
10010
|
if (!SHEET || !SHEET.opened || SHEET.pending) return;
|
|
9973
10011
|
const request = 'save-' + (++SHEET.requests);
|
|
@@ -9991,10 +10029,13 @@ async function onEditorMessage(m) {
|
|
|
9991
10029
|
case 'pixelkiln:opened':
|
|
9992
10030
|
SHEET.opened = true;
|
|
9993
10031
|
SHEET.loading.remove();
|
|
10032
|
+
SHEET.reference = !!m.reference;
|
|
10033
|
+
SHEET.compare.hidden = !SHEET.reference;
|
|
9994
10034
|
if (m.source === 'pxo') sheetStatus('ok', 'editing the edit file' + (m.frames > 1 ? 's' : '') + ' with ' + (m.layers === 1 ? 'the layer' : m.layers + ' layers') + (m.frames > 1 ? ' and ' + m.frames + ' ' + memberNoun(SHEET.item, m.frames) : '') + ' restored');
|
|
9995
10035
|
else if (SHEET.sentProject) sheetStatus('warn', 'editing the flattened edit file' + (m.frames > 1 ? 's' : '') + '; the layer file could not be opened');
|
|
9996
10036
|
else if (m.source === 'frames') sheetStatus('ok', (SHEET.item.edits.length ? 'editing the edit files' : 'editing a copy of the generated ' + memberNoun(SHEET.item, m.frames)) + ' as ' + m.frames + ' frames' + (isFrameSet(SHEET.item) ? '' : ', one per ' + memberNoun(SHEET.item, 1)));
|
|
9997
10037
|
else sheetStatus('ok', SHEET.item.edit && SHEET.item.edit.exists ? 'editing the edit file' : 'editing a copy of the generated art');
|
|
10038
|
+
if (SHEET.reference) SHEET.status.append(document.createTextNode(' \xB7 generated art shown through at 50%'));
|
|
9998
10039
|
sheetButtons();
|
|
9999
10040
|
break;
|
|
10000
10041
|
case 'pixelkiln:dirty':
|
|
@@ -10437,6 +10478,13 @@ function generateActions(item) {
|
|
|
10437
10478
|
};
|
|
10438
10479
|
if (item.state === 'missing' || item.state === 'stale' || item.state === 'failed') add('Generate' + cost, 'primary', {});
|
|
10439
10480
|
else if (item.state === 'ok') add('Regenerate' + cost, null, { force: true });
|
|
10481
|
+
else if (item.state === 'orphaned' && item.outputs.every((o) => o.exists)) {
|
|
10482
|
+
// The file changed after download. Putting the recorded bytes back and
|
|
10483
|
+
// regenerating both discard that change, so both say so and ask.
|
|
10484
|
+
add('Restore recorded bytes \xB7 no cost', null, { restore: true, force: true });
|
|
10485
|
+
add('Regenerate' + cost, null, { force: true });
|
|
10486
|
+
} else if (item.state === 'orphaned') add('Restore \xB7 no cost', 'primary', { restore: true });
|
|
10487
|
+
else if (item.state === 'untracked') add('Generate' + cost + ' \xB7 replaces the file', null, { force: true });
|
|
10440
10488
|
if (item.state === 'in-flight' || item.state === 'recoverable') {
|
|
10441
10489
|
// A job already parked this asset in review: go straight to its sheet.
|
|
10442
10490
|
const holder = GEN.jobs.find((job) => job.phase === 'review' && job.project === item.project && job.review.includes(item.key));
|
|
@@ -11127,6 +11175,15 @@ function renderDrawer() {
|
|
|
11127
11175
|
if (ui.notice && ui.notice.id === item.id) body.append(el('div', 'notice', ui.notice.text));
|
|
11128
11176
|
body.append(stateNode(item.state, item.reason));
|
|
11129
11177
|
if (GENERATION) body.append(generateActions(item));
|
|
11178
|
+
// Writes are opt-in flags on the command line; say so where the buttons would be.
|
|
11179
|
+
if ((!EDITABLE || !GENERATION) && item.declared) {
|
|
11180
|
+
const missing = [];
|
|
11181
|
+
if (!EDITABLE) missing.push('--edit to change its prompt, size, and tags or open it in the browser editor');
|
|
11182
|
+
if (!GENERATION) missing.push('--budget <n> to generate or regenerate it from here');
|
|
11183
|
+
const hint = el('div', 'state-dim readonly-hint');
|
|
11184
|
+
hint.append(document.createTextNode('This gallery is read-only. Restart it with '), el('code', null, 'pixelkiln gallery'), document.createTextNode(' plus ' + missing.join(', and ') + '.'));
|
|
11185
|
+
body.append(hint);
|
|
11186
|
+
}
|
|
11130
11187
|
if (canEdit && ui.editing === item.id) body.append(editForm(item));
|
|
11131
11188
|
// Plan already quotes the error as the reason for a failed entry; only a
|
|
11132
11189
|
// stale or superseded failure needs its own line.
|
|
@@ -11717,13 +11774,21 @@ import { z as z8 } from "zod";
|
|
|
11717
11774
|
var GenerateRequestSchema = z8.object({
|
|
11718
11775
|
project: z8.string().min(1).optional(),
|
|
11719
11776
|
keys: z8.array(z8.string().min(1)).min(1).max(500),
|
|
11720
|
-
/**
|
|
11777
|
+
/**
|
|
11778
|
+
* Regenerate work that is up to date, as `gen --force` does, and let the
|
|
11779
|
+
* fetch replace a locally modified or untracked file. With `restore`, put
|
|
11780
|
+
* the recorded bytes back over a modified file.
|
|
11781
|
+
*/
|
|
11721
11782
|
force: z8.boolean().optional(),
|
|
11722
11783
|
/** Advance in-flight, review, or selected work without submitting anything. */
|
|
11723
11784
|
resume: z8.boolean().optional(),
|
|
11724
11785
|
/** Re-download downloaded outputs and replace files whose object changed upstream. */
|
|
11725
|
-
refresh: z8.boolean().optional()
|
|
11726
|
-
|
|
11786
|
+
refresh: z8.boolean().optional(),
|
|
11787
|
+
/** Put recorded outputs back on disk from the cache or the provider, without generating. */
|
|
11788
|
+
restore: z8.boolean().optional()
|
|
11789
|
+
}).strict().refine((request) => [request.resume, request.refresh, request.restore].filter(Boolean).length <= 1, {
|
|
11790
|
+
message: "resume, refresh, and restore are exclusive"
|
|
11791
|
+
});
|
|
11727
11792
|
var GenerateRequestError = class extends Error {
|
|
11728
11793
|
constructor(message6, status = 400) {
|
|
11729
11794
|
super(message6);
|
|
@@ -11811,7 +11876,9 @@ function createGenerateHandlers(opts) {
|
|
|
11811
11876
|
for (const [providerId, providerSpecs] of groupByRecordedProvider(specs, ctx.lock)) {
|
|
11812
11877
|
const provider = opts.providerFor(job.project ?? void 0, providerId);
|
|
11813
11878
|
const res = await fetchAssets(provider, providerSpecs, ctx.lock, ctx.lockPath, {
|
|
11814
|
-
onProgress: (line) => say(job, line)
|
|
11879
|
+
onProgress: (line) => say(job, line),
|
|
11880
|
+
// A forced regeneration replaces whatever is on disk, as `gen --force`.
|
|
11881
|
+
force: job.force
|
|
11815
11882
|
});
|
|
11816
11883
|
job.counts.downloaded += res.downloaded;
|
|
11817
11884
|
if (res.downloaded || res.failed) {
|
|
@@ -11826,12 +11893,32 @@ function createGenerateHandlers(opts) {
|
|
|
11826
11893
|
job.finishedAt = now().toISOString();
|
|
11827
11894
|
}
|
|
11828
11895
|
}
|
|
11896
|
+
async function restore(job, ctx, specs) {
|
|
11897
|
+
job.phase = "fetching";
|
|
11898
|
+
for (const [providerId, providerSpecs] of groupByRecordedProvider(specs, ctx.lock)) {
|
|
11899
|
+
const provider = opts.providerFor(job.project ?? void 0, providerId);
|
|
11900
|
+
const res = await fetchAssets(provider, providerSpecs, ctx.lock, ctx.lockPath, {
|
|
11901
|
+
onProgress: (line) => say(job, line),
|
|
11902
|
+
repair: true,
|
|
11903
|
+
force: job.force
|
|
11904
|
+
});
|
|
11905
|
+
job.counts.downloaded += res.downloaded;
|
|
11906
|
+
say(job, `${providerId}: restored ${res.downloaded}, skipped ${res.skipped}, failed ${res.failed}`);
|
|
11907
|
+
if (res.failed) throw new Error(`${res.failed} file(s) could not be restored`);
|
|
11908
|
+
}
|
|
11909
|
+
job.phase = "done";
|
|
11910
|
+
job.finishedAt = now().toISOString();
|
|
11911
|
+
}
|
|
11829
11912
|
async function run(job, ctx, specs, groups) {
|
|
11830
11913
|
try {
|
|
11831
11914
|
if (job.mode === "refresh") {
|
|
11832
11915
|
await refresh(job, ctx, specs);
|
|
11833
11916
|
return;
|
|
11834
11917
|
}
|
|
11918
|
+
if (job.mode === "restore") {
|
|
11919
|
+
await restore(job, ctx, specs);
|
|
11920
|
+
return;
|
|
11921
|
+
}
|
|
11835
11922
|
if (job.mode === "generate") {
|
|
11836
11923
|
job.phase = "submitting";
|
|
11837
11924
|
for (const group of groups) {
|
|
@@ -11894,8 +11981,16 @@ function createGenerateHandlers(opts) {
|
|
|
11894
11981
|
const holder = busy.get(`${request.project ?? ""}:${key}`);
|
|
11895
11982
|
if (holder) throw new GenerateRequestError(`"${key}" is already being worked on by job ${holder}`, 409);
|
|
11896
11983
|
}
|
|
11984
|
+
if (request.restore) {
|
|
11985
|
+
for (const key of request.keys) {
|
|
11986
|
+
const entry = ctx.lock.entries[key];
|
|
11987
|
+
if (!entry || entry.status !== "downloaded" || !entry.outputs.length) {
|
|
11988
|
+
throw new GenerateRequestError(`"${key}" has no downloaded output recorded to restore`);
|
|
11989
|
+
}
|
|
11990
|
+
}
|
|
11991
|
+
}
|
|
11897
11992
|
let groups = [];
|
|
11898
|
-
if (!request.resume && !request.refresh) {
|
|
11993
|
+
if (!request.resume && !request.refresh && !request.restore) {
|
|
11899
11994
|
const plan = await buildPlan(specs, ctx.lock, { force: request.force });
|
|
11900
11995
|
if (!plan.actionable.length) {
|
|
11901
11996
|
const reasons = plan.items.map((item) => `${item.key}: ${item.state} \u2014 ${item.reason}`).slice(0, 5);
|
|
@@ -11915,7 +12010,8 @@ function createGenerateHandlers(opts) {
|
|
|
11915
12010
|
id: randomBytes2(8).toString("hex"),
|
|
11916
12011
|
project: request.project ?? null,
|
|
11917
12012
|
keys: request.keys,
|
|
11918
|
-
mode: request.refresh ? "refresh" : request.resume ? "resume" : "generate",
|
|
12013
|
+
mode: request.refresh ? "refresh" : request.resume ? "resume" : request.restore ? "restore" : "generate",
|
|
12014
|
+
force: Boolean(request.force),
|
|
11919
12015
|
phase: "queued",
|
|
11920
12016
|
startedAt: now().toISOString(),
|
|
11921
12017
|
finishedAt: null,
|
|
@@ -11927,7 +12023,7 @@ function createGenerateHandlers(opts) {
|
|
|
11927
12023
|
};
|
|
11928
12024
|
jobs.set(job.id, job);
|
|
11929
12025
|
contexts.set(job.id, ctx);
|
|
11930
|
-
say(job, request.refresh ? `checking ${request.keys.length} asset(s) upstream at no cost` : request.resume ? `resuming ${request.keys.length} asset(s) at no cost` : `generating ${groups.reduce((n, g) => n + g.actionable.length, 0)} asset(s): ` + groups.map((g) => `${g.provider} ${formatCost(g.costUnit, g.cost)}`).join("; "));
|
|
12026
|
+
say(job, request.refresh ? `checking ${request.keys.length} asset(s) upstream at no cost` : request.restore ? `restoring ${request.keys.length} asset(s) at no cost${request.force ? " (replacing modified files)" : ""}` : request.resume ? `resuming ${request.keys.length} asset(s) at no cost` : `generating ${groups.reduce((n, g) => n + g.actionable.length, 0)} asset(s): ` + groups.map((g) => `${g.provider} ${formatCost(g.costUnit, g.cost)}`).join("; "));
|
|
11931
12027
|
void run(job, ctx, specs, groups);
|
|
11932
12028
|
return job;
|
|
11933
12029
|
},
|
|
@@ -11992,8 +12088,8 @@ var pin_default = {
|
|
|
11992
12088
|
pixelorama: "v1.2.2",
|
|
11993
12089
|
godot: "4.7.2",
|
|
11994
12090
|
extensionsApi: 9,
|
|
11995
|
-
protocol:
|
|
11996
|
-
release: "editor-pixelorama-v1.2.2-pk.
|
|
12091
|
+
protocol: 4,
|
|
12092
|
+
release: "editor-pixelorama-v1.2.2-pk.18",
|
|
11997
12093
|
files: {
|
|
11998
12094
|
"index.apple-touch-icon.png": {
|
|
11999
12095
|
sha256: "ee5434873e0fed91e9a888c3ea5a8b79beac25999308be7a6eb41bf4ce640d27",
|
|
@@ -12008,7 +12104,7 @@ var pin_default = {
|
|
|
12008
12104
|
bytes: 7298
|
|
12009
12105
|
},
|
|
12010
12106
|
"index.html": {
|
|
12011
|
-
sha256: "
|
|
12107
|
+
sha256: "d9adf06036660c4b76ef5386e872bc03dc0d5b0628fd6ac660d480893ee1f47a",
|
|
12012
12108
|
bytes: 5442
|
|
12013
12109
|
},
|
|
12014
12110
|
"index.icon.png": {
|
|
@@ -12020,8 +12116,8 @@ var pin_default = {
|
|
|
12020
12116
|
bytes: 279815
|
|
12021
12117
|
},
|
|
12022
12118
|
"index.pck": {
|
|
12023
|
-
sha256: "
|
|
12024
|
-
bytes:
|
|
12119
|
+
sha256: "a41d9c3a39aa99ea5ee49ddcedfeb8b092006aa75c0de67139f13297c17f42e4",
|
|
12120
|
+
bytes: 6431692
|
|
12025
12121
|
},
|
|
12026
12122
|
"index.png": {
|
|
12027
12123
|
sha256: "6e3ca3eb4a99e4c07bdd89ebc873b440ec0ffd66ce1955160da9e7ed80d538d7",
|
|
@@ -13990,7 +14086,7 @@ function announceGalleryReady(url, count, stdout = process.stdout, stderr = proc
|
|
|
13990
14086
|
gallery of ${count} generation${count === 1 ? "" : "s"}: ${url}
|
|
13991
14087
|
` + (notes.length ? ` (${notes.join("; ")}; Ctrl+C to stop)
|
|
13992
14088
|
|
|
13993
|
-
` : " (read-only;
|
|
14089
|
+
` : " (read-only \u2014 add --edit to change prompts or edit sprites, --budget <n> to generate; Ctrl+C to stop)\n\n")
|
|
13994
14090
|
);
|
|
13995
14091
|
}
|
|
13996
14092
|
function sessionBudget(args) {
|