pixelkiln 0.73.0 → 0.75.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 CHANGED
@@ -14225,6 +14225,7 @@ async function priceManifestEdit(manifestPath, edit) {
14225
14225
  styleId: spec.styleId,
14226
14226
  assetId: spec.assetId,
14227
14227
  change: was === void 0 ? "new" : "changed",
14228
+ provider: spec.provider,
14228
14229
  cost: spec.cost,
14229
14230
  costUnit: spec.costUnit,
14230
14231
  candidates: spec.candidates
@@ -15749,6 +15750,7 @@ ${css}</style>
15749
15750
  <option value="none">Group: none</option>
15750
15751
  </select>
15751
15752
  <span id="editing" class="editing" hidden title="This gallery can write the manifest. It never contacts a provider.">editing</span>
15753
+ <button id="studio" type="button" hidden title="Draft a new character: its style, base, loops, and portrait, priced as you go">+ New character</button>
15752
15754
  <button id="refresh" type="button" title="Re-read the manifest, lockfile, and disk">Refresh</button>
15753
15755
  <label class="chip" title="Refresh every 5 seconds while this tab is visible"><input id="auto" type="checkbox"> auto</label>
15754
15756
  </div>
@@ -17332,6 +17334,70 @@ function createGenerateHandlers(opts) {
17332
17334
  job.phase = "done";
17333
17335
  job.finishedAt = now().toISOString();
17334
17336
  }
17337
+ async function submitGroups(job, ctx, groups) {
17338
+ job.phase = "submitting";
17339
+ for (const group of groups) {
17340
+ const provider = opts.providerFor(job.project ?? void 0, group.provider);
17341
+ const ceiling = remainingFor(group, groups.length, opts.budget, spent);
17342
+ if (provider.balance) {
17343
+ const balance = await provider.balance();
17344
+ say(job, `balance: ${formatCost(balance.unit, balance.remaining)} remaining (${group.provider})`);
17345
+ if (balance.unit !== group.costUnit) {
17346
+ throw new Error(
17347
+ `Provider ${group.provider} estimate unit ${group.costUnit} does not match balance unit ${balance.unit}.`
17348
+ );
17349
+ }
17350
+ if (balance.unit !== "free" && group.cost > balance.remaining) {
17351
+ throw new Error(
17352
+ `${group.provider} needs ${formatCost(balance.unit, group.cost)} but only ${formatCost(balance.unit, balance.remaining)} remain.`
17353
+ );
17354
+ }
17355
+ }
17356
+ const res = await submit(provider, ctx.loaded, group.actionable, ctx.lock, ctx.lockPath, {
17357
+ budget: ceiling,
17358
+ onProgress: (line) => say(job, line),
17359
+ spacingMs: opts.submitSpacingMs
17360
+ });
17361
+ spent[group.provider] = (spent[group.provider] ?? 0) + res.spent;
17362
+ units[group.provider] = res.unit;
17363
+ job.spent[group.provider] = (job.spent[group.provider] ?? 0) + res.spent;
17364
+ job.counts.submitted += res.submitted;
17365
+ job.counts.failed += res.failed;
17366
+ say(job, `${group.provider}: submitted ${res.submitted}, failed ${res.failed}, estimated ${formatCost(res.unit, res.spent)}`);
17367
+ }
17368
+ }
17369
+ async function generateInWaves(job, ctx, specs, groups) {
17370
+ for (let wave = 1; ; wave++) {
17371
+ const submittedBefore = job.counts.submitted;
17372
+ await submitGroups(job, ctx, groups);
17373
+ await settle(job, ctx, specs);
17374
+ if (job.phase !== "done" || job.counts.submitted === submittedBefore) return;
17375
+ const finishedAt = job.finishedAt;
17376
+ const finish = () => {
17377
+ job.phase = "done";
17378
+ job.finishedAt = finishedAt;
17379
+ };
17380
+ job.phase = "queued";
17381
+ job.finishedAt = null;
17382
+ const next = await opts.loadProject(job.project ?? void 0);
17383
+ const byKey = new Map(next.specs.map((spec) => [lockKey(spec.styleId, spec.assetId), spec]));
17384
+ const nextSpecs = job.keys.map((key) => byKey.get(key)).filter((spec) => Boolean(spec));
17385
+ const plan = await buildPlan(nextSpecs, next.lock);
17386
+ if (!plan.actionable.length) return finish();
17387
+ for (const group of plan.groups) {
17388
+ const remaining = remainingFor(group, plan.groups.length, opts.budget, spent);
17389
+ if (group.cost > remaining) {
17390
+ say(job, `wave ${wave + 1} would spend ${formatCost(group.costUnit, group.cost)} on ${group.provider} but only ${formatCost(group.costUnit, remaining)} of this session's budget remain; stopping here`);
17391
+ return finish();
17392
+ }
17393
+ }
17394
+ say(job, `wave ${wave + 1}: ${plan.actionable.length} asset(s) whose parents are now on disk: ` + plan.groups.map((g) => `${g.provider} ${formatCost(g.costUnit, g.cost)}`).join("; "));
17395
+ contexts.set(job.id, next);
17396
+ ctx = next;
17397
+ specs = nextSpecs;
17398
+ groups = plan.groups;
17399
+ }
17400
+ }
17335
17401
  async function run(job, ctx, specs, groups) {
17336
17402
  try {
17337
17403
  if (job.mode === "refresh") {
@@ -17359,36 +17425,8 @@ function createGenerateHandlers(opts) {
17359
17425
  return;
17360
17426
  }
17361
17427
  if (job.mode === "generate") {
17362
- job.phase = "submitting";
17363
- for (const group of groups) {
17364
- const provider = opts.providerFor(job.project ?? void 0, group.provider);
17365
- const ceiling = remainingFor(group, groups.length, opts.budget, spent);
17366
- if (provider.balance) {
17367
- const balance = await provider.balance();
17368
- say(job, `balance: ${formatCost(balance.unit, balance.remaining)} remaining (${group.provider})`);
17369
- if (balance.unit !== group.costUnit) {
17370
- throw new Error(
17371
- `Provider ${group.provider} estimate unit ${group.costUnit} does not match balance unit ${balance.unit}.`
17372
- );
17373
- }
17374
- if (balance.unit !== "free" && group.cost > balance.remaining) {
17375
- throw new Error(
17376
- `${group.provider} needs ${formatCost(balance.unit, group.cost)} but only ${formatCost(balance.unit, balance.remaining)} remain.`
17377
- );
17378
- }
17379
- }
17380
- const res = await submit(provider, ctx.loaded, group.actionable, ctx.lock, ctx.lockPath, {
17381
- budget: ceiling,
17382
- onProgress: (line) => say(job, line),
17383
- spacingMs: opts.submitSpacingMs
17384
- });
17385
- spent[group.provider] = (spent[group.provider] ?? 0) + res.spent;
17386
- units[group.provider] = res.unit;
17387
- job.spent[group.provider] = (job.spent[group.provider] ?? 0) + res.spent;
17388
- job.counts.submitted += res.submitted;
17389
- job.counts.failed += res.failed;
17390
- say(job, `${group.provider}: submitted ${res.submitted}, failed ${res.failed}, estimated ${formatCost(res.unit, res.spent)}`);
17391
- }
17428
+ await generateInWaves(job, ctx, specs, groups);
17429
+ return;
17392
17430
  }
17393
17431
  await settle(job, ctx, specs);
17394
17432
  } catch (error) {