@koda-sl/baker-cli 0.345.0-dev.9f4314fa9 → 0.346.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.
Files changed (28) hide show
  1. package/README.md +8 -4
  2. package/dist/{chunk-KGDL7F7M.js → chunk-2IC3CGQO.js} +3 -3
  3. package/dist/chunk-2OKSJNLA.js +855 -0
  4. package/dist/chunk-2OKSJNLA.js.map +1 -0
  5. package/dist/{chunk-XPUSSDLR.js → chunk-4QXKWIFC.js} +3 -3
  6. package/dist/{chunk-JWMLQSGK.js → chunk-NA5CPMUA.js} +4 -4
  7. package/dist/{chunk-PWGY347J.js → chunk-XWHHYHKQ.js} +2 -2
  8. package/dist/{chunk-D5P4COPD.js → chunk-YBD3W4M7.js} +9 -8
  9. package/dist/chunk-YBD3W4M7.js.map +1 -0
  10. package/dist/cli.js +341 -761
  11. package/dist/cli.js.map +1 -1
  12. package/dist/{client-PWRZOA3Q.js → client-ODCNYCP4.js} +4 -4
  13. package/dist/engine/index.js +3 -3
  14. package/dist/{env-TQTHWLBN.js → env-BSG7TY5Y.js} +6 -2
  15. package/dist/{output-HD63GTWI.js → output-2XBCYF7L.js} +5 -5
  16. package/dist/{shared-5662SBDW.js → shared-RYAF7Y7H.js} +6 -6
  17. package/package.json +1 -1
  18. package/dist/chunk-4VP5WWPR.js +0 -376
  19. package/dist/chunk-4VP5WWPR.js.map +0 -1
  20. package/dist/chunk-D5P4COPD.js.map +0 -1
  21. /package/dist/{chunk-KGDL7F7M.js.map → chunk-2IC3CGQO.js.map} +0 -0
  22. /package/dist/{chunk-XPUSSDLR.js.map → chunk-4QXKWIFC.js.map} +0 -0
  23. /package/dist/{chunk-JWMLQSGK.js.map → chunk-NA5CPMUA.js.map} +0 -0
  24. /package/dist/{chunk-PWGY347J.js.map → chunk-XWHHYHKQ.js.map} +0 -0
  25. /package/dist/{client-PWRZOA3Q.js.map → client-ODCNYCP4.js.map} +0 -0
  26. /package/dist/{env-TQTHWLBN.js.map → env-BSG7TY5Y.js.map} +0 -0
  27. /package/dist/{output-HD63GTWI.js.map → output-2XBCYF7L.js.map} +0 -0
  28. /package/dist/{shared-5662SBDW.js.map → shared-RYAF7Y7H.js.map} +0 -0
package/dist/cli.js CHANGED
@@ -67,7 +67,7 @@ import {
67
67
  validateCanvasDeep,
68
68
  withoutCastName,
69
69
  ytDlpBlockSignal
70
- } from "./chunk-D5P4COPD.js";
70
+ } from "./chunk-YBD3W4M7.js";
71
71
  import {
72
72
  csvOrJson,
73
73
  daysAgoIso,
@@ -76,7 +76,7 @@ import {
76
76
  resolveAccountIdArg,
77
77
  resolveEffectiveStatus,
78
78
  todayIso
79
- } from "./chunk-JWMLQSGK.js";
79
+ } from "./chunk-NA5CPMUA.js";
80
80
  import {
81
81
  buildQueryCacheKey,
82
82
  cacheGet,
@@ -92,33 +92,42 @@ import {
92
92
  writeAdsJson,
93
93
  writeAdsOutput,
94
94
  writeJsonEnvelope
95
- } from "./chunk-KGDL7F7M.js";
95
+ } from "./chunk-2IC3CGQO.js";
96
96
  import {
97
97
  ApiError,
98
98
  apiGet,
99
99
  apiGetWithHeaders,
100
100
  apiPost,
101
101
  validateConvexId
102
- } from "./chunk-XPUSSDLR.js";
102
+ } from "./chunk-4QXKWIFC.js";
103
103
  import {
104
104
  installExitRecord,
105
105
  installStreamTaps,
106
106
  logInvocation
107
- } from "./chunk-PWGY347J.js";
107
+ } from "./chunk-XWHHYHKQ.js";
108
108
  import {
109
+ adContentNormalizer,
109
110
  captureBudgetMs,
110
111
  captureProxyCredentials,
111
112
  captureVimeoCookie,
113
+ compactImage,
114
+ compactVideo,
112
115
  getEnv,
113
116
  hasOpenRouterKey,
114
117
  isProxyFailure,
115
118
  openRouterKey,
119
+ optionalChatId,
116
120
  plannedRoutes,
117
121
  refuseNonPublicUrl,
118
122
  requireChatId,
119
123
  resolveChatId,
120
- shouldEscalate
121
- } from "./chunk-4VP5WWPR.js";
124
+ shouldEscalate,
125
+ testimonialNormalizer,
126
+ videoNormalizer,
127
+ winningAdNormalizer,
128
+ writeJson,
129
+ writeOutput
130
+ } from "./chunk-2OKSJNLA.js";
122
131
 
123
132
  // src/cli.ts
124
133
  import { defineCommand as defineCommand245, runMain } from "citty";
@@ -159,420 +168,6 @@ import { defineCommand as defineCommand18 } from "citty";
159
168
  // src/commands/actions/claim.ts
160
169
  import { defineCommand } from "citty";
161
170
 
