@koda-sl/baker-cli 0.410.0 → 0.411.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 +31 -0
- package/dist/cli.js +941 -533
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -21715,7 +21715,7 @@ var require_entry_node = __commonJS({
|
|
|
21715
21715
|
});
|
|
21716
21716
|
|
|
21717
21717
|
// src/cli.ts
|
|
21718
|
-
import { defineCommand as
|
|
21718
|
+
import { defineCommand as defineCommand279, runMain } from "citty";
|
|
21719
21719
|
|
|
21720
21720
|
// src/cache-flag.ts
|
|
21721
21721
|
var NO_CACHE_ARG = {
|
|
@@ -23949,20 +23949,20 @@ var conversionUploadRowSchema = z4.strictObject({
|
|
|
23949
23949
|
value: z4.number().nonnegative().optional(),
|
|
23950
23950
|
currency: z4.string().regex(/^[A-Z]{3}$/, "expected a 3-letter currency code, e.g. EUR").optional()
|
|
23951
23951
|
}).refine(
|
|
23952
|
-
(
|
|
23952
|
+
(row2) => Boolean(row2.gclid ?? row2.gbraid ?? row2.wbraid ?? row2.email ?? row2.phone ?? row2.address),
|
|
23953
23953
|
"every conversion needs something to match on: a click id (gclid / gbraid / wbraid), or a customer's email, phone or name+address"
|
|
23954
23954
|
).refine(
|
|
23955
|
-
(
|
|
23955
|
+
(row2) => [row2.gclid, row2.gbraid, row2.wbraid].filter(Boolean).length <= 1,
|
|
23956
23956
|
"pass at most one click id per conversion \u2014 gclid, gbraid and wbraid are three ways of naming the same click"
|
|
23957
23957
|
).refine(
|
|
23958
|
-
(
|
|
23958
|
+
(row2) => row2.value === void 0 || row2.currency !== void 0,
|
|
23959
23959
|
"a conversion with a value needs its currency too"
|
|
23960
|
-
).refine((
|
|
23960
|
+
).refine((row2) => row2.currency === void 0 || row2.value !== void 0, "currency was given with no value");
|
|
23961
23961
|
var conversionUploadSchema = z4.strictObject({
|
|
23962
23962
|
conversionAction: conversionUploadActionSchema,
|
|
23963
23963
|
conversions: z4.array(conversionUploadRowSchema).min(1).max(CONVERSION_UPLOAD_MAX_ROWS)
|
|
23964
23964
|
}).refine((payload) => {
|
|
23965
|
-
const ids2 = payload.conversions.map((
|
|
23965
|
+
const ids2 = payload.conversions.map((row2) => row2.orderId);
|
|
23966
23966
|
return new Set(ids2).size === ids2.length;
|
|
23967
23967
|
}, "two conversions share an order id \u2014 each order id can appear once per upload");
|
|
23968
23968
|
var MS_PER_DAY = 24 * 60 * 60 * 1e3;
|
|
@@ -28952,7 +28952,8 @@ var researchFetchResponseSchema = z27.object({
|
|
|
28952
28952
|
contentChars: z27.number(),
|
|
28953
28953
|
/** The page was longer than one response may carry, so the tail is missing. */
|
|
28954
28954
|
truncated: z27.boolean(),
|
|
28955
|
-
|
|
28955
|
+
/** `reddit` when the URL was a Reddit thread, which Firecrawl refuses and our Reddit reader serves instead. */
|
|
28956
|
+
provider: z27.enum(["firecrawl", "reddit"])
|
|
28956
28957
|
});
|
|
28957
28958
|
var RESEARCH_KEYWORD_METRICS_MAX_KEYWORDS = 700;
|
|
28958
28959
|
var RESEARCH_KEYWORD_METRICS_MAX_KEYWORD_CHARS = 80;
|
|
@@ -28991,6 +28992,81 @@ var researchKeywordMetricsRowSchema = z27.object({
|
|
|
28991
28992
|
var researchKeywordMetricsResponseSchema = z27.object({
|
|
28992
28993
|
data: z27.array(researchKeywordMetricsRowSchema)
|
|
28993
28994
|
});
|
|
28995
|
+
var RESEARCH_REDDIT_SEARCH_MAX_LIMIT = 50;
|
|
28996
|
+
var RESEARCH_REDDIT_MAX_SUBREDDITS = 10;
|
|
28997
|
+
var RESEARCH_REDDIT_THREAD_MAX_COMMENTS = 200;
|
|
28998
|
+
var RESEARCH_REDDIT_THREAD_DEFAULT_COMMENTS = 40;
|
|
28999
|
+
var researchRedditSourceSchema = z27.enum(["reddit_api", "reddit_web", "archive"]);
|
|
29000
|
+
var researchRedditSubredditSchema = z27.string().trim().transform((name) => name.replace(/^\/?r\//i, "")).pipe(z27.string().regex(/^[A-Za-z0-9_]{2,21}$/, "Not a subreddit name"));
|
|
29001
|
+
var researchRedditSearchRequestSchema = z27.object({
|
|
29002
|
+
query: z27.string().trim().min(1).max(256),
|
|
29003
|
+
subreddits: z27.array(researchRedditSubredditSchema).max(RESEARCH_REDDIT_MAX_SUBREDDITS).default([]),
|
|
29004
|
+
/** `comments` searches what people replied, which is where most of the verbatim voice is. */
|
|
29005
|
+
kind: z27.enum(["posts", "comments"]).default("posts"),
|
|
29006
|
+
/** Only things written on or after this day, `YYYY-MM-DD`. */
|
|
29007
|
+
since: z27.string().regex(/^\d{4}-\d{2}-\d{2}$/, "Use YYYY-MM-DD").optional(),
|
|
29008
|
+
limit: z27.coerce.number().int().min(1).max(RESEARCH_REDDIT_SEARCH_MAX_LIMIT).default(25)
|
|
29009
|
+
});
|
|
29010
|
+
var researchRedditThreadRequestSchema = z27.object({
|
|
29011
|
+
/** A thread link in any form Reddit hands out — full, `old.`, `redd.it`, `/s/` share — or a bare post id. */
|
|
29012
|
+
target: z27.string().trim().min(1).max(2048),
|
|
29013
|
+
comments: z27.coerce.number().int().min(0).max(RESEARCH_REDDIT_THREAD_MAX_COMMENTS).default(RESEARCH_REDDIT_THREAD_DEFAULT_COMMENTS)
|
|
29014
|
+
});
|
|
29015
|
+
var researchRedditItemSchema = z27.object({
|
|
29016
|
+
kind: z27.enum(["post", "comment"]),
|
|
29017
|
+
id: z27.string(),
|
|
29018
|
+
postId: z27.string(),
|
|
29019
|
+
subreddit: z27.string(),
|
|
29020
|
+
/** `null` once the account is deleted. */
|
|
29021
|
+
author: z27.string().nullable(),
|
|
29022
|
+
/** The post's title; on a comment, the title of the post it sits under when known. */
|
|
29023
|
+
title: z27.string().nullable(),
|
|
29024
|
+
/** Verbatim: the post's own text (empty for a link post) or the comment's. */
|
|
29025
|
+
text: z27.string(),
|
|
29026
|
+
score: z27.number().nullable(),
|
|
29027
|
+
/** Posts only. */
|
|
29028
|
+
commentCount: z27.number().nullable(),
|
|
29029
|
+
createdAt: z27.string(),
|
|
29030
|
+
permalink: z27.string(),
|
|
29031
|
+
/** Comments inside a thread: 0 answers the post, 1 answers a comment, and so on. */
|
|
29032
|
+
depth: z27.number().nullable()
|
|
29033
|
+
});
|
|
29034
|
+
var researchRedditAttemptSchema = z27.object({
|
|
29035
|
+
source: researchRedditSourceSchema,
|
|
29036
|
+
outcome: z27.enum(["ok", "empty", "not_found", "not_configured", "unsupported", "blocked", "unavailable"])
|
|
29037
|
+
});
|
|
29038
|
+
var researchRedditSearchResponseSchema = z27.object({
|
|
29039
|
+
results: z27.array(researchRedditItemSchema),
|
|
29040
|
+
source: researchRedditSourceSchema,
|
|
29041
|
+
/** Every source asked, in order — an empty answer from one is not the same as none from all. */
|
|
29042
|
+
attempts: z27.array(researchRedditAttemptSchema)
|
|
29043
|
+
});
|
|
29044
|
+
var researchRedditThreadResponseSchema = z27.object({
|
|
29045
|
+
post: researchRedditItemSchema,
|
|
29046
|
+
/** Highest-scoring first, each followed by its best replies. Removed and deleted comments are left out. */
|
|
29047
|
+
comments: z27.array(researchRedditItemSchema),
|
|
29048
|
+
source: researchRedditSourceSchema,
|
|
29049
|
+
attempts: z27.array(researchRedditAttemptSchema)
|
|
29050
|
+
});
|
|
29051
|
+
var RESEARCH_REDDIT_SUBREDDITS_MAX_LIMIT = 25;
|
|
29052
|
+
var researchRedditSubredditsRequestSchema = z27.object({
|
|
29053
|
+
query: z27.string().trim().min(2).max(100),
|
|
29054
|
+
limit: z27.coerce.number().int().min(1).max(RESEARCH_REDDIT_SUBREDDITS_MAX_LIMIT).default(10)
|
|
29055
|
+
});
|
|
29056
|
+
var researchRedditSubredditSummarySchema = z27.object({
|
|
29057
|
+
name: z27.string(),
|
|
29058
|
+
/** Members; `null` when the source does not say. Size, not activity — a big subreddit can be quiet. */
|
|
29059
|
+
subscribers: z27.number().nullable(),
|
|
29060
|
+
/** `restricted`: anyone reads, only approved members post. `private`: nothing can be read. */
|
|
29061
|
+
access: z27.enum(["public", "restricted", "private", "other"]).nullable(),
|
|
29062
|
+
description: z27.string(),
|
|
29063
|
+
url: z27.string()
|
|
29064
|
+
});
|
|
29065
|
+
var researchRedditSubredditsResponseSchema = z27.object({
|
|
29066
|
+
results: z27.array(researchRedditSubredditSummarySchema),
|
|
29067
|
+
source: researchRedditSourceSchema,
|
|
29068
|
+
attempts: z27.array(researchRedditAttemptSchema)
|
|
29069
|
+
});
|
|
28994
29070
|
|
|
28995
29071
|
// ../api/src/scheduledAgentRules.ts
|
|
28996
29072
|
import { z as z28 } from "zod";
|
|
@@ -31564,9 +31640,9 @@ function toRow(entry) {
|
|
|
31564
31640
|
};
|
|
31565
31641
|
}
|
|
31566
31642
|
function renderEntry(entry, full) {
|
|
31567
|
-
const
|
|
31568
|
-
const suffix =
|
|
31569
|
-
const lines = [`- \`${
|
|
31643
|
+
const row2 = toRow(entry);
|
|
31644
|
+
const suffix = row2.status ? ` [${row2.status}]` : "";
|
|
31645
|
+
const lines = [`- \`${row2.id}\` ${row2.name}${suffix}`];
|
|
31570
31646
|
const nested = entry.action;
|
|
31571
31647
|
const doc = nested && typeof nested === "object" ? nested : entry;
|
|
31572
31648
|
const description = typeof doc.description === "string" ? doc.description : "";
|
|
@@ -31576,8 +31652,8 @@ function renderEntry(entry, full) {
|
|
|
31576
31652
|
...text2.trim().split("\n").map((line) => line ? ` ${line}` : "")
|
|
31577
31653
|
);
|
|
31578
31654
|
}
|
|
31579
|
-
if (
|
|
31580
|
-
lines.push(` ${
|
|
31655
|
+
if (row2.hint) {
|
|
31656
|
+
lines.push(` ${row2.hint}`);
|
|
31581
31657
|
}
|
|
31582
31658
|
return lines;
|
|
31583
31659
|
}
|
|
@@ -35288,10 +35364,10 @@ function membersFromFile(content) {
|
|
|
35288
35364
|
const emailColumn = header.findIndex((cell3) => EMAIL_HEADER.test(cell3));
|
|
35289
35365
|
const phoneColumn = header.findIndex((cell3) => !EMAIL_HEADER.test(cell3) && PHONE_HEADER.test(cell3));
|
|
35290
35366
|
if (emailColumn >= 0 || phoneColumn >= 0) {
|
|
35291
|
-
const cells = (column) => column < 0 ? [] : rows.slice(1).flatMap((
|
|
35367
|
+
const cells = (column) => column < 0 ? [] : rows.slice(1).flatMap((row2) => row2[column] ? [row2[column]] : []);
|
|
35292
35368
|
return { emails: cells(emailColumn), phones: cells(phoneColumn) };
|
|
35293
35369
|
}
|
|
35294
|
-
const values = rows.flatMap((
|
|
35370
|
+
const values = rows.flatMap((row2) => row2[0] ? [row2[0]] : []);
|
|
35295
35371
|
return values.some((value2) => value2.includes("@")) ? { emails: values, phones: [] } : { emails: [], phones: values };
|
|
35296
35372
|
}
|
|
35297
35373
|
|
|
@@ -35820,7 +35896,7 @@ function conversionReportRequestFromArgs(args8) {
|
|
|
35820
35896
|
};
|
|
35821
35897
|
}
|
|
35822
35898
|
function reportHints(report) {
|
|
35823
|
-
const nothing = report.rows.every((
|
|
35899
|
+
const nothing = report.rows.every((row2) => row2.conversions === 0 && (row2.events ?? []).length === 0);
|
|
35824
35900
|
return nothing ? [
|
|
35825
35901
|
"No attributed conversions in this window. Before concluding the ads do not convert: `conversions events` shows whether the site's events arrive at all, and `campaigns` whether anything served."
|
|
35826
35902
|
] : [
|
|
@@ -39331,9 +39407,9 @@ function warnDefaults(ctx) {
|
|
|
39331
39407
|
var PLANNER_SOURCE = "keyword_planner_estimate";
|
|
39332
39408
|
var IDENTITY_FIELDS = /* @__PURE__ */ new Set(["keyword"]);
|
|
39333
39409
|
function stampPlannerRows(rows) {
|
|
39334
|
-
return rows.map((
|
|
39410
|
+
return rows.map((row2) => {
|
|
39335
39411
|
const stamped = { source: PLANNER_SOURCE };
|
|
39336
|
-
for (const [key, value2] of Object.entries(
|
|
39412
|
+
for (const [key, value2] of Object.entries(row2)) {
|
|
39337
39413
|
stamped[IDENTITY_FIELDS.has(key) || key.startsWith("planner_") ? key : `planner_${key}`] = value2;
|
|
39338
39414
|
}
|
|
39339
39415
|
return stamped;
|
|
@@ -40262,33 +40338,33 @@ function toMicros2(value2) {
|
|
|
40262
40338
|
function formatMoney(micros) {
|
|
40263
40339
|
return (micros / 1e6).toFixed(2);
|
|
40264
40340
|
}
|
|
40265
|
-
function keywordLabel(
|
|
40266
|
-
const text2 =
|
|
40267
|
-
const id =
|
|
40341
|
+
function keywordLabel(row2) {
|
|
40342
|
+
const text2 = row2["ad_group_criterion.keyword.text"];
|
|
40343
|
+
const id = row2["ad_group_criterion.criterion_id"];
|
|
40268
40344
|
const name = typeof text2 === "string" && text2 !== "" ? text2 : id == null ? "(unnamed keyword)" : `criterion ${id}`;
|
|
40269
|
-
const adGroup =
|
|
40345
|
+
const adGroup = row2["ad_group.name"];
|
|
40270
40346
|
return typeof adGroup === "string" && adGroup !== "" ? `${name} (ad group ${adGroup})` : name;
|
|
40271
40347
|
}
|
|
40272
|
-
function keywordKey(
|
|
40273
|
-
const adGroup =
|
|
40274
|
-
const criterion =
|
|
40275
|
-
return `${String(adGroup)}~${String(criterion)}` === "~" ? keywordLabel(
|
|
40348
|
+
function keywordKey(row2) {
|
|
40349
|
+
const adGroup = row2["ad_group.id"] ?? row2["ad_group.name"] ?? "";
|
|
40350
|
+
const criterion = row2["ad_group_criterion.criterion_id"] ?? row2["ad_group_criterion.keyword.text"] ?? "";
|
|
40351
|
+
return `${String(adGroup)}~${String(criterion)}` === "~" ? keywordLabel(row2) : `${adGroup}~${criterion}`;
|
|
40276
40352
|
}
|
|
40277
|
-
function reasonsOn(
|
|
40278
|
-
const raw =
|
|
40353
|
+
function reasonsOn(row2) {
|
|
40354
|
+
const raw = row2["ad_group_criterion.primary_status_reasons"];
|
|
40279
40355
|
if (!Array.isArray(raw)) return null;
|
|
40280
40356
|
return raw.filter((r) => typeof r === "string");
|
|
40281
40357
|
}
|
|
40282
|
-
function bidShortfall(
|
|
40283
|
-
const bid = toMicros2(
|
|
40284
|
-
const firstPage = toMicros2(
|
|
40358
|
+
function bidShortfall(row2) {
|
|
40359
|
+
const bid = toMicros2(row2["ad_group_criterion.effective_cpc_bid_micros"] ?? row2["ad_group_criterion.cpc_bid_micros"]);
|
|
40360
|
+
const firstPage = toMicros2(row2["ad_group_criterion.position_estimates.first_page_cpc_micros"]);
|
|
40285
40361
|
if (bid == null || firstPage == null || bid >= firstPage) return null;
|
|
40286
40362
|
return `bids ${formatMoney(bid)} against a ${formatMoney(firstPage)} first-page estimate`;
|
|
40287
40363
|
}
|
|
40288
|
-
function limitsOn(
|
|
40289
|
-
const reasons = reasonsOn(
|
|
40290
|
-
const shortfall = bidShortfall(
|
|
40291
|
-
const label = keywordLabel(
|
|
40364
|
+
function limitsOn(row2, options) {
|
|
40365
|
+
const reasons = reasonsOn(row2);
|
|
40366
|
+
const shortfall = bidShortfall(row2);
|
|
40367
|
+
const label = keywordLabel(row2);
|
|
40292
40368
|
const applicable = reasons ? reasons.filter((reason) => reason in ACTIONABLE_REASONS) : options.statusReasonsSelected || shortfall === null ? [] : [BELOW_FIRST_PAGE_BID];
|
|
40293
40369
|
return applicable.map((reason) => ({
|
|
40294
40370
|
reason,
|
|
@@ -40302,10 +40378,10 @@ function describeLimit(reason, details) {
|
|
|
40302
40378
|
return `${label} \u2014 ${details.length}: ${named}${rest}. Fix: ${advice}.`;
|
|
40303
40379
|
}
|
|
40304
40380
|
function collectKeywordLimits(rows, options, acc = { detailsByReason: /* @__PURE__ */ new Map(), keywords: /* @__PURE__ */ new Set() }) {
|
|
40305
|
-
for (const
|
|
40306
|
-
const limits = limitsOn(
|
|
40381
|
+
for (const row2 of rows) {
|
|
40382
|
+
const limits = limitsOn(row2, options);
|
|
40307
40383
|
if (limits.length === 0) continue;
|
|
40308
|
-
acc.keywords.add(keywordKey(
|
|
40384
|
+
acc.keywords.add(keywordKey(row2));
|
|
40309
40385
|
for (const { reason, detail } of limits) {
|
|
40310
40386
|
const bucket = acc.detailsByReason.get(reason);
|
|
40311
40387
|
if (bucket) bucket.push(detail);
|
|
@@ -40415,11 +40491,11 @@ function writeRowsToFile(filePath, rows, fields, append) {
|
|
|
40415
40491
|
writeFileSync(filePath, `${toCsvRow(fields)}
|
|
40416
40492
|
`, "utf-8");
|
|
40417
40493
|
}
|
|
40418
|
-
const csvRows = rows.map((
|
|
40494
|
+
const csvRows = rows.map((row2) => toCsvRow(fields.map((f) => String(row2[f] ?? ""))));
|
|
40419
40495
|
appendFileSync(filePath, `${csvRows.join("\n")}
|
|
40420
40496
|
`, "utf-8");
|
|
40421
40497
|
} else if (filePath.split(".").pop()?.toLowerCase() === "jsonl") {
|
|
40422
|
-
const lines = rows.map((
|
|
40498
|
+
const lines = rows.map((row2) => JSON.stringify(row2));
|
|
40423
40499
|
if (append && existsSync2(filePath)) {
|
|
40424
40500
|
appendFileSync(filePath, `${lines.join("\n")}
|
|
40425
40501
|
`, "utf-8");
|
|
@@ -40504,26 +40580,26 @@ function writeLine(line) {
|
|
|
40504
40580
|
var writeCsvRows = (rows, fields, notices) => {
|
|
40505
40581
|
writeLine(toCsvRow(fields));
|
|
40506
40582
|
for (const line of notices) writeLine(line);
|
|
40507
|
-
for (const
|
|
40583
|
+
for (const row2 of rows) writeLine(toCsvRow(fields.map((f) => String(row2[f] ?? ""))));
|
|
40508
40584
|
};
|
|
40509
40585
|
var writeJsonlRows = (rows, _fields, notices) => {
|
|
40510
40586
|
for (const line of notices) writeLine(line);
|
|
40511
|
-
for (const
|
|
40587
|
+
for (const row2 of rows) writeLine(JSON.stringify(row2));
|
|
40512
40588
|
};
|
|
40513
40589
|
var OMITTED = "(omitted)";
|
|
40514
40590
|
var OMITTED_FALSE = `false ${OMITTED}`;
|
|
40515
40591
|
var OMITTED_LEGEND = `> ${OMITTED} = Google left this field off the row, which is how it sends false, 0 and "not set": the field WAS read. A field with no column here was not read.`;
|
|
40516
|
-
function mdCell(
|
|
40517
|
-
if (!(field in
|
|
40518
|
-
return String(
|
|
40592
|
+
function mdCell(row2, field, booleanColumns) {
|
|
40593
|
+
if (!(field in row2)) return booleanColumns.has(field) ? OMITTED_FALSE : OMITTED;
|
|
40594
|
+
return String(row2[field] ?? "").replace(/\|/g, "\\|");
|
|
40519
40595
|
}
|
|
40520
40596
|
var writeMdRows = (rows, fields, notices) => {
|
|
40521
|
-
const booleanColumns = new Set(fields.filter((f) => rows.some((
|
|
40597
|
+
const booleanColumns = new Set(fields.filter((f) => rows.some((row2) => typeof row2[f] === "boolean")));
|
|
40522
40598
|
for (const line of notices) writeLine(line);
|
|
40523
40599
|
writeLine(`| ${fields.join(" | ")} |`);
|
|
40524
40600
|
writeLine(`| ${fields.map(() => "---").join(" | ")} |`);
|
|
40525
|
-
for (const
|
|
40526
|
-
if (rows.some((
|
|
40601
|
+
for (const row2 of rows) writeLine(`| ${fields.map((f) => mdCell(row2, f, booleanColumns)).join(" | ")} |`);
|
|
40602
|
+
if (rows.some((row2) => fields.some((f) => !(f in row2)))) writeLine(OMITTED_LEGEND);
|
|
40527
40603
|
};
|
|
40528
40604
|
var ROW_WRITERS = { csv: writeCsvRows, jsonl: writeJsonlRows, md: writeMdRows };
|
|
40529
40605
|
function writeRows(format, rows, columns, notices) {
|
|
@@ -40647,8 +40723,8 @@ function parseSelectedFields(gaql) {
|
|
|
40647
40723
|
return list.split(",").map((field) => field.trim()).filter((field) => /^[A-Za-z_][A-Za-z0-9_]*(\.[A-Za-z0-9_]+)*$/.test(field));
|
|
40648
40724
|
}
|
|
40649
40725
|
function foldObservedFields(rows, observed) {
|
|
40650
|
-
for (const
|
|
40651
|
-
for (const key of Object.keys(
|
|
40726
|
+
for (const row2 of rows) {
|
|
40727
|
+
for (const key of Object.keys(row2)) observed.add(key);
|
|
40652
40728
|
}
|
|
40653
40729
|
}
|
|
40654
40730
|
function unobservedFields(gaql, observed) {
|
|
@@ -41483,11 +41559,11 @@ var PROPERTY_ROW_PREFIX = "properties.";
|
|
|
41483
41559
|
var PROPERTY_KEY_MAX = 64;
|
|
41484
41560
|
var PROPERTY_KEY = /^[A-Za-z0-9_][A-Za-z0-9_.-]*$/;
|
|
41485
41561
|
var RESERVED_PROPERTY_KEYS = ["baker_source", "hubspot_property", "hubspot_value"];
|
|
41486
|
-
function isPropertyRow(
|
|
41487
|
-
return
|
|
41562
|
+
function isPropertyRow(row2) {
|
|
41563
|
+
return row2.startsWith(PROPERTY_ROW_PREFIX);
|
|
41488
41564
|
}
|
|
41489
|
-
function propertyKeyOf(
|
|
41490
|
-
return isPropertyRow(
|
|
41565
|
+
function propertyKeyOf(row2) {
|
|
41566
|
+
return isPropertyRow(row2) ? row2.slice(PROPERTY_ROW_PREFIX.length) : null;
|
|
41491
41567
|
}
|
|
41492
41568
|
function propertyKeyProblem(key) {
|
|
41493
41569
|
const trimmed = key.trim();
|
|
@@ -41499,11 +41575,11 @@ function propertyKeyProblem(key) {
|
|
|
41499
41575
|
if (RESERVED_PROPERTY_KEYS.includes(trimmed)) return `"${trimmed}" is written by Baker itself; pick another name.`;
|
|
41500
41576
|
return null;
|
|
41501
41577
|
}
|
|
41502
|
-
function mappingRowProblem(
|
|
41503
|
-
if (isSourceField(
|
|
41504
|
-
const key = propertyKeyOf(
|
|
41578
|
+
function mappingRowProblem(row2) {
|
|
41579
|
+
if (isSourceField(row2)) return null;
|
|
41580
|
+
const key = propertyKeyOf(row2);
|
|
41505
41581
|
if (key === null) {
|
|
41506
|
-
return `"${
|
|
41582
|
+
return `"${row2}" is not a field an event can carry. A field is one of ${SOURCE_FIELD_NAMES.join(", ")}; an extra property is spelled properties.<name>.`;
|
|
41507
41583
|
}
|
|
41508
41584
|
return propertyKeyProblem(key);
|
|
41509
41585
|
}
|
|
@@ -42090,13 +42166,13 @@ var BOOKKEEPING_TYPES = /* @__PURE__ */ new Set([
|
|
|
42090
42166
|
"side_effect_settled"
|
|
42091
42167
|
]);
|
|
42092
42168
|
var SUBMIT_TRIGGERS = new Set(Object.values(STEP_SUBMIT_TRIGGERS));
|
|
42093
|
-
function isEmbedLifecycle(
|
|
42094
|
-
return
|
|
42169
|
+
function isEmbedLifecycle(row2) {
|
|
42170
|
+
return row2.eventType === "form_trigger" && triggerDescription2(row2.triggerId)?.lifecycle === true;
|
|
42095
42171
|
}
|
|
42096
|
-
function isBookkeepingRow(
|
|
42097
|
-
if (BOOKKEEPING_TYPES.has(
|
|
42098
|
-
if (isEmbedLifecycle(
|
|
42099
|
-
return
|
|
42172
|
+
function isBookkeepingRow(row2) {
|
|
42173
|
+
if (BOOKKEEPING_TYPES.has(row2.eventType)) return true;
|
|
42174
|
+
if (isEmbedLifecycle(row2)) return true;
|
|
42175
|
+
return row2.eventType === "form_trigger" && SUBMIT_TRIGGERS.has(row2.triggerId);
|
|
42100
42176
|
}
|
|
42101
42177
|
|
|
42102
42178
|
// ../api/src/analytics/identityLinks.ts
|
|
@@ -42325,13 +42401,13 @@ var CLICK_LABELS2 = {
|
|
|
42325
42401
|
sccid: "Snapchat click id",
|
|
42326
42402
|
oppref: "ChatGPT Ads click id"
|
|
42327
42403
|
};
|
|
42328
|
-
var PERSON_FIELDS = MATCH_FIELD_FORWARDING.map((
|
|
42329
|
-
name:
|
|
42330
|
-
label: PERSON_LABELS[
|
|
42404
|
+
var PERSON_FIELDS = MATCH_FIELD_FORWARDING.map((row2) => ({
|
|
42405
|
+
name: row2.field,
|
|
42406
|
+
label: PERSON_LABELS[row2.field] ?? row2.field,
|
|
42331
42407
|
group: "person",
|
|
42332
|
-
hashed:
|
|
42333
|
-
...
|
|
42334
|
-
...
|
|
42408
|
+
hashed: row2.hashed,
|
|
42409
|
+
...row2.meta ? { meta: row2.meta } : {},
|
|
42410
|
+
...row2.google ? { google: row2.google } : {}
|
|
42335
42411
|
}));
|
|
42336
42412
|
var CLICK_FIELDS = INGEST_CLICK_ID_PARAMS.map((param) => ({
|
|
42337
42413
|
name: param,
|
|
@@ -44707,18 +44783,18 @@ var ADDRESS_KEYS = [
|
|
|
44707
44783
|
"city",
|
|
44708
44784
|
"administrativeArea"
|
|
44709
44785
|
];
|
|
44710
|
-
function nestAddress(
|
|
44786
|
+
function nestAddress(row2) {
|
|
44711
44787
|
const address = {};
|
|
44712
44788
|
for (const key of ADDRESS_KEYS) {
|
|
44713
|
-
if (
|
|
44714
|
-
address[key] =
|
|
44715
|
-
delete
|
|
44789
|
+
if (row2[key] !== void 0) {
|
|
44790
|
+
address[key] = row2[key];
|
|
44791
|
+
delete row2[key];
|
|
44716
44792
|
}
|
|
44717
44793
|
}
|
|
44718
|
-
return Object.keys(address).length > 0 ? { ...
|
|
44794
|
+
return Object.keys(address).length > 0 ? { ...row2, address } : row2;
|
|
44719
44795
|
}
|
|
44720
44796
|
function toRow2(record) {
|
|
44721
|
-
const
|
|
44797
|
+
const row2 = {};
|
|
44722
44798
|
const ignored = [];
|
|
44723
44799
|
for (const [column, raw] of Object.entries(record)) {
|
|
44724
44800
|
const key = payloadKeyForColumn(column);
|
|
@@ -44730,10 +44806,10 @@ function toRow2(record) {
|
|
|
44730
44806
|
}
|
|
44731
44807
|
const value2 = cellValue(key, raw);
|
|
44732
44808
|
if (value2 !== void 0) {
|
|
44733
|
-
|
|
44809
|
+
row2[key] = value2;
|
|
44734
44810
|
}
|
|
44735
44811
|
}
|
|
44736
|
-
return { row: nestAddress(
|
|
44812
|
+
return { row: nestAddress(row2), ignored };
|
|
44737
44813
|
}
|
|
44738
44814
|
function cellValue(key, raw) {
|
|
44739
44815
|
const value2 = typeof raw === "string" ? raw.trim() : raw;
|
|
@@ -44922,42 +44998,42 @@ function rowFromFlags(args8) {
|
|
|
44922
44998
|
["regionCode", args8.country],
|
|
44923
44999
|
["postalCode", args8["postal-code"]]
|
|
44924
45000
|
];
|
|
44925
|
-
const
|
|
45001
|
+
const row2 = {};
|
|
44926
45002
|
for (const [key, value2] of entries) {
|
|
44927
45003
|
if (typeof value2 === "string" && value2.trim() !== "") {
|
|
44928
|
-
|
|
45004
|
+
row2[key] = value2.trim();
|
|
44929
45005
|
}
|
|
44930
45006
|
}
|
|
44931
|
-
nestAddressFlags(
|
|
44932
|
-
readNumericFlags(args8,
|
|
44933
|
-
return Object.keys(
|
|
45007
|
+
nestAddressFlags(row2);
|
|
45008
|
+
readNumericFlags(args8, row2);
|
|
45009
|
+
return Object.keys(row2).length > 0 ? row2 : void 0;
|
|
44934
45010
|
}
|
|
44935
|
-
function nestAddressFlags(
|
|
45011
|
+
function nestAddressFlags(row2) {
|
|
44936
45012
|
const address = {};
|
|
44937
45013
|
for (const key of ["givenName", "familyName", "regionCode", "postalCode"]) {
|
|
44938
|
-
if (
|
|
44939
|
-
address[key] =
|
|
44940
|
-
delete
|
|
45014
|
+
if (row2[key] !== void 0) {
|
|
45015
|
+
address[key] = row2[key];
|
|
45016
|
+
delete row2[key];
|
|
44941
45017
|
}
|
|
44942
45018
|
}
|
|
44943
45019
|
if (Object.keys(address).length > 0) {
|
|
44944
|
-
|
|
45020
|
+
row2.address = address;
|
|
44945
45021
|
}
|
|
44946
45022
|
}
|
|
44947
|
-
function readNumericFlags(args8,
|
|
45023
|
+
function readNumericFlags(args8, row2) {
|
|
44948
45024
|
if (args8["conversion-count"] !== void 0 && args8["conversion-count"] !== "") {
|
|
44949
45025
|
const count2 = Number(args8["conversion-count"]);
|
|
44950
45026
|
if (!Number.isInteger(count2) || count2 < 1) {
|
|
44951
45027
|
failWriteValidation2("--conversion-count must be a whole number of conversions, e.g. 3");
|
|
44952
45028
|
}
|
|
44953
|
-
|
|
45029
|
+
row2.conversionCount = count2;
|
|
44954
45030
|
}
|
|
44955
45031
|
if (args8.value !== void 0 && args8.value !== "") {
|
|
44956
45032
|
const numeric = Number(args8.value);
|
|
44957
45033
|
if (!Number.isFinite(numeric)) {
|
|
44958
45034
|
failWriteValidation2("--value must be a number in major currency units, e.g. 49.90");
|
|
44959
45035
|
}
|
|
44960
|
-
|
|
45036
|
+
row2.value = numeric;
|
|
44961
45037
|
}
|
|
44962
45038
|
}
|
|
44963
45039
|
var conversionsUploadCommand = defineCommand50({
|
|
@@ -46956,16 +47032,16 @@ function parseListFileArg(path58, maxRows) {
|
|
|
46956
47032
|
const rows = [];
|
|
46957
47033
|
for (const line of lines.slice(1)) {
|
|
46958
47034
|
const cells = parseCsvLine(line);
|
|
46959
|
-
const
|
|
47035
|
+
const row2 = {};
|
|
46960
47036
|
columns.forEach((column, index) => {
|
|
46961
47037
|
const value2 = cells[index] ?? "";
|
|
46962
47038
|
if (!value2) {
|
|
46963
47039
|
return;
|
|
46964
47040
|
}
|
|
46965
|
-
|
|
47041
|
+
row2[column] = column.toLowerCase() === "email" ? sha256Lower(value2) : value2;
|
|
46966
47042
|
});
|
|
46967
|
-
if (Object.keys(
|
|
46968
|
-
rows.push(
|
|
47043
|
+
if (Object.keys(row2).length > 0) {
|
|
47044
|
+
rows.push(row2);
|
|
46969
47045
|
}
|
|
46970
47046
|
}
|
|
46971
47047
|
if (rows.length > maxRows) {
|
|
@@ -49830,8 +49906,8 @@ import { defineCommand as defineCommand64 } from "citty";
|
|
|
49830
49906
|
function asRecord(value2) {
|
|
49831
49907
|
return typeof value2 === "object" && value2 !== null && !Array.isArray(value2) ? value2 : void 0;
|
|
49832
49908
|
}
|
|
49833
|
-
function readPath2(
|
|
49834
|
-
let current =
|
|
49909
|
+
function readPath2(row2, path58) {
|
|
49910
|
+
let current = row2;
|
|
49835
49911
|
for (const segment of path58.split(".")) {
|
|
49836
49912
|
const record = asRecord(current);
|
|
49837
49913
|
if (!record) return void 0;
|
|
@@ -49840,10 +49916,10 @@ function readPath2(row, path58) {
|
|
|
49840
49916
|
return current;
|
|
49841
49917
|
}
|
|
49842
49918
|
function projectFields(rows, paths) {
|
|
49843
|
-
return rows.map((
|
|
49919
|
+
return rows.map((row2) => {
|
|
49844
49920
|
const projected = {};
|
|
49845
49921
|
for (const path58 of paths) {
|
|
49846
|
-
const value2 = readPath2(
|
|
49922
|
+
const value2 = readPath2(row2, path58);
|
|
49847
49923
|
if (value2 !== void 0) {
|
|
49848
49924
|
projected[path58] = value2;
|
|
49849
49925
|
}
|
|
@@ -49857,19 +49933,19 @@ function shapeListRows(rows, args8, compact5) {
|
|
|
49857
49933
|
if (fields) {
|
|
49858
49934
|
const paths = fields.split(",").map((field) => field.trim()).filter((field) => field.length > 0);
|
|
49859
49935
|
if (paths.length > 0) {
|
|
49860
|
-
const projected = projectFields(raw, paths).map((
|
|
49861
|
-
const missing = paths.filter((path58) =>
|
|
49936
|
+
const projected = projectFields(raw, paths).map((row2, i) => {
|
|
49937
|
+
const missing = paths.filter((path58) => row2[path58] === void 0);
|
|
49862
49938
|
const source = rows[i];
|
|
49863
|
-
if (missing.length === 0 || source === void 0) return
|
|
49939
|
+
if (missing.length === 0 || source === void 0) return row2;
|
|
49864
49940
|
const compactRow3 = compact5.row(source);
|
|
49865
49941
|
for (const path58 of missing) {
|
|
49866
|
-
if (compactRow3[path58] !== void 0)
|
|
49942
|
+
if (compactRow3[path58] !== void 0) row2[path58] = compactRow3[path58];
|
|
49867
49943
|
}
|
|
49868
|
-
return
|
|
49944
|
+
return row2;
|
|
49869
49945
|
});
|
|
49870
|
-
const unmatched = paths.filter((path58) => projected.every((
|
|
49946
|
+
const unmatched = paths.filter((path58) => projected.every((row2) => row2[path58] === void 0));
|
|
49871
49947
|
if (projected.length === 0 || unmatched.length === 0) return { rows: projected, fields: paths };
|
|
49872
|
-
const carried = /* @__PURE__ */ new Set([...compact5.columns, ...raw.flatMap((
|
|
49948
|
+
const carried = /* @__PURE__ */ new Set([...compact5.columns, ...raw.flatMap((row2) => Object.keys(row2))]);
|
|
49873
49949
|
return {
|
|
49874
49950
|
rows: projected,
|
|
49875
49951
|
fields: paths,
|
|
@@ -49881,7 +49957,7 @@ function shapeListRows(rows, args8, compact5) {
|
|
|
49881
49957
|
}
|
|
49882
49958
|
if (args8.full) {
|
|
49883
49959
|
const columns = /* @__PURE__ */ new Set();
|
|
49884
|
-
for (const
|
|
49960
|
+
for (const row2 of raw) for (const key of Object.keys(row2)) columns.add(key);
|
|
49885
49961
|
return { rows: raw, fields: [...columns] };
|
|
49886
49962
|
}
|
|
49887
49963
|
return { rows: rows.map(compact5.row), fields: [...compact5.columns] };
|
|
@@ -49917,28 +49993,28 @@ var COMPACT_COLUMNS = [
|
|
|
49917
49993
|
"landingUrl",
|
|
49918
49994
|
"postUrn"
|
|
49919
49995
|
];
|
|
49920
|
-
function compactRow(
|
|
49921
|
-
const destination = creativeDestination(
|
|
49996
|
+
function compactRow(row2) {
|
|
49997
|
+
const destination = creativeDestination(row2.content);
|
|
49922
49998
|
return {
|
|
49923
|
-
id:
|
|
49924
|
-
urn:
|
|
49925
|
-
campaign:
|
|
49926
|
-
intendedStatus:
|
|
49927
|
-
isServing:
|
|
49928
|
-
reviewStatus:
|
|
49999
|
+
id: row2.id,
|
|
50000
|
+
urn: row2.urn,
|
|
50001
|
+
campaign: row2.campaign,
|
|
50002
|
+
intendedStatus: row2.intendedStatus,
|
|
50003
|
+
isServing: row2.isServing,
|
|
50004
|
+
reviewStatus: row2.reviewStatus,
|
|
49929
50005
|
format: destination.format,
|
|
49930
50006
|
...destination.landingUrl ? { landingUrl: destination.landingUrl } : {},
|
|
49931
50007
|
...destination.postUrn ? { postUrn: destination.postUrn } : {}
|
|
49932
50008
|
};
|
|
49933
50009
|
}
|
|
49934
50010
|
function unmappedShapeWarnings(rows, rawContentReturned) {
|
|
49935
|
-
const unmapped = rows.filter((
|
|
50011
|
+
const unmapped = rows.filter((row2) => row2.content && creativeDestination(row2.content).format === "unknown");
|
|
49936
50012
|
if (unmapped.length === 0) return [];
|
|
49937
50013
|
const remedy = rawContentReturned ? "Their raw content is in this response \u2014 read it directly; this command cannot resolve a destination for them." : "Re-read those with --full before drawing any conclusion about where they point.";
|
|
49938
50014
|
return [
|
|
49939
50015
|
{
|
|
49940
50016
|
code: "UNMAPPED_CREATIVE_CONTENT",
|
|
49941
|
-
message: `${unmapped.length} of ${rows.length} creatives have a content shape this command can't read, so their destination is missing (ids: ${unmapped.slice(0, 5).map((
|
|
50017
|
+
message: `${unmapped.length} of ${rows.length} creatives have a content shape this command can't read, so their destination is missing (ids: ${unmapped.slice(0, 5).map((row2) => row2.id).join(", ")}). ${remedy}`
|
|
49942
50018
|
}
|
|
49943
50019
|
];
|
|
49944
50020
|
}
|
|
@@ -50976,18 +51052,18 @@ Examples:
|
|
|
50976
51052
|
import { defineCommand as defineCommand76 } from "citty";
|
|
50977
51053
|
|
|
50978
51054
|
// src/commands/ads/meta/list-rows.ts
|
|
50979
|
-
function pick(
|
|
51055
|
+
function pick(row2, keys) {
|
|
50980
51056
|
const picked = {};
|
|
50981
51057
|
for (const key of keys) {
|
|
50982
|
-
if (
|
|
51058
|
+
if (row2[key] !== void 0) picked[key] = row2[key];
|
|
50983
51059
|
}
|
|
50984
51060
|
return picked;
|
|
50985
51061
|
}
|
|
50986
51062
|
var CAMPAIGN_COLUMNS = ["id", "name", "effective_status", "daily_budget", "lifetime_budget", "created_date"];
|
|
50987
|
-
function compactCampaign(
|
|
50988
|
-
const created =
|
|
51063
|
+
function compactCampaign(row2) {
|
|
51064
|
+
const created = row2.created_time;
|
|
50989
51065
|
return {
|
|
50990
|
-
...pick(
|
|
51066
|
+
...pick(row2, ["id", "name", "effective_status", "daily_budget", "lifetime_budget"]),
|
|
50991
51067
|
...typeof created === "string" ? { created_date: created.slice(0, 10) } : {}
|
|
50992
51068
|
};
|
|
50993
51069
|
}
|
|
@@ -51006,12 +51082,12 @@ var ADSET_KEYS = [
|
|
|
51006
51082
|
"is_dynamic_creative"
|
|
51007
51083
|
];
|
|
51008
51084
|
var ADSET_COLUMNS = [...ADSET_KEYS, "learning_stage", "end_time", "created_time"];
|
|
51009
|
-
function compactAdSet(
|
|
51010
|
-
const learning = asRecord(
|
|
51085
|
+
function compactAdSet(row2) {
|
|
51086
|
+
const learning = asRecord(row2.learning_stage_info)?.status;
|
|
51011
51087
|
return {
|
|
51012
|
-
...pick(
|
|
51088
|
+
...pick(row2, ADSET_KEYS),
|
|
51013
51089
|
...typeof learning === "string" ? { learning_stage: learning } : {},
|
|
51014
|
-
...pick(
|
|
51090
|
+
...pick(row2, ["end_time", "created_time"])
|
|
51015
51091
|
};
|
|
51016
51092
|
}
|
|
51017
51093
|
var AD_COLUMNS = [
|
|
@@ -51032,14 +51108,14 @@ function adIssues(value2) {
|
|
|
51032
51108
|
return typeof text2 === "string" && text2 ? [text2] : [];
|
|
51033
51109
|
});
|
|
51034
51110
|
}
|
|
51035
|
-
function compactAd(
|
|
51036
|
-
const creativeId = asRecord(
|
|
51037
|
-
const issues = adIssues(
|
|
51111
|
+
function compactAd(row2) {
|
|
51112
|
+
const creativeId = asRecord(row2.creative)?.id;
|
|
51113
|
+
const issues = adIssues(row2.issues_info);
|
|
51038
51114
|
return {
|
|
51039
|
-
...pick(
|
|
51115
|
+
...pick(row2, ["id", "name", "adset_id", "campaign_id", "effective_status"]),
|
|
51040
51116
|
...creativeId !== void 0 ? { creative_id: creativeId } : {},
|
|
51041
51117
|
...issues.length > 0 ? { issues } : {},
|
|
51042
|
-
...pick(
|
|
51118
|
+
...pick(row2, ["created_time"])
|
|
51043
51119
|
};
|
|
51044
51120
|
}
|
|
51045
51121
|
var ACTIVITY_COLUMNS = [
|
|
@@ -51069,10 +51145,10 @@ function activityChange(extraData) {
|
|
|
51069
51145
|
const show = (value2) => value2 === null ? "(none)" : String(value2);
|
|
51070
51146
|
return `${show(before)} \u2192 ${show(after)}`;
|
|
51071
51147
|
}
|
|
51072
|
-
function compactActivity(
|
|
51073
|
-
const change = activityChange(
|
|
51148
|
+
function compactActivity(row2) {
|
|
51149
|
+
const change = activityChange(row2.extra_data);
|
|
51074
51150
|
return {
|
|
51075
|
-
...pick(
|
|
51151
|
+
...pick(row2, ["event_time", "event_type", "actor_name", "object_type", "object_name", "object_id"]),
|
|
51076
51152
|
...change !== void 0 ? { change } : {}
|
|
51077
51153
|
};
|
|
51078
51154
|
}
|
|
@@ -54084,16 +54160,16 @@ Examples:
|
|
|
54084
54160
|
|
|
54085
54161
|
// src/commands/ads/meta/custom-conversions.ts
|
|
54086
54162
|
import { defineCommand as defineCommand84 } from "citty";
|
|
54087
|
-
function compactCustomConversion(
|
|
54163
|
+
function compactCustomConversion(row2) {
|
|
54088
54164
|
return {
|
|
54089
|
-
id:
|
|
54090
|
-
name:
|
|
54091
|
-
custom_event_type:
|
|
54092
|
-
counts:
|
|
54093
|
-
pixel_id:
|
|
54094
|
-
last_fired_time:
|
|
54095
|
-
...
|
|
54096
|
-
...
|
|
54165
|
+
id: row2.id,
|
|
54166
|
+
name: row2.name,
|
|
54167
|
+
custom_event_type: row2.custom_event_type,
|
|
54168
|
+
counts: row2.rule_summary,
|
|
54169
|
+
pixel_id: row2.pixel?.id,
|
|
54170
|
+
last_fired_time: row2.last_fired_time,
|
|
54171
|
+
...row2.is_archived ? { archived: true } : {},
|
|
54172
|
+
...row2.is_unavailable ? { unavailable: true } : {}
|
|
54097
54173
|
};
|
|
54098
54174
|
}
|
|
54099
54175
|
function customConversionHints(rows) {
|
|
@@ -54105,10 +54181,10 @@ function customConversionHints(rows) {
|
|
|
54105
54181
|
const hints2 = [
|
|
54106
54182
|
"To optimize an ad set toward one: `baker ads meta adsets create \u2026 --optimization-goal OFFSITE_CONVERSIONS --custom-conversion-id <id>`. Baker fills the pixel and the event category from the conversion \u2014 do not also pass --pixel-id or --custom-event-type."
|
|
54107
54183
|
];
|
|
54108
|
-
const neverFired = rows.filter((
|
|
54184
|
+
const neverFired = rows.filter((row2) => !row2.is_archived && !row2.last_fired_time);
|
|
54109
54185
|
if (neverFired.length > 0) {
|
|
54110
54186
|
hints2.push(
|
|
54111
|
-
`${neverFired.length === 1 ? "This conversion has" : `${neverFired.length} of these have`} never fired (${neverFired.map((
|
|
54187
|
+
`${neverFired.length === 1 ? "This conversion has" : `${neverFired.length} of these have`} never fired (${neverFired.map((row2) => row2.name ?? row2.id).join(
|
|
54112
54188
|
", "
|
|
54113
54189
|
)}). Meta cannot optimize toward an event it has never seen, so an ad set pointed at one delivers without learning \u2014 check the rule matches a page the site actually serves before spending on it.`
|
|
54114
54190
|
);
|
|
@@ -54146,7 +54222,7 @@ var listCommand10 = defineCommand84({
|
|
|
54146
54222
|
if (args8.limit) params.limit = args8.limit;
|
|
54147
54223
|
if (args8["no-cache"]) params["skip-cache"] = "true";
|
|
54148
54224
|
const rows = await apiGet("/api/ads/meta/custom-conversions", params);
|
|
54149
|
-
const data = args8.full ? rows.map((
|
|
54225
|
+
const data = args8.full ? rows.map((row2) => ({ ...row2 })) : rows.map((row2) => ({ ...compactCustomConversion(row2) }));
|
|
54150
54226
|
const fmt = csvOrJson(args8);
|
|
54151
54227
|
if (fmt !== "json") {
|
|
54152
54228
|
writeAdsOutput(data, fmt);
|
|
@@ -54406,16 +54482,16 @@ function sumActionValues(value2) {
|
|
|
54406
54482
|
function aggregate(rows) {
|
|
54407
54483
|
const byPlacement = /* @__PURE__ */ new Map();
|
|
54408
54484
|
let totalImpressions = 0;
|
|
54409
|
-
for (const
|
|
54410
|
-
const position =
|
|
54485
|
+
for (const row2 of rows) {
|
|
54486
|
+
const position = row2.platform_position;
|
|
54411
54487
|
if (typeof position !== "string" || position.length === 0) continue;
|
|
54412
|
-
const platform = typeof
|
|
54413
|
-
const impressions = toNumber(
|
|
54488
|
+
const platform = typeof row2.publisher_platform === "string" ? row2.publisher_platform : "";
|
|
54489
|
+
const impressions = toNumber(row2.impressions);
|
|
54414
54490
|
totalImpressions += impressions;
|
|
54415
54491
|
const key = `${platform}/${position}`;
|
|
54416
54492
|
const current = byPlacement.get(key) ?? { platform, position, impressions: 0, plays: 0, playRate: 0, share: 0 };
|
|
54417
54493
|
current.impressions += impressions;
|
|
54418
|
-
current.plays += sumActionValues(
|
|
54494
|
+
current.plays += sumActionValues(row2.video_play_actions);
|
|
54419
54495
|
byPlacement.set(key, current);
|
|
54420
54496
|
}
|
|
54421
54497
|
return [...byPlacement.values()].map((placement) => ({
|
|
@@ -54430,14 +54506,14 @@ function previewCommand(placement) {
|
|
|
54430
54506
|
}
|
|
54431
54507
|
function isSingleAdQuery(rows) {
|
|
54432
54508
|
const adIds = /* @__PURE__ */ new Set();
|
|
54433
|
-
for (const
|
|
54434
|
-
if (typeof
|
|
54435
|
-
adIds.add(
|
|
54509
|
+
for (const row2 of rows) {
|
|
54510
|
+
if (typeof row2.ad_id !== "string" || row2.ad_id.length === 0) return false;
|
|
54511
|
+
adIds.add(row2.ad_id);
|
|
54436
54512
|
}
|
|
54437
54513
|
return adIds.size === 1;
|
|
54438
54514
|
}
|
|
54439
54515
|
function placementDeliveryHints(rows) {
|
|
54440
|
-
const carriesVideoMetrics = rows.some((
|
|
54516
|
+
const carriesVideoMetrics = rows.some((row2) => Array.isArray(row2.video_play_actions));
|
|
54441
54517
|
if (!carriesVideoMetrics || !isSingleAdQuery(rows)) return [];
|
|
54442
54518
|
const placements = aggregate(rows);
|
|
54443
54519
|
const judgeable = placements.filter((p) => p.impressions >= MIN_IMPRESSIONS);
|
|
@@ -58335,7 +58411,7 @@ function forwarding() {
|
|
|
58335
58411
|
"| Field | Meta | Google | Held as |",
|
|
58336
58412
|
"| --- | --- | --- | --- |",
|
|
58337
58413
|
...MATCH_FIELD_FORWARDING.map(
|
|
58338
|
-
(
|
|
58414
|
+
(row2) => `| \`${row2.field}\` | \`${row2.meta}\` | ${row2.google === null ? "\u2014" : `\`${row2.google}\``} | ${row2.hashed ? "SHA-256" : "plain text"} |`
|
|
58339
58415
|
)
|
|
58340
58416
|
].join("\n");
|
|
58341
58417
|
}
|
|
@@ -58909,7 +58985,7 @@ function parseConversionsArgs(raw, argv) {
|
|
|
58909
58985
|
return args8;
|
|
58910
58986
|
}
|
|
58911
58987
|
function existingNames(existing) {
|
|
58912
|
-
return [...new Set(existing.map((
|
|
58988
|
+
return [...new Set(existing.map((row2) => row2.name))];
|
|
58913
58989
|
}
|
|
58914
58990
|
function checkNameCollision(name, existing) {
|
|
58915
58991
|
const collision = findNameCollision(name, existingNames(existing));
|
|
@@ -58919,7 +58995,7 @@ function checkNameCollision(name, existing) {
|
|
|
58919
58995
|
);
|
|
58920
58996
|
}
|
|
58921
58997
|
function under(name, existing) {
|
|
58922
|
-
const rows = existing.filter((
|
|
58998
|
+
const rows = existing.filter((row2) => row2.name === name);
|
|
58923
58999
|
if (rows.length === 0) {
|
|
58924
59000
|
throw new ConversionArgsError(
|
|
58925
59001
|
`Nothing is counted under "${name}". This company counts: ${existingNames(existing).join(", ") || "nothing yet"}.`
|
|
@@ -58939,7 +59015,7 @@ function setFromEvents(args8, existing) {
|
|
|
58939
59015
|
}));
|
|
58940
59016
|
}
|
|
58941
59017
|
return args8.eventKeys.map((eventKey) => {
|
|
58942
|
-
const current = existing.find((
|
|
59018
|
+
const current = existing.find((row2) => row2.eventKey === eventKey);
|
|
58943
59019
|
if (!current) {
|
|
58944
59020
|
throw new ConversionArgsError(
|
|
58945
59021
|
`"${eventKey}" is not counted, so there is no count mode to change. Add it with --event "${eventKey}" --name "<what a person calls it>" --count-mode ${args8.countMode}.`
|
|
@@ -58952,52 +59028,52 @@ function setFromRename(args8, existing) {
|
|
|
58952
59028
|
if (args8.renameFrom === void 0 || args8.renameTo === void 0) return [];
|
|
58953
59029
|
const renameTo = args8.renameTo;
|
|
58954
59030
|
checkNameCollision(renameTo, existing);
|
|
58955
|
-
return under(args8.renameFrom, existing).map((
|
|
58956
|
-
eventKey:
|
|
59031
|
+
return under(args8.renameFrom, existing).map((row2) => ({
|
|
59032
|
+
eventKey: row2.eventKey,
|
|
58957
59033
|
name: renameTo,
|
|
58958
|
-
countMode:
|
|
59034
|
+
countMode: row2.countMode
|
|
58959
59035
|
}));
|
|
58960
59036
|
}
|
|
58961
59037
|
function conversionsWrite(args8, existing) {
|
|
58962
59038
|
const set = [...setFromEvents(args8, existing), ...setFromRename(args8, existing)];
|
|
58963
59039
|
const remove = [
|
|
58964
59040
|
...args8.removeKeys,
|
|
58965
|
-
...args8.removeName === void 0 ? [] : under(args8.removeName, existing).map((
|
|
59041
|
+
...args8.removeName === void 0 ? [] : under(args8.removeName, existing).map((row2) => row2.eventKey)
|
|
58966
59042
|
];
|
|
58967
59043
|
if (set.length === 0 && remove.length === 0) return null;
|
|
58968
59044
|
return { ...set.length > 0 ? { set } : {}, ...remove.length > 0 ? { remove } : {} };
|
|
58969
59045
|
}
|
|
58970
59046
|
|
|
58971
59047
|
// src/commands/analytics/conversionCandidates.ts
|
|
58972
|
-
function compact3(
|
|
59048
|
+
function compact3(row2) {
|
|
58973
59049
|
return {
|
|
58974
|
-
eventKey:
|
|
58975
|
-
events:
|
|
58976
|
-
sessions:
|
|
58977
|
-
...
|
|
59050
|
+
eventKey: row2.eventKey,
|
|
59051
|
+
events: row2.events,
|
|
59052
|
+
sessions: row2.sessions,
|
|
59053
|
+
...row2.isCounted ? { countedAs: row2.countedAs } : {}
|
|
58978
59054
|
};
|
|
58979
59055
|
}
|
|
58980
59056
|
function foldByForm(rows) {
|
|
58981
59057
|
const byForm = /* @__PURE__ */ new Map();
|
|
58982
|
-
for (const
|
|
58983
|
-
const form = byForm.get(
|
|
59058
|
+
for (const row2 of rows) {
|
|
59059
|
+
const form = byForm.get(row2.flowSlug) ?? { form: row2.flowSlug, keys: 0, countedKeys: 0, events: 0 };
|
|
58984
59060
|
form.keys += 1;
|
|
58985
|
-
if (
|
|
58986
|
-
form.events +=
|
|
58987
|
-
byForm.set(
|
|
59061
|
+
if (row2.isCounted) form.countedKeys += 1;
|
|
59062
|
+
form.events += row2.events;
|
|
59063
|
+
byForm.set(row2.flowSlug, form);
|
|
58988
59064
|
}
|
|
58989
59065
|
return [...byForm.values()];
|
|
58990
59066
|
}
|
|
58991
59067
|
function candidatesForFlow(catalogue, flowSlug) {
|
|
58992
|
-
return flowSlug === void 0 ? [...catalogue] : catalogue.filter((
|
|
59068
|
+
return flowSlug === void 0 ? [...catalogue] : catalogue.filter((row2) => row2.flowSlug === flowSlug);
|
|
58993
59069
|
}
|
|
58994
59070
|
var FOLDED_FORMS_HINT = "A Form's candidates are folded into its `flows` row (`keys` fired, `events`; no `counted` means it counts nothing), or a `formCandidates` row when not listed. `--candidates --flow <slug>` lists that Form's keys; `--full` adds every field of every key.";
|
|
58995
59071
|
function candidatesView(rows, options) {
|
|
58996
59072
|
if (options.full) return { candidates: rows };
|
|
58997
59073
|
if (options.flowSlug !== void 0) return { candidates: rows.map(compact3) };
|
|
58998
|
-
const forms = foldByForm(rows.filter((
|
|
59074
|
+
const forms = foldByForm(rows.filter((row2) => row2.flowSlug !== ""));
|
|
58999
59075
|
return {
|
|
59000
|
-
candidates: rows.filter((
|
|
59076
|
+
candidates: rows.filter((row2) => row2.flowSlug === "").map(compact3),
|
|
59001
59077
|
...forms.length > 0 ? { formCandidates: forms } : {}
|
|
59002
59078
|
};
|
|
59003
59079
|
}
|
|
@@ -59043,24 +59119,24 @@ function conversionHints2(definitions, candidates, askedForCandidates) {
|
|
|
59043
59119
|
"Nothing is counted as a conversion, so EVERY conversion number in every other report is zero \u2014 whatever visitors actually did. Baker never guesses which event is the point of a page. Run `baker analytics conversions --candidates` to see what these pages produce, then count the ones that are worth money."
|
|
59044
59120
|
);
|
|
59045
59121
|
if (!askedForCandidates && candidates.length > 0) {
|
|
59046
|
-
const top = candidates.slice(0, NAMED_CANDIDATES).map((
|
|
59122
|
+
const top = candidates.slice(0, NAMED_CANDIDATES).map((row2) => row2.eventKey).join(", ");
|
|
59047
59123
|
hints2.push(`${candidates.length} distinct events are available to count. Busiest: ${top}.`);
|
|
59048
59124
|
}
|
|
59049
59125
|
return hints2;
|
|
59050
59126
|
}
|
|
59051
|
-
const silent = definitions.filter((
|
|
59127
|
+
const silent = definitions.filter((row2) => row2.lastSeen === null);
|
|
59052
59128
|
if (silent.length > 0) {
|
|
59053
59129
|
hints2.push(
|
|
59054
|
-
`${silent.length} counted outcome(s) have never fired in this window: ${silent.slice(0, NAMED_CANDIDATES).map((
|
|
59130
|
+
`${silent.length} counted outcome(s) have never fired in this window: ${silent.slice(0, NAMED_CANDIDATES).map((row2) => row2.name).join(
|
|
59055
59131
|
", "
|
|
59056
59132
|
)}. Either the page never reaches them, or the wrong event was named \u2014 check against --candidates before reporting a conversion rate.`
|
|
59057
59133
|
);
|
|
59058
59134
|
}
|
|
59059
|
-
const uncounted = candidates.filter((
|
|
59135
|
+
const uncounted = candidates.filter((row2) => !row2.isCounted);
|
|
59060
59136
|
const busiest = uncounted[0];
|
|
59061
59137
|
if (busiest && busiest.sessions > 0) {
|
|
59062
|
-
const counted = candidates.filter((
|
|
59063
|
-
const busiestCounted = counted.reduce((max,
|
|
59138
|
+
const counted = candidates.filter((row2) => row2.isCounted);
|
|
59139
|
+
const busiestCounted = counted.reduce((max, row2) => Math.max(max, row2.sessions), 0);
|
|
59064
59140
|
if (busiest.sessions > busiestCounted) {
|
|
59065
59141
|
hints2.push(
|
|
59066
59142
|
`\`${busiest.eventKey}\` happened in ${busiest.sessions} visits and is not counted \u2014 more than anything that is. Check it is not the real outcome.`
|
|
@@ -59070,7 +59146,7 @@ function conversionHints2(definitions, candidates, askedForCandidates) {
|
|
|
59070
59146
|
return hints2;
|
|
59071
59147
|
}
|
|
59072
59148
|
function namingHints(definitions) {
|
|
59073
|
-
const names = [...new Set(definitions.map((
|
|
59149
|
+
const names = [...new Set(definitions.map((row2) => row2.name))];
|
|
59074
59150
|
if (names.length === 0) {
|
|
59075
59151
|
return [
|
|
59076
59152
|
`Nothing is named yet, so the first name sets the vocabulary for this company. Start from the standard set \u2014 ${STANDARD_CONVERSION_NAMES.map((entry) => entry.name).join(", ")} \u2014 and stay in the company's own language once one exists.`
|
|
@@ -59081,29 +59157,29 @@ function namingHints(definitions) {
|
|
|
59081
59157
|
];
|
|
59082
59158
|
}
|
|
59083
59159
|
function stagedHints(definitions) {
|
|
59084
|
-
const staged = definitions.filter((
|
|
59160
|
+
const staged = definitions.filter((row2) => row2.staged === true);
|
|
59085
59161
|
if (staged.length === 0) return [];
|
|
59086
59162
|
return [
|
|
59087
|
-
`Staged on this chat, not counted yet: ${staged.map((
|
|
59163
|
+
`Staged on this chat, not counted yet: ${staged.map((row2) => `"${row2.name}"`).join(", ")}. The person sees ${staged.length === 1 ? "it" : "them"} under Sources in the chat's changes, on the event's own row, and ${staged.length === 1 ? "it counts" : "they count"} from when they publish the chat; discarding the chat drops ${staged.length === 1 ? "it" : "them"}. Publishing also rescores events Baker ALREADY RECORDED under the key \u2014 never anything that happened in HubSpot or elsewhere before the event existed, so an event this chat just made starts at zero. Tell them that, not that it is already counted.`,
|
|
59088
59164
|
'When you talk to the person, call each event by its name ("Appointment scheduled"), never its key (`page:Appointment scheduled`): the key is how Baker spells it internally, and the `page:` part means nothing to them.'
|
|
59089
59165
|
];
|
|
59090
59166
|
}
|
|
59091
59167
|
|
|
59092
59168
|
// src/commands/analytics/conversionList.ts
|
|
59093
59169
|
function mergeStoredWithCounts(stored, reported) {
|
|
59094
|
-
const counts = new Map(reported.map((
|
|
59170
|
+
const counts = new Map(reported.map((row2) => [row2.name, row2]));
|
|
59095
59171
|
const groups = /* @__PURE__ */ new Map();
|
|
59096
|
-
for (const
|
|
59097
|
-
const group = groups.get(
|
|
59098
|
-
group.push(
|
|
59099
|
-
groups.set(
|
|
59172
|
+
for (const row2 of stored) {
|
|
59173
|
+
const group = groups.get(row2.name) ?? [];
|
|
59174
|
+
group.push(row2);
|
|
59175
|
+
groups.set(row2.name, group);
|
|
59100
59176
|
}
|
|
59101
59177
|
return [...groups.entries()].map(([name, rows]) => {
|
|
59102
|
-
const modes = new Set(rows.map((
|
|
59178
|
+
const modes = new Set(rows.map((row2) => row2.countMode));
|
|
59103
59179
|
const counted = counts.get(name);
|
|
59104
59180
|
return {
|
|
59105
59181
|
name,
|
|
59106
|
-
eventKeys: rows.map((
|
|
59182
|
+
eventKeys: rows.map((row2) => row2.eventKey).sort(),
|
|
59107
59183
|
// A real state and usually an editing accident, which is why it is said
|
|
59108
59184
|
// out loud rather than resolved to one of them.
|
|
59109
59185
|
countMode: modes.size === 1 ? [...modes][0] : "mixed",
|
|
@@ -59115,7 +59191,7 @@ function mergeStoredWithCounts(stored, reported) {
|
|
|
59115
59191
|
unplaced: counted?.unplaced ?? 0,
|
|
59116
59192
|
lastSeen: counted?.lastSeen ?? null,
|
|
59117
59193
|
doors: counted?.doors ?? [],
|
|
59118
|
-
...rows.some((
|
|
59194
|
+
...rows.some((row2) => row2.staged === true) ? { staged: true } : {}
|
|
59119
59195
|
};
|
|
59120
59196
|
}).sort((a, b) => a.name.localeCompare(b.name));
|
|
59121
59197
|
}
|
|
@@ -59152,9 +59228,9 @@ function pruneValue(value2, full) {
|
|
|
59152
59228
|
}
|
|
59153
59229
|
return value2;
|
|
59154
59230
|
}
|
|
59155
|
-
function pruneRow(
|
|
59231
|
+
function pruneRow(row2, full) {
|
|
59156
59232
|
const out = {};
|
|
59157
|
-
for (const [key, value2] of Object.entries(
|
|
59233
|
+
for (const [key, value2] of Object.entries(row2)) {
|
|
59158
59234
|
if (DASHBOARD_ONLY_FIELDS.has(key)) continue;
|
|
59159
59235
|
if (key === "sparkline" && Array.isArray(value2)) {
|
|
59160
59236
|
if (!full) continue;
|
|
@@ -59171,7 +59247,7 @@ function pruneForAgent(data, full) {
|
|
|
59171
59247
|
}
|
|
59172
59248
|
function isTabular(rows) {
|
|
59173
59249
|
return rows.every(
|
|
59174
|
-
(
|
|
59250
|
+
(row2) => typeof row2 === "object" && row2 !== null && !Array.isArray(row2) && Object.values(row2).every(
|
|
59175
59251
|
(value2) => value2 === null || typeof value2 !== "object" || Array.isArray(value2) && value2.every((e) => typeof e !== "object")
|
|
59176
59252
|
)
|
|
59177
59253
|
);
|
|
@@ -59225,40 +59301,40 @@ function byBlockOrder(a, b) {
|
|
|
59225
59301
|
}
|
|
59226
59302
|
function columnsOf(rows) {
|
|
59227
59303
|
const seen = [];
|
|
59228
|
-
for (const
|
|
59229
|
-
for (const key of Object.keys(
|
|
59304
|
+
for (const row2 of rows) {
|
|
59305
|
+
for (const key of Object.keys(row2)) {
|
|
59230
59306
|
if (!seen.includes(key)) seen.push(key);
|
|
59231
59307
|
}
|
|
59232
59308
|
}
|
|
59233
|
-
return seen.filter((key) => rows.some((
|
|
59309
|
+
return seen.filter((key) => rows.some((row2) => !isAbsent(row2[key]) && row2[key] !== null && row2[key] !== void 0)).sort(byIdentityFirst);
|
|
59234
59310
|
}
|
|
59235
59311
|
function table2(rows) {
|
|
59236
59312
|
const cols = columnsOf(rows);
|
|
59237
59313
|
if (cols.length === 0) return "";
|
|
59238
59314
|
const head = `| ${cols.join(" | ")} |`;
|
|
59239
59315
|
const rule = `| ${cols.map(() => "---").join(" | ")} |`;
|
|
59240
|
-
const body = rows.map((
|
|
59316
|
+
const body = rows.map((row2) => `| ${cols.map((col) => cell2(row2[col], col)).join(" | ")} |`);
|
|
59241
59317
|
return [head, rule, ...body].join("\n");
|
|
59242
59318
|
}
|
|
59243
59319
|
function isRowList(value2) {
|
|
59244
59320
|
return Array.isArray(value2) && value2.length > 0 && value2.every((entry) => typeof entry === "object" && entry !== null && !Array.isArray(entry));
|
|
59245
59321
|
}
|
|
59246
|
-
function labelOf(
|
|
59322
|
+
function labelOf(row2, index) {
|
|
59247
59323
|
for (const key of IDENTITY_COLUMNS) {
|
|
59248
|
-
const value2 =
|
|
59324
|
+
const value2 = row2[key];
|
|
59249
59325
|
if (typeof value2 === "string" && value2 !== "") return value2;
|
|
59250
59326
|
}
|
|
59251
59327
|
return `#${index + 1}`;
|
|
59252
59328
|
}
|
|
59253
59329
|
function nestedBlock(name, rows) {
|
|
59254
59330
|
const parents = rows.map(
|
|
59255
|
-
(
|
|
59331
|
+
(row2) => Object.fromEntries(Object.entries(row2).filter(([, value2]) => !isRowList(value2) && !isPlainRow(value2)))
|
|
59256
59332
|
);
|
|
59257
59333
|
const parts = [`### ${name}`, table2(parents)];
|
|
59258
|
-
rows.forEach((
|
|
59259
|
-
for (const [key, value2] of Object.entries(
|
|
59260
|
-
if (isRowList(value2)) parts.push(`#### ${name}[${labelOf(
|
|
59261
|
-
else if (isPlainRow(value2)) parts.push(`#### ${name}[${labelOf(
|
|
59334
|
+
rows.forEach((row2, index) => {
|
|
59335
|
+
for (const [key, value2] of Object.entries(row2)) {
|
|
59336
|
+
if (isRowList(value2)) parts.push(`#### ${name}[${labelOf(row2, index)}].${key}`, table2(value2));
|
|
59337
|
+
else if (isPlainRow(value2)) parts.push(`#### ${name}[${labelOf(row2, index)}].${key}`, table2([value2]));
|
|
59262
59338
|
}
|
|
59263
59339
|
});
|
|
59264
59340
|
return parts.filter((part) => part !== "").join("\n");
|
|
@@ -59303,7 +59379,7 @@ function missingBreakdownHint(data, asked) {
|
|
|
59303
59379
|
if (asked.eventName === void 0) return [];
|
|
59304
59380
|
const breakdown = data.eventBreakdown;
|
|
59305
59381
|
if (Array.isArray(breakdown) && breakdown.length > 0) return [];
|
|
59306
|
-
const known = Array.isArray(data.customEvents) ? data.customEvents.flatMap((
|
|
59382
|
+
const known = Array.isArray(data.customEvents) ? data.customEvents.flatMap((row2) => isPlainRow(row2) && typeof row2.eventName === "string" ? [row2.eventName] : []) : [];
|
|
59307
59383
|
return [
|
|
59308
59384
|
`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" : ""}` : ""}.`
|
|
59309
59385
|
];
|
|
@@ -59348,13 +59424,13 @@ function eventKeysCell(eventKeys) {
|
|
|
59348
59424
|
}
|
|
59349
59425
|
function trafficByForm(candidates) {
|
|
59350
59426
|
const byForm = /* @__PURE__ */ new Map();
|
|
59351
|
-
for (const
|
|
59352
|
-
const form = byForm.get(
|
|
59353
|
-
if (
|
|
59354
|
-
if (
|
|
59355
|
-
form.events +=
|
|
59356
|
-
if (
|
|
59357
|
-
byForm.set(
|
|
59427
|
+
for (const row2 of candidates) {
|
|
59428
|
+
const form = byForm.get(row2.flowSlug) ?? { "endings seen": 0, "endings counted": 0, events: 0, lastSeen: "" };
|
|
59429
|
+
if (row2.events > 0) form["endings seen"] += 1;
|
|
59430
|
+
if (row2.isCounted) form["endings counted"] += 1;
|
|
59431
|
+
form.events += row2.events;
|
|
59432
|
+
if (row2.lastSeen && row2.lastSeen.slice(0, 10) > form.lastSeen) form.lastSeen = row2.lastSeen.slice(0, 10);
|
|
59433
|
+
byForm.set(row2.flowSlug, form);
|
|
59358
59434
|
}
|
|
59359
59435
|
return byForm;
|
|
59360
59436
|
}
|
|
@@ -59363,32 +59439,32 @@ var FORM_KEYS_HINT = "A Form's candidates are one row per Form: `baker analytics
|
|
|
59363
59439
|
function renderConversionsMarkdown(envelope) {
|
|
59364
59440
|
const { definitions, flow, flows, candidates } = envelope.data;
|
|
59365
59441
|
const data = {
|
|
59366
|
-
definitions: definitions.map(({ name, eventKeys, ...
|
|
59442
|
+
definitions: definitions.map(({ name, eventKeys, ...row2 }) => ({
|
|
59367
59443
|
name,
|
|
59368
59444
|
eventKeys: eventKeysCell(eventKeys),
|
|
59369
|
-
...
|
|
59445
|
+
...row2
|
|
59370
59446
|
}))
|
|
59371
59447
|
};
|
|
59372
59448
|
if (flow) {
|
|
59373
59449
|
data.flow = { slug: flow.slug, noConversionReason: flow.noConversionReason };
|
|
59374
59450
|
data.flowEndings = flow.endings;
|
|
59375
59451
|
}
|
|
59376
|
-
const traffic = candidates ? trafficByForm(candidates.filter((
|
|
59452
|
+
const traffic = candidates ? trafficByForm(candidates.filter((row2) => row2.flowSlug !== "")) : /* @__PURE__ */ new Map();
|
|
59377
59453
|
if (flows) {
|
|
59378
|
-
data.flows = flows.map((
|
|
59379
|
-
slug:
|
|
59380
|
-
counted:
|
|
59381
|
-
noConversion:
|
|
59382
|
-
...candidates ? traffic.get(
|
|
59454
|
+
data.flows = flows.map((row2) => ({
|
|
59455
|
+
slug: row2.slug,
|
|
59456
|
+
counted: row2.counted.map((entry) => `${entry.name} @ ${entry.step}`).join("; "),
|
|
59457
|
+
noConversion: row2.noConversion,
|
|
59458
|
+
...candidates ? traffic.get(row2.slug) ?? NO_TRAFFIC : {}
|
|
59383
59459
|
}));
|
|
59384
59460
|
}
|
|
59385
59461
|
const hints2 = [...envelope.hints ?? []];
|
|
59386
59462
|
if (candidates) {
|
|
59387
|
-
const listed2 = new Set(flows?.map((
|
|
59463
|
+
const listed2 = new Set(flows?.map((row2) => row2.slug));
|
|
59388
59464
|
const unlisted = [...traffic].filter(([slug]) => !listed2.has(slug));
|
|
59389
|
-
if (unlisted.length > 0) data.formCandidates = unlisted.map(([form,
|
|
59390
|
-
data.candidates = candidates.filter((
|
|
59391
|
-
...
|
|
59465
|
+
if (unlisted.length > 0) data.formCandidates = unlisted.map(([form, row2]) => ({ form, ...row2 }));
|
|
59466
|
+
data.candidates = candidates.filter((row2) => row2.flowSlug === "").map(({ doors, flowSlug: _flowSlug, nodeType: _nodeType, ...row2 }) => ({
|
|
59467
|
+
...row2,
|
|
59392
59468
|
"doors (events/sessions)": doorsCell(doors)
|
|
59393
59469
|
}));
|
|
59394
59470
|
if (traffic.size > 0) hints2.push(FORM_KEYS_HINT);
|
|
@@ -59412,18 +59488,18 @@ function analyticsErrorEnvelope(err) {
|
|
|
59412
59488
|
function isStreamRow(value2) {
|
|
59413
59489
|
return typeof value2 === "object" && value2 !== null && !Array.isArray(value2);
|
|
59414
59490
|
}
|
|
59415
|
-
function enrich(
|
|
59416
|
-
const out = { ...
|
|
59417
|
-
const destination = typeof
|
|
59491
|
+
function enrich(row2) {
|
|
59492
|
+
const out = { ...row2 };
|
|
59493
|
+
const destination = typeof row2.sideEffectType === "string" ? row2.sideEffectType : "";
|
|
59418
59494
|
if (destination !== "") out.destination = destinationName(destination);
|
|
59419
|
-
const keys = rowMatchKeys(
|
|
59495
|
+
const keys = rowMatchKeys(row2.userData);
|
|
59420
59496
|
if (keys.length > 0) out.matchedBy = keys.map((key) => key.field);
|
|
59421
59497
|
return out;
|
|
59422
59498
|
}
|
|
59423
59499
|
function shapeFeed(stream, everything) {
|
|
59424
59500
|
if (!Array.isArray(stream)) return { rows: [], heldBack: 0 };
|
|
59425
59501
|
const rows = stream.filter(isStreamRow);
|
|
59426
|
-
const kept = everything ? rows : rows.filter((
|
|
59502
|
+
const kept = everything ? rows : rows.filter((row2) => !isBookkeepingRow({ eventType: row2.eventType ?? "", triggerId: row2.triggerId ?? "" }));
|
|
59427
59503
|
return { rows: kept.map(enrich), heldBack: rows.length - kept.length };
|
|
59428
59504
|
}
|
|
59429
59505
|
function feedHints(shape) {
|
|
@@ -59433,7 +59509,7 @@ function feedHints(shape) {
|
|
|
59433
59509
|
`${shape.heldBack} ${shape.heldBack === 1 ? "row is" : "rows are"} not listed: rows recorded to work the reports out \u2014 identifications, step views, session starts, a destination's answer already folded onto its dispatch \u2014 and embedded forms and calendars announcing that they drew. Add --everything to see them.`
|
|
59434
59510
|
);
|
|
59435
59511
|
}
|
|
59436
|
-
const upload = shape.rows.find((
|
|
59512
|
+
const upload = shape.rows.find((row2) => row2.sideEffectType !== void 0 && row2.eventId !== void 0);
|
|
59437
59513
|
if (upload !== void 0) {
|
|
59438
59514
|
hints2.push(
|
|
59439
59515
|
`A delivery row carries the event it was made for in \`eventId\`. \`baker analytics feed --uid ${String(upload.eventId)}\` returns that event and every upload made for it, each with the platform's verdict.`
|
|
@@ -59442,7 +59518,7 @@ function feedHints(shape) {
|
|
|
59442
59518
|
return hints2;
|
|
59443
59519
|
}
|
|
59444
59520
|
function peopleSearchHints(search, rows) {
|
|
59445
|
-
const found = new Set(rows.flatMap((
|
|
59521
|
+
const found = new Set(rows.flatMap((row2) => Array.isArray(row2.personTerms) ? row2.personTerms : []));
|
|
59446
59522
|
return search.filter((term) => !found.has(term.term)).map(
|
|
59447
59523
|
(term) => term.problem !== "" ? `${term.input}: ${term.problem}` : `${term.input}: nothing in this window on this page. \`baker analytics lookup --person '${term.input}'\` reads everything Baker ever saw of them.`
|
|
59448
59524
|
);
|
|
@@ -59474,10 +59550,10 @@ function joinFlowConversions(candidates, definitions) {
|
|
|
59474
59550
|
return candidates.map((candidate) => ({ ...candidate, countedAs: byKey.get(candidate.eventKey) ?? null }));
|
|
59475
59551
|
}
|
|
59476
59552
|
function flowConversionHints(slug, rows, noConversionReason) {
|
|
59477
|
-
const counted = rows.filter((
|
|
59553
|
+
const counted = rows.filter((row2) => row2.countedAs !== null);
|
|
59478
59554
|
if (counted.length > 0) {
|
|
59479
59555
|
return [
|
|
59480
|
-
`Baker counts ${counted.length} of this Form's endings: ${counted.map((
|
|
59556
|
+
`Baker counts ${counted.length} of this Form's endings: ${counted.map((row2) => `${row2.countedAs} (${row2.nodeName})`).join(
|
|
59481
59557
|
", "
|
|
59482
59558
|
)}. Every other ending here scores nothing \u2014 count another with \`--event <key> --name <outcome>\`, reusing the SAME name to make two Forms one number.`
|
|
59483
59559
|
];
|
|
@@ -59499,21 +59575,21 @@ function flowCoverage(forms, definitions) {
|
|
|
59499
59575
|
const seen = /* @__PURE__ */ new Set();
|
|
59500
59576
|
return {
|
|
59501
59577
|
slug,
|
|
59502
|
-
counted: rows.filter((
|
|
59503
|
-
const key = `${
|
|
59578
|
+
counted: rows.filter((row2) => row2.countedAs !== null).filter((row2) => {
|
|
59579
|
+
const key = `${row2.countedAs} ${row2.nodeName}`;
|
|
59504
59580
|
if (seen.has(key)) return false;
|
|
59505
59581
|
seen.add(key);
|
|
59506
59582
|
return true;
|
|
59507
|
-
}).map((
|
|
59583
|
+
}).map((row2) => ({ name: String(row2.countedAs), step: row2.nodeName })),
|
|
59508
59584
|
noConversion: typeof declared === "string" && declared.trim() !== "" ? declared.trim() : null
|
|
59509
59585
|
};
|
|
59510
59586
|
});
|
|
59511
59587
|
}
|
|
59512
59588
|
function flowCoverageHints(coverage) {
|
|
59513
|
-
const unset = coverage.filter((
|
|
59589
|
+
const unset = coverage.filter((row2) => row2.counted.length === 0 && row2.noConversion === null);
|
|
59514
59590
|
if (unset.length === 0) return [];
|
|
59515
59591
|
return [
|
|
59516
|
-
`${unset.length} of ${coverage.length} Form(s) here count nothing, so every report shows them converting nobody: ${unset.map((
|
|
59592
|
+
`${unset.length} of ${coverage.length} Form(s) here count nothing, so every report shows them converting nobody: ${unset.map((row2) => row2.slug).join(
|
|
59517
59593
|
", "
|
|
59518
59594
|
)}. Read each one's endings with \`baker analytics conversions --flow <slug>\` and name the outcome, or declare on its first step why it has none.`
|
|
59519
59595
|
];
|
|
@@ -59542,9 +59618,9 @@ function shapeHints(data) {
|
|
|
59542
59618
|
);
|
|
59543
59619
|
}
|
|
59544
59620
|
}
|
|
59545
|
-
const unread = (data.adParams ?? []).filter((
|
|
59621
|
+
const unread = (data.adParams ?? []).filter((row2) => row2.role === void 0 && row2.sessions > 0);
|
|
59546
59622
|
if (unread.length > 0) {
|
|
59547
|
-
const named = unread.slice(0, 8).map((
|
|
59623
|
+
const named = unread.slice(0, 8).map((row2) => `${row2.name} (${row2.sessions} visits)`).join(", ");
|
|
59548
59624
|
hints2.push(
|
|
59549
59625
|
`Nothing reads these parameters yet: ${named}${unread.length > 8 ? ", \u2026" : ""}. Their values are not being stored \u2014 only the fact that the name appeared \u2014 so any breakdown over them is empty rather than wrong. Name each one with \`baker analytics map --platform <platform> --set <name>=<role>\`; mapping also fixes the history for a parameter Baker was already capturing.`
|
|
59550
59626
|
);
|
|
@@ -59552,7 +59628,7 @@ function shapeHints(data) {
|
|
|
59552
59628
|
const unplaceable = data.adParamsUnattributed ?? [];
|
|
59553
59629
|
if (unplaceable.length > 0) {
|
|
59554
59630
|
hints2.push(
|
|
59555
|
-
`Some parameters arrive on visits with no click id and no recognised source, so no platform can be named for them: ${unplaceable.map((
|
|
59631
|
+
`Some parameters arrive on visits with no click id and no recognised source, so no platform can be named for them: ${unplaceable.map((row2) => row2.name).join(", ")}. If you know where they come from, map them with --platform on that platform.`
|
|
59556
59632
|
);
|
|
59557
59633
|
}
|
|
59558
59634
|
return hints2;
|
|
@@ -59563,7 +59639,7 @@ function adHierarchyHints(data, platform) {
|
|
|
59563
59639
|
if (traffic === void 0 || traffic.sessions < MIN_SESSIONS_FOR_TAGGING_CLAIM) return [];
|
|
59564
59640
|
if (traffic.taggedSessions === 0) return [];
|
|
59565
59641
|
const carried = new Set(
|
|
59566
|
-
(data.adParams ?? []).filter((
|
|
59642
|
+
(data.adParams ?? []).filter((row2) => row2.role !== void 0 && row2.awaitingTraffic !== true).map((row2) => row2.role)
|
|
59567
59643
|
);
|
|
59568
59644
|
const missing = essentialAdDimensions(platform).filter((entry) => !carried.has(entry.role));
|
|
59569
59645
|
if (missing.length === 0) return [];
|
|
@@ -59631,7 +59707,7 @@ function buildAnalyticsHints(data, platform, origin) {
|
|
|
59631
59707
|
}
|
|
59632
59708
|
function campaignOriginHints(rows) {
|
|
59633
59709
|
const campaigns = rows ?? [];
|
|
59634
|
-
const fromAds = campaigns.filter((
|
|
59710
|
+
const fromAds = campaigns.filter((row2) => row2.clickPlatform !== "");
|
|
59635
59711
|
if (fromAds.length === 0) return [];
|
|
59636
59712
|
const own = campaigns.length - fromAds.length;
|
|
59637
59713
|
return [
|
|
@@ -59675,28 +59751,28 @@ function peopleLimitError(args8) {
|
|
|
59675
59751
|
if (dropped === 0) return void 0;
|
|
59676
59752
|
return `${terms.length + dropped} people named; one search takes up to ${PEOPLE_SEARCH_MAX_TERMS}. Split the list and run each part.`;
|
|
59677
59753
|
}
|
|
59678
|
-
function compactRow2(
|
|
59679
|
-
const clicks = [...
|
|
59754
|
+
function compactRow2(row2) {
|
|
59755
|
+
const clicks = [...row2.googleClick ? ["google"] : [], ...row2.metaClick ? ["meta"] : []];
|
|
59680
59756
|
const candidates = {
|
|
59681
|
-
input:
|
|
59682
|
-
readAs:
|
|
59683
|
-
found:
|
|
59684
|
-
problem:
|
|
59685
|
-
contacts:
|
|
59686
|
-
events:
|
|
59687
|
-
firstSeen:
|
|
59688
|
-
lastSeen:
|
|
59689
|
-
pageViews:
|
|
59690
|
-
landings:
|
|
59691
|
-
visits:
|
|
59692
|
-
formSubmits:
|
|
59693
|
-
firstSource:
|
|
59694
|
-
systemEvents:
|
|
59695
|
-
pageEvents:
|
|
59696
|
-
uploads:
|
|
59697
|
-
uploadedTo:
|
|
59757
|
+
input: row2.input,
|
|
59758
|
+
readAs: row2.kind,
|
|
59759
|
+
found: row2.found,
|
|
59760
|
+
problem: row2.problem,
|
|
59761
|
+
contacts: row2.contacts,
|
|
59762
|
+
events: row2.events,
|
|
59763
|
+
firstSeen: row2.firstSeen,
|
|
59764
|
+
lastSeen: row2.lastSeen,
|
|
59765
|
+
pageViews: row2.pageViews,
|
|
59766
|
+
landings: row2.landings,
|
|
59767
|
+
visits: row2.visits,
|
|
59768
|
+
formSubmits: row2.formSubmits,
|
|
59769
|
+
firstSource: row2.firstSource,
|
|
59770
|
+
systemEvents: row2.systemEvents,
|
|
59771
|
+
pageEvents: row2.pageEvents,
|
|
59772
|
+
uploads: row2.uploads,
|
|
59773
|
+
uploadedTo: row2.uploadedTo,
|
|
59698
59774
|
clicks,
|
|
59699
|
-
knownBy:
|
|
59775
|
+
knownBy: row2.knownBy
|
|
59700
59776
|
};
|
|
59701
59777
|
return Object.fromEntries(
|
|
59702
59778
|
Object.entries(candidates).filter(
|
|
@@ -59711,17 +59787,17 @@ function lookupHints(data) {
|
|
|
59711
59787
|
`${data.dropped} lines past the limit of ${PEOPLE_SEARCH_MAX_TERMS} were not looked up. Run the rest separately.`
|
|
59712
59788
|
);
|
|
59713
59789
|
}
|
|
59714
|
-
const unseen = data.people.filter((
|
|
59790
|
+
const unseen = data.people.filter((row2) => !row2.found && row2.problem === "");
|
|
59715
59791
|
if (unseen.length > 0) {
|
|
59716
59792
|
hints2.push(
|
|
59717
|
-
`Not seen by Baker: ${unseen.map((
|
|
59793
|
+
`Not seen by Baker: ${unseen.map((row2) => row2.input).join(", ")}. Baker knows a person by what its pages, Forms and connected systems recorded \u2014 a lead that came in through a form or site Baker does not measure, or a source not connected, is not here.`
|
|
59718
59794
|
);
|
|
59719
59795
|
}
|
|
59720
|
-
for (const
|
|
59721
|
-
hints2.push(`${
|
|
59796
|
+
for (const row2 of data.people.filter((person) => person.problem !== "")) {
|
|
59797
|
+
hints2.push(`${row2.input}: ${row2.problem}`);
|
|
59722
59798
|
}
|
|
59723
|
-
const found = data.people.filter((
|
|
59724
|
-
const neverVisited = found.filter((
|
|
59799
|
+
const found = data.people.filter((row2) => row2.found);
|
|
59800
|
+
const neverVisited = found.filter((row2) => row2.pageViews === 0);
|
|
59725
59801
|
if (neverVisited.length > 0) {
|
|
59726
59802
|
hints2.push(
|
|
59727
59803
|
`${neverVisited.length} of ${found.length} found never visited a page Baker publishes: Baker knows them only from what their systems reported, so no landing can be credited with them.`
|
|
@@ -59735,13 +59811,13 @@ function lookupHints(data) {
|
|
|
59735
59811
|
return hints2;
|
|
59736
59812
|
}
|
|
59737
59813
|
function lookupReport(data, full = false) {
|
|
59738
|
-
const found = data.people.filter((
|
|
59814
|
+
const found = data.people.filter((row2) => row2.found);
|
|
59739
59815
|
return {
|
|
59740
59816
|
summary: {
|
|
59741
59817
|
searched: data.people.length,
|
|
59742
59818
|
found: found.length,
|
|
59743
59819
|
notFound: data.people.length - found.length,
|
|
59744
|
-
visitedABakerPage: found.filter((
|
|
59820
|
+
visitedABakerPage: found.filter((row2) => row2.pageViews > 0).length
|
|
59745
59821
|
},
|
|
59746
59822
|
people: full ? data.people : data.people.map(compactRow2),
|
|
59747
59823
|
hints: lookupHints(data)
|
|
@@ -60216,9 +60292,9 @@ var SOURCE_NAME_MAX = 80;
|
|
|
60216
60292
|
var sourceNameSchema = z49.string().trim().min(1).max(SOURCE_NAME_MAX).refine((name) => validateEventName(name) === null, REJECTION_MESSAGES.invalid_name);
|
|
60217
60293
|
var sourceIdSchema = z49.string().min(1);
|
|
60218
60294
|
var sourceMappingSchema = z49.record(z49.string(), z49.string().min(1)).superRefine((mapping, ctx) => {
|
|
60219
|
-
for (const
|
|
60220
|
-
const problem = mappingRowProblem(
|
|
60221
|
-
if (problem !== null) ctx.addIssue({ code: "custom", message: problem, path: [
|
|
60295
|
+
for (const row2 of Object.keys(mapping)) {
|
|
60296
|
+
const problem = mappingRowProblem(row2);
|
|
60297
|
+
if (problem !== null) ctx.addIssue({ code: "custom", message: problem, path: [row2] });
|
|
60222
60298
|
}
|
|
60223
60299
|
});
|
|
60224
60300
|
var currencyCodeSchema = z49.string().trim().toUpperCase().regex(/^[A-Z]{3}$/, "A currency is a three-letter code, like EUR or USD.");
|
|
@@ -77167,43 +77243,43 @@ var experimentFinishResponseSchema = z62.object({
|
|
|
77167
77243
|
|
|
77168
77244
|
// src/commands/experiment/hints.ts
|
|
77169
77245
|
var LIVE_HINT = {
|
|
77170
|
-
keep_running: (
|
|
77171
|
-
winner: (
|
|
77172
|
-
no_difference: (
|
|
77173
|
-
cannot_win: (
|
|
77174
|
-
stopped_early_harmful: (
|
|
77246
|
+
keep_running: (row2, now) => `${row2.experimentId} cannot yet tell the two pages apart \u2014 do not end it hoping for a result. ${describeWait2(row2, now)} To the user, say only this in plain words: "${row2.summary}" plus how much more data it needs and roughly when. Never quote confidence, chance-better or intervals to them \u2014 those numbers confuse marketers and decide nothing.`,
|
|
77247
|
+
winner: (row2) => row2.winner === "variant" ? `${row2.experimentId} has a winner: the new variant. Run \`baker experiment finish --id ${row2.experimentId} --keep variant\` to show it to everyone, then \`baker experiment fold\`.` : `${row2.experimentId}: the new version is proven worse and is STILL being shown to half the visitors. Run \`baker experiment finish --id ${row2.experimentId} --keep original\` now.`,
|
|
77248
|
+
no_difference: (row2) => `${row2.experimentId} measured the two pages as alike \u2014 it collected enough traffic to see the change it was looking for, and there was none. End it with \`--keep original\` and test a bigger change \u2014 a headline, an offer, the shape of the page, not a button colour.`,
|
|
77249
|
+
cannot_win: (row2) => `${row2.experimentId}: the new version cannot win \u2014 even its best case is under the lift this test was looking for, so there is nothing left to find. Run \`baker experiment finish --id ${row2.experimentId} --keep original --reason numbers_clear\` and write down what it taught.`,
|
|
77250
|
+
stopped_early_harmful: (row2) => `${row2.experimentId} is doing damage and is STILL being shown to visitors. Run \`baker experiment finish --id ${row2.experimentId} --keep original\` now.`,
|
|
77175
77251
|
// Unreachable on a live test — only ending one produces it. The Record is
|
|
77176
77252
|
// exhaustive by type so a verdict added upstream fails to compile here rather
|
|
77177
77253
|
// than printing nothing.
|
|
77178
|
-
inconclusive: (
|
|
77179
|
-
invalid: (
|
|
77254
|
+
inconclusive: (row2) => `${row2.experimentId} was ended before it concluded and found nothing.`,
|
|
77255
|
+
invalid: (row2) => `${row2.experimentId} cannot be read: ${row2.invalidReason === "crossover" ? "too many visitors saw both versions" : "the traffic did not split evenly"}. End it with \`--keep original\` and start again rather than reporting anything from it.`
|
|
77180
77256
|
};
|
|
77181
|
-
function describeWait2(
|
|
77182
|
-
if (
|
|
77257
|
+
function describeWait2(row2, now) {
|
|
77258
|
+
if (row2.willNotConclude) {
|
|
77183
77259
|
return "This page's traffic will not settle it at any point, so end it with `--keep original` and test a bigger change.";
|
|
77184
77260
|
}
|
|
77185
|
-
if (
|
|
77261
|
+
if (row2.earliestDecisionAt === null) {
|
|
77186
77262
|
return "This page has produced no visitors yet, so there is nothing to project a finish date from.";
|
|
77187
77263
|
}
|
|
77188
|
-
if (
|
|
77189
|
-
return `It already has the data it needs; the verdict waits only until it has seen a full day of visitors \u2014 ${describeWait(
|
|
77264
|
+
if (row2.stillNeeded === null) {
|
|
77265
|
+
return `It already has the data it needs; the verdict waits only until it has seen a full day of visitors \u2014 ${describeWait(row2.earliestDecisionAt, now)}.`;
|
|
77190
77266
|
}
|
|
77191
|
-
const needed = `It needs ${roughCount(
|
|
77192
|
-
return `${needed}${describeWait(
|
|
77267
|
+
const needed = `It needs ${roughCount(row2.stillNeeded.visitors)} more visitors (${roughCount(row2.stillNeeded.conversions)} more conversions) for an answer if the gap holds \u2014 `;
|
|
77268
|
+
return `${needed}${describeWait(row2.earliestDecisionAt, now)} at today's traffic (${new Date(row2.earliestDecisionAt).toISOString().slice(0, 10)}). A test is gated on this data, never on days.`;
|
|
77193
77269
|
}
|
|
77194
|
-
function pausedNote(
|
|
77195
|
-
return
|
|
77270
|
+
function pausedNote(row2) {
|
|
77271
|
+
return row2.status === "paused" ? `${row2.experimentId} is paused \u2014 everyone sees the original. \`baker experiment resume --id ${row2.experimentId}\` restarts the split with the same sides. ` : "";
|
|
77196
77272
|
}
|
|
77197
77273
|
function unlearnedHint(experiments) {
|
|
77198
77274
|
const unlearned = experiments.filter(
|
|
77199
|
-
(
|
|
77275
|
+
(row2) => row2.status === "finished" && row2.learning === null && row2.verdict !== "inconclusive"
|
|
77200
77276
|
);
|
|
77201
77277
|
if (unlearned.length === 0) return null;
|
|
77202
|
-
const ids2 = unlearned.map((
|
|
77278
|
+
const ids2 = unlearned.map((row2) => row2.experimentId).join(", ");
|
|
77203
77279
|
return `${unlearned.length === 1 ? "One finished test has" : `${unlearned.length} finished tests have`} no learning written down yet (${ids2}). Say what it taught in one or two sentences \u2014 what the client should believe about this page now \u2014 with \`baker experiment update --id <id> --learning "\u2026"\`. It is what \`plan\` hands the next test.`;
|
|
77204
77280
|
}
|
|
77205
77281
|
function buildStatusHints(experiments, now = Date.now()) {
|
|
77206
|
-
const hints2 = experiments.filter((
|
|
77282
|
+
const hints2 = experiments.filter((row2) => row2.status !== "finished").map((row2) => pausedNote(row2) + LIVE_HINT[row2.verdict](row2, now));
|
|
77207
77283
|
const unlearned = unlearnedHint(experiments);
|
|
77208
77284
|
if (unlearned) hints2.push(unlearned);
|
|
77209
77285
|
if (experiments.length === 0) {
|
|
@@ -77483,52 +77559,52 @@ function fullArm(arm) {
|
|
|
77483
77559
|
...arm.snapshotCapturedAt === void 0 ? {} : { snapshotCapturedAt: isoMoment(arm.snapshotCapturedAt) }
|
|
77484
77560
|
};
|
|
77485
77561
|
}
|
|
77486
|
-
function statusReadout(
|
|
77562
|
+
function statusReadout(row2, { full, now }) {
|
|
77487
77563
|
const decisionWhen = {
|
|
77488
|
-
earliestDecisionAt: isoDay2(
|
|
77489
|
-
...
|
|
77564
|
+
earliestDecisionAt: isoDay2(row2.earliestDecisionAt),
|
|
77565
|
+
...row2.earliestDecisionAt === null ? {} : { earliestDecisionIn: describeWait(row2.earliestDecisionAt, now) }
|
|
77490
77566
|
};
|
|
77491
77567
|
const moments = {
|
|
77492
|
-
startedAt: isoMoment(
|
|
77493
|
-
pausedAt: isoMoment(
|
|
77494
|
-
finishedAt: isoMoment(
|
|
77568
|
+
startedAt: isoMoment(row2.startedAt),
|
|
77569
|
+
pausedAt: isoMoment(row2.pausedAt),
|
|
77570
|
+
finishedAt: isoMoment(row2.finishedAt)
|
|
77495
77571
|
};
|
|
77496
77572
|
if (full) {
|
|
77497
77573
|
return {
|
|
77498
|
-
...
|
|
77574
|
+
...row2,
|
|
77499
77575
|
...moments,
|
|
77500
77576
|
...decisionWhen,
|
|
77501
|
-
control: fullArm(
|
|
77502
|
-
variant: fullArm(
|
|
77577
|
+
control: fullArm(row2.control),
|
|
77578
|
+
variant: fullArm(row2.variant)
|
|
77503
77579
|
};
|
|
77504
77580
|
}
|
|
77505
77581
|
return {
|
|
77506
|
-
experimentId:
|
|
77507
|
-
landingSlug:
|
|
77508
|
-
variantSlug:
|
|
77509
|
-
status:
|
|
77510
|
-
verdict:
|
|
77511
|
-
summary:
|
|
77512
|
-
...
|
|
77513
|
-
...
|
|
77514
|
-
stillNeeded:
|
|
77582
|
+
experimentId: row2.experimentId,
|
|
77583
|
+
landingSlug: row2.landingSlug,
|
|
77584
|
+
variantSlug: row2.variantSlug,
|
|
77585
|
+
status: row2.status,
|
|
77586
|
+
verdict: row2.verdict,
|
|
77587
|
+
summary: row2.summary,
|
|
77588
|
+
...row2.winner === null ? {} : { winner: row2.winner },
|
|
77589
|
+
...row2.invalidReason === null ? {} : { invalidReason: row2.invalidReason },
|
|
77590
|
+
stillNeeded: row2.stillNeeded,
|
|
77515
77591
|
...decisionWhen,
|
|
77516
|
-
willNotConclude:
|
|
77517
|
-
control: compactArm(
|
|
77518
|
-
variant: compactArm(
|
|
77519
|
-
secondary:
|
|
77520
|
-
hypothesisStatement:
|
|
77521
|
-
minDetectableRelativeLift:
|
|
77522
|
-
goal:
|
|
77523
|
-
...
|
|
77524
|
-
goalLabel:
|
|
77592
|
+
willNotConclude: row2.willNotConclude,
|
|
77593
|
+
control: compactArm(row2.control),
|
|
77594
|
+
variant: compactArm(row2.variant),
|
|
77595
|
+
secondary: row2.secondary.map(compactMetric),
|
|
77596
|
+
hypothesisStatement: row2.hypothesisStatement,
|
|
77597
|
+
minDetectableRelativeLift: row2.minDetectableRelativeLift,
|
|
77598
|
+
goal: row2.goal,
|
|
77599
|
+
...row2.variantGoal === null ? {} : { variantGoal: row2.variantGoal },
|
|
77600
|
+
goalLabel: row2.goalLabel,
|
|
77525
77601
|
...moments,
|
|
77526
|
-
...
|
|
77527
|
-
survivor:
|
|
77528
|
-
hypothesisOutcome:
|
|
77529
|
-
fold:
|
|
77530
|
-
learning:
|
|
77531
|
-
endReason:
|
|
77602
|
+
...row2.status === "finished" ? {
|
|
77603
|
+
survivor: row2.survivor,
|
|
77604
|
+
hypothesisOutcome: row2.hypothesisOutcome,
|
|
77605
|
+
fold: row2.fold,
|
|
77606
|
+
learning: row2.learning,
|
|
77607
|
+
endReason: row2.endReason
|
|
77532
77608
|
} : {}
|
|
77533
77609
|
};
|
|
77534
77610
|
}
|
|
@@ -77806,7 +77882,7 @@ var statusCommand7 = defineCommand149({
|
|
|
77806
77882
|
writeJsonEnvelope({
|
|
77807
77883
|
ok: true,
|
|
77808
77884
|
data: {
|
|
77809
|
-
experiments: response.data.experiments.map((
|
|
77885
|
+
experiments: response.data.experiments.map((row2) => statusReadout(row2, { full: args8.full === true, now }))
|
|
77810
77886
|
},
|
|
77811
77887
|
hints: buildStatusHints(response.data.experiments, now)
|
|
77812
77888
|
});
|
|
@@ -79202,7 +79278,7 @@ function assertDestinationReady(slug, tree, chosen, into) {
|
|
|
79202
79278
|
function collectConfigStatusFor(tree, _slug, sideEffectId) {
|
|
79203
79279
|
const rows = [];
|
|
79204
79280
|
collectConfigStatus(tree, rows);
|
|
79205
|
-
return rows.find((
|
|
79281
|
+
return rows.find((row2) => row2.sideEffectId === sideEffectId);
|
|
79206
79282
|
}
|
|
79207
79283
|
function slotHintFor(type, into) {
|
|
79208
79284
|
const others = otherSlots(type, shapeFor(type, into));
|
|
@@ -79629,9 +79705,9 @@ function statusFor(slug) {
|
|
|
79629
79705
|
return config;
|
|
79630
79706
|
}
|
|
79631
79707
|
function pendingInputHints(slug, config) {
|
|
79632
|
-
const pending = config.filter((
|
|
79708
|
+
const pending = config.filter((row2) => row2.needsInput);
|
|
79633
79709
|
if (pending.length === 0) return [];
|
|
79634
|
-
const calls = pending.slice(0, MAX_PENDING_HINTS).map((
|
|
79710
|
+
const calls = pending.slice(0, MAX_PENDING_HINTS).map((row2) => `${row2.kind}: ${requestFlowInputCall(slug, row2)}`);
|
|
79635
79711
|
const rest = pending.length - calls.length;
|
|
79636
79712
|
return [
|
|
79637
79713
|
`${pending.length} item${pending.length === 1 ? "" : "s"} still need the user. Each is one blocking call, and its result carries the destination \u2014 read the names from there rather than looking them up again.`,
|
|
@@ -80555,7 +80631,7 @@ var funnelCommand2 = defineCommand155({
|
|
|
80555
80631
|
...namedByCaller.length === 0 ? {} : { namedByCaller }
|
|
80556
80632
|
};
|
|
80557
80633
|
const response = await apiPost("/api/funnel", request);
|
|
80558
|
-
const counted = new Map((response.data.counted ?? []).map((
|
|
80634
|
+
const counted = new Map((response.data.counted ?? []).map((row2) => [row2.eventKey, row2.name]));
|
|
80559
80635
|
const data = response.data.moments === void 0 ? response.data : { ...response.data, moments: withFormMoments(response.data.moments, forms, counted) };
|
|
80560
80636
|
const hints2 = [...response.hints ?? [], ...unplacedFormHints(response.data.steps ?? [], forms, counted)];
|
|
80561
80637
|
writeJsonEnvelope({
|
|
@@ -81173,11 +81249,11 @@ function writeRowsToFile2(filePath, rows, append) {
|
|
|
81173
81249
|
writeFileSync4(filePath, `${toCsvRow(fields)}
|
|
81174
81250
|
`, "utf-8");
|
|
81175
81251
|
}
|
|
81176
|
-
const csvRows = rows.map((
|
|
81252
|
+
const csvRows = rows.map((row2) => toCsvRow(fields.map((f) => String(row2[f] ?? ""))));
|
|
81177
81253
|
appendFileSync2(filePath, `${csvRows.join("\n")}
|
|
81178
81254
|
`, "utf-8");
|
|
81179
81255
|
} else if (ext === "jsonl") {
|
|
81180
|
-
const lines = rows.map((
|
|
81256
|
+
const lines = rows.map((row2) => JSON.stringify(row2));
|
|
81181
81257
|
const content = `${lines.join("\n")}
|
|
81182
81258
|
`;
|
|
81183
81259
|
if (append && existsSync10(filePath)) {
|
|
@@ -82174,11 +82250,11 @@ function writeRowsToFile3(filePath, rows, append) {
|
|
|
82174
82250
|
writeFileSync5(filePath, `${toCsvRow(fields)}
|
|
82175
82251
|
`, "utf-8");
|
|
82176
82252
|
}
|
|
82177
|
-
const csvRows = rows.map((
|
|
82253
|
+
const csvRows = rows.map((row2) => toCsvRow(fields.map((f) => String(row2[f] ?? ""))));
|
|
82178
82254
|
appendFileSync3(filePath, `${csvRows.join("\n")}
|
|
82179
82255
|
`, "utf-8");
|
|
82180
82256
|
} else if (ext === "jsonl") {
|
|
82181
|
-
const content = `${rows.map((
|
|
82257
|
+
const content = `${rows.map((row2) => JSON.stringify(row2)).join("\n")}
|
|
82182
82258
|
`;
|
|
82183
82259
|
if (append && existsSync11(filePath)) {
|
|
82184
82260
|
appendFileSync3(filePath, content, "utf-8");
|
|
@@ -90311,7 +90387,7 @@ async function composeFilmstrip(frames) {
|
|
|
90311
90387
|
const width = FRAME_WIDTH * Math.min(GRID_COLUMNS, scaled.length);
|
|
90312
90388
|
const composites = scaled.flatMap((frame, index) => {
|
|
90313
90389
|
const column = index % GRID_COLUMNS;
|
|
90314
|
-
const
|
|
90390
|
+
const row2 = Math.floor(index / GRID_COLUMNS);
|
|
90315
90391
|
const label = Buffer.from(
|
|
90316
90392
|
`<svg width="${FRAME_WIDTH}" height="${LABEL_HEIGHT}">
|
|
90317
90393
|
<rect width="100%" height="100%" fill="#111827"/>
|
|
@@ -90319,8 +90395,8 @@ async function composeFilmstrip(frames) {
|
|
|
90319
90395
|
</svg>`
|
|
90320
90396
|
);
|
|
90321
90397
|
return [
|
|
90322
|
-
{ input: label, left: column * FRAME_WIDTH, top:
|
|
90323
|
-
{ input: frame, left: column * FRAME_WIDTH, top:
|
|
90398
|
+
{ input: label, left: column * FRAME_WIDTH, top: row2 * cellHeight },
|
|
90399
|
+
{ input: frame, left: column * FRAME_WIDTH, top: row2 * cellHeight + LABEL_HEIGHT }
|
|
90324
90400
|
];
|
|
90325
90401
|
});
|
|
90326
90402
|
return await sharp6({
|
|
@@ -92477,7 +92553,7 @@ Full guide: __tooling__/docs/tools/baker/media-sources.md`
|
|
|
92477
92553
|
});
|
|
92478
92554
|
|
|
92479
92555
|
// src/commands/research/index.ts
|
|
92480
|
-
import { defineCommand as
|
|
92556
|
+
import { defineCommand as defineCommand228 } from "citty";
|
|
92481
92557
|
|
|
92482
92558
|
// src/commands/research/advertisers.ts
|
|
92483
92559
|
import { defineCommand as defineCommand215 } from "citty";
|
|
@@ -92559,7 +92635,7 @@ function keywordMetricsHints({
|
|
|
92559
92635
|
}
|
|
92560
92636
|
const provenance = "DataForSEO resells Google's Keyword Planner volume \u2014 planner_search_volume here is the SAME number `baker ads google keywords metrics` returns, not an independent reading of it. Quoting the two as agreeing sources is not corroboration." + (clickstream ? " clickstream_search_volume IS independent (DataForSEO's own panels); when the two disagree, say which one you are quoting." : " For a genuinely independent estimate of demand, re-run with --clickstream (costs 2x), or use first-party data: `baker gsc query` for what already reaches the site, or the account's own search terms.");
|
|
92561
92637
|
const hints2 = [provenance];
|
|
92562
|
-
const empty = rows.filter((
|
|
92638
|
+
const empty = rows.filter((row2) => row2.search_volume === null).map((row2) => row2.keyword);
|
|
92563
92639
|
if (empty.length > 0) {
|
|
92564
92640
|
hints2.push(
|
|
92565
92641
|
`${empty.length} of ${asked.length} keywords came back with no data in ${queryContext.location} / ${queryContext.language}: ${empty.join(", ")}. That is missing coverage, NOT zero demand \u2014 do not report them as unsearched, and do not re-query them reworded. \`baker research autocomplete\` on the head term is the way to find the phrasing that market uses.`
|
|
@@ -92567,6 +92643,70 @@ function keywordMetricsHints({
|
|
|
92567
92643
|
}
|
|
92568
92644
|
return hints2;
|
|
92569
92645
|
}
|
|
92646
|
+
var ARCHIVE_NOTE = "Read from the Reddit archive: the words are as posted, but points and comment counts on anything under ~36h old are as first captured \u2014 don't quote those numbers as current.";
|
|
92647
|
+
function redditSearchHints({
|
|
92648
|
+
resultCount,
|
|
92649
|
+
kind,
|
|
92650
|
+
source
|
|
92651
|
+
}) {
|
|
92652
|
+
const hints2 = [];
|
|
92653
|
+
if (resultCount === 0) {
|
|
92654
|
+
hints2.push(
|
|
92655
|
+
'Nothing matched. Reddit search matches words, not meaning: try the audience\'s own phrasing (one distinctive word, or a seed from `baker research autocomplete`), a longer --since window, or other subreddits \u2014 `baker research reddit subreddits "<topic>"` finds where the audience actually talks' + (kind === "posts" ? ", and try --kind comments \u2014 most of the voice is in the replies." : ".")
|
|
92656
|
+
);
|
|
92657
|
+
} else if (kind === "posts") {
|
|
92658
|
+
hints2.push(
|
|
92659
|
+
"Titles rarely carry the voice. Read the promising threads with `baker research reddit thread <permalink>` for the replies, or search the replies directly with --kind comments."
|
|
92660
|
+
);
|
|
92661
|
+
}
|
|
92662
|
+
if (source === "archive" && resultCount > 0) hints2.push(ARCHIVE_NOTE);
|
|
92663
|
+
return hints2;
|
|
92664
|
+
}
|
|
92665
|
+
function redditThreadHints({
|
|
92666
|
+
commentsShown,
|
|
92667
|
+
commentsAsked,
|
|
92668
|
+
commentCount,
|
|
92669
|
+
source
|
|
92670
|
+
}) {
|
|
92671
|
+
const hints2 = [];
|
|
92672
|
+
if (commentsAsked > 0 && commentsShown === 0 && (commentCount ?? 0) > 0) {
|
|
92673
|
+
hints2.push(
|
|
92674
|
+
`This thread has ${commentCount} comments but none could be read just now. Quote the post only, and don't describe the replies.`
|
|
92675
|
+
);
|
|
92676
|
+
}
|
|
92677
|
+
if (source === "archive") hints2.push(ARCHIVE_NOTE);
|
|
92678
|
+
return hints2;
|
|
92679
|
+
}
|
|
92680
|
+
var REDDIT_HOST = /(^|\.)reddit\.com$|^redd\.it$/i;
|
|
92681
|
+
function redditSubredditsHints({ resultCount }) {
|
|
92682
|
+
if (resultCount === 0) {
|
|
92683
|
+
return [
|
|
92684
|
+
'No subreddits matched. Try the audience\'s role or the platform they work on ("amazon flex", "deliveroo", "nurses"), one word at a time, or a web search for `site:reddit.com <topic>`.'
|
|
92685
|
+
];
|
|
92686
|
+
}
|
|
92687
|
+
return [
|
|
92688
|
+
'Members say how big a subreddit is, not how active it is. Before mining one, run `baker research reddit search "<word>" --subreddit <name> --since <a recent date>`: nothing back means it is quiet, so pick another. `restricted` can still be read; `private` cannot.'
|
|
92689
|
+
];
|
|
92690
|
+
}
|
|
92691
|
+
var REDDIT_LINK_REJECTED = {
|
|
92692
|
+
action: "reject",
|
|
92693
|
+
explanation: "Do not quote anything attributed to this link \u2014 it does not lead to those words. Find the real thread with `baker research reddit search`, and if you already wrote a quote from it, remove it."
|
|
92694
|
+
};
|
|
92695
|
+
function redditPageFix(url, code) {
|
|
92696
|
+
let host;
|
|
92697
|
+
try {
|
|
92698
|
+
host = new URL(url).hostname;
|
|
92699
|
+
} catch {
|
|
92700
|
+
return null;
|
|
92701
|
+
}
|
|
92702
|
+
if (!REDDIT_HOST.test(host)) return null;
|
|
92703
|
+
if (code === "NOT_FOUND") return REDDIT_LINK_REJECTED;
|
|
92704
|
+
if (code !== "FORBIDDEN") return null;
|
|
92705
|
+
return {
|
|
92706
|
+
action: "retry_with_changes",
|
|
92707
|
+
explanation: 'Read Reddit with its own commands: `baker research reddit search "<words>" --subreddit <name>` to find what people said, `baker research reddit thread <url>` for one thread.'
|
|
92708
|
+
};
|
|
92709
|
+
}
|
|
92570
92710
|
|
|
92571
92711
|
// src/commands/research/output.ts
|
|
92572
92712
|
var RESEARCH_DATA_NOTE = "Estimates based on third-party SERP data \u2014 not exact figures. Use for directional insights, not precise measurement.";
|
|
@@ -92582,9 +92722,9 @@ function toCsvRow2(values) {
|
|
|
92582
92722
|
return v;
|
|
92583
92723
|
}).join(",");
|
|
92584
92724
|
}
|
|
92585
|
-
function flattenRow(
|
|
92725
|
+
function flattenRow(row2) {
|
|
92586
92726
|
const flat = {};
|
|
92587
|
-
for (const [key, val] of Object.entries(
|
|
92727
|
+
for (const [key, val] of Object.entries(row2)) {
|
|
92588
92728
|
flat[key] = typeof val === "object" && val !== null ? JSON.stringify(val) : String(val ?? "");
|
|
92589
92729
|
}
|
|
92590
92730
|
return flat;
|
|
@@ -92594,15 +92734,15 @@ function writeCsv(data, fields) {
|
|
|
92594
92734
|
const cols = fields ?? Object.keys(data[0] ?? {});
|
|
92595
92735
|
process.stdout.write(`${toCsvRow2(cols)}
|
|
92596
92736
|
`);
|
|
92597
|
-
for (const
|
|
92598
|
-
const flat = flattenRow(
|
|
92737
|
+
for (const row2 of data) {
|
|
92738
|
+
const flat = flattenRow(row2);
|
|
92599
92739
|
process.stdout.write(`${toCsvRow2(cols.map((f) => flat[f] ?? ""))}
|
|
92600
92740
|
`);
|
|
92601
92741
|
}
|
|
92602
92742
|
}
|
|
92603
92743
|
function writeJsonl(data) {
|
|
92604
|
-
for (const
|
|
92605
|
-
process.stdout.write(`${JSON.stringify(
|
|
92744
|
+
for (const row2 of data) {
|
|
92745
|
+
process.stdout.write(`${JSON.stringify(row2)}
|
|
92606
92746
|
`);
|
|
92607
92747
|
}
|
|
92608
92748
|
}
|
|
@@ -92613,8 +92753,8 @@ function writeMd(data, fields) {
|
|
|
92613
92753
|
`);
|
|
92614
92754
|
process.stdout.write(`| ${cols.map(() => "---").join(" | ")} |
|
|
92615
92755
|
`);
|
|
92616
|
-
for (const
|
|
92617
|
-
const flat = flattenRow(
|
|
92756
|
+
for (const row2 of data) {
|
|
92757
|
+
const flat = flattenRow(row2);
|
|
92618
92758
|
process.stdout.write(`| ${cols.map((f) => flat[f] ?? "").join(" | ")} |
|
|
92619
92759
|
`);
|
|
92620
92760
|
}
|
|
@@ -93028,7 +93168,7 @@ Examples:
|
|
|
93028
93168
|
})
|
|
93029
93169
|
});
|
|
93030
93170
|
} catch (err) {
|
|
93031
|
-
handleResearchError(err, fetchFix);
|
|
93171
|
+
handleResearchError(err, (code) => redditPageFix(url, code) ?? fetchFix(code));
|
|
93032
93172
|
}
|
|
93033
93173
|
}
|
|
93034
93174
|
});
|
|
@@ -93213,24 +93353,24 @@ import { defineCommand as defineCommand221 } from "citty";
|
|
|
93213
93353
|
|
|
93214
93354
|
// src/commands/research/keyword-metrics-rows.ts
|
|
93215
93355
|
var KEYWORD_METRICS_SOURCE = "keyword_planner_estimate_via_dataforseo";
|
|
93216
|
-
function toOutputRow(
|
|
93356
|
+
function toOutputRow(row2, options) {
|
|
93217
93357
|
const out = {
|
|
93218
93358
|
source: KEYWORD_METRICS_SOURCE,
|
|
93219
|
-
keyword:
|
|
93220
|
-
planner_search_volume:
|
|
93221
|
-
planner_cpc:
|
|
93222
|
-
planner_low_top_of_page_bid:
|
|
93223
|
-
planner_high_top_of_page_bid:
|
|
93224
|
-
planner_competition:
|
|
93225
|
-
planner_competition_index:
|
|
93359
|
+
keyword: row2.keyword,
|
|
93360
|
+
planner_search_volume: row2.search_volume,
|
|
93361
|
+
planner_cpc: row2.cpc,
|
|
93362
|
+
planner_low_top_of_page_bid: row2.low_top_of_page_bid,
|
|
93363
|
+
planner_high_top_of_page_bid: row2.high_top_of_page_bid,
|
|
93364
|
+
planner_competition: row2.competition,
|
|
93365
|
+
planner_competition_index: row2.competition_index
|
|
93226
93366
|
};
|
|
93227
93367
|
if (options.clickstream) {
|
|
93228
|
-
out.clickstream_search_volume =
|
|
93368
|
+
out.clickstream_search_volume = row2.clickstream_search_volume;
|
|
93229
93369
|
}
|
|
93230
93370
|
if (options.full) {
|
|
93231
|
-
out.keyword_difficulty =
|
|
93232
|
-
out.main_intent =
|
|
93233
|
-
out.planner_monthly_searches =
|
|
93371
|
+
out.keyword_difficulty = row2.keyword_difficulty;
|
|
93372
|
+
out.main_intent = row2.main_intent;
|
|
93373
|
+
out.planner_monthly_searches = row2.monthly_searches;
|
|
93234
93374
|
}
|
|
93235
93375
|
return out;
|
|
93236
93376
|
}
|
|
@@ -93347,7 +93487,7 @@ Examples:
|
|
|
93347
93487
|
skipCache: args8["no-cache"] ? true : void 0
|
|
93348
93488
|
});
|
|
93349
93489
|
const columns = { clickstream, full };
|
|
93350
|
-
const rows = result.data.map((
|
|
93490
|
+
const rows = result.data.map((row2) => toOutputRow(row2, columns));
|
|
93351
93491
|
const hints2 = keywordMetricsHints({ rows: result.data, asked: keywords, clickstream, queryContext });
|
|
93352
93492
|
warnDefaults(queryContext);
|
|
93353
93493
|
const format = args8.output || "json";
|
|
@@ -93563,8 +93703,273 @@ Examples:
|
|
|
93563
93703
|
}
|
|
93564
93704
|
});
|
|
93565
93705
|
|
|
93566
|
-
// src/commands/research/
|
|
93706
|
+
// src/commands/research/reddit.ts
|
|
93567
93707
|
import { defineCommand as defineCommand225 } from "citty";
|
|
93708
|
+
var TIMEOUT_MS2 = 18e4;
|
|
93709
|
+
var TEXT_PREVIEW_CHARS = 1500;
|
|
93710
|
+
var ITEM_FIELDS = {
|
|
93711
|
+
kind: "post or comment",
|
|
93712
|
+
subreddit: "Where it was said",
|
|
93713
|
+
title: "The post's title (on a comment: the post it answers)",
|
|
93714
|
+
text: "Verbatim \u2014 quote this, exactly",
|
|
93715
|
+
text_truncated: "The text was cut; re-run with --full before quoting the end of it",
|
|
93716
|
+
author: "Reddit username, or null once the account is deleted",
|
|
93717
|
+
score: "Points",
|
|
93718
|
+
comment_count: "Posts only",
|
|
93719
|
+
created: "Date written (UTC)",
|
|
93720
|
+
permalink: "Cite THIS link \u2014 it is built from where the words actually are",
|
|
93721
|
+
depth: "Thread comments: 0 answers the post, 1 answers a comment",
|
|
93722
|
+
source: "reddit_api / reddit_web = live Reddit; archive = the Arctic Shift archive of Reddit"
|
|
93723
|
+
};
|
|
93724
|
+
registerSchema({
|
|
93725
|
+
command: "research.reddit.search",
|
|
93726
|
+
description: "Search Reddit posts or comments for the audience's own words. Returns verbatim text with a real permalink for each hit. Start here for voice-of-customer work on Reddit.",
|
|
93727
|
+
args: {
|
|
93728
|
+
query: {
|
|
93729
|
+
type: "string",
|
|
93730
|
+
description: "Words to match \u2014 the audience's phrasing, not a description of it",
|
|
93731
|
+
required: true
|
|
93732
|
+
},
|
|
93733
|
+
subreddit: {
|
|
93734
|
+
type: "string",
|
|
93735
|
+
description: "Subreddits to search (up to 10): comma-separated (couriersofreddit,AmazonFlexUK) or the flag repeated. Needed for --kind comments",
|
|
93736
|
+
required: false
|
|
93737
|
+
},
|
|
93738
|
+
kind: {
|
|
93739
|
+
type: "string",
|
|
93740
|
+
description: "posts (default) or comments \u2014 the replies are where most of the voice is",
|
|
93741
|
+
required: false,
|
|
93742
|
+
enum: ["posts", "comments"]
|
|
93743
|
+
},
|
|
93744
|
+
since: { type: "string", description: "Only things written on or after this day, YYYY-MM-DD", required: false },
|
|
93745
|
+
limit: { type: "number", description: "Results to return (1-50, default 25)", required: false },
|
|
93746
|
+
full: {
|
|
93747
|
+
type: "boolean",
|
|
93748
|
+
description: "Return every text whole instead of a preview",
|
|
93749
|
+
required: false,
|
|
93750
|
+
default: false
|
|
93751
|
+
}
|
|
93752
|
+
}
|
|
93753
|
+
});
|
|
93754
|
+
registerSchema({
|
|
93755
|
+
command: "research.reddit.thread",
|
|
93756
|
+
description: "Read one Reddit thread verbatim: the post and its best comments, each with its own permalink. Use it on any Reddit link before quoting it \u2014 it also exposes links that point somewhere other than they claim.",
|
|
93757
|
+
args: {
|
|
93758
|
+
target: {
|
|
93759
|
+
type: "string",
|
|
93760
|
+
description: "The thread's link (any Reddit form, including share links) or post id",
|
|
93761
|
+
required: true
|
|
93762
|
+
},
|
|
93763
|
+
comments: { type: "number", description: "Comments to return, best first (0-200, default 40)", required: false },
|
|
93764
|
+
full: {
|
|
93765
|
+
type: "boolean",
|
|
93766
|
+
description: "Return every text whole instead of a preview",
|
|
93767
|
+
required: false,
|
|
93768
|
+
default: false
|
|
93769
|
+
}
|
|
93770
|
+
}
|
|
93771
|
+
});
|
|
93772
|
+
registerSchema({
|
|
93773
|
+
command: "research.reddit.subreddits",
|
|
93774
|
+
description: "Find the subreddits where an audience talks about a topic, with member counts and whether they can be read. Run it before searching, instead of guessing subreddit names.",
|
|
93775
|
+
args: {
|
|
93776
|
+
query: { type: "string", description: 'The audience or topic, e.g. "amazon flex uk", "deliveroo"', required: true },
|
|
93777
|
+
limit: { type: "number", description: "Subreddits to return (1-25, default 10)", required: false }
|
|
93778
|
+
}
|
|
93779
|
+
});
|
|
93780
|
+
function row(item, full) {
|
|
93781
|
+
const cut = !full && item.text.length > TEXT_PREVIEW_CHARS;
|
|
93782
|
+
return {
|
|
93783
|
+
kind: item.kind,
|
|
93784
|
+
subreddit: item.subreddit,
|
|
93785
|
+
title: item.title,
|
|
93786
|
+
text: cut ? `${item.text.slice(0, TEXT_PREVIEW_CHARS)}\u2026` : item.text,
|
|
93787
|
+
...cut ? { text_truncated: true } : {},
|
|
93788
|
+
author: item.author,
|
|
93789
|
+
score: item.score,
|
|
93790
|
+
...item.kind === "post" ? { comment_count: item.commentCount } : {},
|
|
93791
|
+
created: item.createdAt.slice(0, 10),
|
|
93792
|
+
permalink: item.permalink,
|
|
93793
|
+
...item.depth !== null ? { depth: item.depth } : {}
|
|
93794
|
+
};
|
|
93795
|
+
}
|
|
93796
|
+
function redditFix(code) {
|
|
93797
|
+
if (code === "NOT_FOUND") return REDDIT_LINK_REJECTED;
|
|
93798
|
+
if (code === "BAD_REQUEST" || code === "VALIDATION_ERROR") {
|
|
93799
|
+
return { action: "retry_with_changes", explanation: "Follow the message above and run the command again." };
|
|
93800
|
+
}
|
|
93801
|
+
return {
|
|
93802
|
+
action: "continue_without",
|
|
93803
|
+
explanation: "Don't loop on it. Finish the job with the other sources, say plainly that Reddit is a gap in this research, and never stand in a summary's paraphrase for a Reddit quote."
|
|
93804
|
+
};
|
|
93805
|
+
}
|
|
93806
|
+
function failValidation4(message) {
|
|
93807
|
+
writeResearchJson({ ok: false, error: { code: "VALIDATION_ERROR", message, fix: redditFix("VALIDATION_ERROR") } });
|
|
93808
|
+
process.exit(1);
|
|
93809
|
+
}
|
|
93810
|
+
function subredditsFromArgs(rawArgs, parsed) {
|
|
93811
|
+
return repeatedValues(rawArgs, "subreddit", parsed).flatMap((value2) => value2.split(",")).map((name) => name.trim()).filter(Boolean);
|
|
93812
|
+
}
|
|
93813
|
+
var searchCommand3 = defineCommand225({
|
|
93814
|
+
meta: {
|
|
93815
|
+
name: "search",
|
|
93816
|
+
description: `Search Reddit for the audience's own words, verbatim, each with a permalink you can cite.
|
|
93817
|
+
|
|
93818
|
+
Start here for Reddit voice-of-customer. Matches words, not meaning \u2014 search the way the audience writes, and quote a phrase to keep it together ('"right to work"').
|
|
93819
|
+
|
|
93820
|
+
Examples:
|
|
93821
|
+
baker research reddit search "agency never paid" --subreddit couriersofreddit,AmazonFlexUK
|
|
93822
|
+
baker research reddit search '"right to work"' --subreddit UKJobs,AmazonFlexUK
|
|
93823
|
+
baker research reddit search "visa" --subreddit UberEATS --kind comments --since 2025-01-01
|
|
93824
|
+
baker research reddit search "side hustle student deliveries"`
|
|
93825
|
+
},
|
|
93826
|
+
args: {
|
|
93827
|
+
query: { type: "positional", description: "Words to match", required: false },
|
|
93828
|
+
subreddit: {
|
|
93829
|
+
type: "string",
|
|
93830
|
+
description: "Subreddits (up to 10): comma-separated, or repeat the flag",
|
|
93831
|
+
required: false
|
|
93832
|
+
},
|
|
93833
|
+
kind: { type: "string", description: "posts (default) or comments", required: false },
|
|
93834
|
+
since: { type: "string", description: "YYYY-MM-DD", required: false },
|
|
93835
|
+
limit: { type: "string", description: "1-50, default 25", required: false },
|
|
93836
|
+
full: { type: "boolean", description: "Whole texts, not previews", required: false }
|
|
93837
|
+
},
|
|
93838
|
+
run: async ({ args: args8, rawArgs }) => {
|
|
93839
|
+
const query = args8.query?.trim();
|
|
93840
|
+
if (!query) failValidation4('Say what to search for: baker research reddit search "<words>"');
|
|
93841
|
+
const kind = args8.kind ?? "posts";
|
|
93842
|
+
if (kind !== "posts" && kind !== "comments") failValidation4("--kind must be posts or comments");
|
|
93843
|
+
const body = {
|
|
93844
|
+
query,
|
|
93845
|
+
kind,
|
|
93846
|
+
subreddits: subredditsFromArgs(rawArgs, args8.subreddit)
|
|
93847
|
+
};
|
|
93848
|
+
if (args8.since) body.since = args8.since;
|
|
93849
|
+
if (args8.limit) body.limit = Number(args8.limit);
|
|
93850
|
+
try {
|
|
93851
|
+
const data = await apiPost("/api/research/reddit/search", body, {
|
|
93852
|
+
timeoutMs: TIMEOUT_MS2
|
|
93853
|
+
});
|
|
93854
|
+
writeResearchJson({
|
|
93855
|
+
ok: true,
|
|
93856
|
+
data: data.results.map((item) => row(item, Boolean(args8.full))),
|
|
93857
|
+
source: data.source,
|
|
93858
|
+
fields: ITEM_FIELDS,
|
|
93859
|
+
hints: redditSearchHints({ resultCount: data.results.length, kind, source: data.source })
|
|
93860
|
+
});
|
|
93861
|
+
} catch (err) {
|
|
93862
|
+
handleResearchError(err, redditFix);
|
|
93863
|
+
}
|
|
93864
|
+
}
|
|
93865
|
+
});
|
|
93866
|
+
var threadCommand = defineCommand225({
|
|
93867
|
+
meta: {
|
|
93868
|
+
name: "thread",
|
|
93869
|
+
description: `Read one Reddit thread verbatim \u2014 the post and its best comments, each with its own permalink.
|
|
93870
|
+
|
|
93871
|
+
Run it on every Reddit link before you quote from it, including links a research summary gave you: it refuses a link whose post is somewhere other than the link claims.
|
|
93872
|
+
|
|
93873
|
+
Examples:
|
|
93874
|
+
baker research reddit thread "https://www.reddit.com/r/AmazonFlexUK/comments/1abc2de/agency_still_hasnt_paid_me/"
|
|
93875
|
+
baker research reddit thread "https://www.reddit.com/r/UberEATS/s/3TzXiyxaMD" --comments 100
|
|
93876
|
+
baker research reddit thread 1abc2de --full`
|
|
93877
|
+
},
|
|
93878
|
+
args: {
|
|
93879
|
+
target: { type: "positional", description: "Thread link or post id", required: false },
|
|
93880
|
+
comments: { type: "string", description: "0-200, default 40", required: false },
|
|
93881
|
+
full: { type: "boolean", description: "Whole texts, not previews", required: false }
|
|
93882
|
+
},
|
|
93883
|
+
run: async ({ args: args8 }) => {
|
|
93884
|
+
const target2 = args8.target?.trim();
|
|
93885
|
+
if (!target2) failValidation4("Pass the thread's link or post id: baker research reddit thread <url>");
|
|
93886
|
+
const commentsAsked = args8.comments ? Number(args8.comments) : RESEARCH_REDDIT_THREAD_DEFAULT_COMMENTS;
|
|
93887
|
+
const body = { target: target2, comments: commentsAsked };
|
|
93888
|
+
try {
|
|
93889
|
+
const data = await apiPost("/api/research/reddit/thread", body, {
|
|
93890
|
+
timeoutMs: TIMEOUT_MS2
|
|
93891
|
+
});
|
|
93892
|
+
const full = Boolean(args8.full);
|
|
93893
|
+
writeResearchJson({
|
|
93894
|
+
ok: true,
|
|
93895
|
+
data: { post: row(data.post, full), comments: data.comments.map((item) => row(item, full)) },
|
|
93896
|
+
source: data.source,
|
|
93897
|
+
fields: ITEM_FIELDS,
|
|
93898
|
+
hints: redditThreadHints({
|
|
93899
|
+
commentsShown: data.comments.length,
|
|
93900
|
+
commentsAsked,
|
|
93901
|
+
commentCount: data.post.commentCount,
|
|
93902
|
+
source: data.source
|
|
93903
|
+
})
|
|
93904
|
+
});
|
|
93905
|
+
} catch (err) {
|
|
93906
|
+
handleResearchError(err, redditFix);
|
|
93907
|
+
}
|
|
93908
|
+
}
|
|
93909
|
+
});
|
|
93910
|
+
var subredditsCommand = defineCommand225({
|
|
93911
|
+
meta: {
|
|
93912
|
+
name: "subreddits",
|
|
93913
|
+
description: `Find the subreddits where an audience talks \u2014 name, members, and whether it can be read.
|
|
93914
|
+
|
|
93915
|
+
Run it before \`reddit search\` instead of guessing names: a plausible-sounding subreddit can be dead (r/CouriersUK has one post), while the conversation sits in r/AmazonFlexUK or r/deliveroos.
|
|
93916
|
+
|
|
93917
|
+
Examples:
|
|
93918
|
+
baker research reddit subreddits "amazon flex uk"
|
|
93919
|
+
baker research reddit subreddits "deliveroo" --limit 5`
|
|
93920
|
+
},
|
|
93921
|
+
args: {
|
|
93922
|
+
query: { type: "positional", description: "Audience or topic", required: false },
|
|
93923
|
+
limit: { type: "string", description: "1-25, default 10", required: false }
|
|
93924
|
+
},
|
|
93925
|
+
run: async ({ args: args8 }) => {
|
|
93926
|
+
const query = args8.query?.trim();
|
|
93927
|
+
if (!query) failValidation4('Say what to look for: baker research reddit subreddits "<audience or topic>"');
|
|
93928
|
+
const body = { query };
|
|
93929
|
+
if (args8.limit) body.limit = Number(args8.limit);
|
|
93930
|
+
try {
|
|
93931
|
+
const data = await apiPost("/api/research/reddit/subreddits", body, {
|
|
93932
|
+
timeoutMs: TIMEOUT_MS2
|
|
93933
|
+
});
|
|
93934
|
+
writeResearchJson({
|
|
93935
|
+
ok: true,
|
|
93936
|
+
data: data.results,
|
|
93937
|
+
source: data.source,
|
|
93938
|
+
fields: {
|
|
93939
|
+
name: "Pass this to --subreddit",
|
|
93940
|
+
subscribers: "Members \u2014 size, not activity",
|
|
93941
|
+
access: "public / restricted (readable, members post) / private (unreadable) / null when the source doesn't say",
|
|
93942
|
+
description: "The subreddit's own summary",
|
|
93943
|
+
url: "The subreddit"
|
|
93944
|
+
},
|
|
93945
|
+
hints: redditSubredditsHints({ resultCount: data.results.length })
|
|
93946
|
+
});
|
|
93947
|
+
} catch (err) {
|
|
93948
|
+
handleResearchError(err, redditFix);
|
|
93949
|
+
}
|
|
93950
|
+
}
|
|
93951
|
+
});
|
|
93952
|
+
var redditCommand = defineCommand225({
|
|
93953
|
+
meta: {
|
|
93954
|
+
name: "reddit",
|
|
93955
|
+
description: `Read Reddit verbatim \u2014 the one site no page reader will open for us.
|
|
93956
|
+
|
|
93957
|
+
Commands:
|
|
93958
|
+
subreddits \u2014 Find where the audience talks (start here)
|
|
93959
|
+
search \u2014 Find posts or comments in the audience's own words
|
|
93960
|
+
thread \u2014 Read one thread: the post and its best comments
|
|
93961
|
+
|
|
93962
|
+
Examples:
|
|
93963
|
+
baker research reddit subreddits "amazon flex uk"
|
|
93964
|
+
baker research reddit search "agency never paid" --subreddit couriersofreddit,AmazonFlexUK --kind comments
|
|
93965
|
+
baker research reddit thread "https://www.reddit.com/r/AmazonFlexUK/comments/1abc2de/"
|
|
93966
|
+
Full guide: __tooling__/docs/tools/baker/research.md`
|
|
93967
|
+
},
|
|
93968
|
+
subCommands: { subreddits: subredditsCommand, search: searchCommand3, thread: threadCommand }
|
|
93969
|
+
});
|
|
93970
|
+
|
|
93971
|
+
// src/commands/research/relevant-pages.ts
|
|
93972
|
+
import { defineCommand as defineCommand226 } from "citty";
|
|
93568
93973
|
registerSchema({
|
|
93569
93974
|
command: "research.relevant-pages",
|
|
93570
93975
|
description: "Get the top pages of a competitor domain with organic traffic and ranking data. Shows which pages drive the most traffic. If --location and --language are omitted, defaults to United States (us) and English (en).",
|
|
@@ -93590,7 +93995,7 @@ var FIELDS12 = {
|
|
|
93590
93995
|
keywords: "Total organic keywords the page ranks for",
|
|
93591
93996
|
top_10: "Keywords in positions 1-10"
|
|
93592
93997
|
};
|
|
93593
|
-
var relevantPagesCommand =
|
|
93998
|
+
var relevantPagesCommand = defineCommand226({
|
|
93594
93999
|
meta: {
|
|
93595
94000
|
name: "relevant-pages",
|
|
93596
94001
|
description: `Get the top pages of a competitor domain with traffic data.
|
|
@@ -93637,7 +94042,7 @@ Examples:
|
|
|
93637
94042
|
});
|
|
93638
94043
|
|
|
93639
94044
|
// src/commands/research/web.ts
|
|
93640
|
-
import { defineCommand as
|
|
94045
|
+
import { defineCommand as defineCommand227 } from "citty";
|
|
93641
94046
|
registerSchema({
|
|
93642
94047
|
command: "research.web",
|
|
93643
94048
|
description: "Search the web with AI to answer marketing questions \u2014 competitors, ICP, pricing, pain points, market trends. Three depth levels: medium (quick, default), high (thorough), xhigh (exhaustive deep research).",
|
|
@@ -93655,7 +94060,7 @@ registerSchema({
|
|
|
93655
94060
|
}
|
|
93656
94061
|
}
|
|
93657
94062
|
});
|
|
93658
|
-
var
|
|
94063
|
+
var TIMEOUT_MS3 = {
|
|
93659
94064
|
medium: 18e4,
|
|
93660
94065
|
high: 36e4,
|
|
93661
94066
|
xhigh: 9e5
|
|
@@ -93671,7 +94076,7 @@ async function runDeepResearch(question) {
|
|
|
93671
94076
|
const { interactionId } = await apiPost("/api/research/deep-research/start", { question });
|
|
93672
94077
|
process.stderr.write(`Deep research started (${interactionId}). Polling...
|
|
93673
94078
|
`);
|
|
93674
|
-
const deadline = Date.now() +
|
|
94079
|
+
const deadline = Date.now() + TIMEOUT_MS3.xhigh;
|
|
93675
94080
|
while (Date.now() < deadline) {
|
|
93676
94081
|
await sleep3(1e4);
|
|
93677
94082
|
const poll = await apiGet("/api/research/deep-research/poll", {
|
|
@@ -93688,7 +94093,7 @@ async function runDeepResearch(question) {
|
|
|
93688
94093
|
}
|
|
93689
94094
|
throw new Error("Deep research timed out");
|
|
93690
94095
|
}
|
|
93691
|
-
var webCommand =
|
|
94096
|
+
var webCommand = defineCommand227({
|
|
93692
94097
|
meta: {
|
|
93693
94098
|
name: "web",
|
|
93694
94099
|
description: `Search the web with AI to answer any open-ended marketing question. Uses live internet data via Google Search.
|
|
@@ -93726,7 +94131,7 @@ Examples:
|
|
|
93726
94131
|
process.exit(1);
|
|
93727
94132
|
}
|
|
93728
94133
|
try {
|
|
93729
|
-
const data = depth === "xhigh" ? await runDeepResearch(question) : await apiPost("/api/research/explore", { question, depth }, { timeoutMs:
|
|
94134
|
+
const data = depth === "xhigh" ? await runDeepResearch(question) : await apiPost("/api/research/explore", { question, depth }, { timeoutMs: TIMEOUT_MS3[depth] });
|
|
93730
94135
|
const format = args8.output || "json";
|
|
93731
94136
|
const hints2 = webDepthHints({ answer: data.answer, sourceCount: data.sources.length, depth });
|
|
93732
94137
|
if (format === "md") {
|
|
@@ -93750,7 +94155,7 @@ Examples:
|
|
|
93750
94155
|
});
|
|
93751
94156
|
|
|
93752
94157
|
// src/commands/research/index.ts
|
|
93753
|
-
var researchCommand =
|
|
94158
|
+
var researchCommand = defineCommand228({
|
|
93754
94159
|
meta: {
|
|
93755
94160
|
name: "research",
|
|
93756
94161
|
description: `Competitive intelligence and AI-powered research commands.
|
|
@@ -93758,6 +94163,7 @@ var researchCommand = defineCommand227({
|
|
|
93758
94163
|
Commands:
|
|
93759
94164
|
web \u2014 Search the web with AI (ask any question)
|
|
93760
94165
|
fetch \u2014 Read a page an ordinary web fetch could not
|
|
94166
|
+
reddit \u2014 Read Reddit verbatim: search posts/comments, read a thread
|
|
93761
94167
|
advertisers \u2014 Who's bidding on a keyword?
|
|
93762
94168
|
autocomplete \u2014 Google Autocomplete suggestions
|
|
93763
94169
|
intent \u2014 Google Search intent classification
|
|
@@ -93773,6 +94179,7 @@ Examples:
|
|
|
93773
94179
|
baker research web "Who are the main competitors of HubSpot?"
|
|
93774
94180
|
baker research web "What is the ICP for CRM buyers?" --depth high
|
|
93775
94181
|
baker research fetch "https://competitor.com/pricing"
|
|
94182
|
+
baker research reddit search "agency never paid" --subreddit AmazonFlexUK --kind comments
|
|
93776
94183
|
baker research advertisers "running shoes"
|
|
93777
94184
|
baker research intent "buy running shoes,best running shoes 2026"
|
|
93778
94185
|
baker research keyword-metrics "chatbot whatsapp,crm whatsapp" --location mx --language es
|
|
@@ -93783,6 +94190,7 @@ Full guide: __tooling__/docs/tools/baker/research.md`
|
|
|
93783
94190
|
subCommands: {
|
|
93784
94191
|
web: webCommand,
|
|
93785
94192
|
fetch: fetchCommand,
|
|
94193
|
+
reddit: redditCommand,
|
|
93786
94194
|
advertisers: advertisersCommand,
|
|
93787
94195
|
autocomplete: autocompleteCommand,
|
|
93788
94196
|
countries: countriesCommand,
|
|
@@ -93797,10 +94205,10 @@ Full guide: __tooling__/docs/tools/baker/research.md`
|
|
|
93797
94205
|
});
|
|
93798
94206
|
|
|
93799
94207
|
// src/commands/scheduled-agents/index.ts
|
|
93800
|
-
import { defineCommand as
|
|
94208
|
+
import { defineCommand as defineCommand239 } from "citty";
|
|
93801
94209
|
|
|
93802
94210
|
// src/commands/scheduled-agents/create.ts
|
|
93803
|
-
import { defineCommand as
|
|
94211
|
+
import { defineCommand as defineCommand229 } from "citty";
|
|
93804
94212
|
|
|
93805
94213
|
// src/commands/scheduled-agents/shared.ts
|
|
93806
94214
|
var TEMP_SCHEDULED_AGENT_PREFIX = "temp_agent_";
|
|
@@ -93808,7 +94216,7 @@ var TEMP_ID_PREFIX = "temp_";
|
|
|
93808
94216
|
function writeOk2(data, hints2) {
|
|
93809
94217
|
writeJson({ ok: true, data: data ?? null, ...hints2 && hints2.length > 0 ? { hints: hints2 } : {} });
|
|
93810
94218
|
}
|
|
93811
|
-
function
|
|
94219
|
+
function failValidation5(message) {
|
|
93812
94220
|
writeJson({ ok: false, error: { code: "VALIDATION_ERROR", message } });
|
|
93813
94221
|
process.exit(1);
|
|
93814
94222
|
}
|
|
@@ -93856,7 +94264,7 @@ function isOtherTempId(id) {
|
|
|
93856
94264
|
}
|
|
93857
94265
|
function validateScheduledAgentRef(ref2) {
|
|
93858
94266
|
if (isOtherTempId(ref2)) {
|
|
93859
|
-
|
|
94267
|
+
failValidation5(`Scheduled agent temp IDs must start with ${TEMP_SCHEDULED_AGENT_PREFIX}.`);
|
|
93860
94268
|
}
|
|
93861
94269
|
if (!isTempScheduledAgentId(ref2)) {
|
|
93862
94270
|
validateConvexId(ref2);
|
|
@@ -93903,14 +94311,14 @@ var NARROWING_ARGS = {
|
|
|
93903
94311
|
};
|
|
93904
94312
|
function validateRunAt(runAt) {
|
|
93905
94313
|
if (!runAt.endsWith("Z")) {
|
|
93906
|
-
|
|
94314
|
+
failValidation5("--run-at must be an ISO UTC timestamp ending in Z.");
|
|
93907
94315
|
}
|
|
93908
94316
|
const parsed = Date.parse(runAt);
|
|
93909
94317
|
if (!Number.isFinite(parsed)) {
|
|
93910
|
-
|
|
94318
|
+
failValidation5("--run-at must be a valid ISO timestamp.");
|
|
93911
94319
|
}
|
|
93912
94320
|
if (parsed <= Date.now()) {
|
|
93913
|
-
|
|
94321
|
+
failValidation5(
|
|
93914
94322
|
`--run-at ${runAt} has already passed. A one-off item only runs at a time still to come \u2014 pass a later ISO UTC timestamp, and check which timezone you converted from if you worked it out from a local time.`
|
|
93915
94323
|
);
|
|
93916
94324
|
}
|
|
@@ -93922,7 +94330,7 @@ function buildTriggerBody(args8, options) {
|
|
|
93922
94330
|
const kind = args8.trigger;
|
|
93923
94331
|
const given = [cron, runAt, kind].filter(Boolean).length;
|
|
93924
94332
|
if (given > 1) {
|
|
93925
|
-
|
|
94333
|
+
failValidation5("--cron, --run-at and --trigger are mutually exclusive.");
|
|
93926
94334
|
}
|
|
93927
94335
|
refuseNarrowingWithoutKind(args8, kind, given);
|
|
93928
94336
|
if (runAt) {
|
|
@@ -93939,7 +94347,7 @@ function buildTriggerBody(args8, options) {
|
|
|
93939
94347
|
return { timezone };
|
|
93940
94348
|
}
|
|
93941
94349
|
if (options.required) {
|
|
93942
|
-
|
|
94350
|
+
failValidation5(
|
|
93943
94351
|
`A trigger is required. Pass --cron or --run-at for a time-based one, or --trigger with one of: ${TRIGGER_KIND_IDS.join(", ")}.`
|
|
93944
94352
|
);
|
|
93945
94353
|
}
|
|
@@ -93951,13 +94359,13 @@ function flagText(args8, key) {
|
|
|
93951
94359
|
}
|
|
93952
94360
|
function refuseMisusedKinds(kind, args8) {
|
|
93953
94361
|
if (kind === "time.once") {
|
|
93954
|
-
|
|
94362
|
+
failValidation5("Use --run-at for a one-off, rather than --trigger time.once.");
|
|
93955
94363
|
}
|
|
93956
94364
|
if (kind === "time.cron") {
|
|
93957
|
-
|
|
94365
|
+
failValidation5("Use --cron for a repeating schedule, rather than --trigger time.cron.");
|
|
93958
94366
|
}
|
|
93959
94367
|
if (kind === "experiment.started" && flagText(args8, "experiment")) {
|
|
93960
|
-
|
|
94368
|
+
failValidation5(
|
|
93961
94369
|
'A test that exists has already started, so --experiment never fires on experiment.started. Leave it off and add --when "page = <page>" to watch one page.'
|
|
93962
94370
|
);
|
|
93963
94371
|
}
|
|
@@ -93970,7 +94378,7 @@ function refuseStrayFlags(kind, args8) {
|
|
|
93970
94378
|
if (stray.length === 0) {
|
|
93971
94379
|
return;
|
|
93972
94380
|
}
|
|
93973
|
-
|
|
94381
|
+
failValidation5(
|
|
93974
94382
|
`${stray.map((flag) => `--${flag}`).join(", ")} does not narrow ${kind}. ${allowed.size > 0 ? `It can be pointed with ${[...allowed].map((flag) => `--${flag}`).join(", ")}.` : "It runs every time it happens \u2014 narrow it with --when rules over its payload instead."}`
|
|
93975
94383
|
);
|
|
93976
94384
|
}
|
|
@@ -93984,7 +94392,7 @@ function refuseNarrowingWithoutKind(args8, kind, given) {
|
|
|
93984
94392
|
return;
|
|
93985
94393
|
}
|
|
93986
94394
|
const flags = stray.map((flag) => `--${flag}`).join(", ");
|
|
93987
|
-
|
|
94395
|
+
failValidation5(
|
|
93988
94396
|
given > 0 ? `${flags} does not narrow --cron or --run-at: a time trigger runs every time it comes round. Use --trigger <kind> with ${flags} instead, or leave ${flags} off.` : `${flags} narrows a trigger, and no trigger was given. Pass --trigger <kind> too \u2014 the item's whole trigger, with ${flags} \u2014 or leave ${flags} off.`
|
|
93989
94397
|
);
|
|
93990
94398
|
}
|
|
@@ -93994,7 +94402,7 @@ function configFromFlag(kind, args8) {
|
|
|
93994
94402
|
return {};
|
|
93995
94403
|
}
|
|
93996
94404
|
if (kind !== "app.event") {
|
|
93997
|
-
|
|
94405
|
+
failValidation5("--config is only for --trigger app.event: the settings of what happens in the tool.");
|
|
93998
94406
|
}
|
|
93999
94407
|
let parsed;
|
|
94000
94408
|
try {
|
|
@@ -94003,13 +94411,13 @@ function configFromFlag(kind, args8) {
|
|
|
94003
94411
|
parsed = null;
|
|
94004
94412
|
}
|
|
94005
94413
|
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
94006
|
-
|
|
94414
|
+
failValidation5(`--config must be a JSON object, e.g. '{"labelIds":"INBOX"}'.`);
|
|
94007
94415
|
}
|
|
94008
94416
|
return { configJson: config };
|
|
94009
94417
|
}
|
|
94010
94418
|
function triggerFromFlags(kind, args8) {
|
|
94011
94419
|
if (!isTriggerKind(kind)) {
|
|
94012
|
-
|
|
94420
|
+
failValidation5(`--trigger must be one of: ${TRIGGER_KIND_IDS.join(", ")}.`);
|
|
94013
94421
|
}
|
|
94014
94422
|
refuseMisusedKinds(kind, args8);
|
|
94015
94423
|
refuseStrayFlags(kind, args8);
|
|
@@ -94019,7 +94427,7 @@ function triggerFromFlags(kind, args8) {
|
|
|
94019
94427
|
if (value2) {
|
|
94020
94428
|
narrowings[field] = value2;
|
|
94021
94429
|
} else if (required) {
|
|
94022
|
-
|
|
94430
|
+
failValidation5(
|
|
94023
94431
|
`--trigger ${kind} needs --${TRIGGER_TARGET_FIELDS[field].flag}. \`baker scheduled-agents targets --trigger ${kind} --field ${field}\` lists what it can be.`
|
|
94024
94432
|
);
|
|
94025
94433
|
}
|
|
@@ -94031,12 +94439,12 @@ function parseOccurrences(raw) {
|
|
|
94031
94439
|
return void 0;
|
|
94032
94440
|
}
|
|
94033
94441
|
if (typeof raw !== "string") {
|
|
94034
|
-
|
|
94442
|
+
failValidation5("--occurrences must be a comma-separated list of whole numbers, e.g. 1 or 1,3.");
|
|
94035
94443
|
}
|
|
94036
94444
|
const parts = raw.split(",").map((part) => part.trim()).filter((part) => part.length > 0);
|
|
94037
94445
|
const numbers = parts.map((part) => Number(part));
|
|
94038
94446
|
if (numbers.length === 0 || numbers.some((n) => !Number.isInteger(n) || n <= 0)) {
|
|
94039
|
-
|
|
94447
|
+
failValidation5("--occurrences must be a comma-separated list of whole numbers above zero, e.g. 1 or 1,3.");
|
|
94040
94448
|
}
|
|
94041
94449
|
return numbers;
|
|
94042
94450
|
}
|
|
@@ -94050,7 +94458,7 @@ function parseBooleanFlag(raw, flagName) {
|
|
|
94050
94458
|
if (raw === false || raw === "false") {
|
|
94051
94459
|
return false;
|
|
94052
94460
|
}
|
|
94053
|
-
|
|
94461
|
+
failValidation5(`${flagName} must be true or false.`);
|
|
94054
94462
|
}
|
|
94055
94463
|
var REACTION_ARGS = {
|
|
94056
94464
|
when: {
|
|
@@ -94080,7 +94488,7 @@ function wholeNumber(raw, flag, { min }) {
|
|
|
94080
94488
|
}
|
|
94081
94489
|
const value2 = Number(raw);
|
|
94082
94490
|
if (!Number.isInteger(value2) || value2 < min) {
|
|
94083
|
-
|
|
94491
|
+
failValidation5(`${flag} must be a whole number of at least ${min}.`);
|
|
94084
94492
|
}
|
|
94085
94493
|
return value2;
|
|
94086
94494
|
}
|
|
@@ -94100,7 +94508,7 @@ function occurrencesFromFlags(args8) {
|
|
|
94100
94508
|
const times = wholeNumber(args8.times, "--times", { min: 1 });
|
|
94101
94509
|
const occurrences = parseOccurrences(args8.occurrences);
|
|
94102
94510
|
if (times !== void 0 && occurrences !== void 0) {
|
|
94103
|
-
|
|
94511
|
+
failValidation5("Pass --times or --occurrences, not both \u2014 --times 3 is --occurrences 1,2,3.");
|
|
94104
94512
|
}
|
|
94105
94513
|
return times !== void 0 ? firstTimes(times) : occurrences;
|
|
94106
94514
|
}
|
|
@@ -94114,7 +94522,7 @@ function whenFromFlag(raw) {
|
|
|
94114
94522
|
}
|
|
94115
94523
|
const parsed = parseConditionsExpression(text2);
|
|
94116
94524
|
if (!parsed.ok) {
|
|
94117
|
-
|
|
94525
|
+
failValidation5(parsed.error);
|
|
94118
94526
|
}
|
|
94119
94527
|
return text2;
|
|
94120
94528
|
}
|
|
@@ -94123,7 +94531,7 @@ function payloadFromFlag(raw) {
|
|
|
94123
94531
|
return void 0;
|
|
94124
94532
|
}
|
|
94125
94533
|
if (raw !== "include" && raw !== "omit") {
|
|
94126
|
-
|
|
94534
|
+
failValidation5("--payload must be include or omit.");
|
|
94127
94535
|
}
|
|
94128
94536
|
return raw === "include";
|
|
94129
94537
|
}
|
|
@@ -94167,7 +94575,7 @@ registerSchema({
|
|
|
94167
94575
|
}
|
|
94168
94576
|
}
|
|
94169
94577
|
});
|
|
94170
|
-
var createCommand3 =
|
|
94578
|
+
var createCommand3 = defineCommand229({
|
|
94171
94579
|
meta: {
|
|
94172
94580
|
name: "create",
|
|
94173
94581
|
description: 'Stage a scheduled agent. Examples: baker scheduled-agents create --name "Weekly report" --description "..." --prompt "..." --cron "0 9 * * MON" | baker scheduled-agents create --name "Chase a new lead" --description "..." --prompt "Research {{payload.answers.company}} and draft a reply" --trigger form.submitted --form quote | baker scheduled-agents create --name "Big deals" --description "..." --prompt "..." --trigger webhook.received --when "deal.stage = won AND deal.amount > 10000" --times 3'
|
|
@@ -94195,13 +94603,13 @@ var createCommand3 = defineCommand228({
|
|
|
94195
94603
|
const prompt = args8.prompt;
|
|
94196
94604
|
const template = typeof args8.template === "string" ? args8.template.trim() : "";
|
|
94197
94605
|
if (!name || name.trim().length === 0) {
|
|
94198
|
-
|
|
94606
|
+
failValidation5("--name is required.");
|
|
94199
94607
|
}
|
|
94200
94608
|
if (!description || description.trim().length === 0) {
|
|
94201
|
-
|
|
94609
|
+
failValidation5("--description is required.");
|
|
94202
94610
|
}
|
|
94203
94611
|
if (template.length === 0 && (!prompt || prompt.trim().length === 0)) {
|
|
94204
|
-
|
|
94612
|
+
failValidation5("--prompt is required: it is the whole of what a run does. Or pass --template to copy a brief.");
|
|
94205
94613
|
}
|
|
94206
94614
|
const trigger = buildTriggerBody(args8, { required: true });
|
|
94207
94615
|
const reaction = reactionBody(args8);
|
|
@@ -94224,7 +94632,7 @@ var createCommand3 = defineCommand228({
|
|
|
94224
94632
|
});
|
|
94225
94633
|
|
|
94226
94634
|
// src/commands/scheduled-agents/delete.ts
|
|
94227
|
-
import { defineCommand as
|
|
94635
|
+
import { defineCommand as defineCommand230 } from "citty";
|
|
94228
94636
|
registerSchema({
|
|
94229
94637
|
command: "scheduled-agents.delete",
|
|
94230
94638
|
description: "Stage deletion of a published scheduled agent, or cancel one this chat staged (temp_agent_*).",
|
|
@@ -94232,7 +94640,7 @@ registerSchema({
|
|
|
94232
94640
|
id: { type: "string", description: "Published scheduled agent ID or temp_agent_* draft ID", required: true }
|
|
94233
94641
|
}
|
|
94234
94642
|
});
|
|
94235
|
-
var deleteCommand3 =
|
|
94643
|
+
var deleteCommand3 = defineCommand230({
|
|
94236
94644
|
meta: {
|
|
94237
94645
|
name: "delete",
|
|
94238
94646
|
description: "Stage a scheduled agent deletion. Example: baker scheduled-agents delete <id-or-temp_agent_id>"
|
|
@@ -94249,7 +94657,7 @@ var deleteCommand3 = defineCommand229({
|
|
|
94249
94657
|
try {
|
|
94250
94658
|
const id = args8.id || args8["scheduled-agent-id"];
|
|
94251
94659
|
if (!id) {
|
|
94252
|
-
|
|
94660
|
+
failValidation5("Scheduled agent ID is required.");
|
|
94253
94661
|
}
|
|
94254
94662
|
validateScheduledAgentRef(id);
|
|
94255
94663
|
await apiPost("/api/scheduled-agents/delete", { chatId: requireChatId(), id });
|
|
@@ -94261,7 +94669,7 @@ var deleteCommand3 = defineCommand229({
|
|
|
94261
94669
|
});
|
|
94262
94670
|
|
|
94263
94671
|
// src/commands/scheduled-agents/get.ts
|
|
94264
|
-
import { defineCommand as
|
|
94672
|
+
import { defineCommand as defineCommand231 } from "citty";
|
|
94265
94673
|
registerSchema({
|
|
94266
94674
|
command: "scheduled-agents.get",
|
|
94267
94675
|
description: "Get a published scheduled agent, or one this chat has staged (temp_agent_*). A published one comes with runsAs: who each run acts as ({ name, personalTools, companyTools, notSignedIn?, toolsOff? }) \u2014 the tools a run loads, which decides whether a job like reading someone's inbox can work. Its recentRuns and any draftOps this chat staged on it come with it.",
|
|
@@ -94270,7 +94678,7 @@ registerSchema({
|
|
|
94270
94678
|
chat: { type: "string", description: CHAT_READ_ARG.description, required: false }
|
|
94271
94679
|
}
|
|
94272
94680
|
});
|
|
94273
|
-
var getCommand4 =
|
|
94681
|
+
var getCommand4 = defineCommand231({
|
|
94274
94682
|
meta: {
|
|
94275
94683
|
name: "get",
|
|
94276
94684
|
description: "Get a scheduled agent. Example: baker scheduled-agents get <id-or-temp_agent_id>"
|
|
@@ -94288,13 +94696,13 @@ var getCommand4 = defineCommand230({
|
|
|
94288
94696
|
try {
|
|
94289
94697
|
const id = args8.id || args8["scheduled-agent-id"];
|
|
94290
94698
|
if (!id) {
|
|
94291
|
-
|
|
94699
|
+
failValidation5("Scheduled agent ID is required.");
|
|
94292
94700
|
}
|
|
94293
94701
|
validateScheduledAgentRef(id);
|
|
94294
94702
|
const env = getEnv();
|
|
94295
94703
|
const chatId = typeof args8.chat === "string" && args8.chat.length > 0 ? args8.chat : env.BAKER_CHAT_ID;
|
|
94296
94704
|
if (isTempScheduledAgentId(id) && !chatId) {
|
|
94297
|
-
|
|
94705
|
+
failValidation5("BAKER_CHAT_ID or --chat is required to get a staged scheduled agent ID.");
|
|
94298
94706
|
}
|
|
94299
94707
|
const body = { id };
|
|
94300
94708
|
if (chatId) {
|
|
@@ -94309,7 +94717,7 @@ var getCommand4 = defineCommand230({
|
|
|
94309
94717
|
});
|
|
94310
94718
|
|
|
94311
94719
|
// src/commands/scheduled-agents/list.ts
|
|
94312
|
-
import { defineCommand as
|
|
94720
|
+
import { defineCommand as defineCommand232 } from "citty";
|
|
94313
94721
|
var FULL_DESCRIPTION2 = "Return every item and staged change as stored, briefs included (tens of KB). Prefer `get <id>` for one item.";
|
|
94314
94722
|
registerSchema({
|
|
94315
94723
|
command: "scheduled-agents.list",
|
|
@@ -94319,7 +94727,7 @@ registerSchema({
|
|
|
94319
94727
|
full: { type: "boolean", description: FULL_DESCRIPTION2, required: false }
|
|
94320
94728
|
}
|
|
94321
94729
|
});
|
|
94322
|
-
var listCommand19 =
|
|
94730
|
+
var listCommand19 = defineCommand232({
|
|
94323
94731
|
meta: {
|
|
94324
94732
|
name: "list",
|
|
94325
94733
|
description: "List scheduled agents: id, name, trigger and status for each, plus this chat's staged changes when BAKER_CHAT_ID is set (or --chat <id> for an earlier chat's). Briefs are left out. Start here: baker scheduled-agents list; then baker scheduled-agents get <id> for one item's brief."
|
|
@@ -94345,7 +94753,7 @@ var listCommand19 = defineCommand231({
|
|
|
94345
94753
|
});
|
|
94346
94754
|
|
|
94347
94755
|
// src/commands/scheduled-agents/run.ts
|
|
94348
|
-
import { defineCommand as
|
|
94756
|
+
import { defineCommand as defineCommand233 } from "citty";
|
|
94349
94757
|
registerSchema({
|
|
94350
94758
|
command: "scheduled-agents.run",
|
|
94351
94759
|
description: "Run a published scheduled agent now, whatever its trigger says. Opens a Session immediately and returns its chatId. Does not need BAKER_CHAT_ID, and refuses a temp_agent_* id \u2014 a staged item has to be published before it can run.",
|
|
@@ -94353,7 +94761,7 @@ registerSchema({
|
|
|
94353
94761
|
id: { type: "string", description: "Published scheduled agent ID", required: true }
|
|
94354
94762
|
}
|
|
94355
94763
|
});
|
|
94356
|
-
var runCommand2 =
|
|
94764
|
+
var runCommand2 = defineCommand233({
|
|
94357
94765
|
meta: {
|
|
94358
94766
|
name: "run",
|
|
94359
94767
|
description: "Run a published scheduled agent now. Example: baker scheduled-agents run <id>"
|
|
@@ -94370,10 +94778,10 @@ var runCommand2 = defineCommand232({
|
|
|
94370
94778
|
try {
|
|
94371
94779
|
const id = args8.id || args8["scheduled-agent-id"];
|
|
94372
94780
|
if (!id) {
|
|
94373
|
-
|
|
94781
|
+
failValidation5("Scheduled agent ID is required.");
|
|
94374
94782
|
}
|
|
94375
94783
|
if (isTempScheduledAgentId(id)) {
|
|
94376
|
-
|
|
94784
|
+
failValidation5("run needs a published scheduled agent ID, not temp_agent_*. Publish this chat first.");
|
|
94377
94785
|
}
|
|
94378
94786
|
validateConvexId(id);
|
|
94379
94787
|
const response = await apiPost("/api/scheduled-agents/run", { id });
|
|
@@ -94385,7 +94793,7 @@ var runCommand2 = defineCommand232({
|
|
|
94385
94793
|
});
|
|
94386
94794
|
|
|
94387
94795
|
// src/commands/scheduled-agents/targets.ts
|
|
94388
|
-
import { defineCommand as
|
|
94796
|
+
import { defineCommand as defineCommand234 } from "citty";
|
|
94389
94797
|
var ARGS9 = {
|
|
94390
94798
|
trigger: {
|
|
94391
94799
|
type: "string",
|
|
@@ -94419,7 +94827,7 @@ registerSchema({
|
|
|
94419
94827
|
description: "What a trigger can be pointed at: the open Tasks, the company's scheduled agents, its review sources, followed competitors, pages, Forms and their steps, Knowledge collections, tools. Returns each option's value (pass it to the matching flag on create/update) with its name.",
|
|
94420
94828
|
args: ARGS9
|
|
94421
94829
|
});
|
|
94422
|
-
var targetsCommand =
|
|
94830
|
+
var targetsCommand = defineCommand234({
|
|
94423
94831
|
meta: {
|
|
94424
94832
|
name: "targets",
|
|
94425
94833
|
description: `What a trigger can be pointed at \u2014 so "when a Task is picked up" becomes "when THIS Task is picked up".
|
|
@@ -94440,7 +94848,7 @@ Full guide: __tooling__/docs/tools/baker/scheduled-agents.md`
|
|
|
94440
94848
|
run: async ({ args: args8 }) => {
|
|
94441
94849
|
const trigger = args8.trigger;
|
|
94442
94850
|
if (!TRIGGER_KIND_IDS.some((kind) => kind === trigger)) {
|
|
94443
|
-
|
|
94851
|
+
failValidation5(`--trigger must be one of: ${TRIGGER_KIND_IDS.join(", ")}.`);
|
|
94444
94852
|
}
|
|
94445
94853
|
try {
|
|
94446
94854
|
const response = await apiPost("/api/scheduled-agents/targets", {
|
|
@@ -94462,7 +94870,7 @@ Full guide: __tooling__/docs/tools/baker/scheduled-agents.md`
|
|
|
94462
94870
|
// src/commands/scheduled-agents/templates.ts
|
|
94463
94871
|
import { readFile as readFile42 } from "fs/promises";
|
|
94464
94872
|
import path55 from "path";
|
|
94465
|
-
import { defineCommand as
|
|
94873
|
+
import { defineCommand as defineCommand235 } from "citty";
|
|
94466
94874
|
registerSchema({
|
|
94467
94875
|
command: "scheduled-agents.templates",
|
|
94468
94876
|
description: "The recipes available to this company: the ones Baker ships plus the ones they wrote themselves, each with its id, what it produces and how often it is meant to run. Read this before proposing a company's automation, so every recipe you name is one that exists. Also how a company gets a recipe of its own \u2014 save a brief, prove it runs, then publish it.",
|
|
@@ -94517,7 +94925,7 @@ registerSchema({
|
|
|
94517
94925
|
}
|
|
94518
94926
|
}
|
|
94519
94927
|
});
|
|
94520
|
-
var templatesCommand =
|
|
94928
|
+
var templatesCommand = defineCommand235({
|
|
94521
94929
|
meta: {
|
|
94522
94930
|
name: "templates",
|
|
94523
94931
|
description: `The recipes this company can run \u2014 Baker's own plus theirs, with what each one produces.
|
|
@@ -94564,13 +94972,13 @@ Full guide: __tooling__/docs/tools/baker/scheduled-agents.md`
|
|
|
94564
94972
|
const publish = flag("publish");
|
|
94565
94973
|
const remove = flag("delete");
|
|
94566
94974
|
if ([save, publish, remove].filter((id) => id.length > 0).length > 1) {
|
|
94567
|
-
|
|
94975
|
+
failValidation5("Pass one of --save, --publish or --delete at a time.");
|
|
94568
94976
|
}
|
|
94569
94977
|
if (save.length > 0) {
|
|
94570
94978
|
const briefFile = flag("brief-file");
|
|
94571
94979
|
const brief2 = briefFile.length > 0 ? await readFile42(path55.resolve(briefFile), "utf8") : flag("brief");
|
|
94572
94980
|
if (brief2.trim().length === 0) {
|
|
94573
|
-
|
|
94981
|
+
failValidation5("--brief-file (preferred) or --brief is required: the brief is the recipe.");
|
|
94574
94982
|
}
|
|
94575
94983
|
const response2 = await apiPost("/api/marketplace/save", {
|
|
94576
94984
|
chatId: requireChatId(),
|
|
@@ -94613,7 +95021,7 @@ Full guide: __tooling__/docs/tools/baker/scheduled-agents.md`
|
|
|
94613
95021
|
});
|
|
94614
95022
|
|
|
94615
95023
|
// src/commands/scheduled-agents/triggers.ts
|
|
94616
|
-
import { defineCommand as
|
|
95024
|
+
import { defineCommand as defineCommand236 } from "citty";
|
|
94617
95025
|
var ARGS10 = {
|
|
94618
95026
|
trigger: {
|
|
94619
95027
|
type: "string",
|
|
@@ -94699,7 +95107,7 @@ function shapeTriggerCatalog(response, filter) {
|
|
|
94699
95107
|
}
|
|
94700
95108
|
return { ok: true, data: { groups, reacting: catalog.reacting } };
|
|
94701
95109
|
}
|
|
94702
|
-
var triggersCommand2 =
|
|
95110
|
+
var triggersCommand2 = defineCommand236({
|
|
94703
95111
|
meta: {
|
|
94704
95112
|
name: "triggers",
|
|
94705
95113
|
description: `Everything that can start a scheduled agent.
|
|
@@ -94729,7 +95137,7 @@ Full guide: __tooling__/docs/tools/baker/scheduled-agents.md`
|
|
|
94729
95137
|
});
|
|
94730
95138
|
|
|
94731
95139
|
// src/commands/scheduled-agents/unstage.ts
|
|
94732
|
-
import { defineCommand as
|
|
95140
|
+
import { defineCommand as defineCommand237 } from "citty";
|
|
94733
95141
|
registerSchema({
|
|
94734
95142
|
command: "scheduled-agents.unstage",
|
|
94735
95143
|
description: "Take back what this chat staged on one scheduled agent, before Publish \u2014 the same as discarding its row in the change list. With --index, only that one change (its `index` in `baker scheduled-agents list` \u2192 draft.ops). Taking back a create also takes back the edits staged on it. Never touches a published item: to change or remove one, stage update/delete instead.",
|
|
@@ -94749,13 +95157,13 @@ function unstageBody(args8, chatId) {
|
|
|
94749
95157
|
}
|
|
94750
95158
|
const index = Number(args8.index);
|
|
94751
95159
|
if (args8.index.trim() === "" || !Number.isInteger(index) || index < 0) {
|
|
94752
|
-
|
|
95160
|
+
failValidation5(
|
|
94753
95161
|
"--index must be a whole number from `baker scheduled-agents list` \u2192 draft.ops[].index. Leave it off to take back everything staged on the item."
|
|
94754
95162
|
);
|
|
94755
95163
|
}
|
|
94756
95164
|
return { chatId, ref: args8.ref, index };
|
|
94757
95165
|
}
|
|
94758
|
-
var unstageCommand =
|
|
95166
|
+
var unstageCommand = defineCommand237({
|
|
94759
95167
|
meta: {
|
|
94760
95168
|
name: "unstage",
|
|
94761
95169
|
description: "Take back what this chat staged on a scheduled agent, before Publish. Examples: baker scheduled-agents unstage temp_agent_abc123 | baker scheduled-agents unstage <id> --index 2"
|
|
@@ -94773,7 +95181,7 @@ var unstageCommand = defineCommand236({
|
|
|
94773
95181
|
try {
|
|
94774
95182
|
const ref2 = args8.id || args8["scheduled-agent-id"];
|
|
94775
95183
|
if (!ref2) {
|
|
94776
|
-
|
|
95184
|
+
failValidation5("Scheduled agent ID is required.");
|
|
94777
95185
|
}
|
|
94778
95186
|
const index = typeof args8.index === "string" ? args8.index : void 0;
|
|
94779
95187
|
const body = unstageBody({ ref: ref2, ...index !== void 0 ? { index } : {} }, requireChatId());
|
|
@@ -94786,7 +95194,7 @@ var unstageCommand = defineCommand236({
|
|
|
94786
95194
|
});
|
|
94787
95195
|
|
|
94788
95196
|
// src/commands/scheduled-agents/update.ts
|
|
94789
|
-
import { defineCommand as
|
|
95197
|
+
import { defineCommand as defineCommand238 } from "citty";
|
|
94790
95198
|
registerSchema({
|
|
94791
95199
|
command: "scheduled-agents.update",
|
|
94792
95200
|
description: "Stage an update to a published scheduled agent, or to one this chat staged (temp_agent_*).",
|
|
@@ -94813,7 +95221,7 @@ registerSchema({
|
|
|
94813
95221
|
enabled: { type: "string", description: "Replacement enabled state", required: false, enum: ["true", "false"] }
|
|
94814
95222
|
}
|
|
94815
95223
|
});
|
|
94816
|
-
var updateCommand4 =
|
|
95224
|
+
var updateCommand4 = defineCommand238({
|
|
94817
95225
|
meta: {
|
|
94818
95226
|
name: "update",
|
|
94819
95227
|
description: "Stage a scheduled agent update. Examples: baker scheduled-agents update <id> --enabled false | baker scheduled-agents update <id> --trigger form.submitted --form quote"
|
|
@@ -94843,7 +95251,7 @@ var updateCommand4 = defineCommand237({
|
|
|
94843
95251
|
refuseAutoPublish(args8);
|
|
94844
95252
|
const id = args8.id || args8["scheduled-agent-id"];
|
|
94845
95253
|
if (!id) {
|
|
94846
|
-
|
|
95254
|
+
failValidation5("Scheduled agent ID is required.");
|
|
94847
95255
|
}
|
|
94848
95256
|
validateScheduledAgentRef(id);
|
|
94849
95257
|
const body = { id };
|
|
@@ -94870,7 +95278,7 @@ var updateCommand4 = defineCommand237({
|
|
|
94870
95278
|
hasPatch = true;
|
|
94871
95279
|
}
|
|
94872
95280
|
if (!hasPatch) {
|
|
94873
|
-
|
|
95281
|
+
failValidation5(
|
|
94874
95282
|
"Provide at least one of --name, --description, --prompt, --cron, --run-at, --trigger, --occurrences, --times, --when, --payload, --cooldown, --timezone, --enabled."
|
|
94875
95283
|
);
|
|
94876
95284
|
}
|
|
@@ -94884,7 +95292,7 @@ var updateCommand4 = defineCommand237({
|
|
|
94884
95292
|
});
|
|
94885
95293
|
|
|
94886
95294
|
// src/commands/scheduled-agents/index.ts
|
|
94887
|
-
var scheduledAgentsCommand =
|
|
95295
|
+
var scheduledAgentsCommand = defineCommand239({
|
|
94888
95296
|
meta: {
|
|
94889
95297
|
name: "scheduled-agents",
|
|
94890
95298
|
description: `Manage Scheduled agents. Subcommands: list, get, triggers, targets, create, update, delete, unstage, run, templates.
|
|
@@ -94927,14 +95335,14 @@ Full guide: __tooling__/docs/tools/baker/scheduled-agents.md`
|
|
|
94927
95335
|
});
|
|
94928
95336
|
|
|
94929
95337
|
// src/commands/schema.ts
|
|
94930
|
-
import { defineCommand as
|
|
95338
|
+
import { defineCommand as defineCommand240 } from "citty";
|
|
94931
95339
|
function narrowToFamily(commandName, available) {
|
|
94932
95340
|
const segments = commandName.split(".");
|
|
94933
95341
|
const prefix = segments[0] === "ads" && segments[1] ? `ads.${segments[1]}.` : `${segments[0]}.`;
|
|
94934
95342
|
const siblings = available.filter((name) => name.startsWith(prefix));
|
|
94935
95343
|
return siblings.length > 0 ? siblings : available;
|
|
94936
95344
|
}
|
|
94937
|
-
var schemaCommand2 =
|
|
95345
|
+
var schemaCommand2 = defineCommand240({
|
|
94938
95346
|
meta: {
|
|
94939
95347
|
name: "schema",
|
|
94940
95348
|
description: "Inspect command argument schemas (for AI agent introspection). Lists all commands if no argument given. Example: baker schema images search (or images.search)"
|
|
@@ -94979,10 +95387,10 @@ var schemaCommand2 = defineCommand239({
|
|
|
94979
95387
|
});
|
|
94980
95388
|
|
|
94981
95389
|
// src/commands/studio/index.ts
|
|
94982
|
-
import { defineCommand as
|
|
95390
|
+
import { defineCommand as defineCommand249 } from "citty";
|
|
94983
95391
|
|
|
94984
95392
|
// src/commands/studio/animate.ts
|
|
94985
|
-
import { defineCommand as
|
|
95393
|
+
import { defineCommand as defineCommand241 } from "citty";
|
|
94986
95394
|
|
|
94987
95395
|
// src/commands/studio/spokenPace.ts
|
|
94988
95396
|
var WORDS_PER_SECOND = SPOKEN_WORDS_PER_SECOND;
|
|
@@ -95495,7 +95903,7 @@ async function adByHandHintFor(body) {
|
|
|
95495
95903
|
return null;
|
|
95496
95904
|
}
|
|
95497
95905
|
}
|
|
95498
|
-
var animateCommand =
|
|
95906
|
+
var animateCommand = defineCommand241({
|
|
95499
95907
|
meta: {
|
|
95500
95908
|
name: "animate",
|
|
95501
95909
|
description: "Render a clip. With an image the look is already fixed, so the prompt describes MOVEMENT \u2014 what the camera does, what the subject does, in what order. With --from text there is no image and the prompt is the whole shot.\n\nA rendered clip is NOT usable anywhere until you keep it: `baker studio keep <id> --slot N` is what puts it in the video library. Takes nobody keeps are never ingested, which is what makes a rejected batch cheap.\n\nFor anything longer than 15 seconds, or to build on footage that already exists, use --model bytedance/seedance-2.5: it renders 4-30s and is the only model that reads an existing clip or an existing soundtrack.\n\nExamples:\n baker studio animate 'slow push in, model turns to camera and smiles' --image j57abc123def456ghi789\n baker studio animate 'she looks to camera and says: \"Hola, soy Elena\"' --avatar elena --quality 720p --aspect-ratio 9:16\n baker studio animate 'handheld drift right, steam rising from the cup' --image './out/hero.png' --duration 6 --quality 1080p\n baker studio animate 'product rotates once on a turntable' --image j57abc\u2026,j57def\u2026 --from references\n baker studio animate 'she keeps walking, camera stays with her, then she stops and looks up' --image j57abc\u2026 --from references --from-clip j57batch\u2026:0 --model bytedance/seedance-2.5 --duration 20\n baker studio animate 'hold on the product, then a slow push-in' --from references --from-video j57vid\u2026 --model bytedance/seedance-2.5\n baker studio animate 'slow drone pull-back over a solar farm at golden hour, no people' --from text --model bytedance/seedance-2.5 --duration 12"
|
|
@@ -95620,7 +96028,7 @@ var animateCommand = defineCommand240({
|
|
|
95620
96028
|
});
|
|
95621
96029
|
|
|
95622
96030
|
// src/commands/studio/generate.ts
|
|
95623
|
-
import { defineCommand as
|
|
96031
|
+
import { defineCommand as defineCommand242 } from "citty";
|
|
95624
96032
|
var MODEL_LIST2 = IMAGE_MODEL_IDS;
|
|
95625
96033
|
var DEFAULT_MAX_WAIT_MS2 = 24e4;
|
|
95626
96034
|
registerSchema({
|
|
@@ -95756,7 +96164,7 @@ function buildGenerateBody(args8, prompt) {
|
|
|
95756
96164
|
}
|
|
95757
96165
|
return body;
|
|
95758
96166
|
}
|
|
95759
|
-
var generateCommand =
|
|
96167
|
+
var generateCommand = defineCommand242({
|
|
95760
96168
|
meta: {
|
|
95761
96169
|
name: "generate",
|
|
95762
96170
|
description: "Start here to make an image. Renders 1-8 takes of one brief, ingests each into the media library as it lands, and shows the batch in the dashboard Studio next to the ones the client ran.\n\nModel choice: google/gemini-3.1-flash-image (default \u2014 fast, best at editing a reference and at extreme ratios), google/gemini-3-pro-image (highest fidelity, slower), openai/gpt-image-2 (photoreal and the cleanest in-image text \u2014 no --image-size, no 4:5 / 5:4), recraft/recraft-v4.1-pro-vector (vector/flat marks with palette control).\n\n--reference is the biggest quality lever there is: a real logo, product shot, Pinterest pin or sandbox screenshot beats any amount of adjectives.\n\nExamples:\n baker studio generate 'matte black bottle on wet marble, hard studio light, 35mm' --aspect-ratio 3:2 --count 3\n baker studio generate 'this bottle on a sunlit kitchen counter' --reference './src/brand/product.png,https://\u2026/kitchen.jpg'\n baker studio generate 'founder-style selfie, kitchen background, natural light' --skill ugc-selfie-hook\n baker studio generate 'flat geometric mascot, brand palette' --model recraft/recraft-v4.1-pro-vector --rgb-colors '[[10,10,10],[255,80,0]]'"
|
|
@@ -95839,7 +96247,7 @@ var generateCommand = defineCommand241({
|
|
|
95839
96247
|
});
|
|
95840
96248
|
|
|
95841
96249
|
// src/commands/studio/get.ts
|
|
95842
|
-
import { defineCommand as
|
|
96250
|
+
import { defineCommand as defineCommand243 } from "citty";
|
|
95843
96251
|
registerSchema({
|
|
95844
96252
|
command: "studio.get",
|
|
95845
96253
|
description: "Read one Studio batch: every take, where it lives, and why a take is missing. This is how you pick up a batch that was still rendering when the start command returned.",
|
|
@@ -95848,7 +96256,7 @@ registerSchema({
|
|
|
95848
96256
|
full: { type: "boolean", description: "Include settings, references and attribution", required: false }
|
|
95849
96257
|
}
|
|
95850
96258
|
});
|
|
95851
|
-
var getCommand5 =
|
|
96259
|
+
var getCommand5 = defineCommand243({
|
|
95852
96260
|
meta: {
|
|
95853
96261
|
name: "get",
|
|
95854
96262
|
description: "Read one Studio batch \u2014 the takes, their urls, whether each is in the library, and the reason for any that failed.\n\nExample: baker studio get j57abc123def456ghi789\nExample: baker studio get j57abc123def456ghi789 --full"
|
|
@@ -95877,7 +96285,7 @@ var getCommand5 = defineCommand242({
|
|
|
95877
96285
|
});
|
|
95878
96286
|
|
|
95879
96287
|
// src/commands/studio/improve.ts
|
|
95880
|
-
import { defineCommand as
|
|
96288
|
+
import { defineCommand as defineCommand244 } from "citty";
|
|
95881
96289
|
var DESCRIPTION7 = "Sharpen a rough brief into directed art direction \u2014 the same rewrite the client gets from the wand in the Studio prompt bar. Reach for it when you are relaying the CLIENT's own words and want them shaped without substituting your voice; when you are writing the art direction yourself, just write it, because you will do a better job than this does.";
|
|
95882
96290
|
registerSchema({
|
|
95883
96291
|
command: "studio.improve",
|
|
@@ -95902,7 +96310,7 @@ registerSchema({
|
|
|
95902
96310
|
}
|
|
95903
96311
|
}
|
|
95904
96312
|
});
|
|
95905
|
-
var improveCommand =
|
|
96313
|
+
var improveCommand = defineCommand244({
|
|
95906
96314
|
meta: {
|
|
95907
96315
|
name: "improve",
|
|
95908
96316
|
description: `${DESCRIPTION7}
|
|
@@ -95946,7 +96354,7 @@ Examples:
|
|
|
95946
96354
|
});
|
|
95947
96355
|
|
|
95948
96356
|
// src/commands/studio/keep.ts
|
|
95949
|
-
import { defineCommand as
|
|
96357
|
+
import { defineCommand as defineCommand245 } from "citty";
|
|
95950
96358
|
registerSchema({
|
|
95951
96359
|
command: "studio.keep",
|
|
95952
96360
|
description: "Mark one take as the keeper. For an image this stars it, so the client reviewing the batch sees which one you used. For a CLIP it is the step that puts it in the video library \u2014 until then the clip cannot be used in a canvas, a landing, or an ad.",
|
|
@@ -95956,7 +96364,7 @@ registerSchema({
|
|
|
95956
96364
|
undo: { type: "boolean", description: "Un-star an image, or take a kept clip back out", required: false }
|
|
95957
96365
|
}
|
|
95958
96366
|
});
|
|
95959
|
-
var keepCommand =
|
|
96367
|
+
var keepCommand = defineCommand245({
|
|
95960
96368
|
meta: {
|
|
95961
96369
|
name: "keep",
|
|
95962
96370
|
description: "Mark one take as the keeper. An image gets starred (it was already in the library); a clip gets INGESTED into the video library, which is what makes it usable anywhere else.\n\nExample: baker studio keep j57abc123def456ghi789 --slot 2\nExample: baker studio keep j57abc123def456ghi789 --slot 2 --undo"
|
|
@@ -96007,7 +96415,7 @@ var keepCommand = defineCommand244({
|
|
|
96007
96415
|
});
|
|
96008
96416
|
|
|
96009
96417
|
// src/commands/studio/list.ts
|
|
96010
|
-
import { defineCommand as
|
|
96418
|
+
import { defineCommand as defineCommand246 } from "citty";
|
|
96011
96419
|
registerSchema({
|
|
96012
96420
|
command: "studio.list",
|
|
96013
96421
|
description: "Recent Studio batches for THIS conversation, newest first \u2014 what you have already generated, so you re-use a take instead of paying for it twice. `--all` widens it to everything the company generated, including what people ran themselves in the dashboard.",
|
|
@@ -96018,7 +96426,7 @@ registerSchema({
|
|
|
96018
96426
|
full: { type: "boolean", description: "Include settings, references and attribution", required: false }
|
|
96019
96427
|
}
|
|
96020
96428
|
});
|
|
96021
|
-
var listCommand20 =
|
|
96429
|
+
var listCommand20 = defineCommand246({
|
|
96022
96430
|
meta: {
|
|
96023
96431
|
name: "list",
|
|
96024
96432
|
description: "Recent Studio batches, newest first. Scoped to this conversation unless you pass --all.\n\nExample: baker studio list\nExample: baker studio list --kind video --limit 5\nExample: baker studio list --all # includes batches the client ran in the dashboard"
|
|
@@ -96052,7 +96460,7 @@ var listCommand20 = defineCommand245({
|
|
|
96052
96460
|
});
|
|
96053
96461
|
|
|
96054
96462
|
// src/commands/studio/models.ts
|
|
96055
|
-
import { defineCommand as
|
|
96463
|
+
import { defineCommand as defineCommand247 } from "citty";
|
|
96056
96464
|
var DESCRIPTION8 = "What each Studio model actually accepts: its shapes, resolutions, clip lengths, prompt character cap, how many reference images it takes, and which knobs it has. Read this before a batch you care about \u2014 the models disagree far more than they look like they do, and a setting the chosen model does not have is REFUSED, not ignored.";
|
|
96057
96465
|
registerSchema({
|
|
96058
96466
|
command: "studio.models",
|
|
@@ -96139,7 +96547,7 @@ function buildModelCards(kind, model) {
|
|
|
96139
96547
|
const selected = model ? ids2.filter((id) => id === model) : ids2;
|
|
96140
96548
|
return selected.map((id) => build(id));
|
|
96141
96549
|
}
|
|
96142
|
-
var modelsCommand =
|
|
96550
|
+
var modelsCommand = defineCommand247({
|
|
96143
96551
|
meta: {
|
|
96144
96552
|
name: "models",
|
|
96145
96553
|
description: `${DESCRIPTION8}
|
|
@@ -96186,13 +96594,13 @@ Examples:
|
|
|
96186
96594
|
});
|
|
96187
96595
|
|
|
96188
96596
|
// src/commands/studio/skills.ts
|
|
96189
|
-
import { defineCommand as
|
|
96597
|
+
import { defineCommand as defineCommand248 } from "citty";
|
|
96190
96598
|
registerSchema({
|
|
96191
96599
|
command: "studio.skills",
|
|
96192
96600
|
description: "The craft directions `studio generate --skill <id>` accepts. Each one carries directed art direction plus the model, shape and take count it wants, so you pick a look by name instead of writing the boilerplate yourself.",
|
|
96193
96601
|
args: {}
|
|
96194
96602
|
});
|
|
96195
|
-
var skillsCommand =
|
|
96603
|
+
var skillsCommand = defineCommand248({
|
|
96196
96604
|
meta: {
|
|
96197
96605
|
name: "skills",
|
|
96198
96606
|
description: "List the craft directions available to `baker studio generate --skill <id>` \u2014 what each one is for, whether it wants a reference image, and the model/shape/count it defaults to.\n\nExample: baker studio skills"
|
|
@@ -96216,7 +96624,7 @@ var skillsCommand = defineCommand247({
|
|
|
96216
96624
|
});
|
|
96217
96625
|
|
|
96218
96626
|
// src/commands/studio/index.ts
|
|
96219
|
-
var studioCommand =
|
|
96627
|
+
var studioCommand = defineCommand249({
|
|
96220
96628
|
meta: {
|
|
96221
96629
|
name: "studio",
|
|
96222
96630
|
description: `Make new imagery and clips. Every batch is recorded and shows up in the dashboard Studio for the client to review, labelled with this conversation.
|
|
@@ -96259,14 +96667,14 @@ Full guide: __tooling__/docs/tools/baker/studio.md`
|
|
|
96259
96667
|
});
|
|
96260
96668
|
|
|
96261
96669
|
// src/commands/tag-manager/index.ts
|
|
96262
|
-
import { defineCommand as
|
|
96670
|
+
import { defineCommand as defineCommand253 } from "citty";
|
|
96263
96671
|
|
|
96264
96672
|
// src/commands/tag-manager/draft.ts
|
|
96265
|
-
import { defineCommand as
|
|
96673
|
+
import { defineCommand as defineCommand250 } from "citty";
|
|
96266
96674
|
|
|
96267
96675
|
// src/commands/tag-manager/shared.ts
|
|
96268
96676
|
import { readFileSync as readFileSync23 } from "fs";
|
|
96269
|
-
function
|
|
96677
|
+
function failValidation6(message) {
|
|
96270
96678
|
writeJsonEnvelope({ ok: false, error: { code: "VALIDATION_ERROR", message } });
|
|
96271
96679
|
process.exit(1);
|
|
96272
96680
|
}
|
|
@@ -96274,7 +96682,7 @@ function requireTarget7(args8, entity) {
|
|
|
96274
96682
|
const positional = Array.isArray(args8._) ? args8._[0] : void 0;
|
|
96275
96683
|
const target2 = args8.id ?? args8.target ?? positional;
|
|
96276
96684
|
if (typeof target2 !== "string" || target2.length === 0) {
|
|
96277
|
-
|
|
96685
|
+
failValidation6(`pass the ${entity} id or path as the positional argument`);
|
|
96278
96686
|
}
|
|
96279
96687
|
return target2;
|
|
96280
96688
|
}
|
|
@@ -96283,16 +96691,16 @@ function loadJsonArg2(args8, flag = "json") {
|
|
|
96283
96691
|
const file = args8.file;
|
|
96284
96692
|
const raw = typeof file === "string" && file.length > 0 ? readFileSync23(file, "utf8") : typeof inline === "string" && inline.length > 0 ? inline : void 0;
|
|
96285
96693
|
if (raw === void 0) {
|
|
96286
|
-
|
|
96694
|
+
failValidation6(`pass --${flag} with inline JSON or --file with a path to a JSON file`);
|
|
96287
96695
|
}
|
|
96288
96696
|
try {
|
|
96289
96697
|
const parsed = JSON.parse(raw);
|
|
96290
96698
|
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
96291
|
-
|
|
96699
|
+
failValidation6(`--${flag} must be a JSON object`);
|
|
96292
96700
|
}
|
|
96293
96701
|
return parsed;
|
|
96294
96702
|
} catch {
|
|
96295
|
-
return
|
|
96703
|
+
return failValidation6(`--${flag} is not valid JSON`);
|
|
96296
96704
|
}
|
|
96297
96705
|
}
|
|
96298
96706
|
var RETRYABLE_CODES2 = /* @__PURE__ */ new Set(["RATE_LIMITED", "UNAVAILABLE", "INTERNAL_ERROR", "NETWORK_ERROR", "TIMEOUT"]);
|
|
@@ -96388,13 +96796,13 @@ registerSchema({
|
|
|
96388
96796
|
chat: { type: "string", description: CHAT_READ_ARG.description, required: false }
|
|
96389
96797
|
}
|
|
96390
96798
|
});
|
|
96391
|
-
var draftCommand6 =
|
|
96799
|
+
var draftCommand6 = defineCommand250({
|
|
96392
96800
|
meta: {
|
|
96393
96801
|
name: "draft",
|
|
96394
96802
|
description: "List, show, amend, remove, or clear staged Tag Manager changes for this chat. `list` and `show` take --chat <id> to read an earlier chat's changes instead."
|
|
96395
96803
|
},
|
|
96396
96804
|
subCommands: {
|
|
96397
|
-
list:
|
|
96805
|
+
list: defineCommand250({
|
|
96398
96806
|
meta: {
|
|
96399
96807
|
name: "list",
|
|
96400
96808
|
description: "Review everything staged on this chat (--json for the raw envelope)"
|
|
@@ -96407,7 +96815,7 @@ var draftCommand6 = defineCommand249({
|
|
|
96407
96815
|
await draftList2(args8.json === true, args8.chat);
|
|
96408
96816
|
}
|
|
96409
96817
|
}),
|
|
96410
|
-
show:
|
|
96818
|
+
show: defineCommand250({
|
|
96411
96819
|
meta: {
|
|
96412
96820
|
name: "show",
|
|
96413
96821
|
description: "Print the full staged payload for one change \u2014 the receipt to verify it looks right before publish (never truncated)."
|
|
@@ -96424,7 +96832,7 @@ var draftCommand6 = defineCommand249({
|
|
|
96424
96832
|
);
|
|
96425
96833
|
}
|
|
96426
96834
|
}),
|
|
96427
|
-
amend:
|
|
96835
|
+
amend: defineCommand250({
|
|
96428
96836
|
meta: {
|
|
96429
96837
|
name: "amend",
|
|
96430
96838
|
description: "Update a staged change in place \u2014 merges a JSON patch into its payload (objects deep-merge, null deletes a key, arrays/scalars replace) and re-validates. Use this instead of remove + re-create."
|
|
@@ -96441,7 +96849,7 @@ var draftCommand6 = defineCommand249({
|
|
|
96441
96849
|
});
|
|
96442
96850
|
}
|
|
96443
96851
|
}),
|
|
96444
|
-
remove:
|
|
96852
|
+
remove: defineCommand250({
|
|
96445
96853
|
meta: { name: "remove", description: "Remove one staged change (cascades to anything depending on it)" },
|
|
96446
96854
|
args: { ref: { type: "positional", description: "Staged ref (gtm_temp_*) or target", required: false } },
|
|
96447
96855
|
run: async ({ args: args8 }) => {
|
|
@@ -96450,7 +96858,7 @@ var draftCommand6 = defineCommand249({
|
|
|
96450
96858
|
});
|
|
96451
96859
|
}
|
|
96452
96860
|
}),
|
|
96453
|
-
clear:
|
|
96861
|
+
clear: defineCommand250({
|
|
96454
96862
|
meta: { name: "clear", description: "Discard all Tag Manager changes staged on this chat" },
|
|
96455
96863
|
run: async () => {
|
|
96456
96864
|
await draftAction3("/api/tag-manager/draft/clear", {});
|
|
@@ -96460,7 +96868,7 @@ var draftCommand6 = defineCommand249({
|
|
|
96460
96868
|
});
|
|
96461
96869
|
|
|
96462
96870
|
// src/commands/tag-manager/read.ts
|
|
96463
|
-
import { defineCommand as
|
|
96871
|
+
import { defineCommand as defineCommand251 } from "citty";
|
|
96464
96872
|
registerSchema({
|
|
96465
96873
|
command: "tagManager.containers",
|
|
96466
96874
|
description: "List the Google Tag Manager containers this company selected. There can be several, and Baker may read and change all of them. Start here to confirm which containers you are managing.",
|
|
@@ -96508,7 +96916,7 @@ function containersHints(containers, unavailableContainers = []) {
|
|
|
96508
96916
|
}
|
|
96509
96917
|
return hints2;
|
|
96510
96918
|
}
|
|
96511
|
-
var containersCommand =
|
|
96919
|
+
var containersCommand = defineCommand251({
|
|
96512
96920
|
meta: {
|
|
96513
96921
|
name: "containers",
|
|
96514
96922
|
description: `List Tag Manager containers selected by this company.
|
|
@@ -96525,7 +96933,7 @@ Start here:
|
|
|
96525
96933
|
}
|
|
96526
96934
|
}
|
|
96527
96935
|
});
|
|
96528
|
-
var readCommand =
|
|
96936
|
+
var readCommand = defineCommand251({
|
|
96529
96937
|
meta: {
|
|
96530
96938
|
name: "read",
|
|
96531
96939
|
description: `Read the current contents of the Tag Manager container \u2014 always do this before staging changes.
|
|
@@ -96567,7 +96975,7 @@ Examples:
|
|
|
96567
96975
|
});
|
|
96568
96976
|
|
|
96569
96977
|
// src/commands/tag-manager/write-commands.ts
|
|
96570
|
-
import { defineCommand as
|
|
96978
|
+
import { defineCommand as defineCommand252 } from "citty";
|
|
96571
96979
|
var CONTAINER_ARG_DESCRIPTION = "Numeric container id (optional only when one container is connected \u2014 run `baker tag-manager containers`)";
|
|
96572
96980
|
var ENTITIES = [
|
|
96573
96981
|
{
|
|
@@ -96623,10 +97031,10 @@ for (const { entity, noun, createHint } of ENTITIES) {
|
|
|
96623
97031
|
});
|
|
96624
97032
|
}
|
|
96625
97033
|
function entityCommand(entity, noun, example) {
|
|
96626
|
-
return
|
|
97034
|
+
return defineCommand252({
|
|
96627
97035
|
meta: { name: entity, description: `Stage ${noun} changes on this chat's Tag Manager draft` },
|
|
96628
97036
|
subCommands: {
|
|
96629
|
-
create:
|
|
97037
|
+
create: defineCommand252({
|
|
96630
97038
|
meta: {
|
|
96631
97039
|
name: "create",
|
|
96632
97040
|
description: `Stage a new ${noun}
|
|
@@ -96648,7 +97056,7 @@ Examples:
|
|
|
96648
97056
|
});
|
|
96649
97057
|
}
|
|
96650
97058
|
}),
|
|
96651
|
-
update:
|
|
97059
|
+
update: defineCommand252({
|
|
96652
97060
|
meta: {
|
|
96653
97061
|
name: "update",
|
|
96654
97062
|
description: `Stage an update to an existing ${noun} (pass its id or path)`
|
|
@@ -96668,7 +97076,7 @@ Examples:
|
|
|
96668
97076
|
});
|
|
96669
97077
|
}
|
|
96670
97078
|
}),
|
|
96671
|
-
delete:
|
|
97079
|
+
delete: defineCommand252({
|
|
96672
97080
|
meta: { name: "delete", description: `Stage the deletion of a ${noun} (pass its id or path)` },
|
|
96673
97081
|
args: {
|
|
96674
97082
|
id: { type: "positional", description: `${noun} id or path`, required: false },
|
|
@@ -96709,7 +97117,7 @@ function builtinTypes(args8) {
|
|
|
96709
97117
|
}
|
|
96710
97118
|
return raw.split(",").map((entry) => entry.trim());
|
|
96711
97119
|
}
|
|
96712
|
-
var builtinCommand =
|
|
97120
|
+
var builtinCommand = defineCommand252({
|
|
96713
97121
|
meta: {
|
|
96714
97122
|
name: "builtin",
|
|
96715
97123
|
description: `Enable or disable built-in variables
|
|
@@ -96719,7 +97127,7 @@ Examples:
|
|
|
96719
97127
|
baker tag-manager builtin disable --types formId`
|
|
96720
97128
|
},
|
|
96721
97129
|
subCommands: {
|
|
96722
|
-
enable:
|
|
97130
|
+
enable: defineCommand252({
|
|
96723
97131
|
meta: { name: "enable", description: "Stage enabling built-in variables" },
|
|
96724
97132
|
args: {
|
|
96725
97133
|
types: { type: "string", description: "Comma-separated types", required: false },
|
|
@@ -96733,7 +97141,7 @@ Examples:
|
|
|
96733
97141
|
});
|
|
96734
97142
|
}
|
|
96735
97143
|
}),
|
|
96736
|
-
disable:
|
|
97144
|
+
disable: defineCommand252({
|
|
96737
97145
|
meta: { name: "disable", description: "Stage disabling built-in variables" },
|
|
96738
97146
|
args: {
|
|
96739
97147
|
types: { type: "string", description: "Comma-separated types", required: false },
|
|
@@ -96751,7 +97159,7 @@ Examples:
|
|
|
96751
97159
|
});
|
|
96752
97160
|
|
|
96753
97161
|
// src/commands/tag-manager/index.ts
|
|
96754
|
-
var tagManagerCommand =
|
|
97162
|
+
var tagManagerCommand = defineCommand253({
|
|
96755
97163
|
meta: {
|
|
96756
97164
|
name: "tag-manager",
|
|
96757
97165
|
description: `Read and change what lives inside the client's Google Tag Manager container \u2014 tags, triggers, variables, folders and built-in variables.
|
|
@@ -96788,7 +97196,7 @@ Full guide: __tooling__/docs/tools/baker/tag-manager.md`
|
|
|
96788
97196
|
});
|
|
96789
97197
|
|
|
96790
97198
|
// src/commands/tags/index.ts
|
|
96791
|
-
import { defineCommand as
|
|
97199
|
+
import { defineCommand as defineCommand254 } from "citty";
|
|
96792
97200
|
|
|
96793
97201
|
// src/commands/tags/shared.ts
|
|
96794
97202
|
function failApi3(err) {
|
|
@@ -96868,7 +97276,7 @@ async function listTags(json) {
|
|
|
96868
97276
|
var listArgs12 = {
|
|
96869
97277
|
json: { type: "boolean", description: "Print the raw JSON envelope instead of the readable list" }
|
|
96870
97278
|
};
|
|
96871
|
-
var listCommand21 =
|
|
97279
|
+
var listCommand21 = defineCommand254({
|
|
96872
97280
|
meta: {
|
|
96873
97281
|
name: "list",
|
|
96874
97282
|
description: "Effective tags for this chat (production + staged), with each tag's full readable config (secrets excluded) \u2014 reuse a stored value to pre-fill a change rather than asking the user. Refs printed here are what flow side-effect tagIds should use. Example: baker tags list"
|
|
@@ -96887,7 +97295,7 @@ async function listDraft4(chat) {
|
|
|
96887
97295
|
failApi3(err);
|
|
96888
97296
|
}
|
|
96889
97297
|
}
|
|
96890
|
-
var draftCommand7 =
|
|
97298
|
+
var draftCommand7 = defineCommand254({
|
|
96891
97299
|
meta: {
|
|
96892
97300
|
name: "draft",
|
|
96893
97301
|
description: "Review the tag changes staged in this chat (read-only). Staged changes were approved via request_tag_input and apply when the chat is published; to amend or drop one, propose a follow-up change through the same tool (a delete on a tag_temp_* ref drops the staged create). Takes --chat <id> to read an earlier chat's staged changes instead."
|
|
@@ -96897,7 +97305,7 @@ var draftCommand7 = defineCommand253({
|
|
|
96897
97305
|
await listDraft4(args8.chat);
|
|
96898
97306
|
}
|
|
96899
97307
|
});
|
|
96900
|
-
var tagsCommand4 =
|
|
97308
|
+
var tagsCommand4 = defineCommand254({
|
|
96901
97309
|
meta: {
|
|
96902
97310
|
name: "tags",
|
|
96903
97311
|
description: `Read the client's marketing/analytics tags (Meta pixel, GA4, Google Ads, GTM, Clarity, Hotjar, \u2026) \u2014 production tags plus the changes staged in this chat.
|
|
@@ -96926,10 +97334,10 @@ Full guide: __tooling__/docs/tools/baker/tags.md`
|
|
|
96926
97334
|
});
|
|
96927
97335
|
|
|
96928
97336
|
// src/commands/testimonials/index.ts
|
|
96929
|
-
import { defineCommand as
|
|
97337
|
+
import { defineCommand as defineCommand258 } from "citty";
|
|
96930
97338
|
|
|
96931
97339
|
// src/commands/testimonials/get.ts
|
|
96932
|
-
import { defineCommand as
|
|
97340
|
+
import { defineCommand as defineCommand255 } from "citty";
|
|
96933
97341
|
registerSchema({
|
|
96934
97342
|
command: "testimonials.get",
|
|
96935
97343
|
description: "Get a single testimonial by ID",
|
|
@@ -96937,7 +97345,7 @@ registerSchema({
|
|
|
96937
97345
|
id: { type: "string", description: "Testimonial ID", required: true }
|
|
96938
97346
|
}
|
|
96939
97347
|
});
|
|
96940
|
-
var getCommand6 =
|
|
97348
|
+
var getCommand6 = defineCommand255({
|
|
96941
97349
|
meta: { name: "get", description: "Get a single testimonial by ID. Example: baker testimonials get j571abc123" },
|
|
96942
97350
|
args: {
|
|
96943
97351
|
id: { type: "positional", description: "Testimonial ID", required: false },
|
|
@@ -96974,7 +97382,7 @@ var getCommand6 = defineCommand254({
|
|
|
96974
97382
|
});
|
|
96975
97383
|
|
|
96976
97384
|
// src/commands/testimonials/list.ts
|
|
96977
|
-
import { defineCommand as
|
|
97385
|
+
import { defineCommand as defineCommand256 } from "citty";
|
|
96978
97386
|
|
|
96979
97387
|
// src/commands/testimonials/emptyCorpusHints.ts
|
|
96980
97388
|
function resolveEmptyReason({
|
|
@@ -97131,7 +97539,7 @@ function buildListParams(args8) {
|
|
|
97131
97539
|
}
|
|
97132
97540
|
return params;
|
|
97133
97541
|
}
|
|
97134
|
-
var listCommand22 =
|
|
97542
|
+
var listCommand22 = defineCommand256({
|
|
97135
97543
|
meta: {
|
|
97136
97544
|
name: "list",
|
|
97137
97545
|
description: "List testimonials with optional filters. Example: baker testimonials list --source google --sentiment positive"
|
|
@@ -97191,7 +97599,7 @@ var listCommand22 = defineCommand255({
|
|
|
97191
97599
|
});
|
|
97192
97600
|
|
|
97193
97601
|
// src/commands/testimonials/search.ts
|
|
97194
|
-
import { defineCommand as
|
|
97602
|
+
import { defineCommand as defineCommand257 } from "citty";
|
|
97195
97603
|
var FILTER_FLAGS2 = ["source", "rating-min", "rating-max", "status", "sentiment", "language", "tags"];
|
|
97196
97604
|
function languageBiasHint(results, requestedLanguage) {
|
|
97197
97605
|
if (requestedLanguage) {
|
|
@@ -97270,7 +97678,7 @@ function buildSearchRequest(query, args8) {
|
|
|
97270
97678
|
}
|
|
97271
97679
|
return body;
|
|
97272
97680
|
}
|
|
97273
|
-
var
|
|
97681
|
+
var searchCommand4 = defineCommand257({
|
|
97274
97682
|
meta: {
|
|
97275
97683
|
name: "search",
|
|
97276
97684
|
description: "Semantic search testimonials by text query. Uses hybrid BM25 + vector + reranking. Example: baker testimonials search 'great service' --rating-min 4"
|
|
@@ -97334,7 +97742,7 @@ var searchCommand3 = defineCommand256({
|
|
|
97334
97742
|
var tagsCommand5 = makeTagsCommand("testimonials", "testimonial", "/api/testimonials/tags");
|
|
97335
97743
|
|
|
97336
97744
|
// src/commands/testimonials/index.ts
|
|
97337
|
-
var testimonialsCommand =
|
|
97745
|
+
var testimonialsCommand = defineCommand258({
|
|
97338
97746
|
meta: {
|
|
97339
97747
|
name: "testimonials",
|
|
97340
97748
|
description: `Find and browse testimonials in Baker. Subcommands: search, get, list, tags.
|
|
@@ -97349,17 +97757,17 @@ Full guide: __tooling__/docs/tools/baker/testimonials.md`
|
|
|
97349
97757
|
},
|
|
97350
97758
|
subCommands: {
|
|
97351
97759
|
get: getCommand6,
|
|
97352
|
-
search:
|
|
97760
|
+
search: searchCommand4,
|
|
97353
97761
|
list: listCommand22,
|
|
97354
97762
|
tags: tagsCommand5
|
|
97355
97763
|
}
|
|
97356
97764
|
});
|
|
97357
97765
|
|
|
97358
97766
|
// src/commands/videos/index.ts
|
|
97359
|
-
import { defineCommand as
|
|
97767
|
+
import { defineCommand as defineCommand267 } from "citty";
|
|
97360
97768
|
|
|
97361
97769
|
// src/commands/videos/delete.ts
|
|
97362
|
-
import { defineCommand as
|
|
97770
|
+
import { defineCommand as defineCommand259 } from "citty";
|
|
97363
97771
|
registerSchema({
|
|
97364
97772
|
command: "videos.delete",
|
|
97365
97773
|
description: "Delete a video by ID",
|
|
@@ -97373,7 +97781,7 @@ registerSchema({
|
|
|
97373
97781
|
}
|
|
97374
97782
|
}
|
|
97375
97783
|
});
|
|
97376
|
-
var deleteCommand4 =
|
|
97784
|
+
var deleteCommand4 = defineCommand259({
|
|
97377
97785
|
meta: {
|
|
97378
97786
|
name: "delete",
|
|
97379
97787
|
description: "Delete a video by ID. Use --dry-run to preview. Example: baker videos delete j571abc123 --dry-run"
|
|
@@ -97414,7 +97822,7 @@ var deleteCommand4 = defineCommand258({
|
|
|
97414
97822
|
});
|
|
97415
97823
|
|
|
97416
97824
|
// src/commands/videos/describe.ts
|
|
97417
|
-
import { defineCommand as
|
|
97825
|
+
import { defineCommand as defineCommand260 } from "citty";
|
|
97418
97826
|
var DESCRIPTION_HELP2 = "What the clip actually shows, in the words someone would search for it by. This is what `baker videos search` retrieves on.";
|
|
97419
97827
|
registerSchema({
|
|
97420
97828
|
command: "videos.describe",
|
|
@@ -97431,7 +97839,7 @@ registerSchema({
|
|
|
97431
97839
|
full: { type: "boolean", description: "Return the whole library row, not just what changed", required: false }
|
|
97432
97840
|
}
|
|
97433
97841
|
});
|
|
97434
|
-
var describeCommand2 =
|
|
97842
|
+
var describeCommand2 = defineCommand260({
|
|
97435
97843
|
meta: {
|
|
97436
97844
|
name: "describe",
|
|
97437
97845
|
description: `Correct what the library says a video is. Only the fields you pass change; the rest keep their current values.
|
|
@@ -97493,7 +97901,7 @@ import { constants } from "fs";
|
|
|
97493
97901
|
import { copyFile as copyFile2, mkdir as mkdir17, mkdtemp as mkdtemp3, rm as rm10 } from "fs/promises";
|
|
97494
97902
|
import { tmpdir as tmpdir4 } from "os";
|
|
97495
97903
|
import path56 from "path";
|
|
97496
|
-
import { defineCommand as
|
|
97904
|
+
import { defineCommand as defineCommand261 } from "citty";
|
|
97497
97905
|
|
|
97498
97906
|
// src/commands/videos/videoPlatforms.ts
|
|
97499
97907
|
var PLATFORM_DOMAINS = {
|
|
@@ -97631,7 +98039,7 @@ function positiveInt(value2) {
|
|
|
97631
98039
|
const parsed = typeof value2 === "string" ? Number.parseInt(value2, 10) : Number.NaN;
|
|
97632
98040
|
return Number.isInteger(parsed) && parsed > 0 ? parsed : void 0;
|
|
97633
98041
|
}
|
|
97634
|
-
var fetchCommand2 =
|
|
98042
|
+
var fetchCommand2 = defineCommand261({
|
|
97635
98043
|
meta: {
|
|
97636
98044
|
name: "fetch",
|
|
97637
98045
|
description: "Download a video page to a local .mp4 without adding it to the library. Same downloader as `ingest` (proxy fallback, duration check).\n\nExample: baker videos fetch https://www.youtube.com/watch?v=abc123 --out ./clips"
|
|
@@ -97717,7 +98125,7 @@ async function placeInto(outDir, source, stem) {
|
|
|
97717
98125
|
}
|
|
97718
98126
|
|
|
97719
98127
|
// src/commands/videos/get.ts
|
|
97720
|
-
import { defineCommand as
|
|
98128
|
+
import { defineCommand as defineCommand262 } from "citty";
|
|
97721
98129
|
registerSchema({
|
|
97722
98130
|
command: "videos.get",
|
|
97723
98131
|
description: "Get a single video by ID",
|
|
@@ -97725,7 +98133,7 @@ registerSchema({
|
|
|
97725
98133
|
id: { type: "string", description: "Video ID", required: true }
|
|
97726
98134
|
}
|
|
97727
98135
|
});
|
|
97728
|
-
var getCommand7 =
|
|
98136
|
+
var getCommand7 = defineCommand262({
|
|
97729
98137
|
meta: { name: "get", description: "Get a single video by ID. Example: baker videos get j571abc123" },
|
|
97730
98138
|
args: {
|
|
97731
98139
|
id: { type: "positional", description: "Video ID", required: false },
|
|
@@ -97762,7 +98170,7 @@ var getCommand7 = defineCommand261({
|
|
|
97762
98170
|
});
|
|
97763
98171
|
|
|
97764
98172
|
// src/commands/videos/group.ts
|
|
97765
|
-
import { defineCommand as
|
|
98173
|
+
import { defineCommand as defineCommand263 } from "citty";
|
|
97766
98174
|
registerSchema({
|
|
97767
98175
|
command: "videos.group",
|
|
97768
98176
|
description: "List every clip and image that arrived in the same set as this video (carousel slides, one page)",
|
|
@@ -97771,7 +98179,7 @@ registerSchema({
|
|
|
97771
98179
|
"group-key": { type: "string", description: "The set key directly, when you already have it", required: false }
|
|
97772
98180
|
}
|
|
97773
98181
|
});
|
|
97774
|
-
var groupCommand2 =
|
|
98182
|
+
var groupCommand2 = defineCommand263({
|
|
97775
98183
|
meta: {
|
|
97776
98184
|
name: "group",
|
|
97777
98185
|
description: "List every asset that arrived in the same set as this clip \u2014 the other slides of the Instagram post it came from, stills included. A carousel is authored to be read in order, so a clip pulled out of one is usually missing half its meaning. Example: baker videos group <videoId>"
|
|
@@ -97794,7 +98202,7 @@ var groupCommand2 = defineCommand262({
|
|
|
97794
98202
|
import { mkdtemp as mkdtemp4, rm as rm11, stat as stat12 } from "fs/promises";
|
|
97795
98203
|
import { tmpdir as tmpdir5 } from "os";
|
|
97796
98204
|
import path57 from "path";
|
|
97797
|
-
import { defineCommand as
|
|
98205
|
+
import { defineCommand as defineCommand264 } from "citty";
|
|
97798
98206
|
|
|
97799
98207
|
// src/lib/streamUpload.ts
|
|
97800
98208
|
import { createHash as createHash3 } from "crypto";
|
|
@@ -97847,7 +98255,7 @@ registerSchema({
|
|
|
97847
98255
|
"dry-run": { type: "boolean", description: "Preview the operation without executing", required: false }
|
|
97848
98256
|
}
|
|
97849
98257
|
});
|
|
97850
|
-
var ingestCommand2 =
|
|
98258
|
+
var ingestCommand2 = defineCommand264({
|
|
97851
98259
|
meta: {
|
|
97852
98260
|
name: "ingest",
|
|
97853
98261
|
description: "Add a video to the library from a URL. A direct file URL is handed straight to Baker, which fetches it. A page URL (YouTube, TikTok, Vimeo, Instagram) is downloaded here first, then uploaded \u2014 and a direct URL that Baker cannot fetch falls back to that same path automatically.\n\nExample: baker videos ingest https://www.youtube.com/watch?v=abc123"
|
|
@@ -98109,7 +98517,7 @@ async function uploadToAssetStore(filePath, sizeBytes) {
|
|
|
98109
98517
|
}
|
|
98110
98518
|
|
|
98111
98519
|
// src/commands/videos/search.ts
|
|
98112
|
-
import { defineCommand as
|
|
98520
|
+
import { defineCommand as defineCommand265 } from "citty";
|
|
98113
98521
|
registerSchema({
|
|
98114
98522
|
command: "videos.search",
|
|
98115
98523
|
description: "Search videos by text query. Only returns ready videos.",
|
|
@@ -98119,7 +98527,7 @@ registerSchema({
|
|
|
98119
98527
|
tags: { type: "string", description: "Comma-separated tags to filter by", required: false }
|
|
98120
98528
|
}
|
|
98121
98529
|
});
|
|
98122
|
-
var
|
|
98530
|
+
var searchCommand5 = defineCommand265({
|
|
98123
98531
|
meta: {
|
|
98124
98532
|
name: "search",
|
|
98125
98533
|
description: "Semantic search videos by text query. Uses hybrid BM25 + vector + reranking. Example: baker videos search 'product demo' --tags tutorial"
|
|
@@ -98171,7 +98579,7 @@ var tagsCommand6 = makeTagsCommand("videos", "video", "/api/videos/tags");
|
|
|
98171
98579
|
// src/commands/videos/upload.ts
|
|
98172
98580
|
import { stat as stat13 } from "fs/promises";
|
|
98173
98581
|
import { basename as basename4 } from "path";
|
|
98174
|
-
import { defineCommand as
|
|
98582
|
+
import { defineCommand as defineCommand266 } from "citty";
|
|
98175
98583
|
registerSchema({
|
|
98176
98584
|
command: "videos.upload",
|
|
98177
98585
|
description: "Upload a video to Baker \u2014 a local file (via Mux direct upload) or a remote http(s) URL.",
|
|
@@ -98198,7 +98606,7 @@ registerSchema({
|
|
|
98198
98606
|
function isRemoteUrl3(value2) {
|
|
98199
98607
|
return /^https?:\/\//i.test(value2);
|
|
98200
98608
|
}
|
|
98201
|
-
var uploadCommand2 =
|
|
98609
|
+
var uploadCommand2 = defineCommand266({
|
|
98202
98610
|
meta: {
|
|
98203
98611
|
name: "upload",
|
|
98204
98612
|
description: "Upload a video to Baker \u2014 accepts a local file path OR a remote http(s) URL.\n\nLocal: auto-detects content type and uploads via Mux direct upload.\nRemote: hands off to `videos ingest` (direct fetch, or download-then-upload for a YouTube/TikTok/Vimeo page).\n\nExamples:\n baker videos upload ./demo.mp4\n baker videos upload https://www.youtube.com/watch?v=abc123"
|
|
@@ -98267,7 +98675,7 @@ var uploadCommand2 = defineCommand265({
|
|
|
98267
98675
|
});
|
|
98268
98676
|
|
|
98269
98677
|
// src/commands/videos/index.ts
|
|
98270
|
-
var videosCommand =
|
|
98678
|
+
var videosCommand = defineCommand267({
|
|
98271
98679
|
meta: {
|
|
98272
98680
|
name: "videos",
|
|
98273
98681
|
description: `Find and manage videos in Baker. Subcommands: search, get, upload, ingest, fetch, describe, delete, tags.
|
|
@@ -98287,7 +98695,7 @@ Full guide: __tooling__/docs/tools/baker/videos.md`
|
|
|
98287
98695
|
subCommands: {
|
|
98288
98696
|
get: getCommand7,
|
|
98289
98697
|
group: groupCommand2,
|
|
98290
|
-
search:
|
|
98698
|
+
search: searchCommand5,
|
|
98291
98699
|
upload: uploadCommand2,
|
|
98292
98700
|
ingest: ingestCommand2,
|
|
98293
98701
|
fetch: fetchCommand2,
|
|
@@ -98298,10 +98706,10 @@ Full guide: __tooling__/docs/tools/baker/videos.md`
|
|
|
98298
98706
|
});
|
|
98299
98707
|
|
|
98300
98708
|
// src/commands/winning-ads/index.ts
|
|
98301
|
-
import { defineCommand as
|
|
98709
|
+
import { defineCommand as defineCommand278 } from "citty";
|
|
98302
98710
|
|
|
98303
98711
|
// src/commands/winning-ads/advertisers.ts
|
|
98304
|
-
import { defineCommand as
|
|
98712
|
+
import { defineCommand as defineCommand268 } from "citty";
|
|
98305
98713
|
|
|
98306
98714
|
// src/commands/winning-ads/shared.ts
|
|
98307
98715
|
function splitList4(value2) {
|
|
@@ -98354,7 +98762,7 @@ function advertiserNormalizer(record, full) {
|
|
|
98354
98762
|
last_synced_at: record.last_synced_at ?? null
|
|
98355
98763
|
};
|
|
98356
98764
|
}
|
|
98357
|
-
var advertisersCommand2 =
|
|
98765
|
+
var advertisersCommand2 = defineCommand268({
|
|
98358
98766
|
meta: {
|
|
98359
98767
|
name: "advertisers",
|
|
98360
98768
|
description: 'List corpus advertisers by name or domain. Find your own advertiser for --exclude-advertiser, or a competitor for --advertiser-id / winners. Example: baker winning-ads advertisers "Deel" --output md'
|
|
@@ -98412,7 +98820,7 @@ var advertisersCommand2 = defineCommand267({
|
|
|
98412
98820
|
});
|
|
98413
98821
|
|
|
98414
98822
|
// src/commands/winning-ads/content.ts
|
|
98415
|
-
import { defineCommand as
|
|
98823
|
+
import { defineCommand as defineCommand269 } from "citty";
|
|
98416
98824
|
registerSchema({
|
|
98417
98825
|
command: "winning-ads.content",
|
|
98418
98826
|
description: "Read what's INSIDE one winning ad: the spoken transcript, the on-screen text, and the ad copy. Use this after `search`/`winners`/`feed` return a shortlist \u2014 pass an ad_id to understand a reference before reproducing it. Static and carousel ads return the text drawn into the image with the job each piece does (headline, benefit, cta\u2026), the card it sits on, and their format_pattern. Add --full for how it is built (composition, colours, CTA, markers), how it persuades (awareness, mechanism, pains, desires, proof, objections), what it offers (offer type, price, guarantee, urgency), plus speech, pacing and soundtrack for video.",
|
|
@@ -98425,7 +98833,7 @@ registerSchema({
|
|
|
98425
98833
|
}
|
|
98426
98834
|
}
|
|
98427
98835
|
});
|
|
98428
|
-
var contentCommand =
|
|
98836
|
+
var contentCommand = defineCommand269({
|
|
98429
98837
|
meta: {
|
|
98430
98838
|
name: "content",
|
|
98431
98839
|
description: "Read the transcript + on-screen text + copy of one winning ad; --full adds its build, persuasion and offer. Example: baker winning-ads content adg_123 --platform meta --full --output md"
|
|
@@ -98474,7 +98882,7 @@ var contentCommand = defineCommand268({
|
|
|
98474
98882
|
});
|
|
98475
98883
|
|
|
98476
98884
|
// src/commands/winning-ads/feed.ts
|
|
98477
|
-
import { defineCommand as
|
|
98885
|
+
import { defineCommand as defineCommand270 } from "citty";
|
|
98478
98886
|
function buildFeedParams(input) {
|
|
98479
98887
|
const params = {};
|
|
98480
98888
|
const advertiser = splitList4(input.advertiser);
|
|
@@ -98526,7 +98934,7 @@ registerSchema({
|
|
|
98526
98934
|
format: { type: "string", description: "Comma-separated formats to include (e.g. static,video)", required: false }
|
|
98527
98935
|
}
|
|
98528
98936
|
});
|
|
98529
|
-
var feedCommand2 =
|
|
98937
|
+
var feedCommand2 = defineCommand270({
|
|
98530
98938
|
meta: {
|
|
98531
98939
|
name: "feed",
|
|
98532
98940
|
description: "Winners across every brand you follow (browse, then trim per advertiser). Example: baker winning-ads feed --per-advertiser 5 --output md"
|
|
@@ -98611,7 +99019,7 @@ var feedCommand2 = defineCommand269({
|
|
|
98611
99019
|
});
|
|
98612
99020
|
|
|
98613
99021
|
// src/commands/winning-ads/follow.ts
|
|
98614
|
-
import { defineCommand as
|
|
99022
|
+
import { defineCommand as defineCommand271 } from "citty";
|
|
98615
99023
|
var PLATFORMS = adLibraryPlatformSchema.options;
|
|
98616
99024
|
registerSchema({
|
|
98617
99025
|
command: "winning-ads.follow",
|
|
@@ -98626,7 +99034,7 @@ registerSchema({
|
|
|
98626
99034
|
label: { type: "string", description: "Optional display label (defaults to the resolved name)", required: false }
|
|
98627
99035
|
}
|
|
98628
99036
|
});
|
|
98629
|
-
var followCommand =
|
|
99037
|
+
var followCommand = defineCommand271({
|
|
98630
99038
|
meta: {
|
|
98631
99039
|
name: "follow",
|
|
98632
99040
|
description: 'Follow a brand to track ALL its ads \u2014 every platform and country. --platform is how we read your input, not a limit. A domain tracks every platform we can resolve. Example: baker winning-ads follow "deel.com" --platform meta'
|
|
@@ -98673,7 +99081,7 @@ var followCommand = defineCommand270({
|
|
|
98673
99081
|
});
|
|
98674
99082
|
|
|
98675
99083
|
// src/commands/winning-ads/follow-competitors.ts
|
|
98676
|
-
import { defineCommand as
|
|
99084
|
+
import { defineCommand as defineCommand272 } from "citty";
|
|
98677
99085
|
var PLATFORMS2 = adLibraryPlatformSchema.options;
|
|
98678
99086
|
var BATCH_TIMEOUT_MS = 3e5;
|
|
98679
99087
|
function buildFollowBatchBody(input) {
|
|
@@ -98706,7 +99114,7 @@ registerSchema({
|
|
|
98706
99114
|
}
|
|
98707
99115
|
}
|
|
98708
99116
|
});
|
|
98709
|
-
var followCompetitorsCommand =
|
|
99117
|
+
var followCompetitorsCommand = defineCommand272({
|
|
98710
99118
|
meta: {
|
|
98711
99119
|
name: "follow-competitors",
|
|
98712
99120
|
description: 'Follow many brands at once by domain \u2014 add every competitor in one call. Example: baker winning-ads follow-competitors "deel.com,notion.so,hubspot.com"'
|
|
@@ -98781,7 +99189,7 @@ var followCompetitorsCommand = defineCommand271({
|
|
|
98781
99189
|
});
|
|
98782
99190
|
|
|
98783
99191
|
// src/commands/winning-ads/following.ts
|
|
98784
|
-
import { defineCommand as
|
|
99192
|
+
import { defineCommand as defineCommand273 } from "citty";
|
|
98785
99193
|
registerSchema({
|
|
98786
99194
|
command: "winning-ads.following",
|
|
98787
99195
|
description: "List the brands you follow in your ad-dna library, with each one's status (ready vs still adding) and cached ad counts. A brand still adding has counts that are a lie in progress; one with `discovery_failed` has counts that are short because we couldn't finish looking, which is not the same as it running no ads.",
|
|
@@ -98835,7 +99243,7 @@ function followingNormalizer(record, full) {
|
|
|
98835
99243
|
platforms: Array.isArray(record.platforms) ? record.platforms : []
|
|
98836
99244
|
};
|
|
98837
99245
|
}
|
|
98838
|
-
var followingCommand =
|
|
99246
|
+
var followingCommand = defineCommand273({
|
|
98839
99247
|
meta: {
|
|
98840
99248
|
name: "following",
|
|
98841
99249
|
description: "List brands you follow, with status (ready / adding\u2026) and cached counts. A brand's counts are only final once it is ready. Example: baker winning-ads following --output md"
|
|
@@ -98871,7 +99279,7 @@ var followingCommand = defineCommand272({
|
|
|
98871
99279
|
});
|
|
98872
99280
|
|
|
98873
99281
|
// src/commands/winning-ads/search.ts
|
|
98874
|
-
import { defineCommand as
|
|
99282
|
+
import { defineCommand as defineCommand274 } from "citty";
|
|
98875
99283
|
registerSchema({
|
|
98876
99284
|
command: "winning-ads.search",
|
|
98877
99285
|
description: "Search the ad-dna corpus of scored winning ads, the company's followed brands first. Returns a lean shortlist (advertiser, summary, scores, days_active/active/winner_category as proof, media_url, and visual/container/mechanism when known) to pick a reference to reproduce. Each search is a paid reranker + judge call: one strong search, at most one adjusted retry. A value outside a closed list (--platform, --format, --winner-category, --awareness, --format-pattern, --sort) is refused with error.fix.validValues.",
|
|
@@ -99149,7 +99557,7 @@ function writeSearchResult(data, opts) {
|
|
|
99149
99557
|
writeOutput({ ok: true, data: rawResults, ...withHints2 }, opts.output, opts.fields, opts.full, winningAdNormalizer);
|
|
99150
99558
|
}
|
|
99151
99559
|
var SEARCH_TIMEOUT_MS = 12e4;
|
|
99152
|
-
var
|
|
99560
|
+
var searchCommand6 = defineCommand274({
|
|
99153
99561
|
meta: {
|
|
99154
99562
|
name: "search",
|
|
99155
99563
|
description: "Search winning reference ads, the company's followed brands first. Start here: baker winning-ads search 'B2B SaaS before/after AI automation' --platform meta --format static --exclude-advertiser adv_123 --output md. A set of references for N concepts in one call: add --diverse --limit N. The best proven ad in one container from any category: --format-pattern comparison_table (no query needed; add --sort longest_running to order by staying power). Each search is a paid reranker + judge call: one strong search, at most one adjusted retry."
|
|
@@ -99275,7 +99683,7 @@ var searchCommand5 = defineCommand273({
|
|
|
99275
99683
|
});
|
|
99276
99684
|
|
|
99277
99685
|
// src/commands/winning-ads/seeds.ts
|
|
99278
|
-
import { defineCommand as
|
|
99686
|
+
import { defineCommand as defineCommand275 } from "citty";
|
|
99279
99687
|
function leanRow(r) {
|
|
99280
99688
|
return {
|
|
99281
99689
|
key: r.key,
|
|
@@ -99303,7 +99711,7 @@ function makeSeedCommand(opts) {
|
|
|
99303
99711
|
limit: { type: "number", description: "Max keys 1-100 (default 20)", required: false, default: 20 }
|
|
99304
99712
|
}
|
|
99305
99713
|
});
|
|
99306
|
-
return
|
|
99714
|
+
return defineCommand275({
|
|
99307
99715
|
meta: { name: opts.name, description: opts.description },
|
|
99308
99716
|
args: {
|
|
99309
99717
|
platform: { type: "string", description: "Single platform to segment on", required: false },
|
|
@@ -99352,7 +99760,7 @@ var formatsCommand = makeSeedCommand({
|
|
|
99352
99760
|
});
|
|
99353
99761
|
|
|
99354
99762
|
// src/commands/winning-ads/unfollow.ts
|
|
99355
|
-
import { defineCommand as
|
|
99763
|
+
import { defineCommand as defineCommand276 } from "citty";
|
|
99356
99764
|
registerSchema({
|
|
99357
99765
|
command: "winning-ads.unfollow",
|
|
99358
99766
|
description: "Stop following a brand \u2014 removes it from your ad-dna library by advertiser id.",
|
|
@@ -99360,7 +99768,7 @@ registerSchema({
|
|
|
99360
99768
|
advertiser: { type: "string", description: "Advertiser id to unfollow", required: true }
|
|
99361
99769
|
}
|
|
99362
99770
|
});
|
|
99363
|
-
var unfollowCommand =
|
|
99771
|
+
var unfollowCommand = defineCommand276({
|
|
99364
99772
|
meta: {
|
|
99365
99773
|
name: "unfollow",
|
|
99366
99774
|
description: "Stop following a brand by advertiser id. Example: baker winning-ads unfollow adv_123"
|
|
@@ -99381,7 +99789,7 @@ var unfollowCommand = defineCommand275({
|
|
|
99381
99789
|
});
|
|
99382
99790
|
|
|
99383
99791
|
// src/commands/winning-ads/winners.ts
|
|
99384
|
-
import { defineCommand as
|
|
99792
|
+
import { defineCommand as defineCommand277 } from "citty";
|
|
99385
99793
|
registerSchema({
|
|
99386
99794
|
command: "winning-ads.winners",
|
|
99387
99795
|
description: "Top winning ads for one advertiser id (from `advertisers` or `following`). Returns lean winner cards; add --full for DNA + longevity.",
|
|
@@ -99391,7 +99799,7 @@ registerSchema({
|
|
|
99391
99799
|
platform: { type: "string", description: "Filter to a single platform: meta|linkedin|tiktok", required: false }
|
|
99392
99800
|
}
|
|
99393
99801
|
});
|
|
99394
|
-
var winnersCommand =
|
|
99802
|
+
var winnersCommand = defineCommand277({
|
|
99395
99803
|
meta: {
|
|
99396
99804
|
name: "winners",
|
|
99397
99805
|
description: "Top winning ads for a specific advertiser id. Example: baker winning-ads winners adv_123 --top 15 --output md"
|
|
@@ -99441,7 +99849,7 @@ var winnersCommand = defineCommand276({
|
|
|
99441
99849
|
});
|
|
99442
99850
|
|
|
99443
99851
|
// src/commands/winning-ads/index.ts
|
|
99444
|
-
var winningAdsCommand =
|
|
99852
|
+
var winningAdsCommand = defineCommand278({
|
|
99445
99853
|
meta: {
|
|
99446
99854
|
name: "winning-ads",
|
|
99447
99855
|
description: `Search the ad-dna corpus of scored "winning" ads for reference creatives to reproduce, and manage the brands your library tracks. Proxied through the Baker backend (BAKER_API_KEY) \u2014 no separate token needed.
|
|
@@ -99477,7 +99885,7 @@ Examples:
|
|
|
99477
99885
|
Full guide: __tooling__/docs/tools/baker/winning-ads.md`
|
|
99478
99886
|
},
|
|
99479
99887
|
subCommands: {
|
|
99480
|
-
search:
|
|
99888
|
+
search: searchCommand6,
|
|
99481
99889
|
advertisers: advertisersCommand2,
|
|
99482
99890
|
follow: followCommand,
|
|
99483
99891
|
"follow-competitors": followCompetitorsCommand,
|
|
@@ -99509,7 +99917,7 @@ function getCliVersion() {
|
|
|
99509
99917
|
}
|
|
99510
99918
|
|
|
99511
99919
|
// src/cli.ts
|
|
99512
|
-
var main =
|
|
99920
|
+
var main = defineCommand279({
|
|
99513
99921
|
meta: {
|
|
99514
99922
|
name: "baker",
|
|
99515
99923
|
version: getCliVersion(),
|