pixelkiln 0.24.0 → 0.26.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.d.ts +2 -0
- package/dist/cli.js +140 -46
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +130 -42
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +32 -11
- package/dist/index.d.ts +32 -11
- package/dist/index.js +130 -42
- package/dist/index.js.map +1 -1
- package/docs/CLI.md +18 -0
- package/docs/PIXELLAB.md +12 -0
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -1515,6 +1515,10 @@ var import_node_fs = require("fs");
|
|
|
1515
1515
|
var import_node_crypto = require("crypto");
|
|
1516
1516
|
var import_node_os = __toESM(require("os"), 1);
|
|
1517
1517
|
var import_node_path = __toESM(require("path"), 1);
|
|
1518
|
+
function pixelLabObjectUrl(generator, objectId) {
|
|
1519
|
+
if (!objectId || generator !== "map" && generator !== "1dir") return null;
|
|
1520
|
+
return `https://www.pixellab.ai/objects/${encodeURIComponent(objectId)}`;
|
|
1521
|
+
}
|
|
1518
1522
|
var PixelLabProvider = class _PixelLabProvider {
|
|
1519
1523
|
constructor(client) {
|
|
1520
1524
|
this.client = client;
|
|
@@ -7967,14 +7971,16 @@ var import_node_path17 = __toESM(require("path"), 1);
|
|
|
7967
7971
|
async function fetchAssets(provider, specs, lock, lockPath, opts = {}) {
|
|
7968
7972
|
const log = opts.onProgress ?? (() => {
|
|
7969
7973
|
});
|
|
7970
|
-
const result = { downloaded: 0, skipped: 0, failed: 0 };
|
|
7974
|
+
const result = { downloaded: 0, skipped: 0, failed: 0, ...opts.refresh ? { unchanged: 0 } : {} };
|
|
7971
7975
|
const specByKey = new Map(specs.map((s) => [lockKey(s.styleId, s.assetId), s]));
|
|
7972
7976
|
normalizeLockOutputPaths(lock, specs);
|
|
7973
7977
|
const cacheDir = opts.cacheDir === false ? null : import_node_path17.default.resolve(opts.cacheDir ?? import_node_path17.default.join(import_node_path17.default.dirname(lockPath), ".pixelkiln", "cache"));
|
|
7974
7978
|
const pending = Object.entries(lock.entries).filter(([key, e]) => {
|
|
7975
7979
|
if (e.provider !== provider.id || !specByKey.has(key)) return false;
|
|
7976
7980
|
if (e.status === "selected" || e.status === "download-failed") return true;
|
|
7977
|
-
if (
|
|
7981
|
+
if (e.status !== "downloaded") return false;
|
|
7982
|
+
if (opts.refresh) return e.outputs.length > 0 && Boolean(e.sourceUrls?.length || e.sourceUrl);
|
|
7983
|
+
if (!opts.repair) return false;
|
|
7978
7984
|
const spec = specByKey.get(key);
|
|
7979
7985
|
return e.outputs.length === 0 || !spec || e.outputs.some((output) => !(0, import_node_fs14.existsSync)(resolveOutputPath(output.path, spec.root)));
|
|
7980
7986
|
});
|
|
@@ -8006,6 +8012,7 @@ async function fetchAssets(provider, specs, lock, lockPath, opts = {}) {
|
|
|
8006
8012
|
continue;
|
|
8007
8013
|
}
|
|
8008
8014
|
const outputs = [];
|
|
8015
|
+
let wrote = false;
|
|
8009
8016
|
try {
|
|
8010
8017
|
for (let index = 0; index < sources.length; index++) {
|
|
8011
8018
|
const source = sources[index];
|
|
@@ -8013,41 +8020,55 @@ async function fetchAssets(provider, specs, lock, lockPath, opts = {}) {
|
|
|
8013
8020
|
(o) => source.role ? o.role === source.role : !o.role && sources.length === 1
|
|
8014
8021
|
);
|
|
8015
8022
|
const target = recorded ? resolveOutputPath(recorded.path, spec.root) : expectedOutputPath(spec, source.role, index, sources.length, source.mediaType);
|
|
8023
|
+
let fresh = null;
|
|
8024
|
+
if (opts.refresh) {
|
|
8025
|
+
if (!source.url) throw new Error(`no durable source URL to refresh ${source.role ?? "asset"} from`);
|
|
8026
|
+
fresh = await provider.download(source.url);
|
|
8027
|
+
if (recorded && sha256(fresh) === recorded.sha256) {
|
|
8028
|
+
log(` current ${import_node_path17.default.relative(process.cwd(), target)}`);
|
|
8029
|
+
outputs.push({ ...recorded, path: portableOutputPath(target, spec.root) });
|
|
8030
|
+
continue;
|
|
8031
|
+
}
|
|
8032
|
+
}
|
|
8016
8033
|
if ((0, import_node_fs14.existsSync)(target)) {
|
|
8017
8034
|
const currentHash = await sha256File(target);
|
|
8018
8035
|
if (recorded && currentHash === recorded.sha256) {
|
|
8019
|
-
if (
|
|
8020
|
-
|
|
8021
|
-
|
|
8022
|
-
|
|
8023
|
-
|
|
8024
|
-
|
|
8025
|
-
|
|
8036
|
+
if (!fresh) {
|
|
8037
|
+
if (cacheDir) {
|
|
8038
|
+
await cacheMedia(
|
|
8039
|
+
cacheDir,
|
|
8040
|
+
await (0, import_promises11.readFile)(target),
|
|
8041
|
+
recorded.mediaType ?? MediaType.PNG,
|
|
8042
|
+
recorded.sha256
|
|
8043
|
+
);
|
|
8044
|
+
}
|
|
8045
|
+
outputs.push({ ...recorded, path: portableOutputPath(target, spec.root) });
|
|
8046
|
+
continue;
|
|
8026
8047
|
}
|
|
8027
|
-
|
|
8028
|
-
|
|
8029
|
-
|
|
8030
|
-
|
|
8031
|
-
|
|
8032
|
-
|
|
8033
|
-
|
|
8034
|
-
|
|
8035
|
-
|
|
8036
|
-
|
|
8037
|
-
|
|
8038
|
-
|
|
8039
|
-
|
|
8040
|
-
|
|
8041
|
-
|
|
8042
|
-
|
|
8048
|
+
log(` changed ${import_node_path17.default.relative(process.cwd(), target)} (upstream)`);
|
|
8049
|
+
} else {
|
|
8050
|
+
const superseded = (entry.supersededOutputs ?? []).find(
|
|
8051
|
+
(output, oldIndex, all) => currentOutputPath(output, spec, oldIndex, all.length) === target
|
|
8052
|
+
);
|
|
8053
|
+
if (!opts.force) {
|
|
8054
|
+
if (!recorded && superseded && currentHash === superseded.sha256) {
|
|
8055
|
+
} else if (recorded || superseded) {
|
|
8056
|
+
throw new Error(
|
|
8057
|
+
`refusing to overwrite modified output ${target}; pass --force to replace it`
|
|
8058
|
+
);
|
|
8059
|
+
} else {
|
|
8060
|
+
throw new Error(
|
|
8061
|
+
`refusing to overwrite untracked output ${target}; pass --force to replace it`
|
|
8062
|
+
);
|
|
8063
|
+
}
|
|
8043
8064
|
}
|
|
8065
|
+
log(
|
|
8066
|
+
` replace ${import_node_path17.default.relative(process.cwd(), target)}` + (opts.force ? " (--force)" : " (previous tracked generation)")
|
|
8067
|
+
);
|
|
8044
8068
|
}
|
|
8045
|
-
log(
|
|
8046
|
-
` replace ${import_node_path17.default.relative(process.cwd(), target)}` + (opts.force ? " (--force)" : " (previous tracked generation)")
|
|
8047
|
-
);
|
|
8048
8069
|
}
|
|
8049
8070
|
const expectedMediaType = source.mediaType ?? recorded?.mediaType ?? MediaType.PNG;
|
|
8050
|
-
let buf = recorded && cacheDir ? await readCachedMedia(cacheDir, recorded.sha256, expectedMediaType) : null;
|
|
8071
|
+
let buf = fresh ?? (recorded && cacheDir ? await readCachedMedia(cacheDir, recorded.sha256, expectedMediaType) : null);
|
|
8051
8072
|
if (buf) {
|
|
8052
8073
|
log(` cached ${import_node_path17.default.relative(process.cwd(), target)}`);
|
|
8053
8074
|
} else {
|
|
@@ -8079,8 +8100,13 @@ async function fetchAssets(provider, specs, lock, lockPath, opts = {}) {
|
|
|
8079
8100
|
upsert(lock, key, { outputs: mergeOutputs(entry.outputs, outputs) });
|
|
8080
8101
|
await saveLock(lockPath, lock);
|
|
8081
8102
|
await (0, import_promises11.rename)(tmp, target);
|
|
8103
|
+
wrote = true;
|
|
8082
8104
|
log(` wrote ${import_node_path17.default.relative(process.cwd(), target)}`);
|
|
8083
8105
|
}
|
|
8106
|
+
if (opts.refresh && !wrote) {
|
|
8107
|
+
result.unchanged = (result.unchanged ?? 0) + 1;
|
|
8108
|
+
continue;
|
|
8109
|
+
}
|
|
8084
8110
|
const persistentSources = sources.filter((source) => shouldPersistSourceUrl(source.url));
|
|
8085
8111
|
upsert(lock, key, {
|
|
8086
8112
|
status: "downloaded",
|
|
@@ -9964,6 +9990,8 @@ async function buildGallerySnapshot(opts) {
|
|
|
9964
9990
|
source: spec.source ?? null,
|
|
9965
9991
|
edit,
|
|
9966
9992
|
editStatus,
|
|
9993
|
+
upstreamUrl: entry?.provider === "pixellab" ? pixelLabObjectUrl(entry.generator, entry.objectId) : null,
|
|
9994
|
+
refreshable: Boolean(entry && entry.status === "downloaded" && entry.outputs.length && (entry.sourceUrls?.length || entry.sourceUrl)),
|
|
9967
9995
|
tags: spec.tags,
|
|
9968
9996
|
category: asset?.category ?? null
|
|
9969
9997
|
});
|
|
@@ -10018,6 +10046,8 @@ async function buildGallerySnapshot(opts) {
|
|
|
10018
10046
|
source: null,
|
|
10019
10047
|
edit: null,
|
|
10020
10048
|
editStatus: null,
|
|
10049
|
+
upstreamUrl: entry.provider === "pixellab" ? pixelLabObjectUrl(entry.generator, entry.objectId) : null,
|
|
10050
|
+
refreshable: false,
|
|
10021
10051
|
tags: [],
|
|
10022
10052
|
category: null
|
|
10023
10053
|
});
|
|
@@ -10689,7 +10719,7 @@ function renderJobs() {
|
|
|
10689
10719
|
const row = el('div', 'job');
|
|
10690
10720
|
const ph = el('span', 'ph');
|
|
10691
10721
|
ph.append(el('i', 'dot ' + PHASE_TONE[job.phase]), document.createTextNode(job.phase));
|
|
10692
|
-
const what = (job.mode === 'resume' ? 'resume ' : 'generate ') + job.keys.length + (job.keys.length === 1 ? ' asset' : ' assets') +
|
|
10722
|
+
const what = (job.mode === 'refresh' ? 'pull upstream for ' : job.mode === 'resume' ? 'resume ' : 'generate ') + job.keys.length + (job.keys.length === 1 ? ' asset' : ' assets') +
|
|
10693
10723
|
(job.project ? ' in ' + job.project : '');
|
|
10694
10724
|
const last = el('span', 'last', what + (job.messages.length ? ' \u2014 ' + job.messages[job.messages.length - 1].trim() : ''));
|
|
10695
10725
|
last.title = job.keys.join('\\n');
|
|
@@ -10728,13 +10758,15 @@ function budgetLine() {
|
|
|
10728
10758
|
|
|
10729
10759
|
// The confirm step: what will be sent, what it is estimated to cost, and what
|
|
10730
10760
|
// this session may still spend. Mirrors gen's "Spend \u2026 on N asset(s)?" prompt.
|
|
10731
|
-
function generateDialog(items, { project = null, force = false, resume = false } = {}) {
|
|
10761
|
+
function generateDialog(items, { project = null, force = false, resume = false, refresh = false } = {}) {
|
|
10732
10762
|
const host = $('dialog-host');
|
|
10733
10763
|
host.textContent = '';
|
|
10734
10764
|
const wrap = el('div', 'dialog');
|
|
10735
10765
|
const form = el('form');
|
|
10736
|
-
|
|
10737
|
-
|
|
10766
|
+
const noun = items.length + (items.length === 1 ? ' asset' : ' assets');
|
|
10767
|
+
form.append(el('h3', null, refresh ? 'Pull upstream changes for ' + noun
|
|
10768
|
+
: resume ? 'Resume ' + noun
|
|
10769
|
+
: (force ? 'Regenerate ' : 'Generate ') + noun));
|
|
10738
10770
|
const table = el('table');
|
|
10739
10771
|
const thead = el('tr'); thead.append(el('th', null, 'asset'), el('th', null, 'state'), el('th', 'n', 'candidates'), el('th', 'n', 'estimate'));
|
|
10740
10772
|
table.append(thead);
|
|
@@ -10742,16 +10774,18 @@ function generateDialog(items, { project = null, force = false, resume = false }
|
|
|
10742
10774
|
for (const item of items) {
|
|
10743
10775
|
const tr = el('tr');
|
|
10744
10776
|
tr.append(el('td', 'mono', item.key), el('td', null, item.state), el('td', 'n', item.candidates ?? '\u2014'),
|
|
10745
|
-
el('td', 'n', resume ? 'no cost' : fmtCost(item.costUnit, item.estimatedCost ?? 0)));
|
|
10777
|
+
el('td', 'n', resume || refresh ? 'no cost' : fmtCost(item.costUnit, item.estimatedCost ?? 0)));
|
|
10746
10778
|
table.append(tr);
|
|
10747
|
-
if (!resume) {
|
|
10779
|
+
if (!resume && !refresh) {
|
|
10748
10780
|
const g = byProvider.get(item.provider) || { cost: 0, unit: item.costUnit };
|
|
10749
10781
|
g.cost += item.estimatedCost ?? 0; byProvider.set(item.provider, g);
|
|
10750
10782
|
}
|
|
10751
10783
|
}
|
|
10752
10784
|
form.append(table);
|
|
10753
10785
|
const sum = el('div', 'sum');
|
|
10754
|
-
if (
|
|
10786
|
+
if (refresh) {
|
|
10787
|
+
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.'));
|
|
10788
|
+
} else if (resume) {
|
|
10755
10789
|
sum.append(el('div', null, 'Polls, reviews, and downloads existing provider work. Nothing is submitted.'));
|
|
10756
10790
|
} else {
|
|
10757
10791
|
for (const [provider, g] of byProvider) {
|
|
@@ -10768,7 +10802,7 @@ function generateDialog(items, { project = null, force = false, resume = false }
|
|
|
10768
10802
|
}
|
|
10769
10803
|
form.append(sum);
|
|
10770
10804
|
const actions = el('div', 'actions');
|
|
10771
|
-
const go = el('button', 'primary', resume ? 'Resume' : 'Generate');
|
|
10805
|
+
const go = el('button', 'primary', refresh ? 'Pull changes' : resume ? 'Resume' : 'Generate');
|
|
10772
10806
|
go.type = 'submit';
|
|
10773
10807
|
const cancel = el('button', null, 'Cancel'); cancel.type = 'button'; cancel.onclick = () => { host.textContent = ''; };
|
|
10774
10808
|
const msg = el('span', 'msg');
|
|
@@ -10782,6 +10816,7 @@ function generateDialog(items, { project = null, force = false, resume = false }
|
|
|
10782
10816
|
if (project) body.project = project;
|
|
10783
10817
|
if (force) body.force = true;
|
|
10784
10818
|
if (resume) body.resume = true;
|
|
10819
|
+
if (refresh) body.refresh = true;
|
|
10785
10820
|
const job = await postGenerate(body);
|
|
10786
10821
|
host.textContent = '';
|
|
10787
10822
|
GEN.jobs.unshift(job);
|
|
@@ -11071,6 +11106,13 @@ function renderMain(items) {
|
|
|
11071
11106
|
g.onclick = () => generateDialog(snap.items.filter((i) => i.project === s.project && s.actionable.keys.includes(i.key)), { project: s.project });
|
|
11072
11107
|
tools.append(g);
|
|
11073
11108
|
}
|
|
11109
|
+
const refreshable = snap.items.filter((i) => i.project === s.project && i.styleId === s.id && i.refreshable && i.upstreamUrl);
|
|
11110
|
+
if (GENERATION && refreshable.length) {
|
|
11111
|
+
const r = el('button', null, 'Pull upstream ' + refreshable.length); r.type = 'button';
|
|
11112
|
+
r.title = 'Re-download objects edited in the provider\u2019s own editor; no cost';
|
|
11113
|
+
r.onclick = () => generateDialog(refreshable, { project: s.project, refresh: true });
|
|
11114
|
+
tools.append(r);
|
|
11115
|
+
}
|
|
11074
11116
|
const styleKey = 'style:' + (s.project || '') + ':' + s.id;
|
|
11075
11117
|
if (EDITABLE && s.outDir) {
|
|
11076
11118
|
const es = el('button', null, ui.editing === styleKey ? 'Cancel' : 'Edit style');
|
|
@@ -11164,6 +11206,30 @@ function candidatesControl(style) {
|
|
|
11164
11206
|
return wrap;
|
|
11165
11207
|
}
|
|
11166
11208
|
|
|
11209
|
+
function upstreamSection(item) {
|
|
11210
|
+
if (!item.upstreamUrl && !(GENERATION && item.refreshable)) return null;
|
|
11211
|
+
const s = el('section', 'meta');
|
|
11212
|
+
s.append(el('h3', null, 'Upstream'));
|
|
11213
|
+
const dl = el('dl');
|
|
11214
|
+
if (item.upstreamUrl) {
|
|
11215
|
+
const a = el('a', null, 'Open in ' + item.provider + ' \u2197');
|
|
11216
|
+
a.href = item.upstreamUrl; a.target = '_blank'; a.rel = 'noopener';
|
|
11217
|
+
row(dl, 'object', a);
|
|
11218
|
+
row(dl, 'note', item.provider === 'pixellab'
|
|
11219
|
+
? 'The account object this generation came from. Edit it there with PixelLab\u2019s editor, then pull the changes back here; the lockfile records the new bytes as this generation.'
|
|
11220
|
+
: 'The provider\u2019s page for this object.');
|
|
11221
|
+
}
|
|
11222
|
+
s.append(dl);
|
|
11223
|
+
if (GENERATION && item.refreshable) {
|
|
11224
|
+
const acts = el('div', 'hand-actions');
|
|
11225
|
+
const b = el('button', null, 'Pull upstream changes \xB7 no cost'); b.type = 'button';
|
|
11226
|
+
b.onclick = () => generateDialog([item], { project: item.project, refresh: true });
|
|
11227
|
+
acts.append(b);
|
|
11228
|
+
s.append(acts);
|
|
11229
|
+
}
|
|
11230
|
+
return s;
|
|
11231
|
+
}
|
|
11232
|
+
|
|
11167
11233
|
function generateActions(item) {
|
|
11168
11234
|
const row = el('div', 'gen');
|
|
11169
11235
|
if (!GENERATION || !item.declared || item.currentSpecHash === null) return row;
|
|
@@ -11882,6 +11948,8 @@ function renderDrawer() {
|
|
|
11882
11948
|
|
|
11883
11949
|
const hand = handEditSection(item);
|
|
11884
11950
|
if (hand) body.append(hand);
|
|
11951
|
+
const upstream = upstreamSection(item);
|
|
11952
|
+
if (upstream) body.append(upstream);
|
|
11885
11953
|
|
|
11886
11954
|
if (item.revision || item.revisionParentKey) {
|
|
11887
11955
|
const { s, dl } = section('Lineage');
|
|
@@ -12375,8 +12443,10 @@ var GenerateRequestSchema = import_zod9.z.object({
|
|
|
12375
12443
|
/** Regenerate work that is up to date, as `gen --force` does. */
|
|
12376
12444
|
force: import_zod9.z.boolean().optional(),
|
|
12377
12445
|
/** Advance in-flight, review, or selected work without submitting anything. */
|
|
12378
|
-
resume: import_zod9.z.boolean().optional()
|
|
12379
|
-
|
|
12446
|
+
resume: import_zod9.z.boolean().optional(),
|
|
12447
|
+
/** Re-download downloaded outputs and replace files whose object changed upstream. */
|
|
12448
|
+
refresh: import_zod9.z.boolean().optional()
|
|
12449
|
+
}).strict().refine((request) => !(request.resume && request.refresh), { message: "resume and refresh are exclusive" });
|
|
12380
12450
|
var GenerateRequestError = class extends Error {
|
|
12381
12451
|
constructor(message6, status = 400) {
|
|
12382
12452
|
super(message6);
|
|
@@ -12434,6 +12504,20 @@ function createGenerateHandlers(opts) {
|
|
|
12434
12504
|
}
|
|
12435
12505
|
return groups;
|
|
12436
12506
|
};
|
|
12507
|
+
async function refresh(job, ctx, specs) {
|
|
12508
|
+
job.phase = "fetching";
|
|
12509
|
+
for (const [providerId, providerSpecs] of groupByRecordedProvider(specs, ctx.lock)) {
|
|
12510
|
+
const provider = opts.providerFor(job.project ?? void 0, providerId);
|
|
12511
|
+
const res = await fetchAssets(provider, providerSpecs, ctx.lock, ctx.lockPath, {
|
|
12512
|
+
onProgress: (line) => say(job, line),
|
|
12513
|
+
refresh: true
|
|
12514
|
+
});
|
|
12515
|
+
job.counts.downloaded += res.downloaded;
|
|
12516
|
+
say(job, `${providerId}: ${res.downloaded} changed upstream and replaced, ${res.unchanged ?? 0} unchanged, ${res.skipped} skipped, ${res.failed} failed`);
|
|
12517
|
+
}
|
|
12518
|
+
job.phase = "done";
|
|
12519
|
+
job.finishedAt = now().toISOString();
|
|
12520
|
+
}
|
|
12437
12521
|
async function settle(job, ctx, specs) {
|
|
12438
12522
|
job.phase = "polling";
|
|
12439
12523
|
for (const [providerId, providerSpecs] of groupByRecordedProvider(specs, ctx.lock)) {
|
|
@@ -12467,6 +12551,10 @@ function createGenerateHandlers(opts) {
|
|
|
12467
12551
|
}
|
|
12468
12552
|
async function run(job, ctx, specs, groups) {
|
|
12469
12553
|
try {
|
|
12554
|
+
if (job.mode === "refresh") {
|
|
12555
|
+
await refresh(job, ctx, specs);
|
|
12556
|
+
return;
|
|
12557
|
+
}
|
|
12470
12558
|
if (job.mode === "generate") {
|
|
12471
12559
|
job.phase = "submitting";
|
|
12472
12560
|
for (const group of groups) {
|
|
@@ -12530,7 +12618,7 @@ function createGenerateHandlers(opts) {
|
|
|
12530
12618
|
if (holder) throw new GenerateRequestError(`"${key}" is already being worked on by job ${holder}`, 409);
|
|
12531
12619
|
}
|
|
12532
12620
|
let groups = [];
|
|
12533
|
-
if (!request.resume) {
|
|
12621
|
+
if (!request.resume && !request.refresh) {
|
|
12534
12622
|
const plan = await buildPlan(specs, ctx.lock, { force: request.force });
|
|
12535
12623
|
if (!plan.actionable.length) {
|
|
12536
12624
|
const reasons = plan.items.map((item) => `${item.key}: ${item.state} \u2014 ${item.reason}`).slice(0, 5);
|
|
@@ -12550,7 +12638,7 @@ function createGenerateHandlers(opts) {
|
|
|
12550
12638
|
id: (0, import_node_crypto8.randomBytes)(8).toString("hex"),
|
|
12551
12639
|
project: request.project ?? null,
|
|
12552
12640
|
keys: request.keys,
|
|
12553
|
-
mode: request.resume ? "resume" : "generate",
|
|
12641
|
+
mode: request.refresh ? "refresh" : request.resume ? "resume" : "generate",
|
|
12554
12642
|
phase: "queued",
|
|
12555
12643
|
startedAt: now().toISOString(),
|
|
12556
12644
|
finishedAt: null,
|
|
@@ -12562,7 +12650,7 @@ function createGenerateHandlers(opts) {
|
|
|
12562
12650
|
};
|
|
12563
12651
|
jobs.set(job.id, job);
|
|
12564
12652
|
contexts.set(job.id, ctx);
|
|
12565
|
-
say(job, 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("; "));
|
|
12653
|
+
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("; "));
|
|
12566
12654
|
void run(job, ctx, specs, groups);
|
|
12567
12655
|
return job;
|
|
12568
12656
|
},
|