162
- // src/output.ts
163
- var ASPECT_RATIO_MAP = {
164
- "1:1": 1,
165
- "16:9": 16 / 9,
166
- "9:16": 9 / 16,
167
- "2:3": 2 / 3,
168
- "3:4": 3 / 4,
169
- "1:2": 1 / 2,
170
- "2:1": 2,
171
- "4:5": 4 / 5,
172
- "3:2": 3 / 2,
173
- "4:3": 4 / 3
174
- };
175
- var ASPECT_RATIO_TOLERANCE = 0.08;
176
- function formatAspectRatio(ratio) {
177
- if (ratio === void 0 || ratio === null) {
178
- return null;
179
- }
180
- for (const [label, value] of Object.entries(ASPECT_RATIO_MAP)) {
181
- if (Math.abs(ratio - value) / value < ASPECT_RATIO_TOLERANCE) {
182
- return label;
183
- }
184
- }
185
- return null;
186
- }
187
- function groupSummary(record) {
188
- const key = record.groupKey;
189
- if (typeof key !== "string" || !key) {
190
- return null;
191
- }
192
- const index = typeof record.groupIndex === "number" ? record.groupIndex : null;
193
- const size = typeof record.groupSize === "number" ? record.groupSize : null;
194
- return {
195
- key,
196
- kind: typeof record.groupKind === "string" ? record.groupKind : null,
197
- position: index === null ? null : size === null ? `${index + 1}` : `${index + 1} of ${size}`,
198
- title: typeof record.groupTitle === "string" ? record.groupTitle : null,
199
- url: typeof record.groupUrl === "string" ? record.groupUrl : null
200
- };
201
- }
202
- function peopleLines(value) {
203
- if (!Array.isArray(value)) {
204
- return [];
205
- }
206
- return value.flatMap((entry) => {
207
- const person = asRecord(entry);
208
- const role = typeof person.role === "string" ? person.role : "";
209
- if (!role) {
210
- return [];
211
- }
212
- const identity = typeof person.identity === "string" && person.identity ? ` \u2014 ${person.identity}` : "";
213
- const appearance = typeof person.appearance === "string" && person.appearance ? ` (${person.appearance})` : "";
214
- const speaking = person.speaking === true ? " [speaking]" : "";
215
- return [`${role}${identity}${appearance}${speaking}`];
216
- });
217
- }
218
- function compactImage(image) {
219
- const analysis = asRecord(image.analysis);
220
- return {
221
- _id: String(image._id ?? ""),
222
- name: String(image.name ?? ""),
223
- imageUrl: String(image.imageUrl ?? ""),
224
- description: String(image.description ?? ""),
225
- tags: Array.isArray(image.tags) ? image.tags : [],
226
- // Two numbers that are not about choosing the image but about placing it.
227
- // A remote <BakerImage> with no width/height makes Astro DOWNLOAD the file
228
- // at build time just to measure it — one request per image per build, on a
229
- // build that is otherwise offline, and a single dropped connection there
230
- // fails a client's whole verify. They are cheap enough to always carry, and
231
- // burying them behind --full meant the agent placing the image never had
232
- // them.
233
- width: typeof image.width === "number" ? image.width : null,
234
- height: typeof image.height === "number" ? image.height : null,
235
- // The facts that decide a pick, so choosing does not need a second call:
236
- // whose brand it is, who is in it, and what text is burned in.
237
- subject: typeof analysis.subject === "string" ? analysis.subject : null,
238
- brandOwnership: typeof analysis.brandOwnership === "string" ? analysis.brandOwnership : null,
239
- people: peopleLines(analysis.people),
240
- textInImage: Array.isArray(analysis.textInImage) ? analysis.textInImage : [],
241
- group: groupSummary(image)
242
- };
243
- }
244
- function fullImage(image) {
245
- const compact3 = compactImage(image);
246
- return {
247
- ...compact3,
248
- aspectRatio: formatAspectRatio(image.aspectRatio),
249
- dominantColor: image.dominantColor ?? null,
250
- imagePalette: Array.isArray(image.imagePalette) ? image.imagePalette : [],
251
- source: String(image.source ?? ""),
252
- externalUrl: image.externalUrl ?? null,
253
- originalFilename: image.originalFilename ?? null,
254
- descriptionContext: image.descriptionContext ?? null,
255
- style: typeof asRecord(image.analysis).style === "string" ? asRecord(image.analysis).style : null,
256
- composition: typeof asRecord(image.analysis).composition === "string" ? asRecord(image.analysis).composition : null,
257
- usageNotes: typeof asRecord(image.analysis).usageNotes === "string" ? asRecord(image.analysis).usageNotes : null,
258
- createdAt: image.createdAt ?? null
259
- };
260
- }
261
- function imageNormalizer(record, full) {
262
- if (full) {
263
- return fullImage(record);
264
- }
265
- return compactImage(record);
266
- }
267
- function compactVideo(video) {
268
- const analysis = asRecord(video.analysis);
269
- const sceneCount = typeof video.sceneCount === "number" ? video.sceneCount : Array.isArray(video.scenes) ? video.scenes.length : 0;
270
- return {
271
- _id: String(video._id ?? ""),
272
- name: String(video.name ?? ""),
273
- thumbnailUrl: String(video.thumbnailUrl ?? ""),
274
- description: String(video.description ?? ""),
275
- tags: Array.isArray(video.tags) ? video.tags : [],
276
- status: String(video.status ?? ""),
277
- // What decides whether this clip is usable, without a second call: who is
278
- // on camera and in what role, what language they speak, how it opens, and
279
- // how many beats there are to cut from.
280
- subject: typeof analysis.subject === "string" ? analysis.subject : null,
281
- brandOwnership: typeof analysis.brandOwnership === "string" ? analysis.brandOwnership : null,
282
- people: peopleLines(analysis.people),
283
- spokenLanguages: Array.isArray(analysis.spokenLanguages) ? analysis.spokenLanguages : [],
284
- hook: typeof analysis.hook === "string" ? analysis.hook : null,
285
- sceneCount,
286
- group: groupSummary(video)
287
- };
288
- }
289
- function sceneDecoration(scene) {
290
- const shot = typeof scene.shot === "string" && scene.shot ? ` [${scene.shot}]` : "";
291
- const change = typeof scene.visualChange === "string" && scene.visualChange ? ` <${scene.visualChange}>` : "";
292
- const overlayText = Array.isArray(scene.onScreenText) ? scene.onScreenText.filter(Boolean) : [];
293
- let overlay = "";
294
- if (scene.hasOverlay === true) {
295
- overlay = overlayText.length > 0 ? ` \u2014 banner: "${overlayText.join('" / "')}"` : " \u2014 banner";
296
- } else if (scene.hasOverlay === false) {
297
- overlay = " \u2014 clean frame";
298
- }
299
- const spoken = typeof scene.spoken === "string" && scene.spoken ? ` \u2014 says: "${scene.spoken}"` : "";
300
- return `${shot}${change}${overlay}${spoken}`;
301
- }
302
- function sceneLines(value) {
303
- if (!Array.isArray(value)) {
304
- return [];
305
- }
306
- return value.flatMap((s) => {
307
- const scene = asRecord(s);
308
- const title = typeof scene.title === "string" ? scene.title : "";
309
- if (!title) {
310
- return [];
311
- }
312
- const start = round2(scene.startSecond);
313
- const end = round2(scene.endSecond);
314
- const range = start !== null && end !== null ? ` (${start}-${end}s)` : "";
315
- return [`${title}${range}${sceneDecoration(scene)}`];
316
- });
317
- }
318
- function fullVideo(video) {
319
- const compact3 = compactVideo(video);
320
- return {
321
- ...compact3,
322
- duration: video.duration ?? null,
323
- muxPlaybackId: String(video.muxPlaybackId ?? ""),
324
- source: String(video.source ?? ""),
325
- // The spoken transcript + AI scene breakdown of the client's own clip — the
326
- // "understand what's in this video" payload. Kept out of the compact/search
327
- // projection (too large for a list) and surfaced here on --full.
328
- transcript: video.transcript ?? null,
329
- transcript_segment_count: Array.isArray(video.transcriptSegments) ? video.transcriptSegments.length : 0,
330
- scenes: sceneLines(video.scenes),
331
- originalFilename: video.originalFilename ?? null,
332
- descriptionContext: video.descriptionContext ?? null,
333
- shotStyle: typeof asRecord(video.analysis).shotStyle === "string" ? asRecord(video.analysis).shotStyle : null,
334
- audioStyle: typeof asRecord(video.analysis).audioStyle === "string" ? asRecord(video.analysis).audioStyle : null,
335
- onScreenText: Array.isArray(asRecord(video.analysis).onScreenText) ? asRecord(video.analysis).onScreenText : [],
336
- usageNotes: typeof asRecord(video.analysis).usageNotes === "string" ? asRecord(video.analysis).usageNotes : null,
337
- createdAt: video.createdAt ?? null
338
- };
339
- }
340
- function videoNormalizer(record, full) {
341
- if (full) {
342
- return fullVideo(record);
343
- }
344
- return compactVideo(record);
345
- }
346
- function truncate(text2, maxLength) {
347
- if (text2.length <= maxLength) return text2;
348
- return `${text2.slice(0, maxLength)}...`;
349
- }
350
- function compactTestimonial(record) {
351
- return {
352
- _id: String(record._id ?? ""),
353
- reviewerName: String(record.reviewerName ?? ""),
354
- reviewText: truncate(String(record.reviewText ?? ""), 200),
355
- rating: record.rating ?? null,
356
- sentiment: record.sentiment ?? null,
357
- tags: Array.isArray(record.tags) ? record.tags : [],
358
- sourceType: String(record.sourceType ?? "")
359
- };
360
- }
361
- function fullTestimonial(record) {
362
- const compact3 = compactTestimonial(record);
363
- return {
364
- ...compact3,
365
- reviewText: String(record.reviewText ?? ""),
366
- highlight: record.highlight ?? null,
367
- summary: record.summary ?? null,
368
- language: record.language ?? null,
369
- reviewerImageUrl: record.reviewerImageUrl ?? null,
370
- sourceUrl: record.sourceUrl ?? null,
371
- reviewDate: record.reviewDate ?? null,
372
- createdAt: record.createdAt ?? null,
373
- status: String(record.status ?? "")
374
- };
375
- }
376
- function testimonialNormalizer(record, full) {
377
- if (full) {
378
- return fullTestimonial(record);
379
- }
380
- return compactTestimonial(record);
381
- }
382
- function round2(value) {
383
- if (typeof value !== "number" || Number.isNaN(value)) {
384
- return null;
385
- }
386
- return Math.round(value * 100) / 100;
387
- }
388
- function asRecord(value) {
389
- return value && typeof value === "object" ? value : {};
390
- }
391
- function compactWinningAd(record) {
392
- const dna = asRecord(record.dna);
393
- return {
394
- advertiser: String(record.advertiser ?? ""),
395
- platform: String(record.platform ?? ""),
396
- format: String(record.format ?? ""),
397
- relevance: round2(record.relevance),
398
- winner_score: round2(record.winner_score),
399
- summary: String(dna.creative_concept ?? ""),
400
- advertiser_id: String(record.advertiser_id ?? ""),
401
- ad_id: String(record.ad_id ?? ""),
402
- media_url: String(record.media_url ?? "")
403
- };
404
- }
405
- function fullWinningAd(record) {
406
- const compact3 = compactWinningAd(record);
407
- const dna = asRecord(record.dna);
408
- return {
409
- ...compact3,
410
- winner_category: String(record.winner_category ?? ""),
411
- media_kind: record.media_kind ?? null,
412
- days_active: record.days_active ?? null,
413
- reach: record.reach ?? null,
414
- active: typeof record.active === "boolean" ? record.active : null,
415
- angle: dna.angle ?? null,
416
- awareness_stage: dna.awareness_stage ?? null,
417
- target_persona: dna.target_persona ?? null,
418
- hook_archetype: dna.hook_archetype ?? null,
419
- industry: dna.industry ?? null
420
- };
421
- }
422
- function winningAdNormalizer(record, full) {
423
- if (full) {
424
- return fullWinningAd(record);
425
- }
426
- return compactWinningAd(record);
427
- }
428
- function overlayLines(value, detailed = false) {
429
- if (!Array.isArray(value)) {
430
- return [];
431
- }
432
- return value.flatMap((o) => {
433
- const item = asRecord(o);
434
- const text2 = typeof item.text === "string" ? item.text : "";
435
- if (!text2) {
436
- return [];
437
- }
438
- const at = round2(item.appears_at_s);
439
- let line = at !== null ? `${text2} @${at}s` : text2;
440
- if (detailed) {
441
- const tags = [item.role, item.position].filter((t) => typeof t === "string" && t.length > 0);
442
- if (tags.length) {
443
- line += ` [${tags.join(", ")}]`;
444
- }
445
- }
446
- return [line];
447
- });
448
- }
449
- function compactAdContent(record) {
450
- return {
451
- ad_id: record.ad_id ?? null,
452
- advertiser: record.advertiser ?? null,
453
- format: record.format ?? null,
454
- media_kind: record.media_kind ?? null,
455
- media_url: record.media_url ?? null,
456
- summary: record.summary ?? null,
457
- transcript: record.transcript ?? null,
458
- on_screen_text: overlayLines(record.on_screen_text),
459
- has_captions: typeof record.has_captions === "boolean" ? record.has_captions : null,
460
- primary_text: record.primary_text ?? null,
461
- headline: record.headline ?? null,
462
- cta: record.cta ?? null
463
- };
464
- }
465
- function fullAdContent(record) {
466
- const speech = asRecord(record.speech);
467
- const pacing = asRecord(record.pacing);
468
- const music = asRecord(record.music);
469
- return {
470
- ...compactAdContent(record),
471
- platform: record.platform ?? null,
472
- // Re-derive with role/position tags — the lean compact version dropped them.
473
- on_screen_text: overlayLines(record.on_screen_text, true),
474
- winner_score: round2(record.winner_score),
475
- winner_category: record.winner_category ?? null,
476
- angle: record.angle ?? null,
477
- hook_archetype: record.hook_archetype ?? null,
478
- caption_style: record.caption_style ?? null,
479
- has_speech: typeof record.has_speech === "boolean" ? record.has_speech : null,
480
- speech_pct_of_runtime: round2(speech.speech_pct_of_runtime),
481
- speaker_count: speech.speaker_count ?? null,
482
- voiceover_vs_sync: speech.voiceover_vs_sync ?? null,
483
- is_direct_address: typeof speech.is_direct_address === "boolean" ? speech.is_direct_address : null,
484
- speech_emotion_dominant: speech.speech_emotion_dominant ?? null,
485
- scene_count: pacing.scene_count ?? null,
486
- avg_shot_length_s: round2(pacing.avg_shot_length_s),
487
- pacing_shape: pacing.pacing_shape ?? null,
488
- hook_ends_at_s: round2(pacing.hook_ends_at_s),
489
- cta_first_shown_at_s: round2(pacing.cta_first_shown_at_s),
490
- product_first_appears_at_s: round2(pacing.product_first_appears_at_s),
491
- music_track_title: music.track_title ?? null,
492
- music_track_artist: music.track_artist ?? null
493
- };
494
- }
495
- function adContentNormalizer(record, full) {
496
- return full ? fullAdContent(record) : compactAdContent(record);
497
- }
498
- function applyFieldMask(data, fields) {
499
- const result = {};
500
- for (const field of fields) {
501
- if (field in data) {
502
- result[field] = data[field];
503
- }
504
- }
505
- return result;
506
- }
507
- function write(text2) {
508
- process.stdout.write(text2);
509
- }
510
- function writeJson(envelope) {
511
- write(`${JSON.stringify(envelope, null, 2)}
512
- `);
513
- }
514
- function writeFiles(data, fields) {
515
- write(`${fields.join(" ")}
516
- `);
517
- for (const item of data) {
518
- const values = fields.map((f) => String(item[f] ?? ""));
519
- write(`${values.join(" ")}
520
- `);
521
- }
522
- }
523
- function writeMd(data, fields) {
524
- write(`| ${fields.join(" | ")} |
525
- `);
526
- write(`| ${fields.map(() => "---").join(" | ")} |
527
- `);
528
- for (const item of data) {
529
- const values = fields.map((f) => String(item[f] ?? ""));
530
- write(`| ${values.join(" | ")} |
531
- `);
532
- }
533
- }
534
- function normalizeData(envelope, fields, full, normalizer) {
535
- const raw = Array.isArray(envelope.data) ? envelope.data : [envelope.data];
536
- const items = raw.map((item) => {
537
- const record = item;
538
- return normalizer(record, full);
539
- });
540
- if (fields) {
541
- return items.map((item) => applyFieldMask(item, fields));
542
- }
543
- return items;
544
- }
545
- function writeOutput(envelope, format, fields, full, normalizer) {
546
- if (!envelope.ok || "dryRun" in envelope) {
547
- writeJson(envelope);
548
- return;
549
- }
550
- if (format === "json") {
551
- writeJson(envelope);
552
- return;
553
- }
554
- if (format !== "files" && format !== "md") {
555
- process.stderr.write(`Unknown --output "${String(format)}". Valid formats: json, files, md. Using json.
556
- `);
557
- writeJson(envelope);
558
- return;
559
- }
560
- const items = normalizeData(envelope, fields, full ?? false, normalizer ?? imageNormalizer);
561
- const displayFields = fields ?? Object.keys(items[0] ?? {});
562
- if (format === "files") {
563
- writeFiles(items, displayFields);
564
- } else {
565
- writeMd(items, displayFields);
566
- }
567
- writeHints(envelope.hints);
568
- }
569
- function writeHints(hints2) {
570
- for (const hint of hints2 ?? []) {
571
- process.stderr.write(`${hint}
572
- `);
573
- }
574
- }
575
-
576
171
  // src/schemas.ts
