@koda-sl/baker-cli 0.200.0 → 0.201.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/README.md CHANGED
@@ -5280,7 +5280,7 @@ baker landing inspiration scrape <url> --out <dir> # capture any page now, syn
5280
5280
  ```
5281
5281
 
5282
5282
  - **Hybrid search over three signals** — keywords, meaning, and *appearance* (the screenshot is embedded, so a query like "dark developer hero with a terminal" can match a section whose text never says "terminal"). Filters: `--type --composition --register --interaction --motion --media --device --theme --max-rank --min-craft --min-fidelity --domain --similar-to --scope --limit`.
5283
- - **Screenshots are downloaded to `.baker/inspiration/`** so an agent can actually look at a result rather than read a description of it.
5283
+ - **Screenshots are downloaded to `.baker/inspiration/`** so an agent can actually look at a result rather than read a description of it — downscaled to the page's own design width (1440px, WebP) rather than the 2x archive capture R2 holds. A vision model bills by pixel area and downsamples past its own long-edge cap anyway, so the extra resolution bought no legibility and cost context on every one of the eight hits a default search returns.
5284
5284
  - **`fidelity` says how much to trust the code.** A measured 0–1 match between the live section and our standalone re-render, always reported. Above ~0.95 the markup renders like the original; below ~0.75 the section is scroll- or JS-driven and it does not.
5285
5285
  - **`used_on_pages`** appears when a section shows up on more than one of that site's pages — a nav or footer they ship everywhere, which is a stronger reference than a one-off.
5286
5286
  - **The corpus is shared across companies; favorites are per-company**, and search defaults to this company's saved sections.
package/dist/cli.js CHANGED
@@ -8746,11 +8746,11 @@ function rawTextEntries(value) {
8746
8746
  const values = Array.isArray(value) ? value : typeof value === "string" ? [value] : [];
8747
8747
  return values.filter((v) => typeof v === "string").flatMap((v) => v.split(",")).map((v) => v.trim()).filter(Boolean);
8748
8748
  }
8749
- function rawFileEntries(path36) {
8750
- if (typeof path36 !== "string" || path36.length === 0) {
8749
+ function rawFileEntries(path37) {
8750
+ if (typeof path37 !== "string" || path37.length === 0) {
8751
8751
  return [];
8752
8752
  }
8753
- return readFileSync2(path36, "utf8").split(/\r?\n/).map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#"));
8753
+ return readFileSync2(path37, "utf8").split(/\r?\n/).map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#"));
8754
8754
  }
8755
8755
  function keywordEntries(args) {
8756
8756
  const defaultMatch = typeof args["match-type"] === "string" ? args["match-type"].toUpperCase() : void 0;
@@ -8773,19 +8773,19 @@ function keywordEntries(args) {
8773
8773
  }
8774
8774
  return entries;
8775
8775
  }
8776
- function loadJsonFileArg(path36) {
8777
- if (typeof path36 !== "string" || path36.length === 0) {
8776
+ function loadJsonFileArg(path37) {
8777
+ if (typeof path37 !== "string" || path37.length === 0) {
8778
8778
  return {};
8779
8779
  }
8780
8780
  try {
8781
- const parsed = JSON.parse(readFileSync2(path36, "utf8"));
8781
+ const parsed = JSON.parse(readFileSync2(path37, "utf8"));
8782
8782
  if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
8783
- failWriteValidation(`${path36} must contain a JSON object`);
8783
+ failWriteValidation(`${path37} must contain a JSON object`);
8784
8784
  }
8785
8785
  return parsed;
8786
8786
  } catch (err) {
8787
8787
  if (err instanceof SyntaxError) {
8788
- failWriteValidation(`${path36} is not valid JSON: ${err.message}`);
8788
+ failWriteValidation(`${path37} is not valid JSON: ${err.message}`);
8789
8789
  }
8790
8790
  throw err;
8791
8791
  }
@@ -8915,10 +8915,10 @@ async function stageUpdate(kind, customerId, target, payload, hints) {
8915
8915
  async function stageTarget(kind, customerId, target, hints) {
8916
8916
  await stageGoogleOp({ kind, customerId, target }, hints);
8917
8917
  }
8918
- async function draftAction(path36, body, chat) {
8918
+ async function draftAction(path37, body, chat) {
8919
8919
  try {
8920
8920
  const chatId = resolveChatId(chat);
8921
- const response = await apiPost(path36, { chatId, ...body });
8921
+ const response = await apiPost(path37, { chatId, ...body });
8922
8922
  writeJsonEnvelope(response);
8923
8923
  } catch (err) {
8924
8924
  handleGoogleError(err);
@@ -11776,19 +11776,19 @@ function failWriteValidation2(message) {
11776
11776
  writeJsonEnvelope({ ok: false, error: { code: "VALIDATION_ERROR", message } });
11777
11777
  process.exit(1);
11778
11778
  }
11779
- function loadJsonFileArg2(path36) {
11780
- if (typeof path36 !== "string" || path36.length === 0) {
11779
+ function loadJsonFileArg2(path37) {
11780
+ if (typeof path37 !== "string" || path37.length === 0) {
11781
11781
  return {};
11782
11782
  }
11783
11783
  try {
11784
- const parsed = JSON.parse(readFileSync4(path36, "utf8"));
11784
+ const parsed = JSON.parse(readFileSync4(path37, "utf8"));
11785
11785
  if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
11786
- failWriteValidation2(`${path36} must contain a JSON object`);
11786
+ failWriteValidation2(`${path37} must contain a JSON object`);
11787
11787
  }
11788
11788
  return parsed;
11789
11789
  } catch (err) {
11790
11790
  if (err instanceof SyntaxError) {
11791
- failWriteValidation2(`${path36} is not valid JSON: ${err.message}`);
11791
+ failWriteValidation2(`${path37} is not valid JSON: ${err.message}`);
11792
11792
  }
11793
11793
  throw err;
11794
11794
  }
@@ -11873,15 +11873,15 @@ function parseLocaleFlag(value) {
11873
11873
  }
11874
11874
  return { language: match[1], country: match[2].toUpperCase() };
11875
11875
  }
11876
- function loadTargetingFileArg(path36) {
11877
- if (typeof path36 !== "string" || path36.length === 0) {
11876
+ function loadTargetingFileArg(path37) {
11877
+ if (typeof path37 !== "string" || path37.length === 0) {
11878
11878
  return void 0;
11879
11879
  }
11880
- const parsed = loadJsonFileArg2(path36);
11880
+ const parsed = loadJsonFileArg2(path37);
11881
11881
  const criteria = parsed.targetingCriteria ?? parsed;
11882
11882
  if (!criteria.include) {
11883
11883
  failWriteValidation2(
11884
- `${path36} must contain targeting criteria with an "include" block (see baker schema ads.linkedin.campaigns.create)`
11884
+ `${path37} must contain targeting criteria with an "include" block (see baker schema ads.linkedin.campaigns.create)`
11885
11885
  );
11886
11886
  }
11887
11887
  return criteria;
@@ -11916,14 +11916,14 @@ function parseCsvLine(line) {
11916
11916
  cells.push(current);
11917
11917
  return cells.map((cell) => cell.trim());
11918
11918
  }
11919
- function parseListFileArg(path36, maxRows) {
11920
- if (typeof path36 !== "string" || path36.length === 0) {
11919
+ function parseListFileArg(path37, maxRows) {
11920
+ if (typeof path37 !== "string" || path37.length === 0) {
11921
11921
  return void 0;
11922
11922
  }
11923
- const raw = readFileSync4(path36, "utf8");
11923
+ const raw = readFileSync4(path37, "utf8");
11924
11924
  const lines = raw.split(/\r?\n/).filter((line) => line.trim().length > 0);
11925
11925
  if (lines.length < 2) {
11926
- failWriteValidation2(`${path36} needs a header row and at least one data row`);
11926
+ failWriteValidation2(`${path37} needs a header row and at least one data row`);
11927
11927
  }
11928
11928
  const columns = parseCsvLine(lines[0]).map((column) => column.trim());
11929
11929
  const rows = [];
@@ -11942,7 +11942,7 @@ function parseListFileArg(path36, maxRows) {
11942
11942
  }
11943
11943
  }
11944
11944
  if (rows.length > maxRows) {
11945
- failWriteValidation2(`${path36} has ${rows.length} rows \u2014 the inline limit is ${maxRows}. Split the list.`);
11945
+ failWriteValidation2(`${path37} has ${rows.length} rows \u2014 the inline limit is ${maxRows}. Split the list.`);
11946
11946
  }
11947
11947
  return { columns, rows };
11948
11948
  }
@@ -12038,11 +12038,11 @@ function readPositionals(args) {
12038
12038
  function splitIdList(raw) {
12039
12039
  return raw.split(",").map((id) => id.trim()).filter(Boolean);
12040
12040
  }
12041
- function idsFileEntries(path36) {
12042
- if (typeof path36 !== "string" || path36.length === 0) {
12041
+ function idsFileEntries(path37) {
12042
+ if (typeof path37 !== "string" || path37.length === 0) {
12043
12043
  return [];
12044
12044
  }
12045
- return readFileSync4(path36, "utf8").split(/\r?\n/).map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#")).flatMap(splitIdList);
12045
+ return readFileSync4(path37, "utf8").split(/\r?\n/).map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#")).flatMap(splitIdList);
12046
12046
  }
12047
12047
  function requireTargets(args, entity) {
12048
12048
  const positionals = readPositionals(args);
@@ -14665,9 +14665,9 @@ function compactRow(row) {
14665
14665
  ...destination.postUrn ? { postUrn: destination.postUrn } : {}
14666
14666
  };
14667
14667
  }
14668
- function readPath(row, path36) {
14668
+ function readPath(row, path37) {
14669
14669
  let current = row;
14670
- for (const segment of path36.split(".")) {
14670
+ for (const segment of path37.split(".")) {
14671
14671
  const record = asRecord2(current);
14672
14672
  if (!record) return void 0;
14673
14673
  current = record[segment];
@@ -14677,10 +14677,10 @@ function readPath(row, path36) {
14677
14677
  function projectFields(rows, paths) {
14678
14678
  return rows.map((row) => {
14679
14679
  const projected = {};
14680
- for (const path36 of paths) {
14681
- const value = readPath(row, path36);
14680
+ for (const path37 of paths) {
14681
+ const value = readPath(row, path37);
14682
14682
  if (value !== void 0) {
14683
- projected[path36] = value;
14683
+ projected[path37] = value;
14684
14684
  }
14685
14685
  }
14686
14686
  return projected;
@@ -15974,11 +15974,11 @@ var updateStatusSchema = z21.enum(UPDATE_STATUSES);
15974
15974
  function currencyMinimums2(currencyCode) {
15975
15975
  return CURRENCY_MINIMUMS2[currencyCode] ?? DEFAULT_CURRENCY_MINIMUM2;
15976
15976
  }
15977
- function validateDailyBudgetFloor(money, ctx, path36) {
15977
+ function validateDailyBudgetFloor(money, ctx, path37) {
15978
15978
  if (money?.currencyCode) {
15979
15979
  const min = currencyMinimums2(money.currencyCode).dailyBudgetMin;
15980
15980
  if (Number(money.amount) < min) {
15981
- ctx.addIssue({ code: "custom", path: path36, message: `below the ${min} ${money.currencyCode} daily minimum` });
15981
+ ctx.addIssue({ code: "custom", path: path37, message: `below the ${min} ${money.currencyCode} daily minimum` });
15982
15982
  }
15983
15983
  }
15984
15984
  }
@@ -16640,19 +16640,19 @@ function failWriteValidation3(message) {
16640
16640
  writeJsonEnvelope({ ok: false, error: { code: "VALIDATION_ERROR", message } });
16641
16641
  process.exit(1);
16642
16642
  }
16643
- function loadJsonFileArg3(path36) {
16644
- if (typeof path36 !== "string" || path36.length === 0) {
16643
+ function loadJsonFileArg3(path37) {
16644
+ if (typeof path37 !== "string" || path37.length === 0) {
16645
16645
  return {};
16646
16646
  }
16647
16647
  try {
16648
- const parsed = JSON.parse(readFileSync8(path36, "utf8"));
16648
+ const parsed = JSON.parse(readFileSync8(path37, "utf8"));
16649
16649
  if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
16650
- failWriteValidation3(`${path36} must contain a JSON object`);
16650
+ failWriteValidation3(`${path37} must contain a JSON object`);
16651
16651
  }
16652
16652
  return parsed;
16653
16653
  } catch (err) {
16654
16654
  if (err instanceof SyntaxError) {
16655
- failWriteValidation3(`${path36} is not valid JSON: ${err.message}`);
16655
+ failWriteValidation3(`${path37} is not valid JSON: ${err.message}`);
16656
16656
  }
16657
16657
  throw err;
16658
16658
  }
@@ -27823,12 +27823,12 @@ function listFlowSlugs() {
27823
27823
  return readdirSync2(dir, { withFileTypes: true }).filter((entry) => entry.isDirectory() && !entry.name.startsWith("_") && entry.name !== ".gitkeep").map((entry) => entry.name).sort();
27824
27824
  }
27825
27825
  function readFlowTree(slug) {
27826
- const path36 = join3(flowsDir(), slug, "_data.json");
27827
- if (!existsSync5(path36)) {
27826
+ const path37 = join3(flowsDir(), slug, "_data.json");
27827
+ if (!existsSync5(path37)) {
27828
27828
  failLocal(`No form "${slug}". Run "baker flows list" to see the forms in this workspace.`);
27829
27829
  }
27830
27830
  try {
27831
- return JSON.parse(readFileSync9(path36, "utf-8"));
27831
+ return JSON.parse(readFileSync9(path37, "utf-8"));
27832
27832
  } catch (error) {
27833
27833
  failLocal(`Could not read form "${slug}": ${error instanceof Error ? error.message : String(error)}`);
27834
27834
  }
@@ -28241,10 +28241,10 @@ async function stageOps(ops) {
28241
28241
  handleError(err);
28242
28242
  }
28243
28243
  }
28244
- async function draftAction2(path36, body, chat) {
28244
+ async function draftAction2(path37, body, chat) {
28245
28245
  const chatId = resolveChatId(chat);
28246
28246
  try {
28247
- const data = await apiPost(path36, { chatId, ...body });
28247
+ const data = await apiPost(path37, { chatId, ...body });
28248
28248
  writeJsonEnvelope({ ok: true, data });
28249
28249
  return data;
28250
28250
  } catch (err) {
@@ -30265,9 +30265,9 @@ async function readImageBuffer(pathOrUrl) {
30265
30265
  }
30266
30266
  return readFile19(pathOrUrl);
30267
30267
  }
30268
- async function isDirectory(path36) {
30268
+ async function isDirectory(path37) {
30269
30269
  try {
30270
- const s = await stat4(path36);
30270
+ const s = await stat4(path37);
30271
30271
  return s.isDirectory();
30272
30272
  } catch {
30273
30273
  return false;
@@ -30551,13 +30551,13 @@ function resolveDownloadPath({ baseName, extension, out, outIsDirectory: outIsDi
30551
30551
  }
30552
30552
  function disambiguate(paths) {
30553
30553
  const taken = /* @__PURE__ */ new Set();
30554
- return paths.map((path36) => {
30555
- if (!taken.has(path36)) {
30556
- taken.add(path36);
30557
- return path36;
30554
+ return paths.map((path37) => {
30555
+ if (!taken.has(path37)) {
30556
+ taken.add(path37);
30557
+ return path37;
30558
30558
  }
30559
- const ext = extname3(path36);
30560
- const stem = path36.slice(0, path36.length - ext.length);
30559
+ const ext = extname3(path37);
30560
+ const stem = path37.slice(0, path37.length - ext.length);
30561
30561
  let n = 2;
30562
30562
  while (taken.has(`${stem}-${n}${ext}`)) n += 1;
30563
30563
  const unique = `${stem}-${n}${ext}`;
@@ -30684,10 +30684,10 @@ async function runDownloads(plan) {
30684
30684
  const paths = disambiguate(fetched.map((item) => item.path));
30685
30685
  const downloaded = [];
30686
30686
  for (const [index, item] of fetched.entries()) {
30687
- const path36 = paths[index] ?? item.path;
30687
+ const path37 = paths[index] ?? item.path;
30688
30688
  try {
30689
- await atomicWrite(path36, item.buffer);
30690
- downloaded.push({ input: item.input, output: path36, bytes: item.buffer.length, contentType: item.contentType });
30689
+ await atomicWrite(path37, item.buffer);
30690
+ downloaded.push({ input: item.input, output: path37, bytes: item.buffer.length, contentType: item.contentType });
30691
30691
  } catch (err) {
30692
30692
  failed.push({ input: item.input, error: failureMessage(err, "Write failed") });
30693
30693
  }
@@ -37803,9 +37803,46 @@ var scrapeCommand = defineCommand152({
37803
37803
  });
37804
37804
 
37805
37805
  // src/commands/landing/inspiration/search.ts
37806
+ import path35 from "path";
37807
+ import { defineCommand as defineCommand153 } from "citty";
37808
+
37809
+ // src/commands/landing/inspiration/shot.ts
37806
37810
  import { mkdir as mkdir12, writeFile as writeFile17 } from "fs/promises";
37807
37811
  import path34 from "path";
37808
- import { defineCommand as defineCommand153 } from "citty";
37812
+ import sharp6 from "sharp";
37813
+ var READABLE_SHOT = {
37814
+ maxWidth: 1440,
37815
+ maxHeight: 4e3,
37816
+ quality: 82,
37817
+ extension: "webp"
37818
+ };
37819
+ async function toReadableShot(original) {
37820
+ try {
37821
+ return await sharp6(original).resize({
37822
+ width: READABLE_SHOT.maxWidth,
37823
+ height: READABLE_SHOT.maxHeight,
37824
+ fit: "inside",
37825
+ withoutEnlargement: true
37826
+ }).webp({ quality: READABLE_SHOT.quality }).toBuffer();
37827
+ } catch {
37828
+ return original;
37829
+ }
37830
+ }
37831
+ async function downloadReadableShot(url, file) {
37832
+ if (!url) return null;
37833
+ try {
37834
+ const response = await fetch(url);
37835
+ if (!response.ok) return null;
37836
+ const shot = await toReadableShot(Buffer.from(await response.arrayBuffer()));
37837
+ await mkdir12(path34.dirname(file), { recursive: true });
37838
+ await writeFile17(file, shot);
37839
+ return path34.relative(process.cwd(), file);
37840
+ } catch {
37841
+ return null;
37842
+ }
37843
+ }
37844
+
37845
+ // src/commands/landing/inspiration/search.ts
37809
37846
  registerSchema({
37810
37847
  command: "landing.inspiration.search",
37811
37848
  description: "Search the shared library of real landing-page sections for reference before you design. Start here: baker landing inspiration search 'pricing with a monthly/annual toggle'. Returns the idea behind each section plus a downloaded screenshot you can Read. Defaults to this company's saved sections; pass --scope all for the whole library.",
@@ -37891,20 +37928,15 @@ function buildSearchBody(args) {
37891
37928
  return body;
37892
37929
  }
37893
37930
  async function downloadShots(results) {
37894
- const dir = path34.join(process.cwd(), ".baker", "inspiration");
37895
- await mkdir12(dir, { recursive: true });
37931
+ const dir = path35.join(process.cwd(), ".baker", "inspiration");
37896
37932
  const saved = /* @__PURE__ */ new Map();
37897
37933
  await Promise.all(
37898
37934
  results.map(async (result) => {
37899
- if (!result.desktopShotUrl) return;
37900
- try {
37901
- const response = await fetch(result.desktopShotUrl);
37902
- if (!response.ok) return;
37903
- const file = path34.join(dir, `${result.id}.png`);
37904
- await writeFile17(file, Buffer.from(await response.arrayBuffer()));
37905
- saved.set(result.id, path34.relative(process.cwd(), file));
37906
- } catch {
37907
- }
37935
+ const file = await downloadReadableShot(
37936
+ result.desktopShotUrl,
37937
+ path35.join(dir, `${result.id}.${READABLE_SHOT.extension}`)
37938
+ );
37939
+ if (file) saved.set(result.id, file);
37908
37940
  })
37909
37941
  );
37910
37942
  return saved;
@@ -38130,8 +38162,7 @@ var sequencesCommand = defineCommand154({
38130
38162
  });
38131
38163
 
38132
38164
  // src/commands/landing/inspiration/view.ts
38133
- import { mkdir as mkdir13, writeFile as writeFile18 } from "fs/promises";
38134
- import path35 from "path";
38165
+ import path36 from "path";
38135
38166
  import { defineCommand as defineCommand155 } from "citty";
38136
38167
  registerSchema({
38137
38168
  command: "landing.inspiration.view",
@@ -38145,18 +38176,6 @@ registerSchema({
38145
38176
  }
38146
38177
  }
38147
38178
  });
38148
- async function download(url, file) {
38149
- if (!url) return null;
38150
- try {
38151
- const response = await fetch(url);
38152
- if (!response.ok) return null;
38153
- await mkdir13(path35.dirname(file), { recursive: true });
38154
- await writeFile18(file, Buffer.from(await response.arrayBuffer()));
38155
- return path35.relative(process.cwd(), file);
38156
- } catch {
38157
- return null;
38158
- }
38159
- }
38160
38179
  var viewCommand2 = defineCommand155({
38161
38180
  meta: {
38162
38181
  name: "view",
@@ -38176,11 +38195,12 @@ var viewCommand2 = defineCommand155({
38176
38195
  const id = args.id;
38177
38196
  const data = await apiGet("/api/landing-inspiration/section", { id });
38178
38197
  const section = data.section;
38179
- const dir = path35.join(process.cwd(), ".baker", "inspiration", id);
38198
+ const dir = path36.join(process.cwd(), ".baker", "inspiration", id);
38199
+ const ext = READABLE_SHOT.extension;
38180
38200
  const [desktop, mobile, filmstrip] = await Promise.all([
38181
- download(section.desktopShotUrl, path35.join(dir, "desktop.png")),
38182
- download(section.mobileShotUrl, path35.join(dir, "mobile.png")),
38183
- download(section.motionFilmstripUrl, path35.join(dir, "motion-filmstrip.png"))
38201
+ downloadReadableShot(section.desktopShotUrl, path36.join(dir, `desktop.${ext}`)),
38202
+ downloadReadableShot(section.mobileShotUrl, path36.join(dir, `mobile.${ext}`)),
38203
+ downloadReadableShot(section.motionFilmstripUrl, path36.join(dir, `motion-filmstrip.${ext}`))
38184
38204
  ]);
38185
38205
  const full = args.full;
38186
38206
  const hints = [INSPIRATION_HINTS.structureNotCopy, INSPIRATION_HINTS.adapt];
@@ -38190,7 +38210,7 @@ var viewCommand2 = defineCommand155({
38190
38210
  if (fidelity) hints.push(fidelity);
38191
38211
  if (filmstrip) {
38192
38212
  hints.push(
38193
- "motion-filmstrip.png shows this section animating in, six frames left-to-right then top-to-bottom, with the elapsed time on each. Read it if you want the motion, not just the layout."
38213
+ `${filmstrip} shows this section animating in, six frames left-to-right then top-to-bottom, with the elapsed time on each. Read it if you want the motion, not just the layout.`
38194
38214
  );
38195
38215
  }
38196
38216
  writeJson({
@@ -40398,7 +40418,7 @@ function parseImageRefs(spec) {
40398
40418
  }
40399
40419
  var defaultDeps = {
40400
40420
  ingest: (url) => apiPost("/api/images/ingest", { url, source: "uploaded" }),
40401
- upload: (path36) => uploadLocalImage({ file: path36, contentType: detectImageContentType(path36), source: "uploaded" })
40421
+ upload: (path37) => uploadLocalImage({ file: path37, contentType: detectImageContentType(path37), source: "uploaded" })
40402
40422
  };
40403
40423
  async function resolveLibraryImageIds(spec, limit, deps = defaultDeps) {
40404
40424
  const refs = parseImageRefs(spec);
@@ -40418,10 +40438,10 @@ async function resolveLibraryImageIds(spec, limit, deps = defaultDeps) {
40418
40438
  }
40419
40439
  return { imageIds, added };
40420
40440
  }
40421
- function uploadFailure(path36) {
40441
+ function uploadFailure(path37) {
40422
40442
  return (error) => {
40423
40443
  if (error instanceof ApiError) throw error;
40424
- throw new ApiError("VALIDATION_ERROR", `Could not read "${path36}" as an image.`);
40444
+ throw new ApiError("VALIDATION_ERROR", `Could not read "${path37}" as an image.`);
40425
40445
  };
40426
40446
  }
40427
40447
 
@@ -41471,10 +41491,10 @@ async function stageOp4(op) {
41471
41491
  handleError4(err);
41472
41492
  }
41473
41493
  }
41474
- async function draftAction3(path36, body, chat) {
41494
+ async function draftAction3(path37, body, chat) {
41475
41495
  const chatId = resolveChatId(chat);
41476
41496
  try {
41477
- const data = await apiPost(path36, { chatId, ...body });
41497
+ const data = await apiPost(path37, { chatId, ...body });
41478
41498
  writeJsonEnvelope({ ok: true, data });
41479
41499
  return data;
41480
41500
  } catch (err) {