577
172
  var registry = /* @__PURE__ */ new Map();
578
173
  function registerSchema(schema) {
@@ -13082,8 +12677,8 @@ var writeMdRows = (rows, fields, notices) => {
13082
12677
  };
13083
12678
  var ROW_WRITERS = { csv: writeCsvRows, jsonl: writeJsonlRows, md: writeMdRows };
13084
12679
  function writeRows(format, rows, columns, notices) {
13085
- const write2 = ROW_WRITERS[format] ?? writeMdRows;
13086
- write2(rows, columns, notices);
12680
+ const write = ROW_WRITERS[format] ?? writeMdRows;
12681
+ write(rows, columns, notices);
13087
12682
  if (rows.length > REPEAT_NOTICE_ABOVE_ROWS) {
13088
12683
  for (const line of notices) writeLine(line);
13089
12684
  }
@@ -22286,7 +21881,7 @@ Subcommands:
22286
21881
 
22287
21882
  // src/commands/ads/linkedin/creatives.ts
22288
21883
  import { defineCommand as defineCommand44 } from "citty";
22289
- function asRecord2(value) {
21884
+ function asRecord(value) {
22290
21885
  return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
22291
21886
  }
22292
21887
  function asString(value) {
@@ -22296,12 +21891,12 @@ function creativeDestination(content) {
22296
21891
  if (!content) return { format: "unknown" };
22297
21892
  const reference = asString(content.reference);
22298
21893
  if (reference) return { format: "post", postUrn: reference };
22299
- const textAd = asRecord2(content.textAd);
21894
+ const textAd = asRecord(content.textAd);
22300
21895
  if (textAd) return { format: "text", landingUrl: asString(textAd.landingPage) };
22301
- const spotlight = asRecord2(content.spotlight);
21896
+ const spotlight = asRecord(content.spotlight);
22302
21897
  if (spotlight) return { format: "spotlight", landingUrl: asString(spotlight.landingPage) };
22303
- if (asRecord2(content.follower)) return { format: "follower" };
22304
- if (asRecord2(content.jobs)) return { format: "jobs" };
21898
+ if (asRecord(content.follower)) return { format: "follower" };
21899
+ if (asRecord(content.jobs)) return { format: "jobs" };
22305
21900
  return { format: "unknown" };
22306
21901
  }
22307
21902
  var COMPACT_COLUMNS = [
@@ -22332,7 +21927,7 @@ function compactRow(row) {
22332
21927
  function readPath(row, path48) {
22333
21928
  let current = row;
22334
21929
  for (const segment of path48.split(".")) {
22335
- const record = asRecord2(current);
21930
+ const record = asRecord(current);
22336
21931
  if (!record) return void 0;
22337
21932
  current = record[segment];
22338
21933
  }
@@ -25705,10 +25300,10 @@ Example: baker ads meta ads retag 120252158038400247 --url-tags "utm_source={{si
25705
25300
  }
25706
25301
  },
25707
25302
  run: async ({ args: args5 }) => {
25708
- const { apiPost: apiPost2 } = await import("./client-PWRZOA3Q.js");
25709
- const { requireChatId: requireChatId2 } = await import("./env-TQTHWLBN.js");
25710
- const { writeJsonEnvelope: writeJsonEnvelope2 } = await import("./output-HD63GTWI.js");
25711
- const { handleMetaError: handleMetaError2 } = await import("./shared-5662SBDW.js");
25303
+ const { apiPost: apiPost2 } = await import("./client-ODCNYCP4.js");
25304
+ const { requireChatId: requireChatId2 } = await import("./env-BSG7TY5Y.js");
25305
+ const { writeJsonEnvelope: writeJsonEnvelope2 } = await import("./output-2XBCYF7L.js");
25306
+ const { handleMetaError: handleMetaError2 } = await import("./shared-RYAF7Y7H.js");
25712
25307
  try {
25713
25308
  const accountId = bareAccountId2(args5);
25714
25309
  const urlTags = typeof args5["url-tags"] === "string" ? args5["url-tags"] : void 0;
@@ -25853,10 +25448,10 @@ function duplicateCommand2(entity, label) {
25853
25448
  replace: { type: "boolean", description: "Pause the original once the copy publishes" }
25854
25449
  },
25855
25450
  run: async ({ args: args5 }) => {
25856
- const { apiPost: apiPost2 } = await import("./client-PWRZOA3Q.js");
25857
- const { requireChatId: requireChatId2 } = await import("./env-TQTHWLBN.js");
25858
- const { writeJsonEnvelope: writeJsonEnvelope2 } = await import("./output-HD63GTWI.js");
25859
- const { handleMetaError: handleMetaError2 } = await import("./shared-5662SBDW.js");
25451
+ const { apiPost: apiPost2 } = await import("./client-ODCNYCP4.js");
25452
+ const { requireChatId: requireChatId2 } = await import("./env-BSG7TY5Y.js");
25453
+ const { writeJsonEnvelope: writeJsonEnvelope2 } = await import("./output-2XBCYF7L.js");
25454
+ const { handleMetaError: handleMetaError2 } = await import("./shared-RYAF7Y7H.js");
25860
25455
  try {
25861
25456
  const accountId = bareAccountId2(args5);
25862
25457
  const chatId = requireChatId2();
@@ -31032,278 +30627,6 @@ function mergeStoredWithCounts(stored, reported) {
31032
30627
  }).sort((a, b) => a.name.localeCompare(b.name));
31033
30628
  }
31034
30629
 
31035
- // src/commands/analytics/render.ts
31036
- var SPARKLINE_POINTS = 30;
31037
- var DASHBOARD_ONLY_FIELDS = /* @__PURE__ */ new Set([
31038
- // The Convex row id. It exists so a dashboard can link to the row; an agent
31039
- // addresses a landing by `landingId`, which is the id in its `_definition.md`.
31040
- "landingRef"
31041
- ]);
31042
- function isAbsent(value) {
31043
- if (value === "") return true;
31044
- if (Array.isArray(value)) return value.length === 0;
31045
- if (typeof value === "object" && value !== null) return Object.keys(value).length === 0;
31046
- return false;
31047
- }
31048
- function resample(series, points) {
31049
- if (series.length <= points || points < 1) return [...series];
31050
- const out = [];
31051
- for (let i = 0; i < points; i += 1) {
31052
- const from = Math.floor(i * series.length / points);
31053
- const to = Math.floor((i + 1) * series.length / points);
31054
- let sum = 0;
31055
- for (let j = from; j < to; j += 1) sum += series[j] ?? 0;
31056
- out.push(sum);
31057
- }
31058
- return out;
31059
- }
31060
- function pruneValue(value, full) {
31061
- if (Array.isArray(value)) return value.map((entry) => pruneValue(entry, full));
31062
- if (typeof value === "object" && value !== null) {
31063
- return pruneRow(value, full);
31064
- }
31065
- return value;
31066
- }
31067
- function pruneRow(row, full) {
31068
- const out = {};
31069
- for (const [key, value] of Object.entries(row)) {
31070
- if (DASHBOARD_ONLY_FIELDS.has(key)) continue;
31071
- if (key === "sparkline" && Array.isArray(value)) {
31072
- if (!full) continue;
31073
- out[key] = resample(value, SPARKLINE_POINTS);
31074
- continue;
31075
- }
31076
- if (isAbsent(value)) continue;
31077
- out[key] = pruneValue(value, full);
31078
- }
31079
- return out;
31080
- }
31081
- function pruneForAgent(data, full) {
31082
- return pruneRow(data, full);
31083
- }
31084
- function isTabular(rows) {
31085
- return rows.every(
31086
- (row) => typeof row === "object" && row !== null && !Array.isArray(row) && Object.values(row).every(
31087
- (value) => value === null || typeof value !== "object" || Array.isArray(value) && value.every((e) => typeof e !== "object")
31088
- )
31089
- );
31090
- }
31091
- function cell2(value, key) {
31092
- if (value === null || value === void 0) return "";
31093
- if (Array.isArray(value)) return value.join(" ");
31094
- if (typeof value === "number" && !Number.isInteger(value)) {
31095
- if (key?.endsWith("Ms")) return String(Math.round(value));
31096
- return value.toFixed(4).replace(/0+$/, "").replace(/\.$/, "");
31097
- }
31098
- return String(value);
31099
- }
31100
- var IDENTITY_COLUMNS = [
31101
- "landingId",
31102
- "name",
31103
- "slug",
31104
- "path",
31105
- "flowSlug",
31106
- "stepId",
31107
- "value",
31108
- "key",
31109
- "bucket",
31110
- "tag",
31111
- "eventName",
31112
- "release",
31113
- "place",
31114
- "bot",
31115
- "source",
31116
- "medium",
31117
- "campaign",
31118
- "platform",
31119
- "sessionId",
31120
- "eventId"
31121
- ];
31122
- function byIdentityFirst(a, b) {
31123
- const ia = IDENTITY_COLUMNS.indexOf(a);
31124
- const ib = IDENTITY_COLUMNS.indexOf(b);
31125
- if (ia !== -1 || ib !== -1)
31126
- return (ia === -1 ? Number.MAX_SAFE_INTEGER : ia) - (ib === -1 ? Number.MAX_SAFE_INTEGER : ib);
31127
- return 0;
31128
- }
31129
- var BLOCK_ORDER = ["window", "totals", "previous", "pageInfo"];
31130
- function byBlockOrder(a, b) {
31131
- const rank = (key) => {
31132
- const at = BLOCK_ORDER.indexOf(key);
31133
- if (at !== -1) return at;
31134
- return key === "preset" ? BLOCK_ORDER.length + 1 : BLOCK_ORDER.length;
31135
- };
31136
- return rank(a) - rank(b);
31137
- }
31138
- function columnsOf(rows) {
31139
- const seen = [];
31140
- for (const row of rows) {
31141
- for (const key of Object.keys(row)) {
31142
- if (!seen.includes(key)) seen.push(key);
31143
- }
31144
- }
31145
- return seen.filter((key) => rows.some((row) => !isAbsent(row[key]) && row[key] !== null && row[key] !== void 0)).sort(byIdentityFirst);
31146
- }
31147
- function table2(rows) {
31148
- const cols = columnsOf(rows);
31149
- if (cols.length === 0) return "";
31150
- const head = `| ${cols.join(" | ")} |`;
31151
- const rule = `| ${cols.map(() => "---").join(" | ")} |`;
31152
- const body = rows.map((row) => `| ${cols.map((col) => cell2(row[col], col)).join(" | ")} |`);
31153
- return [head, rule, ...body].join("\n");
31154
- }
31155
- function isRowList(value) {
31156
- return Array.isArray(value) && value.length > 0 && value.every((entry) => typeof entry === "object" && entry !== null && !Array.isArray(entry));
31157
- }
31158
- function labelOf(row, index) {
31159
- for (const key of IDENTITY_COLUMNS) {
31160
- const value = row[key];
31161
- if (typeof value === "string" && value !== "") return value;
31162
- }
31163
- return `#${index + 1}`;
31164
- }
31165
- function nestedBlock(name, rows) {
31166
- const parents = rows.map(
31167
- (row) => Object.fromEntries(Object.entries(row).filter(([, value]) => !isRowList(value) && !isPlainRow(value)))
31168
- );
31169
- const parts = [`### ${name}`, table2(parents)];
31170
- rows.forEach((row, index) => {
31171
- for (const [key, value] of Object.entries(row)) {
31172
- if (isRowList(value)) parts.push(`#### ${name}[${labelOf(row, index)}].${key}`, table2(value));
31173
- else if (isPlainRow(value)) parts.push(`#### ${name}[${labelOf(row, index)}].${key}`, table2([value]));
31174
- }
31175
- });
31176
- return parts.filter((part) => part !== "").join("\n");
31177
- }
31178
- function isPlainRow(value) {
31179
- return typeof value === "object" && value !== null && !Array.isArray(value);
31180
- }
31181
- function block(name, value) {
31182
- if (Array.isArray(value)) {
31183
- if (value.length === 0) return "";
31184
- if (isTabular(value)) return `### ${name}
31185
- ${table2(value)}`;
31186
- if (isRowList(value)) return nestedBlock(name, value);
31187
- return `### ${name}
31188
- ${JSON.stringify(value)}`;
31189
- }
31190
- if (typeof value === "object" && value !== null) {
31191
- const entries = Object.entries(value);
31192
- if (entries.length === 0) return "";
31193
- if (entries.every(([, v]) => v === null || typeof v !== "object")) {
31194
- return `### ${name}
31195
- ${entries.map(([k, v]) => `- ${k}: ${v === null ? "null" : cell2(v, k)}`).join("\n")}`;
31196
- }
31197
- return `### ${name}
31198
- ${JSON.stringify(value)}`;
31199
- }
31200
- return `### ${name}
31201
- ${cell2(value)}`;
31202
- }
31203
- function renderMarkdown3(envelope) {
31204
- const parts = [];
31205
- for (const warning of envelope.warnings ?? []) parts.push(`> **${warning.code}** \u2014 ${warning.message}`);
31206
- for (const hint of envelope.hints ?? []) parts.push(`> ${hint}`);
31207
- for (const [name, value] of Object.entries(envelope.data).sort(([a], [b]) => byBlockOrder(a, b))) {
31208
- const rendered = block(name, value);
31209
- if (rendered !== "") parts.push(rendered);
31210
- }
31211
- return `${parts.join("\n\n")}
31212
- `;
31213
- }
31214
- function missingBreakdownHint(data, asked) {
31215
- if (asked.eventName === void 0) return [];
31216
- const breakdown = data.eventBreakdown;
31217
- if (Array.isArray(breakdown) && breakdown.length > 0) return [];
31218
- const known = Array.isArray(data.customEvents) ? data.customEvents.flatMap((row) => isPlainRow(row) && typeof row.eventName === "string" ? [row.eventName] : []) : [];
31219
- return [
31220
- `No rows came back for --event ${asked.eventName}${asked.property ? ` --property ${asked.property}` : ""}. That event or property does not exist on these pages${known.length > 0 ? `; the ones that do are: ${known.slice(0, 12).join(", ")}${known.length > 12 ? ", \u2026" : ""}` : ""}.`
31221
- ];
31222
- }
31223
- var ANALYTICS_OUTPUT_FORMATS = ["json", "md"];
31224
- function isAnalyticsOutputFormat(value) {
31225
- return ANALYTICS_OUTPUT_FORMATS.includes(value);
31226
- }
31227
- function writeAnalyticsReport(envelope, format) {
31228
- if (format === "md") {
31229
- process.stdout.write(renderMarkdown3(envelope));
31230
- return;
31231
- }
31232
- process.stdout.write(`${JSON.stringify(envelope)}
31233
- `);
31234
- }
31235
- var ROWS_BEFORE_FORMAT_HINT = 25;
31236
- function rowCount(data) {
31237
- let total = 0;
31238
- for (const value of Object.values(data)) {
31239
- if (Array.isArray(value)) total += value.length;
31240
- }
31241
- return total;
31242
- }
31243
- function formatHint(data, format) {
31244
- if (format === "md") return [];
31245
- const rows = rowCount(data);
31246
- if (rows < ROWS_BEFORE_FORMAT_HINT) return [];
31247
- return [
31248
- `This report is ${rows} rows. If you are going to read the numbers rather than pick one field out of them, --output md returns the same answer as tables, typically a third to a half smaller.`
31249
- ];
31250
- }
31251
-
31252
- // src/commands/analytics/conversionsMarkdown.ts
31253
- function doorsCell(doors) {
31254
- return doors.map((door) => `${door.door}=${door.events}/${door.sessions}`).join(" ");
31255
- }
31256
- function eventKeysCell(eventKeys) {
31257
- const formKeys = eventKeys.filter((key) => key.startsWith("form:")).length;
31258
- const others = eventKeys.filter((key) => !key.startsWith("form:"));
31259
- return [...others, ...formKeys > 0 ? [`${formKeys} Form ending(s)`] : []].join(" + ");
31260
- }
31261
- function trafficByForm(candidates) {
31262
- const byForm = /* @__PURE__ */ new Map();
31263
- for (const row of candidates) {
31264
- const form = byForm.get(row.flowSlug) ?? { "endings seen": 0, "endings counted": 0, events: 0, lastSeen: "" };
31265
- form["endings seen"] += 1;
31266
- if (row.isCounted) form["endings counted"] += 1;
31267
- form.events += row.events;
31268
- if (row.lastSeen && row.lastSeen.slice(0, 10) > form.lastSeen) form.lastSeen = row.lastSeen.slice(0, 10);
31269
- byForm.set(row.flowSlug, form);
31270
- }
31271
- return byForm;
31272
- }
31273
- var NO_TRAFFIC = { "endings seen": 0, "endings counted": 0, events: 0, lastSeen: "" };
31274
- var FORM_KEYS_HINT = "A Form's candidates are one row per Form: `baker analytics conversions --flow <slug>` lists that Form's event keys, and `--candidates --output json` lists every key.";
31275
- function renderConversionsMarkdown(envelope) {
31276
- const { definitions, flow, flows, candidates } = envelope.data;
31277
- const data = {
31278
- definitions: definitions.map(({ name, eventKeys, ...row }) => ({ name, eventKeys: eventKeysCell(eventKeys), ...row }))
31279
- };
31280
- if (flow) {
31281
- data.flow = { slug: flow.slug, noConversionReason: flow.noConversionReason };
31282
- data.flowEndings = flow.endings;
31283
- }
31284
- const traffic = candidates ? trafficByForm(candidates.filter((row) => row.flowSlug !== "")) : /* @__PURE__ */ new Map();
31285
- if (flows) {
31286
- data.flows = flows.map((row) => ({
31287
- slug: row.slug,
31288
- counted: row.counted.map((entry) => `${entry.name} @ ${entry.step}`).join("; "),
31289
- noConversion: row.noConversion,
31290
- ...candidates ? traffic.get(row.slug) ?? NO_TRAFFIC : {}
31291
- }));
31292
- }
31293
- const hints2 = [...envelope.hints ?? []];
31294
- if (candidates) {
31295
- const listed2 = new Set(flows?.map((row) => row.slug));
31296
- const unlisted = [...traffic].filter(([slug]) => !listed2.has(slug));
31297
- if (unlisted.length > 0) data.formCandidates = unlisted.map(([form, row]) => ({ form, ...row }));
31298
- data.candidates = candidates.filter((row) => row.flowSlug === "").map(({ doors, flowSlug: _flowSlug, nodeType: _nodeType, ...row }) => ({
31299
- ...row,
31300
- "doors (events/sessions)": doorsCell(doors)
31301
- }));
31302
- if (traffic.size > 0) hints2.push(FORM_KEYS_HINT);
31303
- }
31304
- return renderMarkdown3({ data, ...hints2.length > 0 ? { hints: hints2 } : {} });
31305
- }
31306
-
31307
30630
  // src/commands/analytics/errorEnvelope.ts
31308
30631
  function analyticsErrorEnvelope(err) {
31309
30632
  if (err instanceof ApiError) {
@@ -31795,6 +31118,223 @@ var ANALYTICS_PRESET_INFO = [
31795
31118
  }
31796
31119
  ];
31797
31120
 
31121
+ // src/commands/analytics/render.ts
31122
+ var SPARKLINE_POINTS = 30;
31123
+ var DASHBOARD_ONLY_FIELDS = /* @__PURE__ */ new Set([
31124
+ // The Convex row id. It exists so a dashboard can link to the row; an agent
31125
+ // addresses a landing by `landingId`, which is the id in its `_definition.md`.
31126
+ "landingRef"
31127
+ ]);
31128
+ function isAbsent(value) {
31129
+ if (value === "") return true;
31130
+ if (Array.isArray(value)) return value.length === 0;
31131
+ if (typeof value === "object" && value !== null) return Object.keys(value).length === 0;
31132
+ return false;
31133
+ }
31134
+ function resample(series, points) {
31135
+ if (series.length <= points || points < 1) return [...series];
31136
+ const out = [];
31137
+ for (let i = 0; i < points; i += 1) {
31138
+ const from = Math.floor(i * series.length / points);
31139
+ const to = Math.floor((i + 1) * series.length / points);
31140
+ let sum = 0;
31141
+ for (let j = from; j < to; j += 1) sum += series[j] ?? 0;
31142
+ out.push(sum);
31143
+ }
31144
+ return out;
31145
+ }
31146
+ function pruneValue(value, full) {
31147
+ if (Array.isArray(value)) return value.map((entry) => pruneValue(entry, full));
31148
+ if (typeof value === "object" && value !== null) {
31149
+ return pruneRow(value, full);
31150
+ }
31151
+ return value;
31152
+ }
31153
+ function pruneRow(row, full) {
31154
+ const out = {};
31155
+ for (const [key, value] of Object.entries(row)) {
31156
+ if (DASHBOARD_ONLY_FIELDS.has(key)) continue;
31157
+ if (key === "sparkline" && Array.isArray(value)) {
31158
+ if (!full) continue;
31159
+ out[key] = resample(value, SPARKLINE_POINTS);
31160
+ continue;
31161
+ }
31162
+ if (isAbsent(value)) continue;
31163
+ out[key] = pruneValue(value, full);
31164
+ }
31165
+ return out;
31166
+ }
31167
+ function pruneForAgent(data, full) {
31168
+ return pruneRow(data, full);
31169
+ }
31170
+ function isTabular(rows) {
31171
+ return rows.every(
31172
+ (row) => typeof row === "object" && row !== null && !Array.isArray(row) && Object.values(row).every(
31173
+ (value) => value === null || typeof value !== "object" || Array.isArray(value) && value.every((e) => typeof e !== "object")
31174
+ )
31175
+ );
31176
+ }
31177
+ function cell2(value, key) {
31178
+ if (value === null || value === void 0) return "";
31179
+ if (Array.isArray(value)) return value.join(" ");
31180
+ if (typeof value === "number" && !Number.isInteger(value)) {
31181
+ if (key?.endsWith("Ms")) return String(Math.round(value));
31182
+ return value.toFixed(4).replace(/0+$/, "").replace(/\.$/, "");
31183
+ }
31184
+ return String(value);
31185
+ }
31186
+ var IDENTITY_COLUMNS = [
31187
+ "landingId",
31188
+ "name",
31189
+ "slug",
31190
+ "path",
31191
+ "flowSlug",
31192
+ "stepId",
31193
+ "value",
31194
+ "key",
31195
+ "bucket",
31196
+ "tag",
31197
+ "eventName",
31198
+ "release",
31199
+ "place",
31200
+ "bot",
31201
+ "source",
31202
+ "medium",
31203
+ "campaign",
31204
+ "platform",
31205
+ "sessionId",
31206
+ "eventId"
31207
+ ];
31208
+ function byIdentityFirst(a, b) {
31209
+ const ia = IDENTITY_COLUMNS.indexOf(a);
31210
+ const ib = IDENTITY_COLUMNS.indexOf(b);
31211
+ if (ia !== -1 || ib !== -1)
31212
+ return (ia === -1 ? Number.MAX_SAFE_INTEGER : ia) - (ib === -1 ? Number.MAX_SAFE_INTEGER : ib);
31213
+ return 0;
31214
+ }
31215
+ var BLOCK_ORDER = ["window", "totals", "previous", "pageInfo"];
31216
+ function byBlockOrder(a, b) {
31217
+ const rank = (key) => {
31218
+ const at = BLOCK_ORDER.indexOf(key);
31219
+ if (at !== -1) return at;
31220
+ return key === "preset" ? BLOCK_ORDER.length + 1 : BLOCK_ORDER.length;
31221
+ };
31222
+ return rank(a) - rank(b);
31223
+ }
31224
+ function columnsOf(rows) {
31225
+ const seen = [];
31226
+ for (const row of rows) {
31227
+ for (const key of Object.keys(row)) {
31228
+ if (!seen.includes(key)) seen.push(key);
31229
+ }
31230
+ }
31231
+ return seen.filter((key) => rows.some((row) => !isAbsent(row[key]) && row[key] !== null && row[key] !== void 0)).sort(byIdentityFirst);
31232
+ }
31233
+ function table2(rows) {
31234
+ const cols = columnsOf(rows);
31235
+ if (cols.length === 0) return "";
31236
+ const head = `| ${cols.join(" | ")} |`;
31237
+ const rule = `| ${cols.map(() => "---").join(" | ")} |`;
31238
+ const body = rows.map((row) => `| ${cols.map((col) => cell2(row[col], col)).join(" | ")} |`);
31239
+ return [head, rule, ...body].join("\n");
31240
+ }
31241
+ function isRowList(value) {
31242
+ return Array.isArray(value) && value.length > 0 && value.every((entry) => typeof entry === "object" && entry !== null && !Array.isArray(entry));
31243
+ }
31244
+ function labelOf(row, index) {
31245
+ for (const key of IDENTITY_COLUMNS) {
31246
+ const value = row[key];
31247
+ if (typeof value === "string" && value !== "") return value;
31248
+ }
31249
+ return `#${index + 1}`;
31250
+ }
31251
+ function nestedBlock(name, rows) {
31252
+ const parents = rows.map(
31253
+ (row) => Object.fromEntries(Object.entries(row).filter(([, value]) => !isRowList(value) && !isPlainRow(value)))
31254
+ );
31255
+ const parts = [`### ${name}`, table2(parents)];
31256
+ rows.forEach((row, index) => {
31257
+ for (const [key, value] of Object.entries(row)) {
31258
+ if (isRowList(value)) parts.push(`#### ${name}[${labelOf(row, index)}].${key}`, table2(value));
31259
+ else if (isPlainRow(value)) parts.push(`#### ${name}[${labelOf(row, index)}].${key}`, table2([value]));
31260
+ }
31261
+ });
31262
+ return parts.filter((part) => part !== "").join("\n");
31263
+ }
31264
+ function isPlainRow(value) {
31265
+ return typeof value === "object" && value !== null && !Array.isArray(value);
31266
+ }
31267
+ function block(name, value) {
31268
+ if (Array.isArray(value)) {
31269
+ if (value.length === 0) return "";
31270
+ if (isTabular(value)) return `### ${name}
31271
+ ${table2(value)}`;
31272
+ if (isRowList(value)) return nestedBlock(name, value);
31273
+ return `### ${name}
31274
+ ${JSON.stringify(value)}`;
31275
+ }
31276
+ if (typeof value === "object" && value !== null) {
31277
+ const entries = Object.entries(value);
31278
+ if (entries.length === 0) return "";
31279
+ if (entries.every(([, v]) => v === null || typeof v !== "object")) {
31280
+ return `### ${name}
31281
+ ${entries.map(([k, v]) => `- ${k}: ${v === null ? "null" : cell2(v, k)}`).join("\n")}`;
31282
+ }
31283
+ return `### ${name}
31284
+ ${JSON.stringify(value)}`;
31285
+ }
31286
+ return `### ${name}
31287
+ ${cell2(value)}`;
31288
+ }
31289
+ function renderMarkdown3(envelope) {
31290
+ const parts = [];
31291
+ for (const warning of envelope.warnings ?? []) parts.push(`> **${warning.code}** \u2014 ${warning.message}`);
31292
+ for (const hint of envelope.hints ?? []) parts.push(`> ${hint}`);
31293
+ for (const [name, value] of Object.entries(envelope.data).sort(([a], [b]) => byBlockOrder(a, b))) {
31294
+ const rendered = block(name, value);
31295
+ if (rendered !== "") parts.push(rendered);
31296
+ }
31297
+ return `${parts.join("\n\n")}
31298
+ `;
31299
+ }
31300
+ function missingBreakdownHint(data, asked) {
31301
+ if (asked.eventName === void 0) return [];
31302
+ const breakdown = data.eventBreakdown;
31303
+ if (Array.isArray(breakdown) && breakdown.length > 0) return [];
31304
+ const known = Array.isArray(data.customEvents) ? data.customEvents.flatMap((row) => isPlainRow(row) && typeof row.eventName === "string" ? [row.eventName] : []) : [];
31305
+ return [
31306
+ `No rows came back for --event ${asked.eventName}${asked.property ? ` --property ${asked.property}` : ""}. That event or property does not exist on these pages${known.length > 0 ? `; the ones that do are: ${known.slice(0, 12).join(", ")}${known.length > 12 ? ", \u2026" : ""}` : ""}.`
31307
+ ];
31308
+ }
31309
+ var ANALYTICS_OUTPUT_FORMATS = ["json", "md"];
31310
+ function isAnalyticsOutputFormat(value) {
31311
+ return ANALYTICS_OUTPUT_FORMATS.includes(value);
31312
+ }
31313
+ function writeAnalyticsReport(envelope, format) {
31314
+ if (format === "md") {
31315
+ process.stdout.write(renderMarkdown3(envelope));
31316
+ return;
31317
+ }
31318
+ process.stdout.write(`${JSON.stringify(envelope)}
31319
+ `);
31320
+ }
31321
+ var ROWS_BEFORE_FORMAT_HINT = 25;
31322
+ function rowCount(data) {
31323
+ let total = 0;
31324
+ for (const value of Object.values(data)) {
31325
+ if (Array.isArray(value)) total += value.length;
31326
+ }
31327
+ return total;
31328
+ }
31329
+ function formatHint(data, format) {
31330
+ if (format === "md") return [];
31331
+ const rows = rowCount(data);
31332
+ if (rows < ROWS_BEFORE_FORMAT_HINT) return [];
31333
+ return [
31334
+ `This report is ${rows} rows. If you are going to read the numbers rather than pick one field out of them, --output md returns the same answer as tables, typically a third to a half smaller.`
31335
+ ];
31336
+ }
31337
+
31798
31338
  // src/commands/analytics/sends.ts
31799
31339
  import { defineCommand as defineCommand97 } from "citty";
31800
31340
  var SET_FLAGS = ["--source-event", "--platform", "--tag", "--event"];
@@ -32969,7 +32509,17 @@ async function runPreset(args5, options) {
32969
32509
  const response = await apiPost("/api/analytics/query", requestBody(args5, options));
32970
32510
  const requestedPlatform = args5.platform === void 0 ? "" : String(args5.platform);
32971
32511
  const adPlatformScope = isAdPlatform(requestedPlatform) ? requestedPlatform : void 0;
32972
- const format = outputFormatOrExit(args5.output);
32512
+ const format = args5.output === void 0 ? "json" : String(args5.output);
32513
+ if (!isAnalyticsOutputFormat(format)) {
32514
+ writeJsonEnvelope({
32515
+ ok: false,
32516
+ error: {
32517
+ code: "VALIDATION_ERROR",
32518
+ message: `Unknown --output "${format}". Use one of: ${ANALYTICS_OUTPUT_FORMATS.join(", ")}.`
32519
+ }
32520
+ });
32521
+ process.exit(1);
32522
+ }
32973
32523
  const data = pruneForAgent(response.data, args5.full === true);
32974
32524
  const feed = "stream" in data ? shapeFeed(data.stream, args5.everything === true) : void 0;
32975
32525
  if (feed !== void 0) data.stream = feed.rows;
@@ -32999,20 +32549,6 @@ async function runPreset(args5, options) {
32999
32549
  handleError3(err);
33000
32550
  }
33001
32551
  }
33002
- function outputFormatOrExit(raw) {
33003
- const format = raw === void 0 ? "json" : String(raw);
33004
- if (!isAnalyticsOutputFormat(format)) {
33005
- writeJsonEnvelope({
33006
- ok: false,
33007
- error: {
33008
- code: "VALIDATION_ERROR",
33009
- message: `Unknown --output "${format}". Use one of: ${ANALYTICS_OUTPUT_FORMATS.join(", ")}.`
33010
- }
33011
- });
33012
- process.exit(1);
33013
- }
33014
- return format;
33015
- }
33016
32552
  function presetCommand(options) {
33017
32553
  registerSchema({
33018
32554
  command: `analytics.${options.name}`,
@@ -33514,8 +33050,7 @@ var conversionsCommand3 = (() => {
33514
33050
  type: "string",
33515
33051
  description: "Lookback for the counts and for --candidates. Default 30",
33516
33052
  required: false
33517
- },
33518
- output: SHARED_ARGS.output
33053
+ }
33519
33054
  };
33520
33055
  registerSchema({
33521
33056
  command: "analytics.conversions",
@@ -33530,23 +33065,22 @@ var conversionsCommand3 = (() => {
33530
33065
  args: args5,
33531
33066
  run: async ({ args: raw }) => {
33532
33067
  try {
33533
- const format = outputFormatOrExit(raw.output);
33534
33068
  const args6 = parseConversionsArgs(raw, process.argv);
33535
33069
  for (const eventKey of args6.eventKeys) refuseUnmatchableKey(eventKey);
33536
33070
  const before = await apiPost("/api/analytics/conversions", {});
33537
- const write2 = conversionsWrite(args6, before.data.definitions);
33538
- const stored = write2 ? (await apiPost("/api/analytics/conversions", write2)).data.definitions : before.data.definitions;
33071
+ const write = conversionsWrite(args6, before.data.definitions);
33072
+ const stored = write ? (await apiPost("/api/analytics/conversions", write)).data.definitions : before.data.definitions;
33539
33073
  const report = await apiPost("/api/analytics/query", {
33540
33074
  preset: "conversions",
33541
33075
  days: args6.days
33542
33076
  });
33543
- const envelope = conversionsEnvelope(
33544
- args6,
33545
- mergeStoredWithCounts(stored, report.data.conversions ?? []),
33546
- report.data.eventCatalog ?? []
33077
+ writeJsonEnvelope(
33078
+ conversionsEnvelope(
33079
+ args6,
33080
+ mergeStoredWithCounts(stored, report.data.conversions ?? []),
33081
+ report.data.eventCatalog ?? []
33082
+ )
33547
33083
  );
33548
- if (format === "md") process.stdout.write(renderConversionsMarkdown(envelope));
33549
- else writeJsonEnvelope(envelope);
33550
33084
  } catch (err) {
33551
33085
  if (err instanceof ConversionArgsError) {
33552
33086
  handleError3(new ApiError("VALIDATION_ERROR", err.message));
@@ -33911,11 +33445,7 @@ function rosterRow(avatar) {
33911
33445
  };
33912
33446
  }
33913
33447
  function chatIdFromEnv() {
33914
- try {
33915
- return getEnv().BAKER_CHAT_ID || void 0;
33916
- } catch {
33917
- return void 0;
33918
- }
33448
+ return optionalChatId();
33919
33449
  }
33920
33450
  function rosterHints(roster, statusFilter) {
33921
33451
  if (roster.length === 0) {
@@ -39444,7 +38974,7 @@ function sceneSpokenText(scene) {
39444
38974
  return (scene.dialogue ?? []).map((d) => d.line?.trim()).filter((l) => Boolean(l)).join(" ") || null;
39445
38975
  }
39446
38976
  function buildMotionBoard(blueprint) {
39447
- const round5 = (n) => Math.round(n * 100) / 100;
38977
+ const round4 = (n) => Math.round(n * 100) / 100;
39448
38978
  let cursor = 0;
39449
38979
  return blueprint.scenes.map((scene, i) => {
39450
38980
  const start_s = scene.start_s ?? cursor;
@@ -39456,15 +38986,15 @@ function buildMotionBoard(blueprint) {
39456
38986
  const graphics = [
39457
38987
  ...(overlays.success ? overlays.data : []).filter((ov) => ov.text?.trim()).map((ov) => ({
39458
38988
  kind: "text",
39459
- at_s: round5(ov.appears_at_s ?? start_s),
39460
- dur_s: round5(ov.duration_s ?? 2.5),
38989
+ at_s: round4(ov.appears_at_s ?? start_s),
38990
+ dur_s: round4(ov.duration_s ?? 2.5),
39461
38991
  position: ov.position ?? "bottom_center",
39462
38992
  text: ov.text?.trim()
39463
38993
  })),
39464
38994
  ...(floats.success ? floats.data : []).map((fe) => ({
39465
38995
  kind: "graphic",
39466
- at_s: round5(fe.appears_at_s ?? start_s),
39467
- dur_s: round5(fe.duration_s ?? 2.5),
38996
+ at_s: round4(fe.appears_at_s ?? start_s),
38997
+ dur_s: round4(fe.duration_s ?? 2.5),
39468
38998
  position: fe.position ?? "bottom_center",
39469
38999
  label: fe.brand_name || fe.what_it_represents || fe.description || fe.kind || "element"
39470
39000
  }))
@@ -39472,7 +39002,7 @@ function buildMotionBoard(blueprint) {
39472
39002
  return {
39473
39003
  scene: i,
39474
39004
  role: resolveSceneRole(scene, i, blueprint.scenes.length),
39475
- window_s: [round5(start_s), round5(end_s)],
39005
+ window_s: [round4(start_s), round4(end_s)],
39476
39006
  // A continuation b-roll scene shares the previous scene's end frame as its start
39477
39007
  // (no own `s<i>_start` node), so point the storyboard at that shared keyframe.
39478
39008
  storyboard_frames: [scene.continues_previous && i > 0 ? `s${i - 1}_end` : `s${i}_start`],
@@ -41235,7 +40765,7 @@ ${describeRewrites(healed.rewrites)}
41235
40765
  // "edited since last render". Computed from the on-disk canvas.json +
41236
40766
  // prompt.json — the same helper the edit-sync uses, so the two are comparable.
41237
40767
  sourceSha: await computeSourceSha(filePath),
41238
- chatId: getEnv().BAKER_CHAT_ID || void 0,
40768
+ chatId: optionalChatId(),
41239
40769
  canvasSnapshotUrl
41240
40770
  };
41241
40771
  if (client) {
@@ -45226,11 +44756,7 @@ function detectCreativeContentType(filePath) {
45226
44756
  });
45227
44757
  }
45228
44758
  function chatIdFromEnv2() {
45229
- try {
45230
- return getEnv().BAKER_CHAT_ID || void 0;
45231
- } catch {
45232
- return void 0;
45233
- }
44759
+ return optionalChatId();
45234
44760
  }
45235
44761
  function parseOptionalUrl(value) {
45236
44762
  if (value === void 0 || value.trim() === "") {
@@ -53147,7 +52673,7 @@ var extractCommand = defineCommand149({
53147
52673
  import { defineCommand as defineCommand150 } from "citty";
53148
52674
 
53149
52675
  // src/commands/images/providerHits.ts
53150
- function asRecord3(value) {
52676
+ function asRecord2(value) {
53151
52677
  return value && typeof value === "object" ? value : {};
53152
52678
  }
53153
52679
  function str(value) {
@@ -53172,7 +52698,7 @@ function providerHitNormalizer(record, full) {
53172
52698
  if (!full) {
53173
52699
  return compact3;
53174
52700
  }
53175
- const group = asRecord3(record.group);
52701
+ const group = asRecord2(record.group);
53176
52702
  return {
53177
52703
  ...compact3,
53178
52704
  externalUrl: str(record.externalUrl),
@@ -57077,7 +56603,7 @@ function dedupe(findings) {
57077
56603
 
57078
56604
  // src/engine/landing/lib/critique.ts
57079
56605
  var FAMILIES = ["typography", "color", "borders_depth", "motion", "spacing", "copy", "integrity"];
57080
- function round4(n) {
56606
+ function round2(n) {
57081
56607
  return Math.round(n * 100) / 100;
57082
56608
  }
57083
56609
  function clamp012(n) {
@@ -57114,11 +56640,11 @@ function critiqueLanding(input) {
57114
56640
  const dimensions = FAMILIES.map((family) => {
57115
56641
  const fam = findings.filter((f) => f.family === family);
57116
56642
  const penalty = fam.reduce((s, f) => s + SEVERITY_WEIGHT[f.severity], 0);
57117
- const score = round4(clamp012(1 - penalty));
56643
+ const score = round2(clamp012(1 - penalty));
57118
56644
  const note = fam.length === 0 ? "clean" : `${fam.length} finding(s): ${describeCounts(fam)}`;
57119
56645
  return { dimension: family, score, note };
57120
56646
  });
57121
- const overall = round4(dimensions.reduce((s, d) => s + d.score, 0) / dimensions.length);
56647
+ const overall = round2(dimensions.reduce((s, d) => s + d.score, 0) / dimensions.length);
57122
56648
  const counts = {
57123
56649
  block: findings.filter((f) => f.severity === "block").length,
57124
56650
  warn: findings.filter((f) => f.severity === "warn").length,
@@ -61277,7 +60803,7 @@ function writeJsonl(data) {
61277
60803
  `);
61278
60804
  }
61279
60805
  }
61280
- function writeMd2(data, fields) {
60806
+ function writeMd(data, fields) {
61281
60807
  if (data.length === 0) return;
61282
60808
  const cols = fields ?? Object.keys(data[0] ?? {});
61283
60809
  process.stdout.write(`| ${cols.join(" | ")} |
@@ -61299,7 +60825,7 @@ function writeResearchOutput(data, format, fields) {
61299
60825
  writeJsonl(data);
61300
60826
  break;
61301
60827
  case "md":
61302
- writeMd2(data, fields);
60828
+ writeMd(data, fields);
61303
60829
  break;
61304
60830
  default:
61305
60831
  writeResearchJson({ ok: true, data });
@@ -67754,6 +67280,7 @@ function commandFor(root, argv) {
67754
67280
  const { command, resolved } = resolveCommand2(root, argv);
67755
67281
  return resolved ? command : null;
67756
67282
  }
67283
+ var OUTPUT_EXPLANATION = "This command has no --output option: `--output json` is accepted by every command that prints the JSON envelope, and md or files only where `baker schema` lists `output`. Drop the option and read the default output. ";
67757
67284
  function unknownFlagEnvelope(unknown, commandPath, suggestion, hasSchema) {
67758
67285
  const list = unknown.join(", ");
67759
67286
  return {
@@ -67763,7 +67290,7 @@ function unknownFlagEnvelope(unknown, commandPath, suggestion, hasSchema) {
67763
67290
  message: `${list} ${unknown.length > 1 ? "are not options" : "is not an option"} of \`baker ${commandPath}\`.`,
67764
67291
  fix: {
67765
67292
  action: hasSchema ? "check_schema" : "check_help",
67766
- explanation: (suggestion ? `Did you mean ${suggestion}? ` : "") + `Run \`${hasSchema ? `baker schema ${commandPath}` : `baker ${commandPath} --help`}\` to see every option this command takes. Do not re-run the same call: until this version an unrecognised option was accepted and ignored, so a command that looked like it had filtered or narrowed the result was in fact answering the unfiltered question.`
67293
+ explanation: (unknown.some((flag) => flag === "--output" || flag.startsWith("--output=")) ? OUTPUT_EXPLANATION : "") + (suggestion ? `Did you mean ${suggestion}? ` : "") + `Run \`${hasSchema ? `baker schema ${commandPath}` : `baker ${commandPath} --help`}\` to see every option this command takes. Do not re-run the same call: until this version an unrecognised option was accepted and ignored, so a command that looked like it had filtered or narrowed the result was in fact answering the unfiltered question.`
67767
67294
  },
67768
67295
  retryable: false
67769
67296
  }
@@ -67802,6 +67329,58 @@ function refuseUnknownFlags(root, argv) {
67802
67329
  process.exit(1);
67803
67330
  }
67804
67331
 
67332
+ // src/output-flag.ts
67333
+ var FLAG = "--output";
67334
+ var ENVELOPE = "json";
67335
+ var PRINTS_NO_ENVELOPE = /* @__PURE__ */ new Set([
67336
+ "analytics sending-events",
67337
+ "canvas run",
67338
+ "canvas rerun",
67339
+ "canvas sync-definition"
67340
+ ]);
67341
+ function declaredArg(command, name) {
67342
+ const args5 = command.args;
67343
+ if (!args5 || typeof args5 !== "object" || !(name in args5)) {
67344
+ return null;
67345
+ }
67346
+ const def = args5[name];
67347
+ return def && typeof def === "object" ? def : {};
67348
+ }
67349
+ function envelopeRequests(argv) {
67350
+ const end = argv.indexOf("--");
67351
+ const limit = end === -1 ? argv.length : end;
67352
+ const found = [];
67353
+ for (let index = 0; index < limit; index++) {
67354
+ const token = argv[index];
67355
+ if (token === `${FLAG}=${ENVELOPE}`) {
67356
+ found.push({ index, length: 1 });
67357
+ } else if (token === FLAG && argv[index + 1] === ENVELOPE && index + 1 < limit) {
67358
+ found.push({ index, length: 2 });
67359
+ index++;
67360
+ }
67361
+ }
67362
+ return found;
67363
+ }
67364
+ function outputFlagForCommand(root, argv) {
67365
+ const requests = envelopeRequests(argv);
67366
+ if (requests.length === 0) {
67367
+ return argv;
67368
+ }
67369
+ const command = commandFor(root, argv);
67370
+ if (!command || declaredArg(command, "output") || PRINTS_NO_ENVELOPE.has(commandPathOf(root, argv))) {
67371
+ return argv;
67372
+ }
67373
+ const jsonSwitch = declaredArg(command, "json")?.type === "boolean";
67374
+ const dropped = new Set(requests.flatMap(({ index, length }) => length === 2 ? [index, index + 1] : [index]));
67375
+ const first = requests[0]?.index;
67376
+ return argv.flatMap((token, index) => {
67377
+ if (!dropped.has(index)) {
67378
+ return [token];
67379
+ }
67380
+ return jsonSwitch && index === first ? ["--json"] : [];
67381
+ });
67382
+ }
67383
+
67805
67384
  // src/version.ts
67806
67385
  import { readFileSync as readFileSync21 } from "fs";
67807
67386
  function packageJsonUrl() {
@@ -67828,7 +67407,7 @@ var main = defineCommand245({
67828
67407
  Auth: Set BAKER_API_KEY (starts with bk_) and BAKER_API_URL environment variables.
67829
67408
  Chat: Set BAKER_CHAT_ID for action and scheduled-action commands that stage changes against a chat.
67830
67409
  Output: All commands return JSON envelopes: { ok: true, data: ... } or { ok: false, error: { code, message } }.
67831
- Formats: Use --output json|files|md to control output format. Default: json. (The research commands additionally accept csv and jsonl.)
67410
+ Formats: --output json works on every command that prints the envelope. --output md|files only where 'baker schema <command>' lists output. (The research commands additionally accept csv and jsonl.)
67832
67411
  Introspection: Run 'baker schema <command>' to inspect argument schemas.`
67833
67412
  },
67834
67413
  subCommands: {
@@ -67866,6 +67445,7 @@ process.argv = [...process.argv.slice(0, 2), ...normalizeCacheFlag(process.argv.
67866
67445
  installStreamTaps();
67867
67446
  logInvocation(process.argv.slice(2));
67868
67447
  installExitRecord();
67448
+ process.argv = [...process.argv.slice(0, 2), ...outputFlagForCommand(main, process.argv.slice(2))];
67869
67449
  refuseUnknownFlags(main, process.argv.slice(2));
67870
67450
  process.argv = [...process.argv.slice(0, 2), ...cacheFlagForParser(process.argv.slice(2))];
67871
67451
  runMain(main);