@koda-sl/baker-cli 0.373.0-dev.5cf4f1409 → 0.374.0-dev.c6366e1f6
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 +19 -1
- package/dist/cli.js +1392 -477
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -133,7 +133,7 @@ import {
|
|
|
133
133
|
} from "./chunk-7YNOGIWQ.js";
|
|
134
134
|
|
|
135
135
|
// src/cli.ts
|
|
136
|
-
import { defineCommand as
|
|
136
|
+
import { defineCommand as defineCommand250, runMain } from "citty";
|
|
137
137
|
|
|
138
138
|
// src/cache-flag.ts
|
|
139
139
|
var NO_CACHE_ARG = {
|
|
@@ -161,8 +161,8 @@ function cacheFlagForParser(argv) {
|
|
|
161
161
|
}
|
|
162
162
|
function rewriteBeforeDoubleDash(argv, rewrite) {
|
|
163
163
|
const end = argv.indexOf("--");
|
|
164
|
-
const
|
|
165
|
-
return argv.flatMap((token, index) => index >=
|
|
164
|
+
const limit2 = end === -1 ? argv.length : end;
|
|
165
|
+
return argv.flatMap((token, index) => index >= limit2 ? [token] : rewrite(token));
|
|
166
166
|
}
|
|
167
167
|
|
|
168
168
|
// src/commands/actions/index.ts
|
|
@@ -1492,7 +1492,7 @@ var audienceCreateSchema = z3.object({
|
|
|
1492
1492
|
}
|
|
1493
1493
|
});
|
|
1494
1494
|
var ENGAGEMENT_LOOKBACK_DAYS = [7, 14, 28, 30, 60, 90, 180, 365];
|
|
1495
|
-
var WEBSITE_LOOKBACK_DAYS = ENGAGEMENT_LOOKBACK_DAYS.filter((
|
|
1495
|
+
var WEBSITE_LOOKBACK_DAYS = ENGAGEMENT_LOOKBACK_DAYS.filter((days2) => days2 >= 14 && days2 <= 180);
|
|
1496
1496
|
var EVENT_LOOKBACK_DAYS = [730];
|
|
1497
1497
|
var WEBSITE_SOURCE_PLATFORM = "LINKEDIN_WEBSITE_RETARGETING";
|
|
1498
1498
|
var TRIGGER_SOURCE_TYPE_BY_PLATFORM = { [WEBSITE_SOURCE_PLATFORM]: "WEBSITE" };
|
|
@@ -1578,9 +1578,9 @@ function retargetingConfigProblems(raw) {
|
|
|
1578
1578
|
const expectedTriggerSource = TRIGGER_SOURCE_TYPE_BY_PLATFORM[sourcePlatform] ?? sourcePlatform;
|
|
1579
1579
|
for (const rule of rules.data) {
|
|
1580
1580
|
problems.push(...ruleProblems(rule, sourcePlatform ? expectedTriggerSource : void 0));
|
|
1581
|
-
const
|
|
1582
|
-
if (!allowed.includes(
|
|
1583
|
-
problems.push(`lookback ${
|
|
1581
|
+
const days2 = rule.timeFrame.timeSpan.duration;
|
|
1582
|
+
if (!allowed.includes(days2)) {
|
|
1583
|
+
problems.push(`lookback ${days2} days is not offered for ${sourcePlatform} \u2014 use ${allowed.join(", ")}`);
|
|
1584
1584
|
}
|
|
1585
1585
|
}
|
|
1586
1586
|
const shapes = new Set(rules.data.map((rule) => `${rule.engagementTrigger}|${rule.timeFrame.timeSpan.duration}`));
|
|
@@ -7143,12 +7143,12 @@ function parseClause(clause) {
|
|
|
7143
7143
|
return { error: `"${clause}" has no operator this understands. ${EXPRESSION_HELP}` };
|
|
7144
7144
|
}
|
|
7145
7145
|
function parseConditionsExpression(expression) {
|
|
7146
|
-
const
|
|
7147
|
-
if ("error" in
|
|
7148
|
-
return { ok: false, error:
|
|
7146
|
+
const split2 = splitClauses(expression);
|
|
7147
|
+
if ("error" in split2) {
|
|
7148
|
+
return { ok: false, error: split2.error };
|
|
7149
7149
|
}
|
|
7150
7150
|
const rules = [];
|
|
7151
|
-
for (const clause of
|
|
7151
|
+
for (const clause of split2.clauses) {
|
|
7152
7152
|
if (clause.length === 0) {
|
|
7153
7153
|
return { ok: false, error: `An empty rule. ${EXPRESSION_HELP}` };
|
|
7154
7154
|
}
|
|
@@ -7161,7 +7161,7 @@ function parseConditionsExpression(expression) {
|
|
|
7161
7161
|
if (rules.length > MAX_CONDITION_RULES) {
|
|
7162
7162
|
return { ok: false, error: `At most ${MAX_CONDITION_RULES} rules.` };
|
|
7163
7163
|
}
|
|
7164
|
-
return { ok: true, conditions: { match:
|
|
7164
|
+
return { ok: true, conditions: { match: split2.match, rules } };
|
|
7165
7165
|
}
|
|
7166
7166
|
|
|
7167
7167
|
// ../api/src/scheduledAgents.ts
|
|
@@ -8618,12 +8618,12 @@ function advisoryHint(name, description) {
|
|
|
8618
8618
|
return executable ? { kind: "executable", ...executable } : null;
|
|
8619
8619
|
}
|
|
8620
8620
|
var ACTIONS_LIST_DEFAULT_LIMIT = 500;
|
|
8621
|
-
function buildListHints({ returned, limit }) {
|
|
8622
|
-
if (returned <
|
|
8621
|
+
function buildListHints({ returned, limit: limit2 }) {
|
|
8622
|
+
if (returned < limit2) {
|
|
8623
8623
|
return [];
|
|
8624
8624
|
}
|
|
8625
8625
|
return [
|
|
8626
|
-
`This list is capped at ${
|
|
8626
|
+
`This list is capped at ${limit2} Tasks and came back full, so older Tasks exist that are NOT shown. Do not treat it as the whole backlog \u2014 re-run with --limit ${limit2 * 2} (or narrow with --status / --search).`
|
|
8627
8627
|
];
|
|
8628
8628
|
}
|
|
8629
8629
|
|
|
@@ -9402,9 +9402,9 @@ function resolveWindow(args5) {
|
|
|
9402
9402
|
const todayStart = startOfLocalDay(new Date(now));
|
|
9403
9403
|
return { fromMs: todayStart - DAY_MS, toMs: todayStart };
|
|
9404
9404
|
}
|
|
9405
|
-
const
|
|
9406
|
-
if (
|
|
9407
|
-
return { fromMs: now - Number(
|
|
9405
|
+
const days2 = /^(\d+)d$/.exec(since);
|
|
9406
|
+
if (days2) {
|
|
9407
|
+
return { fromMs: now - Number(days2[1]) * DAY_MS, toMs: now };
|
|
9408
9408
|
}
|
|
9409
9409
|
return failValidation(`--since must be one of: today | yesterday | <N>d (e.g. 7d). Got: ${since}`);
|
|
9410
9410
|
}
|
|
@@ -9802,15 +9802,15 @@ import { defineCommand as defineCommand19 } from "citty";
|
|
|
9802
9802
|
function listed(resources) {
|
|
9803
9803
|
return resources.map((resource) => resource.label ? `${resource.id} (${resource.label})` : resource.id).join("; ");
|
|
9804
9804
|
}
|
|
9805
|
-
function connectedResourceHints({ plural, singular, flag:
|
|
9805
|
+
function connectedResourceHints({ plural, singular, flag: flag3, resources }) {
|
|
9806
9806
|
if (resources.length === 0) {
|
|
9807
9807
|
return [];
|
|
9808
9808
|
}
|
|
9809
9809
|
if (resources.length === 1) {
|
|
9810
|
-
return [`This company connected one ${singular}: ${listed(resources)}. Commands use it unless you pass ${
|
|
9810
|
+
return [`This company connected one ${singular}: ${listed(resources)}. Commands use it unless you pass ${flag3}.`];
|
|
9811
9811
|
}
|
|
9812
9812
|
return [
|
|
9813
|
-
`This company connected ${resources.length} ${plural}: ${listed(resources)}. Each command works on one, so pass ${
|
|
9813
|
+
`This company connected ${resources.length} ${plural}: ${listed(resources)}. Each command works on one, so pass ${flag3} \u2014 nothing is picked for you. A question about the whole business covers all ${resources.length}: answering from one and reporting it as the total is wrong.`
|
|
9814
9814
|
];
|
|
9815
9815
|
}
|
|
9816
9816
|
|
|
@@ -10033,8 +10033,8 @@ var CHANGE_SCOPE_MAX_DAYS = {
|
|
|
10033
10033
|
};
|
|
10034
10034
|
var DEFAULT_CHANGE_DAYS = 7;
|
|
10035
10035
|
var MAX_CHANGE_DAYS = CHANGE_SCOPE_MAX_DAYS.summary;
|
|
10036
|
-
function isChangeLookbackWithinReach(
|
|
10037
|
-
return
|
|
10036
|
+
function isChangeLookbackWithinReach(days2, scope) {
|
|
10037
|
+
return days2 <= CHANGE_SCOPE_MAX_DAYS[scope];
|
|
10038
10038
|
}
|
|
10039
10039
|
|
|
10040
10040
|
// ../api/src/ads-google/geo-constants.ts
|
|
@@ -10467,8 +10467,8 @@ function equivalentBetween(literal, now) {
|
|
|
10467
10467
|
const unit = rolling[2];
|
|
10468
10468
|
if (n < 1) return null;
|
|
10469
10469
|
if (unit === "DAYS" || unit === "WEEKS") {
|
|
10470
|
-
const
|
|
10471
|
-
return range(utcDate(year, today.getUTCMonth(), today.getUTCDate() -
|
|
10470
|
+
const days2 = unit === "DAYS" ? n : n * 7;
|
|
10471
|
+
return range(utcDate(year, today.getUTCMonth(), today.getUTCDate() - days2), yesterday);
|
|
10472
10472
|
}
|
|
10473
10473
|
const months = unit === "MONTHS" ? n : n * 12;
|
|
10474
10474
|
return range(monthsBefore(today, months), yesterday);
|
|
@@ -10514,13 +10514,13 @@ function resolveChangesWindow(input) {
|
|
|
10514
10514
|
}
|
|
10515
10515
|
};
|
|
10516
10516
|
}
|
|
10517
|
-
const
|
|
10518
|
-
if (!Number.isInteger(
|
|
10517
|
+
const days2 = input.days ?? DEFAULT_CHANGE_DAYS;
|
|
10518
|
+
if (!Number.isInteger(days2) || days2 < 1) {
|
|
10519
10519
|
return {
|
|
10520
10520
|
ok: false,
|
|
10521
10521
|
error: {
|
|
10522
10522
|
code: "INVALID_LOOKBACK",
|
|
10523
|
-
message: `--days must be a whole number of days, got '${
|
|
10523
|
+
message: `--days must be a whole number of days, got '${days2}'.`,
|
|
10524
10524
|
fix: {
|
|
10525
10525
|
action: "retry_with_flag",
|
|
10526
10526
|
explanation: `Pass --days as a positive integer, e.g. --days ${DEFAULT_CHANGE_DAYS}.`
|
|
@@ -10528,32 +10528,32 @@ function resolveChangesWindow(input) {
|
|
|
10528
10528
|
}
|
|
10529
10529
|
};
|
|
10530
10530
|
}
|
|
10531
|
-
if (!isChangeLookbackWithinReach(
|
|
10532
|
-
return { ok: false, error: lookbackTooLong(scope,
|
|
10531
|
+
if (!isChangeLookbackWithinReach(days2, scope)) {
|
|
10532
|
+
return { ok: false, error: lookbackTooLong(scope, days2, CHANGE_SCOPE_MAX_DAYS[scope]) };
|
|
10533
10533
|
}
|
|
10534
|
-
return { ok: true, scope, days, hints: hintsFor(scope,
|
|
10534
|
+
return { ok: true, scope, days: days2, hints: hintsFor(scope, days2) };
|
|
10535
10535
|
}
|
|
10536
|
-
function lookbackTooLong(scope,
|
|
10537
|
-
if (scope === "detail" &&
|
|
10536
|
+
function lookbackTooLong(scope, days2, max) {
|
|
10537
|
+
if (scope === "detail" && days2 <= CHANGE_SCOPE_MAX_DAYS.summary) {
|
|
10538
10538
|
return {
|
|
10539
10539
|
code: "LOOKBACK_TOO_LONG",
|
|
10540
|
-
message: `Google keeps field-level change detail for ${max} days only; ${
|
|
10540
|
+
message: `Google keeps field-level change detail for ${max} days only; ${days2} days is outside that window.`,
|
|
10541
10541
|
fix: {
|
|
10542
10542
|
action: "narrow_date_range",
|
|
10543
|
-
explanation: `Re-run with --scope summary --days ${
|
|
10543
|
+
explanation: `Re-run with --scope summary --days ${days2} to reach back ${CHANGE_SCOPE_MAX_DAYS.summary} days. Summary tells you which campaigns, ad groups, ads and criteria changed and when, but not the old and new values \u2014 read the resource itself for current values.`
|
|
10544
10544
|
}
|
|
10545
10545
|
};
|
|
10546
10546
|
}
|
|
10547
10547
|
return {
|
|
10548
10548
|
code: "LOOKBACK_TOO_LONG",
|
|
10549
|
-
message: `Google's account history API reaches back ${CHANGE_SCOPE_MAX_DAYS.summary} days at most; ${
|
|
10549
|
+
message: `Google's account history API reaches back ${CHANGE_SCOPE_MAX_DAYS.summary} days at most; ${days2} days is outside that window.`,
|
|
10550
10550
|
fix: {
|
|
10551
10551
|
action: "use_different_resource",
|
|
10552
10552
|
explanation: `Do not abort \u2014 infer the change from performance instead. Query monthly metrics per campaign (${monthlySpendTrendQuery()}) to see which campaigns stopped or started spending. That shows what changed and when, though not who changed it. Older history exists only in the Google Ads web UI, which has no API \u2014 ask the user to check it if the 'who' matters.`
|
|
10553
10553
|
}
|
|
10554
10554
|
};
|
|
10555
10555
|
}
|
|
10556
|
-
function hintsFor(scope,
|
|
10556
|
+
function hintsFor(scope, days2) {
|
|
10557
10557
|
const hints2 = [];
|
|
10558
10558
|
if (scope === "detail") {
|
|
10559
10559
|
hints2.push(
|
|
@@ -10563,7 +10563,7 @@ function hintsFor(scope, days) {
|
|
|
10563
10563
|
hints2.push(
|
|
10564
10564
|
"Summary history reports which resources changed and when, not old and new values. To see what a changed resource looks like now, query it by resource_name."
|
|
10565
10565
|
);
|
|
10566
|
-
if (
|
|
10566
|
+
if (days2 > CHANGE_SCOPE_MAX_DAYS.detail) {
|
|
10567
10567
|
hints2.push(
|
|
10568
10568
|
`Changes inside the last ${CHANGE_SCOPE_MAX_DAYS.detail} days also have field-level detail \u2014 re-run with --scope detail for those.`
|
|
10569
10569
|
);
|
|
@@ -11009,11 +11009,11 @@ var CORRECTION_RULES = [
|
|
|
11009
11009
|
{
|
|
11010
11010
|
matchApiError: /changeEventError\W{0,4}START_DATE_TOO_OLD/i,
|
|
11011
11011
|
fix: (ctx) => {
|
|
11012
|
-
const
|
|
11012
|
+
const limit2 = ctx.originalQuery.match(/LIMIT\s+(\d+)/i)?.[1] ?? "50";
|
|
11013
11013
|
const requested = ctx.originalQuery.match(/change_date_time\s*>=\s*'([^']+)'/i)?.[1]?.slice(0, 10);
|
|
11014
11014
|
const oldestStart = new Date(Date.now() - 89 * 24 * 60 * 60 * 1e3).toISOString().slice(0, 10);
|
|
11015
11015
|
const start = requested && requested > oldestStart ? requested : oldestStart;
|
|
11016
|
-
const corrected = `SELECT change_status.last_change_date_time, change_status.resource_type, change_status.resource_status, change_status.resource_name, change_status.campaign, change_status.ad_group FROM change_status WHERE change_status.last_change_date_time >= '${start}' ORDER BY change_status.last_change_date_time DESC LIMIT ${
|
|
11016
|
+
const corrected = `SELECT change_status.last_change_date_time, change_status.resource_type, change_status.resource_status, change_status.resource_name, change_status.campaign, change_status.ad_group FROM change_status WHERE change_status.last_change_date_time >= '${start}' ORDER BY change_status.last_change_date_time DESC LIMIT ${limit2}`;
|
|
11017
11017
|
return {
|
|
11018
11018
|
action: "use_different_resource",
|
|
11019
11019
|
correctedCommand: buildCommand(corrected, ctx),
|
|
@@ -11405,8 +11405,8 @@ import { defineCommand as defineCommand22 } from "citty";
|
|
|
11405
11405
|
import { readFileSync as readFileSync2 } from "fs";
|
|
11406
11406
|
|
|
11407
11407
|
// src/lib/repeatedFlags.ts
|
|
11408
|
-
function repeatedFlagValues(rawArgs,
|
|
11409
|
-
const long = `--${
|
|
11408
|
+
function repeatedFlagValues(rawArgs, flag3) {
|
|
11409
|
+
const long = `--${flag3}`;
|
|
11410
11410
|
const inline = `${long}=`;
|
|
11411
11411
|
const values = [];
|
|
11412
11412
|
for (let i = 0; i < rawArgs.length; i++) {
|
|
@@ -11424,9 +11424,9 @@ function repeatedFlagValues(rawArgs, flag2) {
|
|
|
11424
11424
|
}
|
|
11425
11425
|
return values;
|
|
11426
11426
|
}
|
|
11427
|
-
function repeatedValues(rawArgs,
|
|
11427
|
+
function repeatedValues(rawArgs, flag3, parsed) {
|
|
11428
11428
|
if (rawArgs !== void 0) {
|
|
11429
|
-
const fromLine = repeatedFlagValues(rawArgs,
|
|
11429
|
+
const fromLine = repeatedFlagValues(rawArgs, flag3);
|
|
11430
11430
|
if (fromLine.length > 0) return fromLine;
|
|
11431
11431
|
}
|
|
11432
11432
|
if (Array.isArray(parsed)) return parsed.filter((v) => typeof v === "string");
|
|
@@ -11452,8 +11452,8 @@ function booleanWordPositionalError(args5, entity) {
|
|
|
11452
11452
|
}
|
|
11453
11453
|
const value = word.toLowerCase();
|
|
11454
11454
|
const raised = Object.keys(args5).filter((key) => key !== "_" && args5[key] === true);
|
|
11455
|
-
const named = raised.length > 0 ? raised.map((
|
|
11456
|
-
const fix = raised.length > 0 ? raised.map((
|
|
11455
|
+
const named = raised.length > 0 ? raised.map((flag3) => `--${flag3}`).join(" or ") : "a boolean flag";
|
|
11456
|
+
const fix = raised.length > 0 ? raised.map((flag3) => `--${flag3}=${value}`).join(" or ") : `--<flag>=${value}`;
|
|
11457
11457
|
const negated = raised.length === 1 && value === "false" ? ` (or --no-${raised[0]})` : "";
|
|
11458
11458
|
return `"${word}" was read as the ${entity} target, not as a value for ${named}. A boolean flag written with a space is set to TRUE and the word is dropped \u2014 attach it instead: ${fix}${negated}.`;
|
|
11459
11459
|
}
|
|
@@ -12061,19 +12061,19 @@ function requireTarget(args5, entity) {
|
|
|
12061
12061
|
}
|
|
12062
12062
|
return target2;
|
|
12063
12063
|
}
|
|
12064
|
-
function requireStringFlag(value,
|
|
12064
|
+
function requireStringFlag(value, flag3) {
|
|
12065
12065
|
if (typeof value !== "string" || value.length === 0) {
|
|
12066
|
-
failWriteValidation(`${
|
|
12066
|
+
failWriteValidation(`${flag3} is required`);
|
|
12067
12067
|
}
|
|
12068
12068
|
return value;
|
|
12069
12069
|
}
|
|
12070
|
-
function microsFlag(value,
|
|
12070
|
+
function microsFlag(value, flag3) {
|
|
12071
12071
|
if (value === void 0 || value === null || value === "") {
|
|
12072
12072
|
return void 0;
|
|
12073
12073
|
}
|
|
12074
12074
|
const num4 = Number(value);
|
|
12075
12075
|
if (Number.isNaN(num4) || num4 <= 0) {
|
|
12076
|
-
failWriteValidation(`${
|
|
12076
|
+
failWriteValidation(`${flag3} must be a positive amount (major currency units, e.g. 50 or 50.00)`);
|
|
12077
12077
|
}
|
|
12078
12078
|
return toMicros(num4);
|
|
12079
12079
|
}
|
|
@@ -13000,13 +13000,13 @@ function buildStatusScope(statusFields, includePaused) {
|
|
|
13000
13000
|
}
|
|
13001
13001
|
function expandPreset(preset, params2, now = /* @__PURE__ */ new Date()) {
|
|
13002
13002
|
const dateRange = params2.dateRange ?? preset.defaultDateRange;
|
|
13003
|
-
const
|
|
13003
|
+
const limit2 = params2.limit ?? preset.defaultLimit;
|
|
13004
13004
|
if (preset.gaqlTemplate.includes("{dateRange}") && !isValidDateRange(dateRange)) {
|
|
13005
13005
|
return { query: "", dateRangeError: dateRangeError(dateRange, now) };
|
|
13006
13006
|
}
|
|
13007
13007
|
const statusScope = buildStatusScope(preset.statusFields, params2.includePaused ?? false);
|
|
13008
13008
|
const range = dateRange.trim();
|
|
13009
|
-
const query = preset.gaqlTemplate.replace(/DURING\s+\{dateRange\}/g, BETWEEN_RE.test(range) ? range : `DURING ${range}`).replace(/\{dateRange\}/g, range).replace(/\{limit\}/g, String(
|
|
13009
|
+
const query = preset.gaqlTemplate.replace(/DURING\s+\{dateRange\}/g, BETWEEN_RE.test(range) ? range : `DURING ${range}`).replace(/\{dateRange\}/g, range).replace(/\{limit\}/g, String(limit2)).replace(/\{statusScope\}/g, statusScope);
|
|
13010
13010
|
return { query };
|
|
13011
13011
|
}
|
|
13012
13012
|
|
|
@@ -13126,7 +13126,7 @@ function addRequiredAncestorFields(query) {
|
|
|
13126
13126
|
]
|
|
13127
13127
|
};
|
|
13128
13128
|
}
|
|
13129
|
-
function applyAutoFixes(query,
|
|
13129
|
+
function applyAutoFixes(query, limit2) {
|
|
13130
13130
|
let corrected = query;
|
|
13131
13131
|
const warnings = [];
|
|
13132
13132
|
if (/(?<![\w.])keyword\.text\b/.test(corrected)) {
|
|
@@ -13157,7 +13157,7 @@ function applyAutoFixes(query, limit) {
|
|
|
13157
13157
|
warnings.push({ code: "ENUM_CORRECTED", message: "ad_group.status ACTIVE \u2192 ENABLED" });
|
|
13158
13158
|
}
|
|
13159
13159
|
if (!/\bLIMIT\b/i.test(corrected)) {
|
|
13160
|
-
const effectiveLimit =
|
|
13160
|
+
const effectiveLimit = limit2 ?? 200;
|
|
13161
13161
|
corrected = `${corrected.trimEnd()} LIMIT ${effectiveLimit}`;
|
|
13162
13162
|
if (!isSingleRowRead(corrected)) {
|
|
13163
13163
|
warnings.push({ code: "AUTO_LIMIT", message: `Added LIMIT ${effectiveLimit}. Use --limit to override.` });
|
|
@@ -13275,7 +13275,7 @@ function rejectContainsCall(query, customerId) {
|
|
|
13275
13275
|
}
|
|
13276
13276
|
};
|
|
13277
13277
|
}
|
|
13278
|
-
function validatePreflight(query, customerId,
|
|
13278
|
+
function validatePreflight(query, customerId, limit2) {
|
|
13279
13279
|
const warnings = [];
|
|
13280
13280
|
const corrected = query;
|
|
13281
13281
|
const firstWord = corrected.trim().split(/\s+/)[0]?.toUpperCase();
|
|
@@ -13351,7 +13351,7 @@ function validatePreflight(query, customerId, limit) {
|
|
|
13351
13351
|
message: "ad_group_criterion returns BOTH positive and negative keywords. Add ad_group_criterion.negative to SELECT or filter with WHERE ad_group_criterion.negative = FALSE (positives) / TRUE (negatives). Use --preset positive-keywords or negative-keywords instead."
|
|
13352
13352
|
});
|
|
13353
13353
|
}
|
|
13354
|
-
const { query: autoFixed, warnings: autoFixWarnings } = applyAutoFixes(corrected,
|
|
13354
|
+
const { query: autoFixed, warnings: autoFixWarnings } = applyAutoFixes(corrected, limit2);
|
|
13355
13355
|
warnings.push(...autoFixWarnings);
|
|
13356
13356
|
const analysis = analyzeServingChain(autoFixed);
|
|
13357
13357
|
if (!analysis) return { valid: true, correctedQuery: autoFixed, warnings };
|
|
@@ -13620,12 +13620,12 @@ function writeRowsToFile(filePath, rows, fields, append) {
|
|
|
13620
13620
|
writeFileSync(filePath, JSON.stringify(rows, null, 2), "utf-8");
|
|
13621
13621
|
}
|
|
13622
13622
|
}
|
|
13623
|
-
function effectiveRowLimit(finalQuery,
|
|
13623
|
+
function effectiveRowLimit(finalQuery, limit2, all = false) {
|
|
13624
13624
|
if (isSingleRowRead(finalQuery)) return void 0;
|
|
13625
13625
|
const match = finalQuery.match(/\bLIMIT\s+(\d+)/i);
|
|
13626
13626
|
const statementLimit = match?.[1] ? Number(match[1]) : void 0;
|
|
13627
13627
|
if (all) return statementLimit;
|
|
13628
|
-
return statementLimit === void 0 ?
|
|
13628
|
+
return statementLimit === void 0 ? limit2 : Math.min(limit2, statementLimit);
|
|
13629
13629
|
}
|
|
13630
13630
|
function warningLines(warnings, format) {
|
|
13631
13631
|
return warnings.map((w) => {
|
|
@@ -13743,7 +13743,7 @@ function outputResults(rows, fieldDescs, warnings, args5, cached, read) {
|
|
|
13743
13743
|
}
|
|
13744
13744
|
writeRows(format, rows, resultColumns(read.gaql, rows), warningLines(allWarnings, format));
|
|
13745
13745
|
}
|
|
13746
|
-
function resolveGaql(args5,
|
|
13746
|
+
function resolveGaql(args5, limit2) {
|
|
13747
13747
|
if (args5.preset) {
|
|
13748
13748
|
const preset = getPreset(args5.preset);
|
|
13749
13749
|
if (!preset) {
|
|
@@ -13756,7 +13756,7 @@ function resolveGaql(args5, limit) {
|
|
|
13756
13756
|
}
|
|
13757
13757
|
const expanded = expandPreset(preset, {
|
|
13758
13758
|
dateRange: args5["date-range"],
|
|
13759
|
-
limit,
|
|
13759
|
+
limit: limit2,
|
|
13760
13760
|
includePaused: Boolean(args5["include-paused"])
|
|
13761
13761
|
});
|
|
13762
13762
|
if (expanded.dateRangeError) {
|
|
@@ -13908,8 +13908,8 @@ async function fetchAllPages(gaql, body, args5, servingOptions) {
|
|
|
13908
13908
|
fileColumns: fileColumns ?? []
|
|
13909
13909
|
};
|
|
13910
13910
|
}
|
|
13911
|
-
async function executeQuery(finalQuery, args5, customerId,
|
|
13912
|
-
const body = { customerId, query: finalQuery, pageSize:
|
|
13911
|
+
async function executeQuery(finalQuery, args5, customerId, limit2, warnings, useCache, cacheKey) {
|
|
13912
|
+
const body = { customerId, query: finalQuery, pageSize: limit2 };
|
|
13913
13913
|
const managerId = getManagerIdForCustomer(customerId);
|
|
13914
13914
|
if (managerId) body.managerId = managerId;
|
|
13915
13915
|
if (args5.cursor) body.pageToken = args5.cursor;
|
|
@@ -13924,7 +13924,7 @@ async function executeQuery(finalQuery, args5, customerId, limit, warnings, useC
|
|
|
13924
13924
|
if (args5.out) {
|
|
13925
13925
|
const truncation = completenessWarnings({
|
|
13926
13926
|
rowCount: totalRowsWritten,
|
|
13927
|
-
effectiveLimit: effectiveRowLimit(finalQuery,
|
|
13927
|
+
effectiveLimit: effectiveRowLimit(finalQuery, limit2, Boolean(args5.all)),
|
|
13928
13928
|
all: Boolean(args5.all),
|
|
13929
13929
|
hasMore: Boolean(lastPageToken)
|
|
13930
13930
|
});
|
|
@@ -13966,7 +13966,7 @@ async function executeQuery(finalQuery, args5, customerId, limit, warnings, useC
|
|
|
13966
13966
|
false,
|
|
13967
13967
|
{
|
|
13968
13968
|
gaql: finalQuery,
|
|
13969
|
-
effectiveLimit: effectiveRowLimit(finalQuery,
|
|
13969
|
+
effectiveLimit: effectiveRowLimit(finalQuery, limit2, Boolean(args5.all)),
|
|
13970
13970
|
all: Boolean(args5.all),
|
|
13971
13971
|
nextCursor: lastPageToken
|
|
13972
13972
|
}
|
|
@@ -13978,8 +13978,8 @@ function shouldReadCache(args5) {
|
|
|
13978
13978
|
var DEFAULT_QUERY_LIMIT = 200;
|
|
13979
13979
|
function parseQueryLimit(raw) {
|
|
13980
13980
|
if (raw === void 0 || raw === null || raw === "") return { ok: true, limit: DEFAULT_QUERY_LIMIT };
|
|
13981
|
-
const
|
|
13982
|
-
if (Number.isInteger(
|
|
13981
|
+
const limit2 = Number(raw);
|
|
13982
|
+
if (Number.isInteger(limit2) && limit2 >= 1 && limit2 <= GOOGLE_ADS_QUERY_MAX_PAGE_SIZE) return { ok: true, limit: limit2 };
|
|
13983
13983
|
return {
|
|
13984
13984
|
ok: false,
|
|
13985
13985
|
error: {
|
|
@@ -14002,13 +14002,13 @@ async function validateArgs(args5) {
|
|
|
14002
14002
|
writeAdsJson(parsedLimit.error);
|
|
14003
14003
|
process.exit(1);
|
|
14004
14004
|
}
|
|
14005
|
-
const { limit } = parsedLimit;
|
|
14005
|
+
const { limit: limit2 } = parsedLimit;
|
|
14006
14006
|
const customerId = await resolveCustomerId(args5);
|
|
14007
|
-
const gaql = resolveGaql(args5,
|
|
14007
|
+
const gaql = resolveGaql(args5, limit2);
|
|
14008
14008
|
if (!gaql) {
|
|
14009
14009
|
process.exit(1);
|
|
14010
14010
|
}
|
|
14011
|
-
const preflight = validatePreflight(gaql, customerId,
|
|
14011
|
+
const preflight = validatePreflight(gaql, customerId, limit2);
|
|
14012
14012
|
if (!preflight.valid && preflight.error) {
|
|
14013
14013
|
writeAdsJson(preflight.error);
|
|
14014
14014
|
process.exit(1);
|
|
@@ -14016,7 +14016,7 @@ async function validateArgs(args5) {
|
|
|
14016
14016
|
const finalQuery = preflight.correctedQuery ?? gaql;
|
|
14017
14017
|
return {
|
|
14018
14018
|
customerId,
|
|
14019
|
-
limit,
|
|
14019
|
+
limit: limit2,
|
|
14020
14020
|
finalQuery,
|
|
14021
14021
|
warnings: [...preflight.warnings, ...servingScopeWarnings(args5, finalQuery)],
|
|
14022
14022
|
useCache: !args5["no-cache"],
|
|
@@ -14093,7 +14093,7 @@ Examples:
|
|
|
14093
14093
|
}
|
|
14094
14094
|
const validated = await validateArgs(args5);
|
|
14095
14095
|
if (!validated) return;
|
|
14096
|
-
const { customerId, limit, finalQuery, warnings, useCache, cacheKey } = validated;
|
|
14096
|
+
const { customerId, limit: limit2, finalQuery, warnings, useCache, cacheKey } = validated;
|
|
14097
14097
|
if (shouldReadCache(args5)) {
|
|
14098
14098
|
const cached = cacheGet("queries", cacheKey);
|
|
14099
14099
|
if (cached) {
|
|
@@ -14111,7 +14111,7 @@ Examples:
|
|
|
14111
14111
|
true,
|
|
14112
14112
|
{
|
|
14113
14113
|
gaql: finalQuery,
|
|
14114
|
-
effectiveLimit: effectiveRowLimit(finalQuery,
|
|
14114
|
+
effectiveLimit: effectiveRowLimit(finalQuery, limit2, Boolean(args5.all)),
|
|
14115
14115
|
all: Boolean(args5.all)
|
|
14116
14116
|
}
|
|
14117
14117
|
);
|
|
@@ -14119,7 +14119,7 @@ Examples:
|
|
|
14119
14119
|
}
|
|
14120
14120
|
}
|
|
14121
14121
|
try {
|
|
14122
|
-
await executeQuery(finalQuery, args5, customerId,
|
|
14122
|
+
await executeQuery(finalQuery, args5, customerId, limit2, warnings, useCache, cacheKey);
|
|
14123
14123
|
} catch (err) {
|
|
14124
14124
|
handleQueryError(err, finalQuery, customerId);
|
|
14125
14125
|
}
|
|
@@ -17722,12 +17722,12 @@ function skipBlanks(value, at) {
|
|
|
17722
17722
|
while (value[i] === " " || value[i] === " ") i++;
|
|
17723
17723
|
return i;
|
|
17724
17724
|
}
|
|
17725
|
-
function readQuotedField(value, open,
|
|
17725
|
+
function readQuotedField(value, open, flag3) {
|
|
17726
17726
|
let text2 = "";
|
|
17727
17727
|
let j = open + 1;
|
|
17728
17728
|
while (!(value[j] === '"' && value[j + 1] !== '"')) {
|
|
17729
17729
|
if (j >= value.length) {
|
|
17730
|
-
failWriteValidation(`--${
|
|
17730
|
+
failWriteValidation(`--${flag3} has a quote with no closing quote: ${value} \u2014 ${AD_COPY_LIST_USAGE}`);
|
|
17731
17731
|
}
|
|
17732
17732
|
text2 += value[j];
|
|
17733
17733
|
j += value[j] === '"' ? 2 : 1;
|
|
@@ -17735,18 +17735,18 @@ function readQuotedField(value, open, flag2) {
|
|
|
17735
17735
|
const after = skipBlanks(value, j + 1);
|
|
17736
17736
|
if (after < value.length && value[after] !== ",") {
|
|
17737
17737
|
failWriteValidation(
|
|
17738
|
-
`--${
|
|
17738
|
+
`--${flag3}: text follows the closing quote of "${text2}" \u2014 a quoted item must end at a comma. To put a quote inside an item, double it ("") or ${AD_COPY_LIST_USAGE}`
|
|
17739
17739
|
);
|
|
17740
17740
|
}
|
|
17741
17741
|
return { text: text2, next: after + 1 };
|
|
17742
17742
|
}
|
|
17743
|
-
function splitQuotedList(value,
|
|
17743
|
+
function splitQuotedList(value, flag3) {
|
|
17744
17744
|
const fields = [];
|
|
17745
17745
|
let i = 0;
|
|
17746
17746
|
while (i <= value.length) {
|
|
17747
17747
|
const start = skipBlanks(value, i);
|
|
17748
17748
|
if (value[start] === '"') {
|
|
17749
|
-
const { text: text2, next } = readQuotedField(value, start,
|
|
17749
|
+
const { text: text2, next } = readQuotedField(value, start, flag3);
|
|
17750
17750
|
fields.push({ text: text2, quoted: true });
|
|
17751
17751
|
i = next;
|
|
17752
17752
|
} else {
|
|
@@ -17768,53 +17768,53 @@ function jsonStringArray(value) {
|
|
|
17768
17768
|
return void 0;
|
|
17769
17769
|
}
|
|
17770
17770
|
}
|
|
17771
|
-
function parseCopyList(value,
|
|
17771
|
+
function parseCopyList(value, flag3) {
|
|
17772
17772
|
let items;
|
|
17773
17773
|
if (Array.isArray(value)) {
|
|
17774
17774
|
items = value.filter((v) => typeof v === "string").map((v) => ({ text: v.trim(), splitAtComma: false }));
|
|
17775
17775
|
} else if (typeof value === "string") {
|
|
17776
17776
|
const trimmed = value.trim();
|
|
17777
17777
|
const json = jsonStringArray(trimmed);
|
|
17778
|
-
items = json ? json.map((v) => ({ text: v.trim(), splitAtComma: false })) : splitQuotedList(trimmed,
|
|
17778
|
+
items = json ? json.map((v) => ({ text: v.trim(), splitAtComma: false })) : splitQuotedList(trimmed, flag3);
|
|
17779
17779
|
} else {
|
|
17780
17780
|
return void 0;
|
|
17781
17781
|
}
|
|
17782
17782
|
const kept = items.filter((item) => item.text.length > 0);
|
|
17783
17783
|
return kept.length > 0 ? kept : void 0;
|
|
17784
17784
|
}
|
|
17785
|
-
function copyListFlag(value,
|
|
17786
|
-
return parseCopyList(value,
|
|
17785
|
+
function copyListFlag(value, flag3) {
|
|
17786
|
+
return parseCopyList(value, flag3)?.map((item) => item.text);
|
|
17787
17787
|
}
|
|
17788
|
-
function refuseDashLedValue(rawArgs,
|
|
17788
|
+
function refuseDashLedValue(rawArgs, flag3) {
|
|
17789
17789
|
for (let i = 0; i < rawArgs.length - 1; i++) {
|
|
17790
17790
|
if (rawArgs[i] === "--") return;
|
|
17791
17791
|
const value = rawArgs[i + 1];
|
|
17792
|
-
if (rawArgs[i] === `--${
|
|
17792
|
+
if (rawArgs[i] === `--${flag3}` && value?.startsWith("-")) {
|
|
17793
17793
|
failWriteValidation(
|
|
17794
|
-
`--${
|
|
17794
|
+
`--${flag3} is followed by "${value}", which starts with a dash and is read as another flag, not as copy. Join copy that starts with a dash to its flag with =: --${flag3}="${value}"`
|
|
17795
17795
|
);
|
|
17796
17796
|
}
|
|
17797
17797
|
}
|
|
17798
17798
|
}
|
|
17799
17799
|
function withRepeatedCopyFlags(args5, rawArgs) {
|
|
17800
17800
|
const out = { ...args5 };
|
|
17801
|
-
for (const
|
|
17802
|
-
refuseDashLedValue(rawArgs ?? [],
|
|
17803
|
-
const values = repeatedFlagValues(rawArgs ?? [],
|
|
17801
|
+
for (const flag3 of AD_COPY_LIST_FLAGS) {
|
|
17802
|
+
refuseDashLedValue(rawArgs ?? [], flag3);
|
|
17803
|
+
const values = repeatedFlagValues(rawArgs ?? [], flag3);
|
|
17804
17804
|
if (values.length > 1) {
|
|
17805
|
-
out[
|
|
17805
|
+
out[flag3] = values;
|
|
17806
17806
|
}
|
|
17807
17807
|
}
|
|
17808
17808
|
return out;
|
|
17809
17809
|
}
|
|
17810
17810
|
function copyListSplitHints(args5) {
|
|
17811
17811
|
const hints2 = [];
|
|
17812
|
-
for (const
|
|
17813
|
-
const items = parseCopyList(args5[
|
|
17812
|
+
for (const flag3 of AD_COPY_LIST_FLAGS) {
|
|
17813
|
+
const items = parseCopyList(args5[flag3], flag3) ?? [];
|
|
17814
17814
|
const fragments = items.filter((item, index) => index > 0 && item.splitAtComma && new RegExp("^\\p{Ll}", "u").test(item.text));
|
|
17815
17815
|
if (fragments.length > 0) {
|
|
17816
17816
|
hints2.push(
|
|
17817
|
-
`--${
|
|
17817
|
+
`--${flag3}: ${fragments.map((f) => `"${f.text}"`).join(", ")} starts in lowercase \u2014 it looks like the end of one item cut at a comma. Check the staged copy; if it was one item, fix this op in place with \`draft amend <ref>\` (the ref this response returned) rather than staging it again, which would leave the cut copy staged too \u2014 ${AD_COPY_LIST_USAGE}.`
|
|
17818
17818
|
);
|
|
17819
17819
|
}
|
|
17820
17820
|
}
|
|
@@ -18365,11 +18365,11 @@ function urlCustomParametersFromFlags(args5, rawArgs) {
|
|
|
18365
18365
|
failWriteValidation("--clear-custom-params removes every parameter \u2014 pass it OR --custom-param, not both");
|
|
18366
18366
|
}
|
|
18367
18367
|
return entries.map((entry) => {
|
|
18368
|
-
const
|
|
18369
|
-
if (
|
|
18368
|
+
const split2 = entry.indexOf("=");
|
|
18369
|
+
if (split2 <= 0) {
|
|
18370
18370
|
failWriteValidation(`--custom-param "${entry}" must be key=value (e.g. --custom-param season=summer)`);
|
|
18371
18371
|
}
|
|
18372
|
-
return { key: entry.slice(0,
|
|
18372
|
+
return { key: entry.slice(0, split2), value: entry.slice(split2 + 1) };
|
|
18373
18373
|
});
|
|
18374
18374
|
}
|
|
18375
18375
|
var geoTargetTypeArgs = {
|
|
@@ -18382,13 +18382,13 @@ var geoTargetTypeArgs = {
|
|
|
18382
18382
|
description: "How EXCLUDED locations are matched \u2014 PRESENCE (default: excludes people in the location) or PRESENCE_OR_INTEREST (also excludes people interested in it)."
|
|
18383
18383
|
}
|
|
18384
18384
|
};
|
|
18385
|
-
function enumFlag(value,
|
|
18385
|
+
function enumFlag(value, flag3, allowed) {
|
|
18386
18386
|
if (typeof value !== "string" || value.length === 0) {
|
|
18387
18387
|
return void 0;
|
|
18388
18388
|
}
|
|
18389
18389
|
const upper2 = value.toUpperCase();
|
|
18390
18390
|
if (!allowed.includes(upper2)) {
|
|
18391
|
-
failWriteValidation(`${
|
|
18391
|
+
failWriteValidation(`${flag3} must be one of ${allowed.join(" | ")}`);
|
|
18392
18392
|
}
|
|
18393
18393
|
return upper2;
|
|
18394
18394
|
}
|
|
@@ -19108,8 +19108,8 @@ var FORMAT_CONTENT_FLAGS = {
|
|
|
19108
19108
|
// same silent drop this gate exists for, on the one format that reads no flags at all.
|
|
19109
19109
|
call: []
|
|
19110
19110
|
};
|
|
19111
|
-
function formatsReading(
|
|
19112
|
-
return Object.entries(FORMAT_CONTENT_FLAGS).filter(([, flags]) => flags.includes(
|
|
19111
|
+
function formatsReading(flag3) {
|
|
19112
|
+
return Object.entries(FORMAT_CONTENT_FLAGS).filter(([, flags]) => flags.includes(flag3)).map(([format]) => format);
|
|
19113
19113
|
}
|
|
19114
19114
|
function assertFlagsFitFormat(args5, format) {
|
|
19115
19115
|
const allowed = FORMAT_CONTENT_FLAGS[format];
|
|
@@ -19117,14 +19117,14 @@ function assertFlagsFitFormat(args5, format) {
|
|
|
19117
19117
|
return;
|
|
19118
19118
|
}
|
|
19119
19119
|
const stray = Object.keys(adContentArgs).filter(
|
|
19120
|
-
(
|
|
19120
|
+
(flag3) => flag3 !== "format" && args5[flag3] !== void 0 && !allowed.includes(flag3)
|
|
19121
19121
|
);
|
|
19122
19122
|
if (stray.length === 0) {
|
|
19123
19123
|
return;
|
|
19124
19124
|
}
|
|
19125
|
-
const detail = stray.map((
|
|
19126
|
-
const elsewhere = formatsReading(
|
|
19127
|
-
return elsewhere.length > 0 ? `--${
|
|
19125
|
+
const detail = stray.map((flag3) => {
|
|
19126
|
+
const elsewhere = formatsReading(flag3);
|
|
19127
|
+
return elsewhere.length > 0 ? `--${flag3} (${elsewhere.join(" / ")})` : `--${flag3}`;
|
|
19128
19128
|
}).join(", ");
|
|
19129
19129
|
failWriteValidation(
|
|
19130
19130
|
`a ${format} ad has no ${stray.map((f) => `--${f}`).join(", ")} \u2014 passing it here would stage the change without it. Formats that take it: ${detail}. Check the format with \`baker ads google query "SELECT ad_group_ad.ad.type FROM ad_group_ad WHERE ad_group_ad.ad.id = <id>"\`.`
|
|
@@ -19203,10 +19203,10 @@ var adContentArgs = {
|
|
|
19203
19203
|
"final-url": { type: "string", description: "Comma-separated final URLs" }
|
|
19204
19204
|
};
|
|
19205
19205
|
function assertNoContentFlags(args5) {
|
|
19206
|
-
const passed = Object.keys(adContentArgs).filter((
|
|
19206
|
+
const passed = Object.keys(adContentArgs).filter((flag3) => flag3 !== "format" && args5[flag3] !== void 0);
|
|
19207
19207
|
if (passed.length > 0) {
|
|
19208
19208
|
failWriteValidation(
|
|
19209
|
-
`${passed.map((
|
|
19209
|
+
`${passed.map((flag3) => `--${flag3}`).join(", ")} only build an ad's content alongside --format (responsiveSearch | responsiveDisplay | call | app | video | demandGen | demandGenVideo) \u2014 add it, or pass --file with the complete content`
|
|
19210
19210
|
);
|
|
19211
19211
|
}
|
|
19212
19212
|
}
|
|
@@ -19929,8 +19929,8 @@ function resolveAccountIdArg2(args5) {
|
|
|
19929
19929
|
function todayIso2() {
|
|
19930
19930
|
return (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
19931
19931
|
}
|
|
19932
|
-
function daysAgoIso2(
|
|
19933
|
-
return new Date(Date.now() -
|
|
19932
|
+
function daysAgoIso2(days2) {
|
|
19933
|
+
return new Date(Date.now() - days2 * DAY_MS2).toISOString().slice(0, 10);
|
|
19934
19934
|
}
|
|
19935
19935
|
function csvOrJson2(args5) {
|
|
19936
19936
|
return args5.output ?? "json";
|
|
@@ -20004,7 +20004,7 @@ function loadPatchArg2(args5) {
|
|
|
20004
20004
|
throw err;
|
|
20005
20005
|
}
|
|
20006
20006
|
}
|
|
20007
|
-
function parseMoneyFlag(amount, currency,
|
|
20007
|
+
function parseMoneyFlag(amount, currency, flag3) {
|
|
20008
20008
|
if (amount === void 0 || amount === null || amount === "") {
|
|
20009
20009
|
return void 0;
|
|
20010
20010
|
}
|
|
@@ -20012,11 +20012,11 @@ function parseMoneyFlag(amount, currency, flag2) {
|
|
|
20012
20012
|
return { amount: String(amount) };
|
|
20013
20013
|
}
|
|
20014
20014
|
if (typeof currency !== "string" || currency.length !== 3) {
|
|
20015
|
-
failWriteValidation2(`${
|
|
20015
|
+
failWriteValidation2(`${flag3} got an invalid --currency (expected a 3-letter code like EUR)`);
|
|
20016
20016
|
}
|
|
20017
20017
|
return { amount: String(amount), currencyCode: currency.toUpperCase() };
|
|
20018
20018
|
}
|
|
20019
|
-
function parseDateFlag(value,
|
|
20019
|
+
function parseDateFlag(value, flag3) {
|
|
20020
20020
|
if (value === void 0 || value === null || value === "") {
|
|
20021
20021
|
return void 0;
|
|
20022
20022
|
}
|
|
@@ -20026,11 +20026,11 @@ function parseDateFlag(value, flag2) {
|
|
|
20026
20026
|
}
|
|
20027
20027
|
const parsed = Date.parse(raw);
|
|
20028
20028
|
if (Number.isNaN(parsed)) {
|
|
20029
|
-
failWriteValidation2(`${
|
|
20029
|
+
failWriteValidation2(`${flag3} must be an ISO date (2026-07-10) or epoch milliseconds`);
|
|
20030
20030
|
}
|
|
20031
20031
|
return parsed;
|
|
20032
20032
|
}
|
|
20033
|
-
function parseOnOffFlag(value,
|
|
20033
|
+
function parseOnOffFlag(value, flag3) {
|
|
20034
20034
|
if (value === void 0 || value === null || value === "") {
|
|
20035
20035
|
return void 0;
|
|
20036
20036
|
}
|
|
@@ -20041,7 +20041,7 @@ function parseOnOffFlag(value, flag2) {
|
|
|
20041
20041
|
if (raw === "off" || raw === "false") {
|
|
20042
20042
|
return false;
|
|
20043
20043
|
}
|
|
20044
|
-
failWriteValidation2(`${
|
|
20044
|
+
failWriteValidation2(`${flag3} must be on|off`);
|
|
20045
20045
|
}
|
|
20046
20046
|
function parseLocaleFlag(value) {
|
|
20047
20047
|
if (value === void 0 || value === null || value === "") {
|
|
@@ -21227,13 +21227,13 @@ Examples:
|
|
|
21227
21227
|
}
|
|
21228
21228
|
});
|
|
21229
21229
|
}
|
|
21230
|
-
function trackingPairs(value,
|
|
21230
|
+
function trackingPairs(value, flag3) {
|
|
21231
21231
|
const raw = value === void 0 ? [] : Array.isArray(value) ? value : [value];
|
|
21232
21232
|
return raw.flatMap(
|
|
21233
21233
|
(entry) => String(entry).split("&").filter((pair) => pair.length > 0).map((pair) => {
|
|
21234
21234
|
const at = pair.indexOf("=");
|
|
21235
21235
|
if (at < 1 || at === pair.length - 1) {
|
|
21236
|
-
failWriteValidation2(`${
|
|
21236
|
+
failWriteValidation2(`${flag3} expects key=value pairs \u2014 got "${pair}"`);
|
|
21237
21237
|
}
|
|
21238
21238
|
return [pair.slice(0, at).trim(), pair.slice(at + 1).trim()];
|
|
21239
21239
|
})
|
|
@@ -21569,9 +21569,9 @@ var CREATIVE_AMEND_CONTENT_FLAGS = [
|
|
|
21569
21569
|
];
|
|
21570
21570
|
function creativeContentPatch(args5) {
|
|
21571
21571
|
const patch = {};
|
|
21572
|
-
for (const [
|
|
21573
|
-
if (args5[
|
|
21574
|
-
patch[key] = args5[
|
|
21572
|
+
for (const [flag3, key] of CREATIVE_AMEND_CONTENT_FLAGS) {
|
|
21573
|
+
if (args5[flag3] !== void 0) {
|
|
21574
|
+
patch[key] = args5[flag3];
|
|
21575
21575
|
}
|
|
21576
21576
|
}
|
|
21577
21577
|
if (args5.cta !== void 0) {
|
|
@@ -22226,11 +22226,11 @@ function resolveDateRange(args5) {
|
|
|
22226
22226
|
return { start, end };
|
|
22227
22227
|
}
|
|
22228
22228
|
const lastDaysRaw = args5["last-days"];
|
|
22229
|
-
const
|
|
22230
|
-
if (!Number.isFinite(
|
|
22229
|
+
const days2 = lastDaysRaw ? Number(lastDaysRaw) : 7;
|
|
22230
|
+
if (!Number.isFinite(days2) || days2 < 1 || days2 > 730) {
|
|
22231
22231
|
handleLinkedinError(new Error(`Invalid --last-days "${lastDaysRaw}". Use a positive number \u2264 730.`));
|
|
22232
22232
|
}
|
|
22233
|
-
return { start: daysAgoIso2(
|
|
22233
|
+
return { start: daysAgoIso2(days2), end: todayIso2() };
|
|
22234
22234
|
}
|
|
22235
22235
|
function resolveScopeIds(args5, level) {
|
|
22236
22236
|
if (level === "account") {
|
|
@@ -22358,7 +22358,7 @@ Examples \u2014 common AI questions:
|
|
|
22358
22358
|
}
|
|
22359
22359
|
const range = resolveDateRange(args5);
|
|
22360
22360
|
const metrics = commaSplit(args5.metrics);
|
|
22361
|
-
const
|
|
22361
|
+
const limit2 = args5.limit ? Number(args5.limit) : 1e3;
|
|
22362
22362
|
const request = {
|
|
22363
22363
|
level,
|
|
22364
22364
|
ids,
|
|
@@ -22368,7 +22368,7 @@ Examples \u2014 common AI questions:
|
|
|
22368
22368
|
start: range.start,
|
|
22369
22369
|
end: range.end,
|
|
22370
22370
|
granularity,
|
|
22371
|
-
limit
|
|
22371
|
+
limit: limit2
|
|
22372
22372
|
};
|
|
22373
22373
|
try {
|
|
22374
22374
|
const data = await apiPost("/api/ads/linkedin/analytics", {
|
|
@@ -23160,8 +23160,8 @@ function numberOf2(v) {
|
|
|
23160
23160
|
}
|
|
23161
23161
|
return 0;
|
|
23162
23162
|
}
|
|
23163
|
-
function topByImpressions(rows,
|
|
23164
|
-
return [...rows].sort((a, b) => numberOf2(b.impressions) - numberOf2(a.impressions)).slice(0,
|
|
23163
|
+
function topByImpressions(rows, limit2) {
|
|
23164
|
+
return [...rows].sort((a, b) => numberOf2(b.impressions) - numberOf2(a.impressions)).slice(0, limit2);
|
|
23165
23165
|
}
|
|
23166
23166
|
var demographicsCommand = defineCommand45({
|
|
23167
23167
|
meta: {
|
|
@@ -23252,11 +23252,11 @@ function resolveRange(args5) {
|
|
|
23252
23252
|
if (start) {
|
|
23253
23253
|
return { start, end };
|
|
23254
23254
|
}
|
|
23255
|
-
const
|
|
23256
|
-
if (!Number.isFinite(
|
|
23255
|
+
const days2 = args5["last-days"] ? Number(args5["last-days"]) : 30;
|
|
23256
|
+
if (!Number.isFinite(days2) || days2 < 1) {
|
|
23257
23257
|
handleLinkedinError(new Error("Invalid --last-days; must be a positive integer."));
|
|
23258
23258
|
}
|
|
23259
|
-
return { start: daysAgoIso2(
|
|
23259
|
+
return { start: daysAgoIso2(days2), end: todayIso2() };
|
|
23260
23260
|
}
|
|
23261
23261
|
|
|
23262
23262
|
// src/commands/ads/linkedin/draft.ts
|
|
@@ -25842,7 +25842,7 @@ function mergePayload3(file, flags) {
|
|
|
25842
25842
|
}
|
|
25843
25843
|
return merged;
|
|
25844
25844
|
}
|
|
25845
|
-
function parseMoneyFlag2(amount, currency,
|
|
25845
|
+
function parseMoneyFlag2(amount, currency, flag3) {
|
|
25846
25846
|
if (amount === void 0 || amount === null || amount === "") {
|
|
25847
25847
|
return void 0;
|
|
25848
25848
|
}
|
|
@@ -25850,11 +25850,11 @@ function parseMoneyFlag2(amount, currency, flag2) {
|
|
|
25850
25850
|
return { amount: String(amount) };
|
|
25851
25851
|
}
|
|
25852
25852
|
if (typeof currency !== "string" || currency.length !== 3) {
|
|
25853
|
-
failWriteValidation3(`${
|
|
25853
|
+
failWriteValidation3(`${flag3} got an invalid --currency (expected a 3-letter code like USD)`);
|
|
25854
25854
|
}
|
|
25855
25855
|
return { amount: String(amount), currencyCode: currency.toUpperCase() };
|
|
25856
25856
|
}
|
|
25857
|
-
function parseDateFlag2(value,
|
|
25857
|
+
function parseDateFlag2(value, flag3) {
|
|
25858
25858
|
if (value === void 0 || value === null || value === "") {
|
|
25859
25859
|
return void 0;
|
|
25860
25860
|
}
|
|
@@ -25864,7 +25864,7 @@ function parseDateFlag2(value, flag2) {
|
|
|
25864
25864
|
}
|
|
25865
25865
|
const parsed = Date.parse(raw);
|
|
25866
25866
|
if (Number.isNaN(parsed)) {
|
|
25867
|
-
failWriteValidation3(`${
|
|
25867
|
+
failWriteValidation3(`${flag3} must be an ISO date (2026-07-10) or epoch milliseconds`);
|
|
25868
25868
|
}
|
|
25869
25869
|
return parsed;
|
|
25870
25870
|
}
|
|
@@ -25878,13 +25878,13 @@ function listFlag2(value) {
|
|
|
25878
25878
|
function upper(value) {
|
|
25879
25879
|
return typeof value === "string" && value.length > 0 ? value.toUpperCase() : void 0;
|
|
25880
25880
|
}
|
|
25881
|
-
function numberFlag(value,
|
|
25881
|
+
function numberFlag(value, flag3) {
|
|
25882
25882
|
if (value === void 0 || value === null || value === "") {
|
|
25883
25883
|
return void 0;
|
|
25884
25884
|
}
|
|
25885
25885
|
const n = Number(value);
|
|
25886
25886
|
if (!Number.isFinite(n)) {
|
|
25887
|
-
failWriteValidation3(`${
|
|
25887
|
+
failWriteValidation3(`${flag3} must be a number`);
|
|
25888
25888
|
}
|
|
25889
25889
|
return n;
|
|
25890
25890
|
}
|
|
@@ -27313,8 +27313,8 @@ function shouldAutoAsync(body) {
|
|
|
27313
27313
|
return true;
|
|
27314
27314
|
}
|
|
27315
27315
|
if (body.since && body.until) {
|
|
27316
|
-
const
|
|
27317
|
-
if (
|
|
27316
|
+
const days2 = (Date.parse(body.until) - Date.parse(body.since)) / 864e5;
|
|
27317
|
+
if (days2 > 90) {
|
|
27318
27318
|
return true;
|
|
27319
27319
|
}
|
|
27320
27320
|
}
|
|
@@ -27783,14 +27783,14 @@ async function runStats(args5) {
|
|
|
27783
27783
|
if (!pixelId) {
|
|
27784
27784
|
handleMetaError(new Error("--pixel-id is required when --stats is set"));
|
|
27785
27785
|
}
|
|
27786
|
-
const
|
|
27787
|
-
if (!Number.isInteger(
|
|
27786
|
+
const days2 = args5.days ? Number(args5.days) : 7;
|
|
27787
|
+
if (!Number.isInteger(days2) || days2 <= 0 || days2 > 90) {
|
|
27788
27788
|
handleMetaError(new Error("--days must be a positive integer \u2264 90"));
|
|
27789
27789
|
}
|
|
27790
27790
|
const params2 = {
|
|
27791
27791
|
"pixel-id": pixelId,
|
|
27792
27792
|
aggregation: args5.aggregation ?? "event",
|
|
27793
|
-
"start-time": daysAgoIso(
|
|
27793
|
+
"start-time": daysAgoIso(days2),
|
|
27794
27794
|
"end-time": todayIso()
|
|
27795
27795
|
};
|
|
27796
27796
|
if (args5["no-cache"]) params2["skip-cache"] = "true";
|
|
@@ -28450,10 +28450,10 @@ function parseXMoney(value) {
|
|
|
28450
28450
|
}
|
|
28451
28451
|
return { ok: true, micros: Math.round(amount * 1e6) };
|
|
28452
28452
|
}
|
|
28453
|
-
function microsFromArg(value,
|
|
28453
|
+
function microsFromArg(value, flag3) {
|
|
28454
28454
|
const parsed = parseXMoney(value);
|
|
28455
28455
|
if (!parsed.ok) {
|
|
28456
|
-
failWriteValidation4(`${
|
|
28456
|
+
failWriteValidation4(`${flag3} ${parsed.reason}`);
|
|
28457
28457
|
}
|
|
28458
28458
|
return parsed.micros;
|
|
28459
28459
|
}
|
|
@@ -30237,14 +30237,14 @@ function resolveQueryFromArgs(args5, timeZone) {
|
|
|
30237
30237
|
granularity = args5.granularity ?? preset.granularity;
|
|
30238
30238
|
placement = args5.placement ?? preset.placement;
|
|
30239
30239
|
const requested = args5.days ? Number(args5.days) : preset.defaultDays;
|
|
30240
|
-
const
|
|
30240
|
+
const days2 = Math.min(SYNC_MAX_DAYS, Math.max(1, requested));
|
|
30241
30241
|
if (requested > SYNC_MAX_DAYS) {
|
|
30242
30242
|
hints2.push(
|
|
30243
|
-
`Sync stats cover at most ${SYNC_MAX_DAYS} days, so '${preset.name}' was shortened to the last ${
|
|
30243
|
+
`Sync stats cover at most ${SYNC_MAX_DAYS} days, so '${preset.name}' was shortened to the last ${days2}. For the full window run: baker ads x stats job --entity ${entity} --entity-ids <ids> --metric-groups ${metricGroups?.join(",")} --start-time <start> --end-time <end>`
|
|
30244
30244
|
);
|
|
30245
30245
|
}
|
|
30246
30246
|
const floor = granularity === "DAY" ? (d) => accountMidnightIso(d, timeZone) : isoHourFloor;
|
|
30247
|
-
startTime ??= floor(
|
|
30247
|
+
startTime ??= floor(days2);
|
|
30248
30248
|
endTime ??= floor(0);
|
|
30249
30249
|
}
|
|
30250
30250
|
const entityIds = parseCsv5(args5["entity-ids"]);
|
|
@@ -31549,32 +31549,32 @@ function isUsableStepNameKey(key) {
|
|
|
31549
31549
|
// src/commands/analytics/conversionArgs.ts
|
|
31550
31550
|
var ConversionArgsError = class extends Error {
|
|
31551
31551
|
};
|
|
31552
|
-
function allValues(
|
|
31552
|
+
function allValues(flag3, argv) {
|
|
31553
31553
|
const values = [];
|
|
31554
31554
|
for (let i = 0; i < argv.length; i += 1) {
|
|
31555
31555
|
const arg = argv[i];
|
|
31556
|
-
if (arg ===
|
|
31556
|
+
if (arg === flag3) {
|
|
31557
31557
|
const next = argv[i + 1];
|
|
31558
31558
|
if (next !== void 0 && !next.startsWith("--")) values.push(next);
|
|
31559
31559
|
continue;
|
|
31560
31560
|
}
|
|
31561
|
-
if (arg.startsWith(`${
|
|
31561
|
+
if (arg.startsWith(`${flag3}=`)) values.push(arg.slice(flag3.length + 1));
|
|
31562
31562
|
}
|
|
31563
31563
|
return values.map((value) => value.trim()).filter((value) => value !== "");
|
|
31564
31564
|
}
|
|
31565
|
-
function onlyOnce(value,
|
|
31565
|
+
function onlyOnce(value, flag3, argv) {
|
|
31566
31566
|
if (value === void 0) return void 0;
|
|
31567
|
-
const given = argv.filter((arg) => arg ===
|
|
31567
|
+
const given = argv.filter((arg) => arg === flag3 || arg.startsWith(`${flag3}=`)).length;
|
|
31568
31568
|
if (given > 1) {
|
|
31569
31569
|
throw new ConversionArgsError(
|
|
31570
|
-
`${
|
|
31570
|
+
`${flag3} was given ${given} times and only the last would have been used. It takes one value.`
|
|
31571
31571
|
);
|
|
31572
31572
|
}
|
|
31573
31573
|
const trimmed = String(value).trim();
|
|
31574
31574
|
return trimmed === "" ? void 0 : trimmed;
|
|
31575
31575
|
}
|
|
31576
|
-
function repeatable(
|
|
31577
|
-
const fromArgv = allValues(
|
|
31576
|
+
function repeatable(flag3, cittyValue, argv) {
|
|
31577
|
+
const fromArgv = allValues(flag3, argv);
|
|
31578
31578
|
if (fromArgv.length > 0) return fromArgv;
|
|
31579
31579
|
if (cittyValue === void 0) return [];
|
|
31580
31580
|
const trimmed = String(cittyValue).trim();
|
|
@@ -31614,11 +31614,11 @@ function checkEventKeys(eventKeys) {
|
|
|
31614
31614
|
}
|
|
31615
31615
|
function readDays(raw) {
|
|
31616
31616
|
if (raw === void 0) return void 0;
|
|
31617
|
-
const
|
|
31618
|
-
if (!Number.isFinite(
|
|
31617
|
+
const days2 = Number(raw);
|
|
31618
|
+
if (!Number.isFinite(days2) || days2 <= 0) {
|
|
31619
31619
|
throw new ConversionArgsError("--days must be a positive number of days.");
|
|
31620
31620
|
}
|
|
31621
|
-
return
|
|
31621
|
+
return days2;
|
|
31622
31622
|
}
|
|
31623
31623
|
function parseConversionsArgs(raw, argv) {
|
|
31624
31624
|
const countMode = onlyOnce(raw["count-mode"], "--count-mode", argv);
|
|
@@ -32564,8 +32564,8 @@ var SET_FLAGS = ["--source-event", "--platform", "--tag", "--event"];
|
|
|
32564
32564
|
function parseSendsArgs(raw, argv, now = Date.now()) {
|
|
32565
32565
|
const set = parseSetFlags(raw, argv, now);
|
|
32566
32566
|
const remove = onlyOnce(raw.remove, "--remove", argv);
|
|
32567
|
-
const
|
|
32568
|
-
if (
|
|
32567
|
+
const days2 = raw.days === void 0 ? void 0 : Number(raw.days);
|
|
32568
|
+
if (days2 !== void 0 && (!Number.isInteger(days2) || days2 <= 0 || days2 > 400)) {
|
|
32569
32569
|
throw new ConversionArgsError("--days must be a whole number of days, from 1 to 400.");
|
|
32570
32570
|
}
|
|
32571
32571
|
const uploads = onlyOnce(raw.uploads, "--uploads", argv);
|
|
@@ -32580,17 +32580,17 @@ function parseSendsArgs(raw, argv, now = Date.now()) {
|
|
|
32580
32580
|
return {
|
|
32581
32581
|
...set === void 0 ? {} : { set },
|
|
32582
32582
|
...remove === void 0 ? {} : { remove },
|
|
32583
|
-
...
|
|
32583
|
+
...days2 === void 0 ? {} : { days: days2 },
|
|
32584
32584
|
...uploads === void 0 ? {} : { uploads },
|
|
32585
32585
|
...raw.failed === true ? { failedOnly: true } : {},
|
|
32586
32586
|
...raw.skipped === true ? { skippedOnly: true } : {},
|
|
32587
32587
|
full: raw.full === true
|
|
32588
32588
|
};
|
|
32589
32589
|
}
|
|
32590
|
-
function flagValueAt(argv,
|
|
32590
|
+
function flagValueAt(argv, flag3, index) {
|
|
32591
32591
|
const arg = argv[index] ?? "";
|
|
32592
|
-
if (arg ===
|
|
32593
|
-
return arg.startsWith(`${
|
|
32592
|
+
if (arg === flag3) return argv[index + 1];
|
|
32593
|
+
return arg.startsWith(`${flag3}=`) ? arg.slice(flag3.length + 1) : void 0;
|
|
32594
32594
|
}
|
|
32595
32595
|
function parseParameterArgs(argv) {
|
|
32596
32596
|
const parameters = {};
|
|
@@ -32621,7 +32621,7 @@ function parseSetFlags(raw, argv, now) {
|
|
|
32621
32621
|
"--tag": onlyOnce(raw.tag, "--tag", argv),
|
|
32622
32622
|
"--event": onlyOnce(raw.event, "--event", argv)
|
|
32623
32623
|
};
|
|
32624
|
-
const given = SET_FLAGS.filter((
|
|
32624
|
+
const given = SET_FLAGS.filter((flag3) => values[flag3] !== void 0);
|
|
32625
32625
|
const set = raw.set === true;
|
|
32626
32626
|
if (!set) {
|
|
32627
32627
|
if (given.length > 0) {
|
|
@@ -32631,7 +32631,7 @@ function parseSetFlags(raw, argv, now) {
|
|
|
32631
32631
|
}
|
|
32632
32632
|
return void 0;
|
|
32633
32633
|
}
|
|
32634
|
-
const missing = SET_FLAGS.filter((
|
|
32634
|
+
const missing = SET_FLAGS.filter((flag3) => values[flag3] === void 0);
|
|
32635
32635
|
if (missing.length > 0) {
|
|
32636
32636
|
throw new ConversionArgsError(
|
|
32637
32637
|
`--set needs ${SET_FLAGS.join(", ")}; missing ${missing.join(", ")}. Copy --tag from platforms[].tags of a plain \`baker analytics sends\`, never from memory.`
|
|
@@ -33286,15 +33286,15 @@ registerSchema({
|
|
|
33286
33286
|
description: "Where events reach Baker from a server: the client's own server door, and HubSpot events Baker reads \u2014 what each one watches and reads, and how it is doing",
|
|
33287
33287
|
args: args3
|
|
33288
33288
|
});
|
|
33289
|
-
function flagValueAt2(argv,
|
|
33289
|
+
function flagValueAt2(argv, flag3, index) {
|
|
33290
33290
|
const arg = argv[index] ?? "";
|
|
33291
|
-
if (arg ===
|
|
33292
|
-
return arg.startsWith(`${
|
|
33291
|
+
if (arg === flag3) return argv[index + 1];
|
|
33292
|
+
return arg.startsWith(`${flag3}=`) ? arg.slice(flag3.length + 1) : void 0;
|
|
33293
33293
|
}
|
|
33294
|
-
function parseRepeated(argv,
|
|
33294
|
+
function parseRepeated(argv, flag3) {
|
|
33295
33295
|
const values = [];
|
|
33296
33296
|
for (let index = 0; index < argv.length; index += 1) {
|
|
33297
|
-
const value = flagValueAt2(argv,
|
|
33297
|
+
const value = flagValueAt2(argv, flag3, index);
|
|
33298
33298
|
if (value !== void 0) values.push(value);
|
|
33299
33299
|
}
|
|
33300
33300
|
return values;
|
|
@@ -34178,10 +34178,10 @@ Examples:
|
|
|
34178
34178
|
});
|
|
34179
34179
|
process.exit(1);
|
|
34180
34180
|
}
|
|
34181
|
-
const
|
|
34181
|
+
const days2 = raw.days === void 0 ? void 0 : Number(raw.days);
|
|
34182
34182
|
const response = await apiPost("/api/analytics/tracking", {
|
|
34183
34183
|
...platform ? { platform } : {},
|
|
34184
|
-
...
|
|
34184
|
+
...days2 !== void 0 && Number.isFinite(days2) ? { days: days2 } : {}
|
|
34185
34185
|
});
|
|
34186
34186
|
writeJsonEnvelope(response);
|
|
34187
34187
|
} catch (err) {
|
|
@@ -34412,12 +34412,12 @@ Examples:
|
|
|
34412
34412
|
args: args5,
|
|
34413
34413
|
run: async ({ args: raw }) => {
|
|
34414
34414
|
try {
|
|
34415
|
-
const
|
|
34415
|
+
const limit2 = raw.limit === void 0 ? void 0 : Number(raw.limit);
|
|
34416
34416
|
const response = await apiPost("/api/analytics/arrivals", {
|
|
34417
34417
|
...raw.mode ? { mode: String(raw.mode) } : {},
|
|
34418
34418
|
...raw.outcome ? { outcome: String(raw.outcome) } : {},
|
|
34419
34419
|
...raw.event ? { eventName: String(raw.event) } : {},
|
|
34420
|
-
...
|
|
34420
|
+
...limit2 !== void 0 && Number.isFinite(limit2) ? { limit: limit2 } : {},
|
|
34421
34421
|
...raw.full ? { full: true } : {}
|
|
34422
34422
|
});
|
|
34423
34423
|
writeJsonEnvelope(response);
|
|
@@ -34869,8 +34869,8 @@ function parseGuardrails(value) {
|
|
|
34869
34869
|
}
|
|
34870
34870
|
function buildProfilePatch(args5) {
|
|
34871
34871
|
const patch = {};
|
|
34872
|
-
for (const [
|
|
34873
|
-
const value = args5[
|
|
34872
|
+
for (const [flag3, field] of TEXT_FIELDS) {
|
|
34873
|
+
const value = args5[flag3];
|
|
34874
34874
|
if (typeof value === "string") {
|
|
34875
34875
|
Object.assign(patch, { [field]: value.trim() });
|
|
34876
34876
|
}
|
|
@@ -35726,7 +35726,7 @@ function undisambiguatedSubsets(faces) {
|
|
|
35726
35726
|
if (new Set(faces.map((face) => face.subset)).size < 2) return [];
|
|
35727
35727
|
return [...new Set(faces.filter((face) => !face.unicodeRange).map((face) => face.subset))];
|
|
35728
35728
|
}
|
|
35729
|
-
function suggestFamilies(wanted, catalogue,
|
|
35729
|
+
function suggestFamilies(wanted, catalogue, limit2 = 5) {
|
|
35730
35730
|
const key = (s) => s.toLowerCase().replace(/[^a-z0-9]+/g, " ").trim();
|
|
35731
35731
|
const target2 = key(wanted);
|
|
35732
35732
|
if (!target2) return [];
|
|
@@ -35739,7 +35739,7 @@ function suggestFamilies(wanted, catalogue, limit = 5) {
|
|
|
35739
35739
|
const shared = words2.filter((w) => w.length > 2 && candidate.includes(w)).length;
|
|
35740
35740
|
return { family, score: shared > 0 ? 3 + (words2.length - shared) : Number.POSITIVE_INFINITY };
|
|
35741
35741
|
}).filter((c) => Number.isFinite(c.score)).sort((a, b) => a.score - b.score || a.family.localeCompare(b.family));
|
|
35742
|
-
return scored.slice(0,
|
|
35742
|
+
return scored.slice(0, limit2).map((c) => c.family);
|
|
35743
35743
|
}
|
|
35744
35744
|
function asFontsourceMeta(body) {
|
|
35745
35745
|
if (typeof body !== "object" || body === null) return null;
|
|
@@ -35876,16 +35876,16 @@ function googleFontRequests(css) {
|
|
|
35876
35876
|
}
|
|
35877
35877
|
return out;
|
|
35878
35878
|
}
|
|
35879
|
-
function resolveFetchWeights(
|
|
35880
|
-
if (
|
|
35881
|
-
return String(
|
|
35879
|
+
function resolveFetchWeights(flag3, declared) {
|
|
35880
|
+
if (flag3) {
|
|
35881
|
+
return String(flag3).split(",").map((w) => Number(w.trim())).filter((w) => Number.isInteger(w) && w >= 100 && w <= 900);
|
|
35882
35882
|
}
|
|
35883
35883
|
return declared?.length ? declared : [400];
|
|
35884
35884
|
}
|
|
35885
35885
|
var DEFAULT_SUBSETS = ["latin"];
|
|
35886
|
-
function resolveFetchSubsets(
|
|
35887
|
-
const asked = String(
|
|
35888
|
-
const named =
|
|
35886
|
+
function resolveFetchSubsets(flag3, selfHosted) {
|
|
35887
|
+
const asked = String(flag3 ?? "").split(",").map((subset) => subset.trim()).filter(Boolean);
|
|
35888
|
+
const named = flag3 ? [...new Set(asked)] : selfHosted;
|
|
35889
35889
|
return named.length > 0 ? named : [...DEFAULT_SUBSETS];
|
|
35890
35890
|
}
|
|
35891
35891
|
function resolveFetchStyles(italic, selfHosted) {
|
|
@@ -41631,11 +41631,11 @@ function classifyAudio(volumedetect, hasAudioStream) {
|
|
|
41631
41631
|
}
|
|
41632
41632
|
var RENDER_REVIEW_UNAVAILABLE = "The render was NOT reviewed \u2014 no model gateway key in this environment. Watch it yourself before sending it on.";
|
|
41633
41633
|
var REVIEW_CONCURRENCY = 3;
|
|
41634
|
-
async function mapWithLimit(items,
|
|
41634
|
+
async function mapWithLimit(items, limit2, fn) {
|
|
41635
41635
|
const out = new Array(items.length);
|
|
41636
41636
|
let next = 0;
|
|
41637
41637
|
await Promise.all(
|
|
41638
|
-
Array.from({ length: Math.min(
|
|
41638
|
+
Array.from({ length: Math.min(limit2, items.length) }, async () => {
|
|
41639
41639
|
for (let i = next++; i < items.length; i = next++) {
|
|
41640
41640
|
out[i] = await fn(items[i]);
|
|
41641
41641
|
}
|
|
@@ -42958,8 +42958,8 @@ async function resolveCastAvatar(handle) {
|
|
|
42958
42958
|
};
|
|
42959
42959
|
}
|
|
42960
42960
|
}
|
|
42961
|
-
async function resolveCastAvatars(
|
|
42962
|
-
const handles = String(
|
|
42961
|
+
async function resolveCastAvatars(flag3) {
|
|
42962
|
+
const handles = String(flag3 ?? "").split(",").map((h) => h.trim()).filter(Boolean);
|
|
42963
42963
|
const avatars = [];
|
|
42964
42964
|
for (const handle of handles) {
|
|
42965
42965
|
const resolved = await resolveCastAvatar(handle);
|
|
@@ -43072,7 +43072,7 @@ function fail3(code, message) {
|
|
|
43072
43072
|
process.exit(2);
|
|
43073
43073
|
}
|
|
43074
43074
|
function resolveModels(args5) {
|
|
43075
|
-
const pick = (
|
|
43075
|
+
const pick = (flag3, kind, fallback) => args5[flag3] ? String(args5[flag3]) : resolveModel(kind, fallback);
|
|
43076
43076
|
return {
|
|
43077
43077
|
describeModel: pick("describe-model", "image_describe", "~google/gemini-pro-latest"),
|
|
43078
43078
|
selectModel: pick("select-model", "text_generate", "~google/gemini-flash-latest"),
|
|
@@ -43090,13 +43090,13 @@ var PLATFORM_PLACEMENTS = {
|
|
|
43090
43090
|
other: void 0
|
|
43091
43091
|
};
|
|
43092
43092
|
function resolvePlacements(placementsFlag, definitionPlatform) {
|
|
43093
|
-
const
|
|
43094
|
-
if (
|
|
43095
|
-
if (
|
|
43096
|
-
if (AD_FORMAT_PLATFORMS.includes(
|
|
43093
|
+
const flag3 = placementsFlag?.trim();
|
|
43094
|
+
if (flag3 === void 0 || flag3 === "") return { ok: true, placements: PLATFORM_PLACEMENTS[definitionPlatform] };
|
|
43095
|
+
if (flag3 === "none") return { ok: true, placements: void 0 };
|
|
43096
|
+
if (AD_FORMAT_PLATFORMS.includes(flag3)) return { ok: true, placements: flag3 };
|
|
43097
43097
|
return {
|
|
43098
43098
|
ok: false,
|
|
43099
|
-
message: `--placements "${
|
|
43099
|
+
message: `--placements "${flag3}" is not a valid placement set. Use one of: ${AD_FORMAT_PLATFORMS.join(", ")}, or "none" for a single base ad.`
|
|
43100
43100
|
};
|
|
43101
43101
|
}
|
|
43102
43102
|
function regionCount(layout) {
|
|
@@ -45111,7 +45111,7 @@ function parseBudget(raw) {
|
|
|
45111
45111
|
throw new Error(`--budget must be one of ${VIDEO_BUDGETS.join(", ")} (got "${v}")`);
|
|
45112
45112
|
}
|
|
45113
45113
|
function resolveModels2(args5) {
|
|
45114
|
-
const pick = (
|
|
45114
|
+
const pick = (flag3, kind, fallback) => args5[flag3] ? String(args5[flag3]) : resolveModel2(kind, fallback);
|
|
45115
45115
|
return {
|
|
45116
45116
|
// Flash by default: ~2-3× faster per call than Pro, which keeps each
|
|
45117
45117
|
// per-scene deconstruct step well inside the action time budget and turns a
|
|
@@ -45886,9 +45886,9 @@ var listCommand13 = defineCommand121({
|
|
|
45886
45886
|
}
|
|
45887
45887
|
body.status = args5.status;
|
|
45888
45888
|
}
|
|
45889
|
-
const
|
|
45890
|
-
if (
|
|
45891
|
-
body.limit =
|
|
45889
|
+
const limit2 = parseBoundedInt(args5.limit, "limit", 1, 100);
|
|
45890
|
+
if (limit2 !== void 0) {
|
|
45891
|
+
body.limit = limit2;
|
|
45892
45892
|
}
|
|
45893
45893
|
if (args5.full) {
|
|
45894
45894
|
body.full = true;
|
|
@@ -45985,9 +45985,9 @@ var transcriptCommand = defineCommand121({
|
|
|
45985
45985
|
run: async ({ args: args5 }) => {
|
|
45986
45986
|
try {
|
|
45987
45987
|
const body = { chatId: args5.id };
|
|
45988
|
-
const
|
|
45989
|
-
if (
|
|
45990
|
-
body.limit =
|
|
45988
|
+
const limit2 = parseBoundedInt(args5.limit, "limit", 1, 1e3);
|
|
45989
|
+
if (limit2 !== void 0) {
|
|
45990
|
+
body.limit = limit2;
|
|
45991
45991
|
}
|
|
45992
45992
|
if (args5.full) {
|
|
45993
45993
|
body.full = true;
|
|
@@ -46786,12 +46786,12 @@ function count(value, unit) {
|
|
|
46786
46786
|
return `about ${value} ${unit}${value === 1 ? "" : "s"}`;
|
|
46787
46787
|
}
|
|
46788
46788
|
function describeWait(earliestDecisionAt, now) {
|
|
46789
|
-
const
|
|
46790
|
-
if (
|
|
46791
|
-
if (
|
|
46792
|
-
if (
|
|
46793
|
-
if (
|
|
46794
|
-
return count(Math.round(
|
|
46789
|
+
const days2 = (earliestDecisionAt - now) / DAY_MS5;
|
|
46790
|
+
if (days2 <= 0) return "any moment now";
|
|
46791
|
+
if (days2 < 1) return "within a day";
|
|
46792
|
+
if (days2 < 14) return count(Math.round(days2), "day");
|
|
46793
|
+
if (days2 < 60) return count(Math.round(days2 / 7), "week");
|
|
46794
|
+
return count(Math.round(days2 / 30), "month");
|
|
46795
46795
|
}
|
|
46796
46796
|
function roughCount(count2) {
|
|
46797
46797
|
if (count2 < 10) return `about ${Math.max(1, Math.round(count2)).toLocaleString("en-US")}`;
|
|
@@ -47933,12 +47933,12 @@ var historyCommand = defineCommand124({
|
|
|
47933
47933
|
limit: { type: "string", description: "At most this many, newest first", required: false }
|
|
47934
47934
|
},
|
|
47935
47935
|
run: async ({ args: args5 }) => {
|
|
47936
|
-
const
|
|
47937
|
-
if (
|
|
47936
|
+
const limit2 = args5.limit === void 0 ? void 0 : Number(args5.limit);
|
|
47937
|
+
if (limit2 !== void 0 && !(Number.isInteger(limit2) && limit2 > 0)) fail5("`--limit` is a whole number above zero");
|
|
47938
47938
|
try {
|
|
47939
47939
|
const response = await apiPost("/api/experiments/history", {
|
|
47940
47940
|
...args5.landing ? { landingSlug: String(args5.landing) } : {},
|
|
47941
|
-
...
|
|
47941
|
+
...limit2 === void 0 ? {} : { limit: limit2 }
|
|
47942
47942
|
});
|
|
47943
47943
|
writeJsonEnvelope({
|
|
47944
47944
|
ok: true,
|
|
@@ -50419,8 +50419,8 @@ function parseFunnelArgs(raw) {
|
|
|
50419
50419
|
"Give one of --steps, --step, --remove-step, --preset, --add-trigger or --remove-trigger, not two."
|
|
50420
50420
|
);
|
|
50421
50421
|
}
|
|
50422
|
-
const
|
|
50423
|
-
if (
|
|
50422
|
+
const days2 = raw.days === void 0 ? void 0 : Number(raw.days);
|
|
50423
|
+
if (days2 !== void 0 && (!Number.isInteger(days2) || days2 <= 0 || days2 > 400)) {
|
|
50424
50424
|
throw new FunnelArgsError("--days must be a whole number of days, from 1 to 400.");
|
|
50425
50425
|
}
|
|
50426
50426
|
return {
|
|
@@ -50432,7 +50432,7 @@ function parseFunnelArgs(raw) {
|
|
|
50432
50432
|
...removeTrigger === void 0 ? {} : { removeTrigger },
|
|
50433
50433
|
templates: raw.templates === true,
|
|
50434
50434
|
moments: raw.moments === true,
|
|
50435
|
-
...
|
|
50435
|
+
...days2 === void 0 ? {} : { days: days2 }
|
|
50436
50436
|
};
|
|
50437
50437
|
}
|
|
50438
50438
|
function funnelWrite(args5) {
|
|
@@ -50552,19 +50552,19 @@ function parsePreset(raw) {
|
|
|
50552
50552
|
}
|
|
50553
50553
|
return raw;
|
|
50554
50554
|
}
|
|
50555
|
-
function parseWayIn(stepKey, eventKey,
|
|
50555
|
+
function parseWayIn(stepKey, eventKey, flag3) {
|
|
50556
50556
|
if (stepKey === void 0) return void 0;
|
|
50557
50557
|
if (typeof stepKey !== "string" || stepKey.trim() === "") {
|
|
50558
|
-
throw new FunnelArgsError(`${
|
|
50558
|
+
throw new FunnelArgsError(`${flag3} takes the key of the step to change: \`${flag3} lead\`.`);
|
|
50559
50559
|
}
|
|
50560
50560
|
if (typeof eventKey !== "string" || eventKey.trim() === "") {
|
|
50561
|
-
throw new FunnelArgsError(`${
|
|
50561
|
+
throw new FunnelArgsError(`${flag3} needs --event, naming what gets somebody there.`, {
|
|
50562
50562
|
action: "read_the_moments",
|
|
50563
|
-
explanation: `Run \`baker funnel --moments\` and copy an \`events[].eventKey\` \u2014 a Form's own outcome is listed there at zero before anybody has filled it in. Then: \`${
|
|
50563
|
+
explanation: `Run \`baker funnel --moments\` and copy an \`events[].eventKey\` \u2014 a Form's own outcome is listed there at zero before anybody has filled it in. Then: \`${flag3} ${stepKey.trim()} --event "<key>"\`.`
|
|
50564
50564
|
});
|
|
50565
50565
|
}
|
|
50566
50566
|
const key = eventKey.trim();
|
|
50567
|
-
if (
|
|
50567
|
+
if (flag3 === "--add-trigger" && parseEventKey(key) === null) {
|
|
50568
50568
|
throw new FunnelArgsError(`"${key}" is not an event key, so nothing would ever reach that step through it.`, {
|
|
50569
50569
|
action: "read_the_moments",
|
|
50570
50570
|
explanation: "Run `baker funnel --moments` and copy an `events[].eventKey`. They look like form:<form>:<step>:<trigger>, submit:<form>, page:<name> or view:<landing>."
|
|
@@ -50830,29 +50830,29 @@ function requireTarget5(args5, entity) {
|
|
|
50830
50830
|
}
|
|
50831
50831
|
return target2;
|
|
50832
50832
|
}
|
|
50833
|
-
function readJsonSource(args5,
|
|
50834
|
-
const inline = args5[
|
|
50833
|
+
function readJsonSource(args5, flag3) {
|
|
50834
|
+
const inline = args5[flag3];
|
|
50835
50835
|
const file = args5.file;
|
|
50836
50836
|
const raw = typeof file === "string" && file.length > 0 ? readFileSync17(file, "utf8") : typeof inline === "string" && inline.length > 0 ? inline : void 0;
|
|
50837
50837
|
if (raw === void 0) {
|
|
50838
|
-
failValidation3(`pass --${
|
|
50838
|
+
failValidation3(`pass --${flag3} with inline JSON or --file with a path to a JSON file`);
|
|
50839
50839
|
}
|
|
50840
50840
|
try {
|
|
50841
50841
|
return JSON.parse(raw);
|
|
50842
50842
|
} catch {
|
|
50843
|
-
return failValidation3(`--${
|
|
50843
|
+
return failValidation3(`--${flag3} is not valid JSON`);
|
|
50844
50844
|
}
|
|
50845
50845
|
}
|
|
50846
|
-
function asObject(value,
|
|
50846
|
+
function asObject(value, flag3, index) {
|
|
50847
50847
|
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
50848
50848
|
failValidation3(
|
|
50849
|
-
index === void 0 ? `--${
|
|
50849
|
+
index === void 0 ? `--${flag3} must be a JSON object` : `--${flag3}[${index}] must be a JSON object`
|
|
50850
50850
|
);
|
|
50851
50851
|
}
|
|
50852
50852
|
return value;
|
|
50853
50853
|
}
|
|
50854
|
-
function loadJsonArg(args5,
|
|
50855
|
-
return asObject(readJsonSource(args5,
|
|
50854
|
+
function loadJsonArg(args5, flag3 = "json") {
|
|
50855
|
+
return asObject(readJsonSource(args5, flag3), flag3);
|
|
50856
50856
|
}
|
|
50857
50857
|
function loadJsonPayloads(args5) {
|
|
50858
50858
|
const parsed = readJsonSource(args5, "json");
|
|
@@ -51702,18 +51702,18 @@ var RETENTION_BY_MONTHS = {
|
|
|
51702
51702
|
"38": "THIRTY_EIGHT_MONTHS",
|
|
51703
51703
|
"50": "FIFTY_MONTHS"
|
|
51704
51704
|
};
|
|
51705
|
-
function retentionValue(input,
|
|
51705
|
+
function retentionValue(input, flag3) {
|
|
51706
51706
|
const normalized = input.trim();
|
|
51707
51707
|
const mapped = RETENTION_BY_MONTHS[normalized] ?? (Object.values(RETENTION_BY_MONTHS).includes(normalized.toUpperCase()) ? normalized.toUpperCase() : void 0);
|
|
51708
51708
|
if (!mapped) {
|
|
51709
|
-
failValidation3(`--${
|
|
51709
|
+
failValidation3(`--${flag3} must be one of 2, 14, 26, 38 or 50 (months)`);
|
|
51710
51710
|
}
|
|
51711
51711
|
return mapped;
|
|
51712
51712
|
}
|
|
51713
|
-
function booleanArg(input,
|
|
51713
|
+
function booleanArg(input, flag3) {
|
|
51714
51714
|
if (input === "true") return true;
|
|
51715
51715
|
if (input === "false") return false;
|
|
51716
|
-
return failValidation3(`--${
|
|
51716
|
+
return failValidation3(`--${flag3} must be true or false`);
|
|
51717
51717
|
}
|
|
51718
51718
|
var dataRetentionCommand = defineCommand136({
|
|
51719
51719
|
meta: {
|
|
@@ -52049,10 +52049,10 @@ Examples:
|
|
|
52049
52049
|
},
|
|
52050
52050
|
run: async ({ args: args5 }) => {
|
|
52051
52051
|
const payload = {};
|
|
52052
|
-
for (const [
|
|
52053
|
-
const value = args5[
|
|
52052
|
+
for (const [flag3, field] of Object.entries(MEASUREMENT_FLAGS)) {
|
|
52053
|
+
const value = args5[flag3];
|
|
52054
52054
|
if (typeof value === "string") {
|
|
52055
|
-
payload[field] = booleanArg(value,
|
|
52055
|
+
payload[field] = booleanArg(value, flag3);
|
|
52056
52056
|
}
|
|
52057
52057
|
}
|
|
52058
52058
|
if (typeof args5["search-parameters"] === "string" && args5["search-parameters"].length > 0) {
|
|
@@ -52624,13 +52624,13 @@ var listCommand15 = defineCommand142({
|
|
|
52624
52624
|
run: async ({ args: args5 }) => {
|
|
52625
52625
|
try {
|
|
52626
52626
|
const body = {};
|
|
52627
|
-
const
|
|
52628
|
-
if (
|
|
52629
|
-
body.limit =
|
|
52627
|
+
const limit2 = parseBoundedInt2(args5.limit, "limit", 1, 200);
|
|
52628
|
+
if (limit2 !== void 0) {
|
|
52629
|
+
body.limit = limit2;
|
|
52630
52630
|
}
|
|
52631
|
-
const
|
|
52632
|
-
if (
|
|
52633
|
-
body.days =
|
|
52631
|
+
const days2 = parseBoundedInt2(args5.days, "days", 1, 365);
|
|
52632
|
+
if (days2 !== void 0) {
|
|
52633
|
+
body.days = days2;
|
|
52634
52634
|
}
|
|
52635
52635
|
if (args5.category) {
|
|
52636
52636
|
if (!CATEGORIES.includes(args5.category)) {
|
|
@@ -53015,20 +53015,20 @@ var formsSubmissionsCommand = defineCommand143({
|
|
|
53015
53015
|
},
|
|
53016
53016
|
run: async ({ args: args5 }) => {
|
|
53017
53017
|
try {
|
|
53018
|
-
const
|
|
53019
|
-
if (!Number.isInteger(
|
|
53018
|
+
const days2 = args5.days === void 0 ? 30 : Number(args5.days);
|
|
53019
|
+
if (!Number.isInteger(days2) || days2 < 1 || days2 > 365) {
|
|
53020
53020
|
throw new ApiError("VALIDATION_ERROR", "--days must be a whole number between 1 and 365.");
|
|
53021
53021
|
}
|
|
53022
53022
|
const response = await apiPost("/api/hubspot/forms/submissions", {
|
|
53023
53023
|
formId: args5.formId,
|
|
53024
|
-
days,
|
|
53024
|
+
days: days2,
|
|
53025
53025
|
...args5.full ? { full: true } : {}
|
|
53026
53026
|
});
|
|
53027
53027
|
const { inWindow, total, truncated, lastSubmittedAt } = response.data;
|
|
53028
53028
|
const hints2 = [];
|
|
53029
53029
|
if (inWindow === 0) {
|
|
53030
53030
|
hints2.push(
|
|
53031
|
-
total === 0 ? "This form has received nothing at all \u2014 check it is the form actually embedded on the page before concluding the campaign is at fault." : `No leads in the last ${
|
|
53031
|
+
total === 0 ? "This form has received nothing at all \u2014 check it is the form actually embedded on the page before concluding the campaign is at fault." : `No leads in the last ${days2} days, though the form has ${total} older submission(s). It worked once and has gone quiet.`
|
|
53032
53032
|
);
|
|
53033
53033
|
}
|
|
53034
53034
|
if (truncated) {
|
|
@@ -53215,15 +53215,15 @@ var contactsSummaryCommand = defineCommand143({
|
|
|
53215
53215
|
},
|
|
53216
53216
|
run: async ({ args: args5 }) => {
|
|
53217
53217
|
try {
|
|
53218
|
-
const
|
|
53219
|
-
if (!Number.isInteger(
|
|
53218
|
+
const days2 = args5.days === void 0 ? 30 : Number(args5.days);
|
|
53219
|
+
if (!Number.isInteger(days2) || days2 < 1 || days2 > 365) {
|
|
53220
53220
|
throw new ApiError("VALIDATION_ERROR", "--days must be a whole number between 1 and 365.");
|
|
53221
53221
|
}
|
|
53222
|
-
const response = await apiPost("/api/hubspot/contacts/summary", { days });
|
|
53222
|
+
const response = await apiPost("/api/hubspot/contacts/summary", { days: days2 });
|
|
53223
53223
|
const { total, withOwner, withoutOwner, byStage, truncated } = response.data;
|
|
53224
53224
|
const hints2 = [];
|
|
53225
53225
|
if (total === 0) {
|
|
53226
|
-
hints2.push(`No contacts were created in the last ${
|
|
53226
|
+
hints2.push(`No contacts were created in the last ${days2} days \u2014 nothing reached the CRM in that window.`);
|
|
53227
53227
|
} else if (withOwner === 0) {
|
|
53228
53228
|
hints2.push(
|
|
53229
53229
|
`None of these ${total} contacts has anyone assigned. That is the strongest available signal that the leads are arriving and nobody is picking them up \u2014 worth raising with the user.`
|
|
@@ -54308,7 +54308,7 @@ function emptyResultHints({ provider, hitCount, activeFilters, subject }) {
|
|
|
54308
54308
|
return hints2;
|
|
54309
54309
|
}
|
|
54310
54310
|
function activeFilterFlags(args5, candidates) {
|
|
54311
|
-
return candidates.filter((
|
|
54311
|
+
return candidates.filter((flag3) => args5[flag3] !== void 0 && args5[flag3] !== "").map((flag3) => `--${flag3}`);
|
|
54312
54312
|
}
|
|
54313
54313
|
|
|
54314
54314
|
// src/commands/images/find.ts
|
|
@@ -58661,8 +58661,8 @@ var favoritesCommand = defineCommand174({
|
|
|
58661
58661
|
const params2 = {};
|
|
58662
58662
|
const types = splitList2(args5.type);
|
|
58663
58663
|
if (types) params2.type = types.join(",");
|
|
58664
|
-
const
|
|
58665
|
-
if (
|
|
58664
|
+
const limit2 = parseNumber(args5.limit);
|
|
58665
|
+
if (limit2 !== void 0) params2.limit = String(limit2);
|
|
58666
58666
|
const data = await apiGet("/api/landing-inspiration/favorites", params2);
|
|
58667
58667
|
const sections = Array.isArray(data?.sections) ? data.sections : [];
|
|
58668
58668
|
const full = args5.full;
|
|
@@ -60147,8 +60147,8 @@ var inPageSegment = (options) => {
|
|
|
60147
60147
|
};
|
|
60148
60148
|
const leaves = [];
|
|
60149
60149
|
const all = document.querySelectorAll("*");
|
|
60150
|
-
const
|
|
60151
|
-
for (let i = 0; i <
|
|
60150
|
+
const limit2 = Math.min(all.length, options.maxElements);
|
|
60151
|
+
for (let i = 0; i < limit2; i++) {
|
|
60152
60152
|
const element = all[i];
|
|
60153
60153
|
if (!element) continue;
|
|
60154
60154
|
const tag = element.tagName.toLowerCase();
|
|
@@ -61233,8 +61233,8 @@ function buildSearchBody(args5) {
|
|
|
61233
61233
|
if (minCraft !== void 0) body.minCraftScore = minCraft;
|
|
61234
61234
|
const minFidelity = parseNumber(args5["min-fidelity"]);
|
|
61235
61235
|
if (minFidelity !== void 0) body.minFidelity = minFidelity;
|
|
61236
|
-
const
|
|
61237
|
-
body.limit =
|
|
61236
|
+
const limit2 = parseNumber(args5.limit);
|
|
61237
|
+
body.limit = limit2 ?? 8;
|
|
61238
61238
|
body.scope = args5.scope === "all" ? "all" : "favorites";
|
|
61239
61239
|
return body;
|
|
61240
61240
|
}
|
|
@@ -61381,8 +61381,8 @@ function buildSequencesBody(args5) {
|
|
|
61381
61381
|
const sectionType = splitList2(args5["section-type"]);
|
|
61382
61382
|
if (sectionType) body.sectionType = sectionType;
|
|
61383
61383
|
body.scope = args5.scope === "all" ? "all" : "favorites";
|
|
61384
|
-
const
|
|
61385
|
-
if (
|
|
61384
|
+
const limit2 = parseNumber(args5.limit);
|
|
61385
|
+
if (limit2 !== void 0) body.limit = limit2;
|
|
61386
61386
|
else if (args5.full) body.limit = MAX_PAGES;
|
|
61387
61387
|
return body;
|
|
61388
61388
|
}
|
|
@@ -62439,7 +62439,7 @@ Examples:
|
|
|
62439
62439
|
const keyword = args5.keyword;
|
|
62440
62440
|
const location2 = args5.location || void 0;
|
|
62441
62441
|
const language = args5.language || void 0;
|
|
62442
|
-
const
|
|
62442
|
+
const limit2 = args5.limit ? Number(args5.limit) : void 0;
|
|
62443
62443
|
const skipCache = args5["no-cache"] ? true : void 0;
|
|
62444
62444
|
const queryContext = buildResearchQueryContext(location2, language);
|
|
62445
62445
|
try {
|
|
@@ -62447,7 +62447,7 @@ Examples:
|
|
|
62447
62447
|
keyword,
|
|
62448
62448
|
location: location2,
|
|
62449
62449
|
language,
|
|
62450
|
-
limit,
|
|
62450
|
+
limit: limit2,
|
|
62451
62451
|
skipCache
|
|
62452
62452
|
});
|
|
62453
62453
|
warnDefaults(queryContext);
|
|
@@ -62517,7 +62517,7 @@ Examples:
|
|
|
62517
62517
|
const keyword = args5.keyword;
|
|
62518
62518
|
const location2 = args5.location || void 0;
|
|
62519
62519
|
const language = args5.language || void 0;
|
|
62520
|
-
const
|
|
62520
|
+
const limit2 = args5.limit ? Number(args5.limit) : void 0;
|
|
62521
62521
|
const skipCache = args5["no-cache"] ? true : void 0;
|
|
62522
62522
|
const queryContext = buildResearchQueryContext(location2, language);
|
|
62523
62523
|
try {
|
|
@@ -62525,7 +62525,7 @@ Examples:
|
|
|
62525
62525
|
keyword,
|
|
62526
62526
|
location: location2,
|
|
62527
62527
|
language,
|
|
62528
|
-
limit,
|
|
62528
|
+
limit: limit2,
|
|
62529
62529
|
skipCache
|
|
62530
62530
|
});
|
|
62531
62531
|
warnDefaults(queryContext);
|
|
@@ -62896,7 +62896,7 @@ Examples:
|
|
|
62896
62896
|
const location2 = args5.location || void 0;
|
|
62897
62897
|
const language = args5.language || void 0;
|
|
62898
62898
|
const type = args5.type || void 0;
|
|
62899
|
-
const
|
|
62899
|
+
const limit2 = args5.limit ? Number(args5.limit) : void 0;
|
|
62900
62900
|
const offset = args5.offset ? Number(args5.offset) : void 0;
|
|
62901
62901
|
const skipCache = args5["no-cache"] ? true : void 0;
|
|
62902
62902
|
const queryContext = buildResearchQueryContext(location2, language);
|
|
@@ -62907,12 +62907,12 @@ Examples:
|
|
|
62907
62907
|
location: location2,
|
|
62908
62908
|
language,
|
|
62909
62909
|
type,
|
|
62910
|
-
limit,
|
|
62910
|
+
limit: limit2,
|
|
62911
62911
|
offset,
|
|
62912
62912
|
skipCache
|
|
62913
62913
|
});
|
|
62914
62914
|
const currentOffset = offset ?? 0;
|
|
62915
|
-
const currentLimit =
|
|
62915
|
+
const currentLimit = limit2 ?? 50;
|
|
62916
62916
|
const hasMore = currentOffset + result.data.length < result.total_count;
|
|
62917
62917
|
warnDefaults(queryContext);
|
|
62918
62918
|
const format = args5.output || "json";
|
|
@@ -63165,7 +63165,7 @@ Examples:
|
|
|
63165
63165
|
const language = args5.language || void 0;
|
|
63166
63166
|
const sort = args5.sort || void 0;
|
|
63167
63167
|
const type = args5.type || void 0;
|
|
63168
|
-
const
|
|
63168
|
+
const limit2 = args5.limit ? Number(args5.limit) : void 0;
|
|
63169
63169
|
const skipCache = args5["no-cache"] ? true : void 0;
|
|
63170
63170
|
const queryContext = buildResearchQueryContext(location2, language);
|
|
63171
63171
|
try {
|
|
@@ -63175,7 +63175,7 @@ Examples:
|
|
|
63175
63175
|
language,
|
|
63176
63176
|
sort,
|
|
63177
63177
|
type,
|
|
63178
|
-
limit,
|
|
63178
|
+
limit: limit2,
|
|
63179
63179
|
skipCache
|
|
63180
63180
|
});
|
|
63181
63181
|
warnDefaults(queryContext);
|
|
@@ -63346,7 +63346,7 @@ Examples:
|
|
|
63346
63346
|
const target2 = args5.target;
|
|
63347
63347
|
const location2 = args5.location || void 0;
|
|
63348
63348
|
const language = args5.language || void 0;
|
|
63349
|
-
const
|
|
63349
|
+
const limit2 = args5.limit ? Number(args5.limit) : void 0;
|
|
63350
63350
|
const skipCache = args5["no-cache"] ? true : void 0;
|
|
63351
63351
|
const queryContext = buildResearchQueryContext(location2, language);
|
|
63352
63352
|
try {
|
|
@@ -63354,7 +63354,7 @@ Examples:
|
|
|
63354
63354
|
target: target2,
|
|
63355
63355
|
location: location2,
|
|
63356
63356
|
language,
|
|
63357
|
-
limit,
|
|
63357
|
+
limit: limit2,
|
|
63358
63358
|
skipCache
|
|
63359
63359
|
});
|
|
63360
63360
|
warnDefaults(queryContext);
|
|
@@ -63700,13 +63700,13 @@ function refuseMisusedKinds(kind, args5) {
|
|
|
63700
63700
|
function refuseStrayFlags(kind, args5) {
|
|
63701
63701
|
const allowed = new Set(TRIGGER_TARGETS[kind].map((target2) => TRIGGER_TARGET_FIELDS[target2.field].flag));
|
|
63702
63702
|
const stray = TRIGGER_TARGET_FIELD_IDS.map((field) => TRIGGER_TARGET_FIELDS[field].flag).filter(
|
|
63703
|
-
(
|
|
63703
|
+
(flag3) => !allowed.has(flag3) && flagText(args5, flag3) !== void 0 && !(kind === "experiment.started" && flag3 === "experiment")
|
|
63704
63704
|
);
|
|
63705
63705
|
if (stray.length === 0) {
|
|
63706
63706
|
return;
|
|
63707
63707
|
}
|
|
63708
63708
|
failValidation4(
|
|
63709
|
-
`${stray.map((
|
|
63709
|
+
`${stray.map((flag3) => `--${flag3}`).join(", ")} does not narrow ${kind}. ${allowed.size > 0 ? `It can be pointed with ${[...allowed].map((flag3) => `--${flag3}`).join(", ")}.` : "It runs every time it happens \u2014 narrow it with --when rules over its payload instead."}`
|
|
63710
63710
|
);
|
|
63711
63711
|
}
|
|
63712
63712
|
var NARROWING_FLAGS = [...TRIGGER_TARGET_FIELD_IDS.map((field) => TRIGGER_TARGET_FIELDS[field].flag), "config"];
|
|
@@ -63714,11 +63714,11 @@ function refuseNarrowingWithoutKind(args5, kind, given) {
|
|
|
63714
63714
|
if (kind) {
|
|
63715
63715
|
return;
|
|
63716
63716
|
}
|
|
63717
|
-
const stray = NARROWING_FLAGS.filter((
|
|
63717
|
+
const stray = NARROWING_FLAGS.filter((flag3) => flagText(args5, flag3) !== void 0);
|
|
63718
63718
|
if (stray.length === 0) {
|
|
63719
63719
|
return;
|
|
63720
63720
|
}
|
|
63721
|
-
const flags = stray.map((
|
|
63721
|
+
const flags = stray.map((flag3) => `--${flag3}`).join(", ");
|
|
63722
63722
|
failValidation4(
|
|
63723
63723
|
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.`
|
|
63724
63724
|
);
|
|
@@ -63809,13 +63809,13 @@ var REACTION_ARGS = {
|
|
|
63809
63809
|
required: false
|
|
63810
63810
|
}
|
|
63811
63811
|
};
|
|
63812
|
-
function wholeNumber(raw,
|
|
63812
|
+
function wholeNumber(raw, flag3, { min }) {
|
|
63813
63813
|
if (raw === void 0) {
|
|
63814
63814
|
return void 0;
|
|
63815
63815
|
}
|
|
63816
63816
|
const value = Number(raw);
|
|
63817
63817
|
if (!Number.isInteger(value) || value < min) {
|
|
63818
|
-
failValidation4(`${
|
|
63818
|
+
failValidation4(`${flag3} must be a whole number of at least ${min}.`);
|
|
63819
63819
|
}
|
|
63820
63820
|
return value;
|
|
63821
63821
|
}
|
|
@@ -64285,31 +64285,31 @@ Full guide: __tooling__/docs/tools/baker/scheduled-agents.md`
|
|
|
64285
64285
|
delete: { type: "string", description: "Remove one of this company's own, by id", required: false }
|
|
64286
64286
|
},
|
|
64287
64287
|
run: async ({ args: args5 }) => {
|
|
64288
|
-
const
|
|
64288
|
+
const flag3 = (key) => {
|
|
64289
64289
|
const value = args5[key];
|
|
64290
64290
|
return typeof value === "string" ? value.trim() : "";
|
|
64291
64291
|
};
|
|
64292
64292
|
try {
|
|
64293
|
-
const save =
|
|
64294
|
-
const publish =
|
|
64295
|
-
const remove =
|
|
64293
|
+
const save = flag3("save");
|
|
64294
|
+
const publish = flag3("publish");
|
|
64295
|
+
const remove = flag3("delete");
|
|
64296
64296
|
if ([save, publish, remove].filter((id) => id.length > 0).length > 1) {
|
|
64297
64297
|
failValidation4("Pass one of --save, --publish or --delete at a time.");
|
|
64298
64298
|
}
|
|
64299
64299
|
if (save.length > 0) {
|
|
64300
|
-
const briefFile =
|
|
64301
|
-
const brief2 = briefFile.length > 0 ? await readFile32(path46.resolve(briefFile), "utf8") :
|
|
64300
|
+
const briefFile = flag3("brief-file");
|
|
64301
|
+
const brief2 = briefFile.length > 0 ? await readFile32(path46.resolve(briefFile), "utf8") : flag3("brief");
|
|
64302
64302
|
if (brief2.trim().length === 0) {
|
|
64303
64303
|
failValidation4("--brief-file (preferred) or --brief is required: the brief is the recipe.");
|
|
64304
64304
|
}
|
|
64305
64305
|
const response2 = await apiPost("/api/marketplace/save", {
|
|
64306
64306
|
chatId: requireChatId(),
|
|
64307
64307
|
slug: save,
|
|
64308
|
-
name:
|
|
64309
|
-
category:
|
|
64310
|
-
tagline:
|
|
64308
|
+
name: flag3("name"),
|
|
64309
|
+
category: flag3("recipe-category"),
|
|
64310
|
+
tagline: flag3("tagline"),
|
|
64311
64311
|
prompt: brief2,
|
|
64312
|
-
frequency:
|
|
64312
|
+
frequency: flag3("frequency")
|
|
64313
64313
|
});
|
|
64314
64314
|
writeJson(response2);
|
|
64315
64315
|
return;
|
|
@@ -64330,7 +64330,7 @@ Full guide: __tooling__/docs/tools/baker/scheduled-agents.md`
|
|
|
64330
64330
|
writeJson(response2);
|
|
64331
64331
|
return;
|
|
64332
64332
|
}
|
|
64333
|
-
const category =
|
|
64333
|
+
const category = flag3("category");
|
|
64334
64334
|
const response = await apiPost("/api/marketplace/list", {
|
|
64335
64335
|
...category.length > 0 ? { category } : {},
|
|
64336
64336
|
...args5.drafts === true ? { drafts: true } : {}
|
|
@@ -64622,6 +64622,165 @@ var schemaCommand2 = defineCommand208({
|
|
|
64622
64622
|
}
|
|
64623
64623
|
});
|
|
64624
64624
|
|
|
64625
|
+
// src/commands/shopify/index.ts
|
|
64626
|
+
import { defineCommand as defineCommand210 } from "citty";
|
|
64627
|
+
|
|
64628
|
+
// src/commands/shopify/buyers.ts
|
|
64629
|
+
import { defineCommand as defineCommand209 } from "citty";
|
|
64630
|
+
|
|
64631
|
+
// src/commands/shopify/buyerHints.ts
|
|
64632
|
+
var AUDIENCE_PATH = "To reach more people like your buyers on Meta, stage an audience of the store's purchasers (`baker ads meta audiences create --subtype WEBSITE`) and a lookalike of it (`--subtype LOOKALIKE`). Baker cannot upload a list of buyers to an ad platform.";
|
|
64633
|
+
var MIN_ORDERS_TO_SCALE = 5;
|
|
64634
|
+
function storeFactsHint(currency) {
|
|
64635
|
+
return `These are the store's own orders: build the answer on them and quote their numbers${currency ? ` in ${currency}` : ""}. If Knowledge describes other products or markets, say the two disagree and ask which is current \u2014 never drop these numbers for it.`;
|
|
64636
|
+
}
|
|
64637
|
+
var percent2 = (value) => `${Math.round(value * 100)}%`;
|
|
64638
|
+
function buyerHistoryHints(history, basis) {
|
|
64639
|
+
const hints2 = [];
|
|
64640
|
+
if (history.state === "importing") {
|
|
64641
|
+
hints2.push(
|
|
64642
|
+
`Baker is still bringing in this store's past orders (${history.orders} so far): older periods, repeat buyers and cohorts are incomplete \u2014 say so rather than reporting a drop.`
|
|
64643
|
+
);
|
|
64644
|
+
}
|
|
64645
|
+
if (history.state === "failed") {
|
|
64646
|
+
hints2.push("Baker couldn't finish bringing in this store's past orders; older periods may be incomplete.");
|
|
64647
|
+
}
|
|
64648
|
+
if (basis === "last_60_days") {
|
|
64649
|
+
hints2.push(
|
|
64650
|
+
"Shopify gives Baker this store's last 60 days of orders only: older behaviour is not here, and a buyer whose earlier orders are older counts as new."
|
|
64651
|
+
);
|
|
64652
|
+
}
|
|
64653
|
+
return hints2;
|
|
64654
|
+
}
|
|
64655
|
+
function countriesHints(answer) {
|
|
64656
|
+
const hints2 = buyerHistoryHints(answer.history, answer.basis);
|
|
64657
|
+
if (answer.total.orders === 0) {
|
|
64658
|
+
hints2.push("No orders were placed in this period.");
|
|
64659
|
+
return hints2;
|
|
64660
|
+
}
|
|
64661
|
+
hints2.push(storeFactsHint(answer.currency));
|
|
64662
|
+
const money = (amount) => `${answer.currency ?? ""} ${amount}`.trim();
|
|
64663
|
+
const growing = answer.rows.filter((row) => row.orders >= MIN_ORDERS_TO_SCALE && (row.ordersGrowth ?? 0) > 0).sort((a, b) => (b.ordersGrowth ?? 0) - (a.ordersGrowth ?? 0))[0];
|
|
64664
|
+
if (growing && answer.level === "country") {
|
|
64665
|
+
hints2.push(
|
|
64666
|
+
`Scale ${growing.name}: its orders grew ${percent2(growing.ordersGrowth ?? 0)} against the period before (${growing.orders} orders, ${money(growing.net)}). Check which campaigns already target it (\`baker ads meta campaigns\`, \`baker ads google query --preset campaign-performance\`) before staging a change.`
|
|
64667
|
+
);
|
|
64668
|
+
}
|
|
64669
|
+
const average = answer.total.averageOrder;
|
|
64670
|
+
const promising = answer.rows.filter((row) => row.orders < MIN_ORDERS_TO_SCALE && average > 0 && row.averageOrder >= 1.5 * average).sort((a, b) => b.averageOrder - a.averageOrder)[0];
|
|
64671
|
+
if (promising && answer.level === "country") {
|
|
64672
|
+
hints2.push(
|
|
64673
|
+
`Test ${promising.name}: its average order is ${money(promising.averageOrder)}, ${(promising.averageOrder / average).toFixed(1)}\xD7 the store's, from only ${promising.orders} orders \u2014 a small budget there would show whether it scales.`
|
|
64674
|
+
);
|
|
64675
|
+
}
|
|
64676
|
+
if (answer.unknown && answer.total.orders > 0 && answer.unknown.orders / answer.total.orders > 0.25) {
|
|
64677
|
+
const lack = answer.level === "country" ? "have no address Baker could read (Unknown)" : `name no ${answer.level} on their address (${answer.unknown.name})`;
|
|
64678
|
+
hints2.push(
|
|
64679
|
+
`${answer.unknown.orders} of these orders ${lack}: say so rather than leaving them out of a place's share.`
|
|
64680
|
+
);
|
|
64681
|
+
}
|
|
64682
|
+
return hints2;
|
|
64683
|
+
}
|
|
64684
|
+
function bestSellersHints(answer) {
|
|
64685
|
+
const hints2 = buyerHistoryHints(answer.history, answer.basis);
|
|
64686
|
+
const leader = answer.repeat?.[0];
|
|
64687
|
+
if (leader && leader.buyersAgain > 0) {
|
|
64688
|
+
hints2.push(
|
|
64689
|
+
`Lead a landing with "${leader.title}" (find it with \`baker shopify products --search\`): ${percent2(leader.share)} of the buyers whose first order held it came back.`
|
|
64690
|
+
);
|
|
64691
|
+
}
|
|
64692
|
+
const top = answer.products?.[0];
|
|
64693
|
+
if (top && top.units > 0) {
|
|
64694
|
+
hints2.push(
|
|
64695
|
+
`"${top.title}" is the best seller (${top.units} units). For which first buys bring buyers back, run \`baker shopify best-sellers --view repeat\`; for what is losing steam, \`--view momentum\`.`
|
|
64696
|
+
);
|
|
64697
|
+
}
|
|
64698
|
+
const losing = answer.momentum?.[0];
|
|
64699
|
+
if (losing) {
|
|
64700
|
+
hints2.push(
|
|
64701
|
+
`"${losing.title}" sold ${losing.units} against ${losing.previousUnits} the period before: check its stock (\`baker shopify stock\`) and the ads that promote it before cutting it.`
|
|
64702
|
+
);
|
|
64703
|
+
}
|
|
64704
|
+
const sellers = answer.products ?? answer.variants ?? [];
|
|
64705
|
+
if (sellers.some((row) => row.units > 0)) hints2.push(storeFactsHint(answer.currency));
|
|
64706
|
+
const withRefunds = sellers.filter((row) => row.refundedUnits > 0).sort((a, b) => b.refundRate - a.refundRate).slice(0, 5);
|
|
64707
|
+
if (withRefunds.length > 0) {
|
|
64708
|
+
const money = (amount) => `${answer.currency ?? ""} ${amount}`.trim();
|
|
64709
|
+
hints2.push(
|
|
64710
|
+
`Refunds: ${withRefunds.map((row) => `"${row.title}" ${percent2(row.refundRate)} of units (${money(row.refunded)})`).join(", ")}. Only a product not named here had no refunds in the period.`
|
|
64711
|
+
);
|
|
64712
|
+
}
|
|
64713
|
+
const refunded = answer.products?.find((row) => row.units >= 5 && row.refundRate >= 0.2);
|
|
64714
|
+
if (refunded) {
|
|
64715
|
+
hints2.push(
|
|
64716
|
+
`"${refunded.title}" comes back often (${percent2(refunded.refundRate)} of units refunded): don't lead a campaign with it before finding out why.`
|
|
64717
|
+
);
|
|
64718
|
+
}
|
|
64719
|
+
return hints2;
|
|
64720
|
+
}
|
|
64721
|
+
function bestBuyersHint(best) {
|
|
64722
|
+
if (!best || best.buyers === 0) return [];
|
|
64723
|
+
const countries = best.topCountries.map((country) => country.name).join(", ");
|
|
64724
|
+
const products = best.topFirstProducts.map((product) => `"${product.title}"`).join(", ");
|
|
64725
|
+
if (!countries && !products) return [];
|
|
64726
|
+
return [
|
|
64727
|
+
`Find more buyers like your best (${best.buyers}): ${countries ? `target ${countries}` : ""}${countries && products ? "; " : ""}${products ? `lead with ${products}, what their first orders held` : ""}.`
|
|
64728
|
+
];
|
|
64729
|
+
}
|
|
64730
|
+
function segmentsHints(answer) {
|
|
64731
|
+
const hints2 = buyerHistoryHints(answer.history, answer.basis);
|
|
64732
|
+
hints2.push(...bestBuyersHint(answer.segments.find((segment) => segment.segment === "best")));
|
|
64733
|
+
const atRisk = answer.segments.find((segment) => segment.segment === "at_risk");
|
|
64734
|
+
if (atRisk && atRisk.buyers > 0) {
|
|
64735
|
+
hints2.push(
|
|
64736
|
+
`${atRisk.buyers} repeat buyers have not ordered in ${Math.round(2 * answer.thresholds.gapDays)} days (At risk): a win-back offer to them is worth more than a new buyer of the same value.`
|
|
64737
|
+
);
|
|
64738
|
+
}
|
|
64739
|
+
if (answer.buyers > 0) hints2.push(AUDIENCE_PATH);
|
|
64740
|
+
return hints2;
|
|
64741
|
+
}
|
|
64742
|
+
function buyersHints(answer) {
|
|
64743
|
+
const hints2 = buyerHistoryHints(answer.history, answer.basis);
|
|
64744
|
+
if (answer.summary.orders === 0) {
|
|
64745
|
+
hints2.push("No orders were placed in this period.");
|
|
64746
|
+
return hints2;
|
|
64747
|
+
}
|
|
64748
|
+
hints2.push(storeFactsHint(answer.currency));
|
|
64749
|
+
if (answer.acquisition?.by === "campaign" && answer.acquisition.rows.length > 0) {
|
|
64750
|
+
hints2.push(
|
|
64751
|
+
"Compare each campaign's lifetime value with what it cost for the same dates (`baker ads meta insights`, `baker ads google query --preset campaign-performance`): Baker keeps no ad spend."
|
|
64752
|
+
);
|
|
64753
|
+
}
|
|
64754
|
+
if (!answer.acquisition) {
|
|
64755
|
+
hints2.push(
|
|
64756
|
+
"For lifetime value by the campaign or channel that brought each buyer, add `--by campaign` or `--by channel`."
|
|
64757
|
+
);
|
|
64758
|
+
}
|
|
64759
|
+
if (answer.summary.guestOrders > 0 && answer.summary.guestOrders >= answer.summary.orders / 2) {
|
|
64760
|
+
hints2.push(
|
|
64761
|
+
`${answer.summary.guestOrders} of ${answer.summary.orders} orders were placed as guests: they count in sales, never as buyers, so repeat rates read low.`
|
|
64762
|
+
);
|
|
64763
|
+
}
|
|
64764
|
+
if (answer.summary.buyers > 0) hints2.push(AUDIENCE_PATH);
|
|
64765
|
+
return hints2;
|
|
64766
|
+
}
|
|
64767
|
+
function cohortsHints(answer) {
|
|
64768
|
+
const hints2 = buyerHistoryHints(answer.history, answer.basis);
|
|
64769
|
+
const reached = answer.cohorts.filter((cohort) => cohort.retention.m3 !== null && cohort.size >= 5);
|
|
64770
|
+
if (reached.length >= 2) {
|
|
64771
|
+
const best = [...reached].sort((a, b) => (b.retention.m3 ?? 0) - (a.retention.m3 ?? 0))[0];
|
|
64772
|
+
if (best) {
|
|
64773
|
+
hints2.push(
|
|
64774
|
+
`Buyers who first ordered in ${best.month} came back best (${percent2(best.retention.m3 ?? 0)} ordered again in their third month): look at what was running then (\`baker shopify buyers --by campaign\`).`
|
|
64775
|
+
);
|
|
64776
|
+
}
|
|
64777
|
+
}
|
|
64778
|
+
return hints2;
|
|
64779
|
+
}
|
|
64780
|
+
|
|
64781
|
+
// ../api/src/shopify/buyers.ts
|
|
64782
|
+
import { z as z51 } from "zod";
|
|
64783
|
+
|
|
64625
64784
|
// ../api/src/shopify/wire.ts
|
|
64626
64785
|
import { z as z50 } from "zod";
|
|
64627
64786
|
var SHOPIFY_NOT_CONNECTED_MESSAGE = "No Shopify store is connected for this company.";
|
|
@@ -64999,13 +65158,42 @@ var shopifyStockResponseSchema = z50.object({
|
|
|
64999
65158
|
/** More products in that group than `limit`. */
|
|
65000
65159
|
more: z50.object({ outOfStock: z50.boolean(), partlyOut: z50.boolean() })
|
|
65001
65160
|
});
|
|
65002
|
-
var shopifySalesBySchema = z50.enum([
|
|
65161
|
+
var shopifySalesBySchema = z50.enum([
|
|
65162
|
+
"day",
|
|
65163
|
+
"product",
|
|
65164
|
+
"campaign",
|
|
65165
|
+
"landing",
|
|
65166
|
+
"weekday",
|
|
65167
|
+
"hour",
|
|
65168
|
+
"month",
|
|
65169
|
+
"month_of_year"
|
|
65170
|
+
]);
|
|
65171
|
+
var SHOPIFY_SALES_TIMING_BY = ["weekday", "hour", "month", "month_of_year"];
|
|
65003
65172
|
var SHOPIFY_SALES_MAX_DAYS = 395;
|
|
65173
|
+
var SHOPIFY_SALES_TIMING_MAX_DAYS = 3650;
|
|
65004
65174
|
var shopifySalesRequestSchema = z50.object({
|
|
65005
|
-
days: z50.coerce.number().int().min(1).max(
|
|
65175
|
+
days: z50.coerce.number().int().min(1).max(SHOPIFY_SALES_TIMING_MAX_DAYS).optional(),
|
|
65176
|
+
/** Every order the store has (timing breakdowns only). */
|
|
65177
|
+
all: flag.optional(),
|
|
65006
65178
|
by: shopifySalesBySchema.optional(),
|
|
65007
65179
|
limit: z50.coerce.number().int().min(1).max(100).optional(),
|
|
65008
65180
|
full: flag.optional()
|
|
65181
|
+
}).superRefine((request, ctx) => {
|
|
65182
|
+
if (SHOPIFY_SALES_TIMING_BY.includes(request.by ?? "day")) return;
|
|
65183
|
+
if (request.all) {
|
|
65184
|
+
ctx.addIssue({
|
|
65185
|
+
code: "custom",
|
|
65186
|
+
path: ["all"],
|
|
65187
|
+
message: "all is only for --by weekday, hour, month or month_of_year"
|
|
65188
|
+
});
|
|
65189
|
+
}
|
|
65190
|
+
if ((request.days ?? 0) > SHOPIFY_SALES_MAX_DAYS) {
|
|
65191
|
+
ctx.addIssue({
|
|
65192
|
+
code: "custom",
|
|
65193
|
+
path: ["days"],
|
|
65194
|
+
message: `by day, product, campaign or landing reads up to ${SHOPIFY_SALES_MAX_DAYS} days`
|
|
65195
|
+
});
|
|
65196
|
+
}
|
|
65009
65197
|
});
|
|
65010
65198
|
var shopifySalesCreditSchema = z50.enum([
|
|
65011
65199
|
"campaign",
|
|
@@ -65028,6 +65216,8 @@ var shopifySalesRowSchema = z50.object({
|
|
|
65028
65216
|
/** By product: units sold and given back. */
|
|
65029
65217
|
units: z50.number().optional(),
|
|
65030
65218
|
refundedUnits: z50.number().optional(),
|
|
65219
|
+
/** By weekday, hour or month: the buyers who ordered. */
|
|
65220
|
+
buyers: z50.number().optional(),
|
|
65031
65221
|
// --full
|
|
65032
65222
|
source: z50.string().optional(),
|
|
65033
65223
|
medium: z50.string().optional(),
|
|
@@ -65048,7 +65238,13 @@ var shopifySalesTotalsSchema = z50.object({
|
|
|
65048
65238
|
var shopifySalesReportingSchema = z50.enum(["shopify_channel", "baker", "undecided"]);
|
|
65049
65239
|
var shopifySalesResponseSchema = z50.object({
|
|
65050
65240
|
store: shopifyStoreStateSchema,
|
|
65051
|
-
|
|
65241
|
+
/** `days` is "all" with `all`; `timezone` names the store's time a timing breakdown was read in. */
|
|
65242
|
+
window: z50.object({
|
|
65243
|
+
days: z50.union([z50.number(), z50.literal("all")]),
|
|
65244
|
+
from: z50.string(),
|
|
65245
|
+
to: z50.string(),
|
|
65246
|
+
timezone: z50.string().optional()
|
|
65247
|
+
}),
|
|
65052
65248
|
by: shopifySalesBySchema,
|
|
65053
65249
|
/** The store's currency; null when nothing sold in the window. */
|
|
65054
65250
|
currency: z50.string().nullable(),
|
|
@@ -65063,8 +65259,442 @@ var shopifySalesResponseSchema = z50.object({
|
|
|
65063
65259
|
ordersUnreadable: z50.literal(true).optional()
|
|
65064
65260
|
});
|
|
65065
65261
|
|
|
65066
|
-
// src/
|
|
65067
|
-
|
|
65262
|
+
// ../api/src/shopify/buyers.ts
|
|
65263
|
+
var flag2 = z51.enum(["true", "false"]).transform((value) => value === "true");
|
|
65264
|
+
var SHOPIFY_STORE_WINDOW_MAX_DAYS = 3650;
|
|
65265
|
+
var days = z51.coerce.number().int().min(1).max(SHOPIFY_STORE_WINDOW_MAX_DAYS);
|
|
65266
|
+
var countryCode2 = z51.string().regex(/^[A-Za-z]{2}$/, "a two-letter country code, like DE").transform((value) => value.toUpperCase());
|
|
65267
|
+
var limit = z51.coerce.number().int().min(1).max(100);
|
|
65268
|
+
var windowRequest = {
|
|
65269
|
+
days: days.optional(),
|
|
65270
|
+
/** Every order the store has. */
|
|
65271
|
+
all: flag2.optional(),
|
|
65272
|
+
country: countryCode2.optional(),
|
|
65273
|
+
full: flag2.optional()
|
|
65274
|
+
};
|
|
65275
|
+
var shopifyStoreWindowSchema = z51.object({
|
|
65276
|
+
from: z51.string().nullable(),
|
|
65277
|
+
to: z51.string(),
|
|
65278
|
+
days: z51.union([z51.number(), z51.literal("all")]),
|
|
65279
|
+
timezone: z51.string()
|
|
65280
|
+
});
|
|
65281
|
+
var shopifyBuyerHistorySchema = z51.object({
|
|
65282
|
+
state: z51.enum(["importing", "done", "failed", "none"]),
|
|
65283
|
+
reach: z51.enum(["all", "60_days"]).nullable(),
|
|
65284
|
+
/** The day of the oldest order Baker holds (YYYY-MM-DD). */
|
|
65285
|
+
from: z51.string().nullable(),
|
|
65286
|
+
/** Orders brought in so far. */
|
|
65287
|
+
orders: z51.number()
|
|
65288
|
+
});
|
|
65289
|
+
var shopifyBuyerBasisSchema = z51.enum(["all_orders", "last_60_days"]);
|
|
65290
|
+
var answerCommon = {
|
|
65291
|
+
store: shopifyStoreStateSchema,
|
|
65292
|
+
window: shopifyStoreWindowSchema,
|
|
65293
|
+
/** The store's currency; null when it has no orders yet. */
|
|
65294
|
+
currency: z51.string().nullable(),
|
|
65295
|
+
/** Orders in another currency (the store changed currency), never added to these. */
|
|
65296
|
+
otherCurrencies: z51.array(z51.object({ currency: z51.string(), orders: z51.number(), net: z51.number() })).optional(),
|
|
65297
|
+
history: shopifyBuyerHistorySchema,
|
|
65298
|
+
basis: shopifyBuyerBasisSchema
|
|
65299
|
+
};
|
|
65300
|
+
var shopifyPlaceSchema = z51.object({
|
|
65301
|
+
country: z51.string(),
|
|
65302
|
+
countryName: z51.string(),
|
|
65303
|
+
region: z51.string(),
|
|
65304
|
+
city: z51.string()
|
|
65305
|
+
});
|
|
65306
|
+
var shopifyBuyerSegmentSchema = z51.enum(["best", "loyal", "at_risk", "new", "one_time"]);
|
|
65307
|
+
var shopifySegmentThresholdsSchema = z51.object({
|
|
65308
|
+
/** The store's typical days between a repeat buyer's orders (90 with too few to go on). */
|
|
65309
|
+
gapDays: z51.number(),
|
|
65310
|
+
/** The lifetime net that puts a buyer in the top fifth. */
|
|
65311
|
+
netP80: z51.number()
|
|
65312
|
+
});
|
|
65313
|
+
var shopifySegmentRowSchema = z51.object({
|
|
65314
|
+
segment: shopifyBuyerSegmentSchema,
|
|
65315
|
+
label: z51.string(),
|
|
65316
|
+
/** The rule in words, with this store's numbers. */
|
|
65317
|
+
rule: z51.string(),
|
|
65318
|
+
buyers: z51.number(),
|
|
65319
|
+
/** Of every buyer, 0–1. */
|
|
65320
|
+
share: z51.number(),
|
|
65321
|
+
net: z51.number(),
|
|
65322
|
+
averageOrders: z51.number(),
|
|
65323
|
+
averageDaysSinceLastOrder: z51.number(),
|
|
65324
|
+
topCountries: z51.array(z51.object({ code: z51.string(), name: z51.string(), buyers: z51.number() })),
|
|
65325
|
+
/** The products their first orders held most often. */
|
|
65326
|
+
topFirstProducts: z51.array(z51.object({ title: z51.string(), buyers: z51.number(), productId: z51.string().optional() }))
|
|
65327
|
+
});
|
|
65328
|
+
var shopifySegmentsRequestSchema = z51.object({ ...windowRequest });
|
|
65329
|
+
var shopifySegmentsResponseSchema = z51.object({
|
|
65330
|
+
...answerCommon,
|
|
65331
|
+
thresholds: shopifySegmentThresholdsSchema,
|
|
65332
|
+
buyers: z51.number(),
|
|
65333
|
+
segments: z51.array(shopifySegmentRowSchema)
|
|
65334
|
+
});
|
|
65335
|
+
var shopifyBuyersSortSchema = z51.enum(["net", "orders", "recent", "first"]);
|
|
65336
|
+
var shopifyAcquisitionBySchema = z51.enum(["campaign", "channel"]);
|
|
65337
|
+
var shopifyBuyersRequestSchema = z51.object({
|
|
65338
|
+
...windowRequest,
|
|
65339
|
+
segment: shopifyBuyerSegmentSchema.optional(),
|
|
65340
|
+
/** Lifetime value by the campaign or channel of each buyer's first order. */
|
|
65341
|
+
by: shopifyAcquisitionBySchema.optional(),
|
|
65342
|
+
/** Only buyers whose first order came through this channel (a key from `by=channel`). */
|
|
65343
|
+
channel: z51.string().max(200).optional(),
|
|
65344
|
+
sort: shopifyBuyersSortSchema.optional(),
|
|
65345
|
+
limit: limit.optional(),
|
|
65346
|
+
offset: z51.coerce.number().int().min(0).max(1e5).optional()
|
|
65347
|
+
});
|
|
65348
|
+
var shopifyBuyersSummarySchema = z51.object({
|
|
65349
|
+
/** Buyers who ordered in the period. */
|
|
65350
|
+
buyers: z51.number(),
|
|
65351
|
+
/** Of them, whose first order was in the period. */
|
|
65352
|
+
newBuyers: z51.number(),
|
|
65353
|
+
returningBuyers: z51.number(),
|
|
65354
|
+
/** Orders with no customer: counted in money, never a buyer. */
|
|
65355
|
+
guestOrders: z51.number(),
|
|
65356
|
+
orders: z51.number(),
|
|
65357
|
+
net: z51.number(),
|
|
65358
|
+
averageOrder: z51.number(),
|
|
65359
|
+
/** Orders that were their buyer's first, and the rest (Shopify's new vs returning). */
|
|
65360
|
+
newOrders: z51.number(),
|
|
65361
|
+
returningOrders: z51.number(),
|
|
65362
|
+
newNet: z51.number(),
|
|
65363
|
+
returningNet: z51.number(),
|
|
65364
|
+
/** Of every buyer up to the period's end, the share who ordered again (0–1). */
|
|
65365
|
+
repeatRate: z51.number(),
|
|
65366
|
+
/** Median days from a buyer's first order to their second; null when nobody ordered twice. */
|
|
65367
|
+
medianDaysToSecond: z51.number().nullable()
|
|
65368
|
+
});
|
|
65369
|
+
var shopifyCameFromSchema = z51.object({
|
|
65370
|
+
channel: z51.string(),
|
|
65371
|
+
label: z51.string(),
|
|
65372
|
+
campaign: z51.string().optional(),
|
|
65373
|
+
// --full
|
|
65374
|
+
source: z51.string().optional(),
|
|
65375
|
+
medium: z51.string().optional(),
|
|
65376
|
+
clickPlatform: z51.string().optional(),
|
|
65377
|
+
landingId: z51.string().optional()
|
|
65378
|
+
});
|
|
65379
|
+
var shopifyBuyerRowSchema = z51.object({
|
|
65380
|
+
number: z51.number(),
|
|
65381
|
+
/** "Customer #1042". */
|
|
65382
|
+
label: z51.string(),
|
|
65383
|
+
place: shopifyPlaceSchema.nullable(),
|
|
65384
|
+
orders: z51.number(),
|
|
65385
|
+
net: z51.number(),
|
|
65386
|
+
firstOrderAt: z51.string(),
|
|
65387
|
+
lastOrderAt: z51.string(),
|
|
65388
|
+
segment: shopifyBuyerSegmentSchema,
|
|
65389
|
+
cameFrom: shopifyCameFromSchema,
|
|
65390
|
+
// --full
|
|
65391
|
+
gross: z51.number().optional(),
|
|
65392
|
+
refunded: z51.number().optional(),
|
|
65393
|
+
ordersWithDiscount: z51.number().optional(),
|
|
65394
|
+
averageDaysBetweenOrders: z51.number().nullable().optional()
|
|
65395
|
+
});
|
|
65396
|
+
var shopifyAcquisitionRowSchema = z51.object({
|
|
65397
|
+
key: z51.string(),
|
|
65398
|
+
label: z51.string(),
|
|
65399
|
+
buyers: z51.number(),
|
|
65400
|
+
/** What their first orders came to. */
|
|
65401
|
+
firstNet: z51.number(),
|
|
65402
|
+
/** What they have spent since, to date. */
|
|
65403
|
+
lifetimeNet: z51.number(),
|
|
65404
|
+
lifetimePerBuyer: z51.number(),
|
|
65405
|
+
averageOrders: z51.number(),
|
|
65406
|
+
repeatRate: z51.number(),
|
|
65407
|
+
// --full
|
|
65408
|
+
source: z51.string().optional(),
|
|
65409
|
+
medium: z51.string().optional(),
|
|
65410
|
+
campaign: z51.string().optional(),
|
|
65411
|
+
clickPlatform: z51.string().optional()
|
|
65412
|
+
});
|
|
65413
|
+
var shopifyBuyersResponseSchema = z51.object({
|
|
65414
|
+
...answerCommon,
|
|
65415
|
+
summary: shopifyBuyersSummarySchema,
|
|
65416
|
+
thresholds: shopifySegmentThresholdsSchema,
|
|
65417
|
+
segments: z51.array(z51.object({ segment: shopifyBuyerSegmentSchema, label: z51.string(), buyers: z51.number() })),
|
|
65418
|
+
/** Buyers matching the filters, before the limit. */
|
|
65419
|
+
total: z51.number(),
|
|
65420
|
+
buyers: z51.array(shopifyBuyerRowSchema),
|
|
65421
|
+
acquisition: z51.object({ by: shopifyAcquisitionBySchema, rows: z51.array(shopifyAcquisitionRowSchema) }).optional()
|
|
65422
|
+
});
|
|
65423
|
+
var shopifyBuyerNumberSchema = z51.string().trim().transform((value) => value.replace(/^customer\s*/i, "").replace(/^#/, "")).pipe(z51.string().regex(/^\d+$/, "a buyer's number, like 1042 or #1042")).transform((value) => Number(value)).pipe(z51.number().int().min(1).max(4e9));
|
|
65424
|
+
var shopifyBuyerRequestSchema = z51.object({ number: shopifyBuyerNumberSchema, full: flag2.optional() });
|
|
65425
|
+
var SHOPIFY_BUYER_GONE_MESSAGE = "This buyer is no longer in Baker.";
|
|
65426
|
+
var shopifyBuyerOrderSchema = z51.object({
|
|
65427
|
+
name: z51.string(),
|
|
65428
|
+
/** When it was placed, in the store's time (YYYY-MM-DD HH:MM). */
|
|
65429
|
+
placedAt: z51.string(),
|
|
65430
|
+
net: z51.number(),
|
|
65431
|
+
gross: z51.number(),
|
|
65432
|
+
refunded: z51.number(),
|
|
65433
|
+
products: z51.array(z51.object({ title: z51.string(), quantity: z51.number() })),
|
|
65434
|
+
discountCodes: z51.array(z51.string()),
|
|
65435
|
+
place: shopifyPlaceSchema.nullable(),
|
|
65436
|
+
/** Placed before Baker measured the store: counted, credited to no campaign. */
|
|
65437
|
+
beforeBaker: z51.boolean(),
|
|
65438
|
+
// --full
|
|
65439
|
+
orderId: z51.string().optional(),
|
|
65440
|
+
items: z51.array(z51.object({ productId: z51.string(), variantId: z51.string(), quantity: z51.number(), amount: z51.number() })).optional(),
|
|
65441
|
+
refundedItems: z51.array(z51.object({ productId: z51.string(), variantId: z51.string(), quantity: z51.number(), amount: z51.number() })).optional(),
|
|
65442
|
+
cameFrom: shopifyCameFromSchema.optional()
|
|
65443
|
+
});
|
|
65444
|
+
var shopifyBuyerProfileSchema = z51.object({
|
|
65445
|
+
number: z51.number(),
|
|
65446
|
+
label: z51.string(),
|
|
65447
|
+
place: shopifyPlaceSchema.nullable(),
|
|
65448
|
+
orders: z51.number(),
|
|
65449
|
+
gross: z51.number(),
|
|
65450
|
+
refunded: z51.number(),
|
|
65451
|
+
net: z51.number(),
|
|
65452
|
+
averageOrder: z51.number(),
|
|
65453
|
+
/** Average days between their orders; null with one order. */
|
|
65454
|
+
averageDaysBetweenOrders: z51.number().nullable(),
|
|
65455
|
+
firstOrderAt: z51.string(),
|
|
65456
|
+
lastOrderAt: z51.string(),
|
|
65457
|
+
ordersWithDiscount: z51.number(),
|
|
65458
|
+
segment: shopifyBuyerSegmentSchema,
|
|
65459
|
+
segmentLabel: z51.string(),
|
|
65460
|
+
cameFrom: shopifyCameFromSchema
|
|
65461
|
+
});
|
|
65462
|
+
var shopifyBuyerResponseSchema = z51.object({
|
|
65463
|
+
...answerCommon,
|
|
65464
|
+
buyer: shopifyBuyerProfileSchema,
|
|
65465
|
+
/** Newest first. */
|
|
65466
|
+
orders: z51.array(shopifyBuyerOrderSchema),
|
|
65467
|
+
products: z51.array(
|
|
65468
|
+
z51.object({ title: z51.string(), quantity: z51.number(), orders: z51.number(), productId: z51.string().optional() })
|
|
65469
|
+
)
|
|
65470
|
+
});
|
|
65471
|
+
var shopifyCountriesRequestSchema = z51.object({
|
|
65472
|
+
...windowRequest,
|
|
65473
|
+
/** Inside one region of `country` (its code or name): its cities. */
|
|
65474
|
+
region: z51.string().max(100).optional(),
|
|
65475
|
+
/** Only orders holding this product (its id or handle); money is that product's lines. */
|
|
65476
|
+
product: z51.string().max(255).optional(),
|
|
65477
|
+
limit: limit.optional()
|
|
65478
|
+
});
|
|
65479
|
+
var shopifyPlaceRowSchema = z51.object({
|
|
65480
|
+
/** A country code, a region code (or name) or a city. */
|
|
65481
|
+
key: z51.string(),
|
|
65482
|
+
name: z51.string(),
|
|
65483
|
+
orders: z51.number(),
|
|
65484
|
+
buyers: z51.number(),
|
|
65485
|
+
newBuyers: z51.number(),
|
|
65486
|
+
net: z51.number(),
|
|
65487
|
+
averageOrder: z51.number(),
|
|
65488
|
+
/** Of the total's net, 0–1. */
|
|
65489
|
+
share: z51.number(),
|
|
65490
|
+
/** Net against the period before; null when there was none before. */
|
|
65491
|
+
growth: z51.number().nullable(),
|
|
65492
|
+
ordersGrowth: z51.number().nullable(),
|
|
65493
|
+
/** The currency its buyers paid in most. */
|
|
65494
|
+
paidIn: z51.string(),
|
|
65495
|
+
// --full
|
|
65496
|
+
previousNet: z51.number().optional(),
|
|
65497
|
+
previousOrders: z51.number().optional()
|
|
65498
|
+
});
|
|
65499
|
+
var shopifyCountriesResponseSchema = z51.object({
|
|
65500
|
+
...answerCommon,
|
|
65501
|
+
level: z51.enum(["country", "region", "city"]),
|
|
65502
|
+
country: z51.string().optional(),
|
|
65503
|
+
region: z51.string().optional(),
|
|
65504
|
+
product: z51.object({ id: z51.string(), title: z51.string() }).optional(),
|
|
65505
|
+
total: shopifyPlaceRowSchema,
|
|
65506
|
+
/** Named places, best first; with `unknown` and `other` they add up to `total`. */
|
|
65507
|
+
rows: z51.array(shopifyPlaceRowSchema),
|
|
65508
|
+
/** Orders whose address Baker could not read. Always shown, never folded into `other`. */
|
|
65509
|
+
unknown: shopifyPlaceRowSchema.nullable(),
|
|
65510
|
+
/** Places past the limit, together. */
|
|
65511
|
+
other: shopifyPlaceRowSchema.nullable()
|
|
65512
|
+
});
|
|
65513
|
+
var shopifyBestSellersViewSchema = z51.enum(["products", "variants", "trend", "together", "repeat", "momentum"]);
|
|
65514
|
+
var shopifyBestSellersSortSchema = z51.enum(["units", "revenue", "orders"]);
|
|
65515
|
+
var shopifyBestSellersRequestSchema = z51.object({
|
|
65516
|
+
...windowRequest,
|
|
65517
|
+
view: shopifyBestSellersViewSchema.optional(),
|
|
65518
|
+
sort: shopifyBestSellersSortSchema.optional(),
|
|
65519
|
+
/** Only this product (its id or handle): its variants, its trend. */
|
|
65520
|
+
product: z51.string().max(255).optional(),
|
|
65521
|
+
limit: limit.optional()
|
|
65522
|
+
});
|
|
65523
|
+
var shopifyBestSellerRowSchema = z51.object({
|
|
65524
|
+
title: z51.string(),
|
|
65525
|
+
/** The option's own title, by variant. */
|
|
65526
|
+
variantTitle: z51.string().optional(),
|
|
65527
|
+
units: z51.number(),
|
|
65528
|
+
refundedUnits: z51.number(),
|
|
65529
|
+
orders: z51.number(),
|
|
65530
|
+
buyers: z51.number(),
|
|
65531
|
+
revenue: z51.number(),
|
|
65532
|
+
refunded: z51.number(),
|
|
65533
|
+
/** Refunded units of units ordered, 0–1. */
|
|
65534
|
+
refundRate: z51.number(),
|
|
65535
|
+
/** Units against the period before; null when none sold before. */
|
|
65536
|
+
growth: z51.number().nullable(),
|
|
65537
|
+
// --full
|
|
65538
|
+
productId: z51.string().optional(),
|
|
65539
|
+
variantId: z51.string().optional(),
|
|
65540
|
+
previousUnits: z51.number().optional(),
|
|
65541
|
+
previousRevenue: z51.number().optional()
|
|
65542
|
+
});
|
|
65543
|
+
var shopifyTrendRowSchema = z51.object({
|
|
65544
|
+
/** YYYY-MM, in the store's time. */
|
|
65545
|
+
month: z51.string(),
|
|
65546
|
+
title: z51.string(),
|
|
65547
|
+
units: z51.number(),
|
|
65548
|
+
revenue: z51.number(),
|
|
65549
|
+
productId: z51.string().optional()
|
|
65550
|
+
});
|
|
65551
|
+
var shopifyTogetherRowSchema = z51.object({
|
|
65552
|
+
first: z51.string(),
|
|
65553
|
+
second: z51.string(),
|
|
65554
|
+
/** Orders holding both. */
|
|
65555
|
+
orders: z51.number(),
|
|
65556
|
+
/** Of the orders holding the first, the share that also held the second (0–1). */
|
|
65557
|
+
confidence: z51.number(),
|
|
65558
|
+
firstId: z51.string().optional(),
|
|
65559
|
+
secondId: z51.string().optional()
|
|
65560
|
+
});
|
|
65561
|
+
var shopifyRepeatRowSchema = z51.object({
|
|
65562
|
+
title: z51.string(),
|
|
65563
|
+
/** Buyers whose first order held it. */
|
|
65564
|
+
buyers: z51.number(),
|
|
65565
|
+
/** Of them, who ordered again. */
|
|
65566
|
+
buyersAgain: z51.number(),
|
|
65567
|
+
share: z51.number(),
|
|
65568
|
+
medianDaysToSecond: z51.number().nullable(),
|
|
65569
|
+
productId: z51.string().optional()
|
|
65570
|
+
});
|
|
65571
|
+
var shopifyMomentumRowSchema = z51.object({
|
|
65572
|
+
title: z51.string(),
|
|
65573
|
+
units: z51.number(),
|
|
65574
|
+
previousUnits: z51.number(),
|
|
65575
|
+
/** Units against the period before (−0.4 = 40% fewer). */
|
|
65576
|
+
change: z51.number(),
|
|
65577
|
+
revenue: z51.number(),
|
|
65578
|
+
productId: z51.string().optional()
|
|
65579
|
+
});
|
|
65580
|
+
var shopifyBestSellersResponseSchema = z51.object({
|
|
65581
|
+
...answerCommon,
|
|
65582
|
+
view: shopifyBestSellersViewSchema,
|
|
65583
|
+
sort: shopifyBestSellersSortSchema,
|
|
65584
|
+
product: z51.object({ id: z51.string(), title: z51.string() }).optional(),
|
|
65585
|
+
products: z51.array(shopifyBestSellerRowSchema).optional(),
|
|
65586
|
+
variants: z51.array(shopifyBestSellerRowSchema).optional(),
|
|
65587
|
+
trend: z51.array(shopifyTrendRowSchema).optional(),
|
|
65588
|
+
together: z51.array(shopifyTogetherRowSchema).optional(),
|
|
65589
|
+
repeat: z51.array(shopifyRepeatRowSchema).optional(),
|
|
65590
|
+
momentum: z51.array(shopifyMomentumRowSchema).optional()
|
|
65591
|
+
});
|
|
65592
|
+
var shopifyCohortsRequestSchema = z51.object({
|
|
65593
|
+
months: z51.coerce.number().int().min(3).max(36).optional(),
|
|
65594
|
+
country: countryCode2.optional(),
|
|
65595
|
+
full: flag2.optional()
|
|
65596
|
+
});
|
|
65597
|
+
var shopifyCohortMonthSchema = z51.object({
|
|
65598
|
+
/** Months since the first order: 0 is the month they first bought. */
|
|
65599
|
+
offset: z51.number(),
|
|
65600
|
+
activeBuyers: z51.number(),
|
|
65601
|
+
orders: z51.number(),
|
|
65602
|
+
net: z51.number(),
|
|
65603
|
+
/** Of the cohort, who ordered in that month (0–1). */
|
|
65604
|
+
retention: z51.number(),
|
|
65605
|
+
/** What the cohort spent up to that month, per buyer. */
|
|
65606
|
+
cumulativeNetPerBuyer: z51.number()
|
|
65607
|
+
});
|
|
65608
|
+
var shopifyCohortSchema = z51.object({
|
|
65609
|
+
/** The month of their first order, YYYY-MM in the store's time. */
|
|
65610
|
+
month: z51.string(),
|
|
65611
|
+
size: z51.number(),
|
|
65612
|
+
/** Who ordered again 1, 2, 3, 6 and 12 months after; null for a month not reached yet. */
|
|
65613
|
+
retention: z51.object({
|
|
65614
|
+
m1: z51.number().nullable(),
|
|
65615
|
+
m2: z51.number().nullable(),
|
|
65616
|
+
m3: z51.number().nullable(),
|
|
65617
|
+
m6: z51.number().nullable(),
|
|
65618
|
+
m12: z51.number().nullable()
|
|
65619
|
+
}),
|
|
65620
|
+
netPerBuyerAt12: z51.number().nullable(),
|
|
65621
|
+
// --full
|
|
65622
|
+
matrix: z51.array(shopifyCohortMonthSchema).optional()
|
|
65623
|
+
});
|
|
65624
|
+
var shopifyCohortsResponseSchema = z51.object({
|
|
65625
|
+
...answerCommon,
|
|
65626
|
+
months: z51.number(),
|
|
65627
|
+
cohorts: z51.array(shopifyCohortSchema)
|
|
65628
|
+
});
|
|
65629
|
+
|
|
65630
|
+
// src/commands/shopify/refusal.ts
|
|
65631
|
+
var CONTINUE2 = "Keep going with the wider task: say which part you could not read, skip it, and finish everything else.";
|
|
65632
|
+
function shopifyRefusal(code, message) {
|
|
65633
|
+
if (code !== "NOT_FOUND") return null;
|
|
65634
|
+
if (message.includes(SHOPIFY_NOT_CONNECTED_MESSAGE)) {
|
|
65635
|
+
return {
|
|
65636
|
+
code: "NO_SHOPIFY_CONNECTION",
|
|
65637
|
+
fix: {
|
|
65638
|
+
action: "request_connection",
|
|
65639
|
+
explanation: 'AGENT INSTRUCTION: no Shopify store is connected, and only the user can connect it. Call the `request_connection` tool with { platform: "shopify", reason } \u2014 reason in their words, e.g. "so I can use your real products and prices" \u2014 and they connect it inside the chat; then run this command again. ' + CONTINUE2
|
|
65640
|
+
}
|
|
65641
|
+
};
|
|
65642
|
+
}
|
|
65643
|
+
if (message.includes(SHOPIFY_NOT_OFFERED_MESSAGE)) {
|
|
65644
|
+
return {
|
|
65645
|
+
code: "SHOPIFY_NOT_AVAILABLE",
|
|
65646
|
+
fix: {
|
|
65647
|
+
action: "continue_without",
|
|
65648
|
+
explanation: "Do not call `request_connection` for Shopify and do not tell the user to connect it: a Shopify store cannot be connected on this Baker yet. Answer from the knowledge vault and the client's own website instead (`baker images extract <store url>` for product photos, `baker research fetch <url>` for a product page), say the product facts are unverified, and finish the rest of the task."
|
|
65649
|
+
}
|
|
65650
|
+
};
|
|
65651
|
+
}
|
|
65652
|
+
return null;
|
|
65653
|
+
}
|
|
65654
|
+
|
|
65655
|
+
// src/commands/shopify/common.ts
|
|
65656
|
+
var PRODUCT_NOT_FOUND_HINT = "Find it with `baker shopify products --search <words>`. If `baker shopify store` shows products Baker couldn't read or products still coming in, it may be one of those \u2014 say so rather than that it doesn't exist.";
|
|
65657
|
+
function fail9(err) {
|
|
65658
|
+
if (err instanceof ApiError) {
|
|
65659
|
+
const refusal = shopifyRefusal(err.code, err.message);
|
|
65660
|
+
if (refusal) {
|
|
65661
|
+
writeJson({ ok: false, error: { code: refusal.code, message: err.message, fix: refusal.fix } });
|
|
65662
|
+
process.exit(1);
|
|
65663
|
+
}
|
|
65664
|
+
if (err.code === "NOT_FOUND" && err.message === SHOPIFY_BUYER_GONE_MESSAGE) {
|
|
65665
|
+
writeJson({
|
|
65666
|
+
ok: false,
|
|
65667
|
+
error: { code: err.code, message: err.message },
|
|
65668
|
+
hints: [
|
|
65669
|
+
"Their store asked Baker to erase this buyer, or they were merged into another one: say the buyer is no longer in Baker, and never guess who they were."
|
|
65670
|
+
]
|
|
65671
|
+
});
|
|
65672
|
+
process.exit(1);
|
|
65673
|
+
}
|
|
65674
|
+
if (err.code === "NOT_FOUND" && err.message.startsWith(SHOPIFY_PRODUCT_NOT_FOUND_PREFIX)) {
|
|
65675
|
+
writeJson({ ok: false, error: { code: err.code, message: err.message }, hints: [PRODUCT_NOT_FOUND_HINT] });
|
|
65676
|
+
process.exit(1);
|
|
65677
|
+
}
|
|
65678
|
+
}
|
|
65679
|
+
failApi(err);
|
|
65680
|
+
}
|
|
65681
|
+
function emit2(data, meta, hints2) {
|
|
65682
|
+
writeJson({ ok: true, data, meta, ...hints2.length > 0 ? { hints: hints2 } : {} });
|
|
65683
|
+
}
|
|
65684
|
+
function params(entries) {
|
|
65685
|
+
const out = {};
|
|
65686
|
+
for (const [name, value] of Object.entries(entries)) {
|
|
65687
|
+
if (value === void 0 || value === "" || value === false) continue;
|
|
65688
|
+
out[name] = String(value);
|
|
65689
|
+
}
|
|
65690
|
+
return out;
|
|
65691
|
+
}
|
|
65692
|
+
var fullArg = {
|
|
65693
|
+
type: "boolean",
|
|
65694
|
+
description: "Everything Baker keeps, not just what answers the question",
|
|
65695
|
+
required: false,
|
|
65696
|
+
default: false
|
|
65697
|
+
};
|
|
65068
65698
|
|
|
65069
65699
|
// src/commands/shopify/hints.ts
|
|
65070
65700
|
var RECONNECT = 'Baker has lost access to this Shopify store, so these products may be out of date. Say so, and ask the user to reconnect with `request_connection` { platform: "shopify" } \u2014 the card offers Reconnect.';
|
|
@@ -65249,7 +65879,7 @@ function productHints(product, store) {
|
|
|
65249
65879
|
}
|
|
65250
65880
|
return hints2;
|
|
65251
65881
|
}
|
|
65252
|
-
function stockHints(answer,
|
|
65882
|
+
function stockHints(answer, limit2) {
|
|
65253
65883
|
const hints2 = [];
|
|
65254
65884
|
const any = answer.outOfStock.length > 0 || answer.partlyOut.length > 0;
|
|
65255
65885
|
const unfinished = answer.more.outOfStock || answer.more.partlyOut;
|
|
@@ -65260,9 +65890,9 @@ function stockHints(answer, limit) {
|
|
|
65260
65890
|
);
|
|
65261
65891
|
} else if (!unfinished) hints2.push("Nothing is out of stock.");
|
|
65262
65892
|
if (unfinished) {
|
|
65263
|
-
const full = answer.more.outOfStock && answer.outOfStock.length >=
|
|
65893
|
+
const full = answer.more.outOfStock && answer.outOfStock.length >= limit2 || answer.more.partlyOut && answer.partlyOut.length >= limit2;
|
|
65264
65894
|
hints2.push(
|
|
65265
|
-
full ? `More than ${
|
|
65895
|
+
full ? `More than ${limit2} products in a list; raise \`--limit\` (up to 200) or ${pageThrough}.` : `Baker didn't check every product in one go, so these lists may be missing some \u2014 don't say nothing else is out of stock; ${pageThrough}.`
|
|
65266
65896
|
);
|
|
65267
65897
|
}
|
|
65268
65898
|
const big = answer.partlyOut.find((row) => row.outOptionsMore);
|
|
@@ -65297,6 +65927,12 @@ function storeHints(summary) {
|
|
|
65297
65927
|
}
|
|
65298
65928
|
return hints2;
|
|
65299
65929
|
}
|
|
65930
|
+
function timingHints(answer) {
|
|
65931
|
+
const peak = [...answer.rows].sort((a, b) => b.orders - a.orders)[0];
|
|
65932
|
+
if (!peak || peak.orders === 0) return [];
|
|
65933
|
+
const zone = answer.window.timezone ? `, ${answer.window.timezone}` : "";
|
|
65934
|
+
return [`Most orders are placed on ${peak.label} (store time${zone}): weight budgets and sends towards it.`];
|
|
65935
|
+
}
|
|
65300
65936
|
function salesHints(answer) {
|
|
65301
65937
|
const hints2 = [];
|
|
65302
65938
|
if (answer.ordersUnreadable) {
|
|
@@ -65313,6 +65949,9 @@ function salesHints(answer) {
|
|
|
65313
65949
|
hints2.push("No orders were paid in this period.");
|
|
65314
65950
|
return hints2;
|
|
65315
65951
|
}
|
|
65952
|
+
if (answer.by === "weekday" || answer.by === "hour" || answer.by === "month_of_year") {
|
|
65953
|
+
return [...hints2, ...timingHints(answer)];
|
|
65954
|
+
}
|
|
65316
65955
|
const notBaker = ["meta", "google"].filter((family) => answer.salesReporting[family] !== "baker");
|
|
65317
65956
|
if (notBaker.length > 0) {
|
|
65318
65957
|
const names = notBaker.map((family) => family === "meta" ? "Meta" : "Google").join(" and ");
|
|
@@ -65335,64 +65974,303 @@ function salesHints(answer) {
|
|
|
65335
65974
|
return hints2;
|
|
65336
65975
|
}
|
|
65337
65976
|
|
|
65338
|
-
// src/commands/shopify/
|
|
65339
|
-
var
|
|
65340
|
-
|
|
65341
|
-
|
|
65342
|
-
|
|
65343
|
-
|
|
65344
|
-
|
|
65345
|
-
|
|
65346
|
-
|
|
65347
|
-
explanation: 'AGENT INSTRUCTION: no Shopify store is connected, and only the user can connect it. Call the `request_connection` tool with { platform: "shopify", reason } \u2014 reason in their words, e.g. "so I can use your real products and prices" \u2014 and they connect it inside the chat; then run this command again. ' + CONTINUE2
|
|
65348
|
-
}
|
|
65349
|
-
};
|
|
65350
|
-
}
|
|
65351
|
-
if (message.includes(SHOPIFY_NOT_OFFERED_MESSAGE)) {
|
|
65352
|
-
return {
|
|
65353
|
-
code: "SHOPIFY_NOT_AVAILABLE",
|
|
65354
|
-
fix: {
|
|
65355
|
-
action: "continue_without",
|
|
65356
|
-
explanation: "Do not call `request_connection` for Shopify and do not tell the user to connect it: a Shopify store cannot be connected on this Baker yet. Answer from the knowledge vault and the client's own website instead (`baker images extract <store url>` for product photos, `baker research fetch <url>` for a product page), say the product facts are unverified, and finish the rest of the task."
|
|
65357
|
-
}
|
|
65358
|
-
};
|
|
65977
|
+
// src/commands/shopify/buyers.ts
|
|
65978
|
+
var PERIOD_NOTE = "Orders placed in the period, in the store's own time, with what was refunded on them since (Shopify's own reports date a refund when it happens instead, so a short period can differ by the refunds that cross its edges).";
|
|
65979
|
+
var periodArgs = {
|
|
65980
|
+
days: { type: "number", description: "The last N days, 1-3650 (default 365)", required: false },
|
|
65981
|
+
all: { type: "boolean", description: "Every order the store has", required: false, default: false },
|
|
65982
|
+
country: {
|
|
65983
|
+
type: "string",
|
|
65984
|
+
description: "Only orders to this country: its two-letter code, like DE",
|
|
65985
|
+
required: false
|
|
65359
65986
|
}
|
|
65360
|
-
|
|
65987
|
+
};
|
|
65988
|
+
var periodFlags = {
|
|
65989
|
+
days: { type: "string", description: "The last N days, 1-3650 (default 365)", required: false },
|
|
65990
|
+
all: { type: "boolean", description: "Every order the store has", required: false, default: false },
|
|
65991
|
+
country: { type: "string", description: "Only orders to this country (two-letter code, like DE)", required: false }
|
|
65992
|
+
};
|
|
65993
|
+
function split(answer) {
|
|
65994
|
+
const { store, window: window2, history, basis, ...data } = answer;
|
|
65995
|
+
return { data, meta: { store, window: window2, history, basis } };
|
|
65361
65996
|
}
|
|
65362
|
-
|
|
65363
|
-
|
|
65364
|
-
|
|
65365
|
-
|
|
65366
|
-
|
|
65367
|
-
|
|
65368
|
-
|
|
65369
|
-
|
|
65370
|
-
|
|
65997
|
+
registerSchema({
|
|
65998
|
+
command: "shopify.buyers",
|
|
65999
|
+
description: `Start here for 'who buys from us', 'how many come back' and 'which campaign brings the best customers': the store's buyers in the period \u2014 how many, how many were new, repeat rate, days to a second order, guest orders \u2014 their segments (best, loyal, at risk, new, one-time, by this store's own rhythm) and the top buyers as numbered customers with their country, lifetime spend and the channel of their first order. --by campaign|channel adds lifetime value per acquisition campaign or channel (Baker keeps no ad spend: read it with the ads commands for the same dates). ${PERIOD_NOTE} A buyer is a Shopify customer: guest orders count in money, never as buyers.`,
|
|
66000
|
+
args: {
|
|
66001
|
+
...periodArgs,
|
|
66002
|
+
segment: {
|
|
66003
|
+
type: "string",
|
|
66004
|
+
description: "Only one segment's buyers: best | loyal | at_risk | new | one_time",
|
|
66005
|
+
required: false,
|
|
66006
|
+
enum: ["best", "loyal", "at_risk", "new", "one_time"]
|
|
66007
|
+
},
|
|
66008
|
+
by: {
|
|
66009
|
+
type: "string",
|
|
66010
|
+
description: "Lifetime value by the campaign or channel of each buyer's first order",
|
|
66011
|
+
required: false,
|
|
66012
|
+
enum: ["campaign", "channel"]
|
|
66013
|
+
},
|
|
66014
|
+
channel: {
|
|
66015
|
+
type: "string",
|
|
66016
|
+
description: "Only buyers who first came through this channel (a key from --by channel)",
|
|
66017
|
+
required: false
|
|
66018
|
+
},
|
|
66019
|
+
sort: {
|
|
66020
|
+
type: "string",
|
|
66021
|
+
description: "net (lifetime spend, default) | orders | recent (last order) | first (first order)",
|
|
66022
|
+
required: false,
|
|
66023
|
+
enum: ["net", "orders", "recent", "first"]
|
|
66024
|
+
},
|
|
66025
|
+
limit: { type: "number", description: "Buyers listed, 1-100 (default 10)", required: false },
|
|
66026
|
+
offset: { type: "number", description: "Skip this many buyers (paging)", required: false },
|
|
66027
|
+
full: {
|
|
66028
|
+
...fullArg,
|
|
66029
|
+
description: "Add each buyer's gross, refunds, orders with a discount, days between orders and first-order campaign"
|
|
65371
66030
|
}
|
|
65372
|
-
|
|
65373
|
-
|
|
65374
|
-
|
|
66031
|
+
}
|
|
66032
|
+
});
|
|
66033
|
+
var buyersCommand = defineCommand209({
|
|
66034
|
+
meta: {
|
|
66035
|
+
name: "buyers",
|
|
66036
|
+
description: "Who buys: buyers, new and returning, repeat rate, segments and the top buyers. Start here: baker shopify buyers --days 90 --by channel"
|
|
66037
|
+
},
|
|
66038
|
+
args: {
|
|
66039
|
+
...periodFlags,
|
|
66040
|
+
segment: { type: "string", description: "best | loyal | at_risk | new | one_time", required: false },
|
|
66041
|
+
by: { type: "string", description: "campaign | channel", required: false },
|
|
66042
|
+
channel: { type: "string", description: "A channel key from --by channel", required: false },
|
|
66043
|
+
sort: { type: "string", description: "net | orders | recent | first", required: false },
|
|
66044
|
+
limit: { type: "string", description: "Buyers listed, 1-100", required: false },
|
|
66045
|
+
offset: { type: "string", description: "Skip this many buyers", required: false },
|
|
66046
|
+
full: { type: "boolean", description: "Add each buyer's detail", required: false, default: false }
|
|
66047
|
+
},
|
|
66048
|
+
run: async ({ args: args5 }) => {
|
|
66049
|
+
try {
|
|
66050
|
+
const answer = await apiGet(
|
|
66051
|
+
"/api/shopify/buyers",
|
|
66052
|
+
params({
|
|
66053
|
+
days: args5.days,
|
|
66054
|
+
all: args5.all,
|
|
66055
|
+
country: args5.country,
|
|
66056
|
+
segment: args5.segment,
|
|
66057
|
+
by: args5.by,
|
|
66058
|
+
channel: args5.channel,
|
|
66059
|
+
sort: args5.sort,
|
|
66060
|
+
limit: args5.limit,
|
|
66061
|
+
offset: args5.offset,
|
|
66062
|
+
full: args5.full
|
|
66063
|
+
})
|
|
66064
|
+
);
|
|
66065
|
+
const { data, meta } = split(answer);
|
|
66066
|
+
emit2(data, meta, [...storeStateHints(answer.store), ...buyersHints(answer)]);
|
|
66067
|
+
} catch (err) {
|
|
66068
|
+
fail9(err);
|
|
65375
66069
|
}
|
|
65376
66070
|
}
|
|
65377
|
-
|
|
65378
|
-
|
|
65379
|
-
|
|
65380
|
-
|
|
65381
|
-
|
|
65382
|
-
|
|
65383
|
-
|
|
65384
|
-
for (const [name, value] of Object.entries(entries)) {
|
|
65385
|
-
if (value === void 0 || value === "" || value === false) continue;
|
|
65386
|
-
out[name] = String(value);
|
|
66071
|
+
});
|
|
66072
|
+
registerSchema({
|
|
66073
|
+
command: "shopify.buyer",
|
|
66074
|
+
description: "One buyer by their number (1042, #1042 or 'Customer #1042'): where they are, how many orders and how much they spent net of refunds, average order, days between orders, first and last order, discount use, segment and how they first came \u2014 then every order (date in the store's time, products and quantities, net, refunded, discount codes, city), newest first, and what they bought. Never a name or an email. A buyer their store asked Baker to erase, or merged into another, answers 'This buyer is no longer in Baker'.",
|
|
66075
|
+
args: {
|
|
66076
|
+
number: { type: "positional", description: "The buyer's number: 1042, #1042 or 'Customer #1042'", required: true },
|
|
66077
|
+
full: { ...fullArg, description: "Add each order's id, lines with their amounts, refunded lines and campaign" }
|
|
65387
66078
|
}
|
|
65388
|
-
|
|
65389
|
-
|
|
65390
|
-
|
|
65391
|
-
|
|
65392
|
-
|
|
65393
|
-
|
|
65394
|
-
|
|
65395
|
-
}
|
|
66079
|
+
});
|
|
66080
|
+
var buyerCommand = defineCommand209({
|
|
66081
|
+
meta: {
|
|
66082
|
+
name: "buyer",
|
|
66083
|
+
description: "One buyer's history by number. Example: baker shopify buyer 1042"
|
|
66084
|
+
},
|
|
66085
|
+
args: {
|
|
66086
|
+
number: { type: "positional", description: "1042, #1042 or 'Customer #1042'", required: true },
|
|
66087
|
+
full: { type: "boolean", description: "Add ids, lines and campaigns", required: false, default: false }
|
|
66088
|
+
},
|
|
66089
|
+
run: async ({ args: args5 }) => {
|
|
66090
|
+
try {
|
|
66091
|
+
const answer = await apiGet(
|
|
66092
|
+
"/api/shopify/buyer",
|
|
66093
|
+
params({ number: args5.number, full: args5.full })
|
|
66094
|
+
);
|
|
66095
|
+
const { data, meta } = split(answer);
|
|
66096
|
+
emit2(data, meta, [...storeStateHints(answer.store), ...buyerHistoryHints(answer.history, answer.basis)]);
|
|
66097
|
+
} catch (err) {
|
|
66098
|
+
fail9(err);
|
|
66099
|
+
}
|
|
66100
|
+
}
|
|
66101
|
+
});
|
|
66102
|
+
registerSchema({
|
|
66103
|
+
command: "shopify.countries",
|
|
66104
|
+
description: `Start here for 'where do our buyers come from' and 'which countries should we push': orders, buyers, new buyers, net, average order, share and growth against the period before, by country \u2014 or by region inside --country (straight by city for a country whose addresses carry no region, like Germany), or by city inside --country and --region \u2014 with the currency buyers paid in. Orders whose address Baker could not read are 'Unknown' ('No region given' / 'No city given' inside a country), always shown; the rows, that row and 'Other' add up to the total. --product narrows to the orders holding one product, with that product's money. ${PERIOD_NOTE}`,
|
|
66105
|
+
args: {
|
|
66106
|
+
...periodArgs,
|
|
66107
|
+
region: { type: "string", description: "Inside --country, one region (code or name): its cities", required: false },
|
|
66108
|
+
product: { type: "string", description: "Only orders holding this product: its id or handle", required: false },
|
|
66109
|
+
limit: { type: "number", description: "Places listed, 1-100 (default 25; the rest are 'Other')", required: false },
|
|
66110
|
+
full: { ...fullArg, description: "Add the period before's net and orders per place" }
|
|
66111
|
+
}
|
|
66112
|
+
});
|
|
66113
|
+
var countriesCommand2 = defineCommand209({
|
|
66114
|
+
meta: {
|
|
66115
|
+
name: "countries",
|
|
66116
|
+
description: "Where buyers are: orders, buyers and net by country, region or city, with growth. Start here: baker shopify countries --days 90"
|
|
66117
|
+
},
|
|
66118
|
+
args: {
|
|
66119
|
+
...periodFlags,
|
|
66120
|
+
region: { type: "string", description: "A region inside --country", required: false },
|
|
66121
|
+
product: { type: "string", description: "A product's id or handle", required: false },
|
|
66122
|
+
limit: { type: "string", description: "Places listed, 1-100", required: false },
|
|
66123
|
+
full: { type: "boolean", description: "Add the period before", required: false, default: false }
|
|
66124
|
+
},
|
|
66125
|
+
run: async ({ args: args5 }) => {
|
|
66126
|
+
try {
|
|
66127
|
+
const answer = await apiGet(
|
|
66128
|
+
"/api/shopify/countries",
|
|
66129
|
+
params({
|
|
66130
|
+
days: args5.days,
|
|
66131
|
+
all: args5.all,
|
|
66132
|
+
country: args5.country,
|
|
66133
|
+
region: args5.region,
|
|
66134
|
+
product: args5.product,
|
|
66135
|
+
limit: args5.limit,
|
|
66136
|
+
full: args5.full
|
|
66137
|
+
})
|
|
66138
|
+
);
|
|
66139
|
+
const { data, meta } = split(answer);
|
|
66140
|
+
emit2(data, meta, [...storeStateHints(answer.store), ...countriesHints(answer)]);
|
|
66141
|
+
} catch (err) {
|
|
66142
|
+
fail9(err);
|
|
66143
|
+
}
|
|
66144
|
+
}
|
|
66145
|
+
});
|
|
66146
|
+
registerSchema({
|
|
66147
|
+
command: "shopify.bestSellers",
|
|
66148
|
+
description: `Start here for 'what sells best' and 'which product should we lead with': best sellers by units ordered (before anything came back) beside refunded units, orders, buyers, revenue (the lines' price after discounts, before tax and shipping), refunds, refund rate and growth. --view variants (one product's options, with --product), trend (top products by month), together (products bought in one order), repeat (first buys whose buyers came back most) or momentum (products selling 30% or more below the period before). ${PERIOD_NOTE} For the catalog itself use \`baker shopify products\`.`,
|
|
66149
|
+
args: {
|
|
66150
|
+
...periodArgs,
|
|
66151
|
+
view: {
|
|
66152
|
+
type: "string",
|
|
66153
|
+
description: "products (default) | variants | trend | together | repeat | momentum",
|
|
66154
|
+
required: false,
|
|
66155
|
+
enum: ["products", "variants", "trend", "together", "repeat", "momentum"]
|
|
66156
|
+
},
|
|
66157
|
+
sort: {
|
|
66158
|
+
type: "string",
|
|
66159
|
+
description: "units (default) | revenue | orders",
|
|
66160
|
+
required: false,
|
|
66161
|
+
enum: ["units", "revenue", "orders"]
|
|
66162
|
+
},
|
|
66163
|
+
product: { type: "string", description: "One product (id or handle): its variants or its trend", required: false },
|
|
66164
|
+
limit: { type: "number", description: "Rows, 1-100 (default 10; 5 products for trend)", required: false },
|
|
66165
|
+
full: { ...fullArg, description: "Add product and variant ids and the period before" }
|
|
66166
|
+
}
|
|
66167
|
+
});
|
|
66168
|
+
var bestSellersCommand = defineCommand209({
|
|
66169
|
+
meta: {
|
|
66170
|
+
name: "best-sellers",
|
|
66171
|
+
description: "What sells: best sellers, variants, trend, bought together, repeat leaders, losing momentum. Start here: baker shopify best-sellers --days 90"
|
|
66172
|
+
},
|
|
66173
|
+
args: {
|
|
66174
|
+
...periodFlags,
|
|
66175
|
+
view: {
|
|
66176
|
+
type: "string",
|
|
66177
|
+
description: "products | variants | trend | together | repeat | momentum",
|
|
66178
|
+
required: false
|
|
66179
|
+
},
|
|
66180
|
+
sort: { type: "string", description: "units | revenue | orders", required: false },
|
|
66181
|
+
product: { type: "string", description: "A product's id or handle", required: false },
|
|
66182
|
+
limit: { type: "string", description: "Rows, 1-100", required: false },
|
|
66183
|
+
full: { type: "boolean", description: "Add ids and the period before", required: false, default: false }
|
|
66184
|
+
},
|
|
66185
|
+
run: async ({ args: args5 }) => {
|
|
66186
|
+
try {
|
|
66187
|
+
const answer = await apiGet(
|
|
66188
|
+
"/api/shopify/best-sellers",
|
|
66189
|
+
params({
|
|
66190
|
+
days: args5.days,
|
|
66191
|
+
all: args5.all,
|
|
66192
|
+
country: args5.country,
|
|
66193
|
+
view: args5.view,
|
|
66194
|
+
sort: args5.sort,
|
|
66195
|
+
product: args5.product,
|
|
66196
|
+
limit: args5.limit,
|
|
66197
|
+
full: args5.full
|
|
66198
|
+
})
|
|
66199
|
+
);
|
|
66200
|
+
const { data, meta } = split(answer);
|
|
66201
|
+
emit2(data, meta, [...storeStateHints(answer.store), ...bestSellersHints(answer)]);
|
|
66202
|
+
} catch (err) {
|
|
66203
|
+
fail9(err);
|
|
66204
|
+
}
|
|
66205
|
+
}
|
|
66206
|
+
});
|
|
66207
|
+
registerSchema({
|
|
66208
|
+
command: "shopify.cohorts",
|
|
66209
|
+
description: "Buyers grouped by the month of their first order (the last --months, default 12): how many, the share who ordered again 1, 2, 3, 6 and 12 months later, and what they spent per buyer by month 12. Months not reached yet are null. Use it for 'are buyers coming back more than they used to' and lifetime value over time.",
|
|
66210
|
+
args: {
|
|
66211
|
+
months: { type: "number", description: "Cohorts, the last 3-36 months (default 12)", required: false },
|
|
66212
|
+
country: { type: "string", description: "Only orders to this country (two-letter code)", required: false },
|
|
66213
|
+
full: {
|
|
66214
|
+
...fullArg,
|
|
66215
|
+
description: "Add every month of every cohort: active buyers, orders, net, cumulative net per buyer"
|
|
66216
|
+
}
|
|
66217
|
+
}
|
|
66218
|
+
});
|
|
66219
|
+
var cohortsCommand = defineCommand209({
|
|
66220
|
+
meta: {
|
|
66221
|
+
name: "cohorts",
|
|
66222
|
+
description: "Buyers by first-order month: who came back and what they spent. Example: baker shopify cohorts --months 12"
|
|
66223
|
+
},
|
|
66224
|
+
args: {
|
|
66225
|
+
months: { type: "string", description: "The last 3-36 months", required: false },
|
|
66226
|
+
country: { type: "string", description: "A two-letter country code", required: false },
|
|
66227
|
+
full: { type: "boolean", description: "Every month of every cohort", required: false, default: false }
|
|
66228
|
+
},
|
|
66229
|
+
run: async ({ args: args5 }) => {
|
|
66230
|
+
try {
|
|
66231
|
+
const answer = await apiGet(
|
|
66232
|
+
"/api/shopify/cohorts",
|
|
66233
|
+
params({ months: args5.months, country: args5.country, full: args5.full })
|
|
66234
|
+
);
|
|
66235
|
+
const { data, meta } = split(answer);
|
|
66236
|
+
emit2(data, meta, [...storeStateHints(answer.store), ...cohortsHints(answer)]);
|
|
66237
|
+
} catch (err) {
|
|
66238
|
+
fail9(err);
|
|
66239
|
+
}
|
|
66240
|
+
}
|
|
66241
|
+
});
|
|
66242
|
+
registerSchema({
|
|
66243
|
+
command: "shopify.segments",
|
|
66244
|
+
description: "Every buyer at the period's end by segment \u2014 best buyers, loyal, at risk, new and one-time \u2014 with how many, their share, net, average orders, days since their last order, the countries they are in and the products their first orders held. Each segment's rule is given in words with this store's own numbers (its typical gap between orders and the spend of its top fifth). Start here for 'who are our best customers' and 'who should we win back'.",
|
|
66245
|
+
args: {
|
|
66246
|
+
...periodArgs,
|
|
66247
|
+
full: { ...fullArg, description: "Add the first products' ids" }
|
|
66248
|
+
}
|
|
66249
|
+
});
|
|
66250
|
+
var segmentsCommand = defineCommand209({
|
|
66251
|
+
meta: {
|
|
66252
|
+
name: "segments",
|
|
66253
|
+
description: "Buyers by segment: best, loyal, at risk, new, one-time. Example: baker shopify segments"
|
|
66254
|
+
},
|
|
66255
|
+
args: {
|
|
66256
|
+
...periodFlags,
|
|
66257
|
+
full: { type: "boolean", description: "Add product ids", required: false, default: false }
|
|
66258
|
+
},
|
|
66259
|
+
run: async ({ args: args5 }) => {
|
|
66260
|
+
try {
|
|
66261
|
+
const answer = await apiGet(
|
|
66262
|
+
"/api/shopify/segments",
|
|
66263
|
+
params({ days: args5.days, all: args5.all, country: args5.country, full: args5.full })
|
|
66264
|
+
);
|
|
66265
|
+
const { data, meta } = split(answer);
|
|
66266
|
+
emit2(data, meta, [...storeStateHints(answer.store), ...segmentsHints(answer)]);
|
|
66267
|
+
} catch (err) {
|
|
66268
|
+
fail9(err);
|
|
66269
|
+
}
|
|
66270
|
+
}
|
|
66271
|
+
});
|
|
66272
|
+
|
|
66273
|
+
// src/commands/shopify/index.ts
|
|
65396
66274
|
registerSchema({
|
|
65397
66275
|
command: "shopify.store",
|
|
65398
66276
|
description: 'Start here for the connected Shopify store: its name, currency, home country, how many products and collections it has, how many products are out of stock or partly out, the prices by country it sells at (only when it has more than one), where its buy links send a shopper (`buyDestination`: checkout, store_cart or product_page), its languages and policies, whether it asks for a password, and the link to the Products page in Baker. `capped: true` means a count stopped at its ceiling (100 out of stock or partly out, 500 collections): say "100 or more", never the number as exact. `meta.store` says whether Baker\'s copy is complete and whether Baker can still reach the store.',
|
|
@@ -65490,14 +66368,24 @@ registerSchema({
|
|
|
65490
66368
|
});
|
|
65491
66369
|
registerSchema({
|
|
65492
66370
|
command: "shopify.sales",
|
|
65493
|
-
description: "Start here for 'how much did we sell', 'which campaigns or pages brought sales' and 'which products sold most': the connected store's orders, sales, refunds and net, in the store's currency. --by day (default) and --by product count each sale, addition and refund in the period it happened; --by campaign and --by landing take the orders paid in the period and credit each to the campaign or Baker page that brought its buyer, with what was added to and refunded on it since \u2014 so the rows add up to `totals` exactly, with named rows for sales from before Baker measured the store and sales from no campaign. A sale counts when it is paid, at what was received. `salesReporting` says who reports the sales to Meta and Google: `undecided` and `shopify_channel` mean Baker only reports them, and only a manager can change that in Brain \u2192 Store.",
|
|
66371
|
+
description: "Start here for 'how much did we sell', 'which campaigns or pages brought sales' and 'which products sold most': the connected store's orders, sales, refunds and net, in the store's currency. --by day (default) and --by product count each sale, addition and refund in the period it happened; --by campaign and --by landing take the orders paid in the period and credit each to the campaign or Baker page that brought its buyer, with what was added to and refunded on it since \u2014 so the rows add up to `totals` exactly, with named rows for sales from before Baker measured the store and sales from no campaign. A sale counts when it is paid, at what was received. `salesReporting` says who reports the sales to Meta and Google: `undecided` and `shopify_channel` mean Baker only reports them, and only a manager can change that in Brain \u2192 Store. --by weekday|hour|month|month_of_year says when buyers order, in the store's own time, from the store's whole order history (orders placed in the period, with what was refunded on them since): use it for when to run or schedule campaigns; only these take --days up to 3650 or --all.",
|
|
65494
66372
|
args: {
|
|
65495
|
-
days: {
|
|
66373
|
+
days: {
|
|
66374
|
+
type: "number",
|
|
66375
|
+
description: "The last N days: 1-395 by day, product, campaign or landing; 1-3650 by weekday, hour or month (default 30)",
|
|
66376
|
+
required: false
|
|
66377
|
+
},
|
|
66378
|
+
all: {
|
|
66379
|
+
type: "boolean",
|
|
66380
|
+
description: "Every order the store has (by weekday, hour, month or month_of_year only)",
|
|
66381
|
+
required: false,
|
|
66382
|
+
default: false
|
|
66383
|
+
},
|
|
65496
66384
|
by: {
|
|
65497
66385
|
type: "string",
|
|
65498
|
-
description: "day | product | campaign | landing (default day)",
|
|
66386
|
+
description: "day | product | campaign | landing | weekday | hour | month | month_of_year (default day)",
|
|
65499
66387
|
required: false,
|
|
65500
|
-
enum: ["day", "product", "campaign", "landing"]
|
|
66388
|
+
enum: ["day", "product", "campaign", "landing", "weekday", "hour", "month", "month_of_year"]
|
|
65501
66389
|
},
|
|
65502
66390
|
limit: {
|
|
65503
66391
|
type: "number",
|
|
@@ -65510,14 +66398,28 @@ registerSchema({
|
|
|
65510
66398
|
}
|
|
65511
66399
|
}
|
|
65512
66400
|
});
|
|
65513
|
-
var salesCommand =
|
|
66401
|
+
var salesCommand = defineCommand210({
|
|
65514
66402
|
meta: {
|
|
65515
66403
|
name: "sales",
|
|
65516
|
-
description: "What the store sold: orders, sales, refunds and net, by day, product, campaign or landing. Example: baker shopify sales --days 7 --by campaign"
|
|
66404
|
+
description: "What the store sold: orders, sales, refunds and net, by day, product, campaign or landing \u2014 or when buyers order (weekday, hour, month). Example: baker shopify sales --days 7 --by campaign"
|
|
65517
66405
|
},
|
|
65518
66406
|
args: {
|
|
65519
|
-
days: {
|
|
65520
|
-
|
|
66407
|
+
days: {
|
|
66408
|
+
type: "string",
|
|
66409
|
+
description: "The last N days (1-395; up to 3650 by weekday, hour or month)",
|
|
66410
|
+
required: false
|
|
66411
|
+
},
|
|
66412
|
+
all: {
|
|
66413
|
+
type: "boolean",
|
|
66414
|
+
description: "Every order (by weekday, hour or month only)",
|
|
66415
|
+
required: false,
|
|
66416
|
+
default: false
|
|
66417
|
+
},
|
|
66418
|
+
by: {
|
|
66419
|
+
type: "string",
|
|
66420
|
+
description: "day | product | campaign | landing | weekday | hour | month | month_of_year (default day)",
|
|
66421
|
+
required: false
|
|
66422
|
+
},
|
|
65521
66423
|
limit: { type: "string", description: "Rows of a breakdown, 1-100", required: false },
|
|
65522
66424
|
full: { type: "boolean", description: "Add sources, campaigns and ids per row", required: false, default: false }
|
|
65523
66425
|
},
|
|
@@ -65525,7 +66427,7 @@ var salesCommand = defineCommand209({
|
|
|
65525
66427
|
try {
|
|
65526
66428
|
const answer = await apiGet(
|
|
65527
66429
|
"/api/shopify/sales",
|
|
65528
|
-
params({ days: args5.days, by: args5.by, limit: args5.limit, full: args5.full })
|
|
66430
|
+
params({ days: args5.days, all: args5.all, by: args5.by, limit: args5.limit, full: args5.full })
|
|
65529
66431
|
);
|
|
65530
66432
|
const { store, salesReporting, history, ...data } = answer;
|
|
65531
66433
|
emit2(data, { store, salesReporting, history }, [...storeStateHints(store), ...salesHints(answer)]);
|
|
@@ -65534,7 +66436,7 @@ var salesCommand = defineCommand209({
|
|
|
65534
66436
|
}
|
|
65535
66437
|
}
|
|
65536
66438
|
});
|
|
65537
|
-
var storeCommand =
|
|
66439
|
+
var storeCommand = defineCommand210({
|
|
65538
66440
|
meta: {
|
|
65539
66441
|
name: "store",
|
|
65540
66442
|
description: "The connected Shopify store: currency, countries, how many products, what is out of stock. Example: baker shopify store"
|
|
@@ -65551,7 +66453,7 @@ var storeCommand = defineCommand209({
|
|
|
65551
66453
|
}
|
|
65552
66454
|
}
|
|
65553
66455
|
});
|
|
65554
|
-
var productsCommand =
|
|
66456
|
+
var productsCommand = defineCommand210({
|
|
65555
66457
|
meta: {
|
|
65556
66458
|
name: "products",
|
|
65557
66459
|
description: 'What the store sells, with price and stock. Example: baker shopify products --search "snowboard" --stock out_of_stock'
|
|
@@ -65597,7 +66499,7 @@ var productsCommand = defineCommand209({
|
|
|
65597
66499
|
}
|
|
65598
66500
|
}
|
|
65599
66501
|
});
|
|
65600
|
-
var productCommand =
|
|
66502
|
+
var productCommand = defineCommand210({
|
|
65601
66503
|
meta: {
|
|
65602
66504
|
name: "product",
|
|
65603
66505
|
description: "One product: options with buy links, prices by country, stock, photo imageIds, video playbackIds. Accepts an id, handle or link. Example: baker shopify product the-complete-snowboard --content"
|
|
@@ -65660,7 +66562,7 @@ var productCommand = defineCommand209({
|
|
|
65660
66562
|
}
|
|
65661
66563
|
}
|
|
65662
66564
|
});
|
|
65663
|
-
var collectionsCommand =
|
|
66565
|
+
var collectionsCommand = defineCommand210({
|
|
65664
66566
|
meta: {
|
|
65665
66567
|
name: "collections",
|
|
65666
66568
|
description: "The store's collections, with the handles --collection takes. Example: baker shopify collections --full"
|
|
@@ -65685,7 +66587,7 @@ var collectionsCommand = defineCommand209({
|
|
|
65685
66587
|
}
|
|
65686
66588
|
}
|
|
65687
66589
|
});
|
|
65688
|
-
var stockCommand2 =
|
|
66590
|
+
var stockCommand2 = defineCommand210({
|
|
65689
66591
|
meta: {
|
|
65690
66592
|
name: "stock",
|
|
65691
66593
|
description: "Every product with options out of stock, naming them. Example: baker shopify stock"
|
|
@@ -65702,18 +66604,18 @@ var stockCommand2 = defineCommand209({
|
|
|
65702
66604
|
params({ collection: args5.collection, limit: args5.limit, full: args5.full })
|
|
65703
66605
|
);
|
|
65704
66606
|
const { store, ...data } = answer;
|
|
65705
|
-
const
|
|
65706
|
-
emit2(data, { store }, [...storeStateHints(store), ...stockHints(answer, Number.isFinite(
|
|
66607
|
+
const limit2 = Number(args5.limit ?? 50);
|
|
66608
|
+
emit2(data, { store }, [...storeStateHints(store), ...stockHints(answer, Number.isFinite(limit2) ? limit2 : 50)]);
|
|
65707
66609
|
} catch (err) {
|
|
65708
66610
|
fail9(err);
|
|
65709
66611
|
}
|
|
65710
66612
|
}
|
|
65711
66613
|
});
|
|
65712
|
-
var shopifyCommand =
|
|
66614
|
+
var shopifyCommand = defineCommand210({
|
|
65713
66615
|
meta: {
|
|
65714
66616
|
name: "shopify",
|
|
65715
66617
|
description: `Read the connected Shopify store as Baker keeps it \u2014 products, prices by country, stock, collections,
|
|
65716
|
-
product photos
|
|
66618
|
+
product photos, what it sold, and who buys, where and what. Read-only: nothing here changes the store.
|
|
65717
66619
|
|
|
65718
66620
|
Examples:
|
|
65719
66621
|
baker shopify store # currency, countries, how many products, what is out of stock
|
|
@@ -65728,6 +66630,13 @@ Examples:
|
|
|
65728
66630
|
baker shopify stock # every product with options out of stock
|
|
65729
66631
|
baker shopify collections # collections, with handles for --collection
|
|
65730
66632
|
baker shopify sales --days 7 --by campaign # what sold and which campaigns brought it (--by day|product|landing)
|
|
66633
|
+
baker shopify sales --all --by weekday # when buyers order, in the store's time (--by hour|month|month_of_year)
|
|
66634
|
+
baker shopify countries --days 90 # where buyers are, with growth (--country DE for its regions)
|
|
66635
|
+
baker shopify best-sellers --days 90 # what sells (--view variants|trend|together|repeat|momentum)
|
|
66636
|
+
baker shopify buyers --days 90 --by channel # who buys, repeat rate, segments, value by acquisition channel
|
|
66637
|
+
baker shopify buyer 1042 # one numbered customer's orders
|
|
66638
|
+
baker shopify segments # best, loyal, at risk, new, one-time \u2014 and who to find more of
|
|
66639
|
+
baker shopify cohorts --months 12 # buyers by first-order month: who came back
|
|
65731
66640
|
|
|
65732
66641
|
Product photos are already in the image library: place them by imageId
|
|
65733
66642
|
(baker images download <imageIds> --out src/pages/<page>/_images/), never by a cdn.shopify.com link.
|
|
@@ -65744,15 +66653,21 @@ Full guide: __tooling__/docs/tools/baker/shopify.md`
|
|
|
65744
66653
|
product: productCommand,
|
|
65745
66654
|
collections: collectionsCommand,
|
|
65746
66655
|
stock: stockCommand2,
|
|
65747
|
-
sales: salesCommand
|
|
66656
|
+
sales: salesCommand,
|
|
66657
|
+
buyers: buyersCommand,
|
|
66658
|
+
buyer: buyerCommand,
|
|
66659
|
+
countries: countriesCommand2,
|
|
66660
|
+
"best-sellers": bestSellersCommand,
|
|
66661
|
+
cohorts: cohortsCommand,
|
|
66662
|
+
segments: segmentsCommand
|
|
65748
66663
|
}
|
|
65749
66664
|
});
|
|
65750
66665
|
|
|
65751
66666
|
// src/commands/studio/index.ts
|
|
65752
|
-
import { defineCommand as
|
|
66667
|
+
import { defineCommand as defineCommand219 } from "citty";
|
|
65753
66668
|
|
|
65754
66669
|
// src/commands/studio/animate.ts
|
|
65755
|
-
import { defineCommand as
|
|
66670
|
+
import { defineCommand as defineCommand211 } from "citty";
|
|
65756
66671
|
|
|
65757
66672
|
// src/commands/studio/spokenPace.ts
|
|
65758
66673
|
var WORDS_PER_SECOND3 = SPOKEN_WORDS_PER_SECOND;
|
|
@@ -65763,9 +66678,9 @@ function wordsThatFit(seconds) {
|
|
|
65763
66678
|
function spokenPaceHint(prompt, seconds, model) {
|
|
65764
66679
|
const words2 = spokenWords(prompt);
|
|
65765
66680
|
if (words2 === 0) return null;
|
|
65766
|
-
const
|
|
65767
|
-
if (
|
|
65768
|
-
return `The line is ${words2} words \u2014 about ${Math.ceil(words2 / WORDS_PER_SECOND3)}s of speech \u2014 and ${model} renders at most ${
|
|
66681
|
+
const split2 = model ? splitScriptFor(words2, model) : null;
|
|
66682
|
+
if (split2) {
|
|
66683
|
+
return `The line is ${words2} words \u2014 about ${Math.ceil(words2 / WORDS_PER_SECOND3)}s of speech \u2014 and ${model} renders at most ${split2.maxSeconds}s (~${Math.floor(split2.maxSeconds * WORDS_PER_SECOND3)} words). It will not refuse and it will not cut: it will RUSH, swallowing syllables. Split the script into ${split2.clips} takes of roughly ${split2.wordsPerClip} words each, rendered separately and cut together \u2014 break it where the thought breaks, not mid-sentence. Or move to a model that renders longer (bytedance/seedance-2.5 goes to 30s).`;
|
|
65769
66684
|
}
|
|
65770
66685
|
if (!seconds) return null;
|
|
65771
66686
|
const fits = wordsThatFit(seconds);
|
|
@@ -65980,10 +66895,10 @@ var defaultDeps = {
|
|
|
65980
66895
|
ingest: (url) => apiPost("/api/images/ingest", { url, source: "uploaded" }),
|
|
65981
66896
|
upload: (path48) => uploadLocalImage({ file: path48, contentType: detectImageContentType(path48), source: "uploaded" })
|
|
65982
66897
|
};
|
|
65983
|
-
async function resolveLibraryImageIds(spec,
|
|
66898
|
+
async function resolveLibraryImageIds(spec, limit2, deps = defaultDeps) {
|
|
65984
66899
|
const refs = parseImageRefs(spec);
|
|
65985
|
-
if (
|
|
65986
|
-
throw new ApiError("VALIDATION_ERROR",
|
|
66900
|
+
if (limit2 && refs.length > limit2.max) {
|
|
66901
|
+
throw new ApiError("VALIDATION_ERROR", limit2.refuse(refs.length));
|
|
65987
66902
|
}
|
|
65988
66903
|
const imageIds = [];
|
|
65989
66904
|
const added = [];
|
|
@@ -66265,7 +67180,7 @@ async function adByHandHintFor(body) {
|
|
|
66265
67180
|
return null;
|
|
66266
67181
|
}
|
|
66267
67182
|
}
|
|
66268
|
-
var animateCommand =
|
|
67183
|
+
var animateCommand = defineCommand211({
|
|
66269
67184
|
meta: {
|
|
66270
67185
|
name: "animate",
|
|
66271
67186
|
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"
|
|
@@ -66390,7 +67305,7 @@ var animateCommand = defineCommand210({
|
|
|
66390
67305
|
});
|
|
66391
67306
|
|
|
66392
67307
|
// src/commands/studio/generate.ts
|
|
66393
|
-
import { defineCommand as
|
|
67308
|
+
import { defineCommand as defineCommand212 } from "citty";
|
|
66394
67309
|
var MODEL_LIST2 = IMAGE_MODEL_IDS;
|
|
66395
67310
|
var DEFAULT_MAX_WAIT_MS2 = 24e4;
|
|
66396
67311
|
registerSchema({
|
|
@@ -66478,11 +67393,11 @@ registerSchema({
|
|
|
66478
67393
|
full: { type: "boolean", description: "Include settings, references and attribution", required: false }
|
|
66479
67394
|
}
|
|
66480
67395
|
});
|
|
66481
|
-
function parseJsonArg(raw,
|
|
67396
|
+
function parseJsonArg(raw, flag3) {
|
|
66482
67397
|
try {
|
|
66483
67398
|
return JSON.parse(raw);
|
|
66484
67399
|
} catch {
|
|
66485
|
-
throw new ApiError("VALIDATION_ERROR", `--${
|
|
67400
|
+
throw new ApiError("VALIDATION_ERROR", `--${flag3} must be valid JSON (got: ${raw})`);
|
|
66486
67401
|
}
|
|
66487
67402
|
}
|
|
66488
67403
|
function referenceLimitFor(model) {
|
|
@@ -66526,7 +67441,7 @@ function buildGenerateBody(args5, prompt) {
|
|
|
66526
67441
|
}
|
|
66527
67442
|
return body;
|
|
66528
67443
|
}
|
|
66529
|
-
var generateCommand =
|
|
67444
|
+
var generateCommand = defineCommand212({
|
|
66530
67445
|
meta: {
|
|
66531
67446
|
name: "generate",
|
|
66532
67447
|
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-preview (default \u2014 fast, best at editing a reference and at extreme ratios), google/gemini-3-pro-image-preview (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]]'"
|
|
@@ -66609,7 +67524,7 @@ var generateCommand = defineCommand211({
|
|
|
66609
67524
|
});
|
|
66610
67525
|
|
|
66611
67526
|
// src/commands/studio/get.ts
|
|
66612
|
-
import { defineCommand as
|
|
67527
|
+
import { defineCommand as defineCommand213 } from "citty";
|
|
66613
67528
|
registerSchema({
|
|
66614
67529
|
command: "studio.get",
|
|
66615
67530
|
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.",
|
|
@@ -66618,7 +67533,7 @@ registerSchema({
|
|
|
66618
67533
|
full: { type: "boolean", description: "Include settings, references and attribution", required: false }
|
|
66619
67534
|
}
|
|
66620
67535
|
});
|
|
66621
|
-
var getCommand5 =
|
|
67536
|
+
var getCommand5 = defineCommand213({
|
|
66622
67537
|
meta: {
|
|
66623
67538
|
name: "get",
|
|
66624
67539
|
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"
|
|
@@ -66647,7 +67562,7 @@ var getCommand5 = defineCommand212({
|
|
|
66647
67562
|
});
|
|
66648
67563
|
|
|
66649
67564
|
// src/commands/studio/improve.ts
|
|
66650
|
-
import { defineCommand as
|
|
67565
|
+
import { defineCommand as defineCommand214 } from "citty";
|
|
66651
67566
|
var DESCRIPTION2 = "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.";
|
|
66652
67567
|
registerSchema({
|
|
66653
67568
|
command: "studio.improve",
|
|
@@ -66672,7 +67587,7 @@ registerSchema({
|
|
|
66672
67587
|
}
|
|
66673
67588
|
}
|
|
66674
67589
|
});
|
|
66675
|
-
var improveCommand =
|
|
67590
|
+
var improveCommand = defineCommand214({
|
|
66676
67591
|
meta: {
|
|
66677
67592
|
name: "improve",
|
|
66678
67593
|
description: `${DESCRIPTION2}
|
|
@@ -66716,7 +67631,7 @@ Examples:
|
|
|
66716
67631
|
});
|
|
66717
67632
|
|
|
66718
67633
|
// src/commands/studio/keep.ts
|
|
66719
|
-
import { defineCommand as
|
|
67634
|
+
import { defineCommand as defineCommand215 } from "citty";
|
|
66720
67635
|
registerSchema({
|
|
66721
67636
|
command: "studio.keep",
|
|
66722
67637
|
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.",
|
|
@@ -66726,7 +67641,7 @@ registerSchema({
|
|
|
66726
67641
|
undo: { type: "boolean", description: "Un-star an image, or take a kept clip back out", required: false }
|
|
66727
67642
|
}
|
|
66728
67643
|
});
|
|
66729
|
-
var keepCommand =
|
|
67644
|
+
var keepCommand = defineCommand215({
|
|
66730
67645
|
meta: {
|
|
66731
67646
|
name: "keep",
|
|
66732
67647
|
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"
|
|
@@ -66777,7 +67692,7 @@ var keepCommand = defineCommand214({
|
|
|
66777
67692
|
});
|
|
66778
67693
|
|
|
66779
67694
|
// src/commands/studio/list.ts
|
|
66780
|
-
import { defineCommand as
|
|
67695
|
+
import { defineCommand as defineCommand216 } from "citty";
|
|
66781
67696
|
registerSchema({
|
|
66782
67697
|
command: "studio.list",
|
|
66783
67698
|
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.",
|
|
@@ -66788,7 +67703,7 @@ registerSchema({
|
|
|
66788
67703
|
full: { type: "boolean", description: "Include settings, references and attribution", required: false }
|
|
66789
67704
|
}
|
|
66790
67705
|
});
|
|
66791
|
-
var listCommand18 =
|
|
67706
|
+
var listCommand18 = defineCommand216({
|
|
66792
67707
|
meta: {
|
|
66793
67708
|
name: "list",
|
|
66794
67709
|
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"
|
|
@@ -66822,7 +67737,7 @@ var listCommand18 = defineCommand215({
|
|
|
66822
67737
|
});
|
|
66823
67738
|
|
|
66824
67739
|
// src/commands/studio/models.ts
|
|
66825
|
-
import { defineCommand as
|
|
67740
|
+
import { defineCommand as defineCommand217 } from "citty";
|
|
66826
67741
|
var DESCRIPTION3 = "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.";
|
|
66827
67742
|
registerSchema({
|
|
66828
67743
|
command: "studio.models",
|
|
@@ -66909,7 +67824,7 @@ function buildModelCards(kind, model) {
|
|
|
66909
67824
|
const selected = model ? ids.filter((id) => id === model) : ids;
|
|
66910
67825
|
return selected.map((id) => build(id));
|
|
66911
67826
|
}
|
|
66912
|
-
var modelsCommand =
|
|
67827
|
+
var modelsCommand = defineCommand217({
|
|
66913
67828
|
meta: {
|
|
66914
67829
|
name: "models",
|
|
66915
67830
|
description: `${DESCRIPTION3}
|
|
@@ -66956,13 +67871,13 @@ Examples:
|
|
|
66956
67871
|
});
|
|
66957
67872
|
|
|
66958
67873
|
// src/commands/studio/skills.ts
|
|
66959
|
-
import { defineCommand as
|
|
67874
|
+
import { defineCommand as defineCommand218 } from "citty";
|
|
66960
67875
|
registerSchema({
|
|
66961
67876
|
command: "studio.skills",
|
|
66962
67877
|
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.",
|
|
66963
67878
|
args: {}
|
|
66964
67879
|
});
|
|
66965
|
-
var skillsCommand =
|
|
67880
|
+
var skillsCommand = defineCommand218({
|
|
66966
67881
|
meta: {
|
|
66967
67882
|
name: "skills",
|
|
66968
67883
|
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"
|
|
@@ -66986,7 +67901,7 @@ var skillsCommand = defineCommand217({
|
|
|
66986
67901
|
});
|
|
66987
67902
|
|
|
66988
67903
|
// src/commands/studio/index.ts
|
|
66989
|
-
var studioCommand =
|
|
67904
|
+
var studioCommand = defineCommand219({
|
|
66990
67905
|
meta: {
|
|
66991
67906
|
name: "studio",
|
|
66992
67907
|
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.
|
|
@@ -67029,10 +67944,10 @@ Full guide: __tooling__/docs/tools/baker/studio.md`
|
|
|
67029
67944
|
});
|
|
67030
67945
|
|
|
67031
67946
|
// src/commands/tag-manager/index.ts
|
|
67032
|
-
import { defineCommand as
|
|
67947
|
+
import { defineCommand as defineCommand223 } from "citty";
|
|
67033
67948
|
|
|
67034
67949
|
// src/commands/tag-manager/draft.ts
|
|
67035
|
-
import { defineCommand as
|
|
67950
|
+
import { defineCommand as defineCommand220 } from "citty";
|
|
67036
67951
|
|
|
67037
67952
|
// src/commands/tag-manager/shared.ts
|
|
67038
67953
|
import { readFileSync as readFileSync20 } from "fs";
|
|
@@ -67048,21 +67963,21 @@ function requireTarget6(args5, entity) {
|
|
|
67048
67963
|
}
|
|
67049
67964
|
return target2;
|
|
67050
67965
|
}
|
|
67051
|
-
function loadJsonArg2(args5,
|
|
67052
|
-
const inline = args5[
|
|
67966
|
+
function loadJsonArg2(args5, flag3 = "json") {
|
|
67967
|
+
const inline = args5[flag3];
|
|
67053
67968
|
const file = args5.file;
|
|
67054
67969
|
const raw = typeof file === "string" && file.length > 0 ? readFileSync20(file, "utf8") : typeof inline === "string" && inline.length > 0 ? inline : void 0;
|
|
67055
67970
|
if (raw === void 0) {
|
|
67056
|
-
failValidation5(`pass --${
|
|
67971
|
+
failValidation5(`pass --${flag3} with inline JSON or --file with a path to a JSON file`);
|
|
67057
67972
|
}
|
|
67058
67973
|
try {
|
|
67059
67974
|
const parsed = JSON.parse(raw);
|
|
67060
67975
|
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
67061
|
-
failValidation5(`--${
|
|
67976
|
+
failValidation5(`--${flag3} must be a JSON object`);
|
|
67062
67977
|
}
|
|
67063
67978
|
return parsed;
|
|
67064
67979
|
} catch {
|
|
67065
|
-
return failValidation5(`--${
|
|
67980
|
+
return failValidation5(`--${flag3} is not valid JSON`);
|
|
67066
67981
|
}
|
|
67067
67982
|
}
|
|
67068
67983
|
var RETRYABLE_CODES2 = /* @__PURE__ */ new Set(["RATE_LIMITED", "UNAVAILABLE", "INTERNAL_ERROR", "NETWORK_ERROR", "TIMEOUT"]);
|
|
@@ -67155,13 +68070,13 @@ registerSchema({
|
|
|
67155
68070
|
chat: { type: "string", description: CHAT_READ_ARG.description, required: false }
|
|
67156
68071
|
}
|
|
67157
68072
|
});
|
|
67158
|
-
var draftCommand5 =
|
|
68073
|
+
var draftCommand5 = defineCommand220({
|
|
67159
68074
|
meta: {
|
|
67160
68075
|
name: "draft",
|
|
67161
68076
|
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."
|
|
67162
68077
|
},
|
|
67163
68078
|
subCommands: {
|
|
67164
|
-
list:
|
|
68079
|
+
list: defineCommand220({
|
|
67165
68080
|
meta: {
|
|
67166
68081
|
name: "list",
|
|
67167
68082
|
description: "Review everything staged on this chat (--json for the raw envelope)"
|
|
@@ -67174,7 +68089,7 @@ var draftCommand5 = defineCommand219({
|
|
|
67174
68089
|
await draftList2(args5.json === true, args5.chat);
|
|
67175
68090
|
}
|
|
67176
68091
|
}),
|
|
67177
|
-
show:
|
|
68092
|
+
show: defineCommand220({
|
|
67178
68093
|
meta: {
|
|
67179
68094
|
name: "show",
|
|
67180
68095
|
description: "Print the full staged payload for one change \u2014 the receipt to verify it looks right before publish (never truncated)."
|
|
@@ -67191,7 +68106,7 @@ var draftCommand5 = defineCommand219({
|
|
|
67191
68106
|
);
|
|
67192
68107
|
}
|
|
67193
68108
|
}),
|
|
67194
|
-
amend:
|
|
68109
|
+
amend: defineCommand220({
|
|
67195
68110
|
meta: {
|
|
67196
68111
|
name: "amend",
|
|
67197
68112
|
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."
|
|
@@ -67208,7 +68123,7 @@ var draftCommand5 = defineCommand219({
|
|
|
67208
68123
|
});
|
|
67209
68124
|
}
|
|
67210
68125
|
}),
|
|
67211
|
-
remove:
|
|
68126
|
+
remove: defineCommand220({
|
|
67212
68127
|
meta: { name: "remove", description: "Remove one staged change (cascades to anything depending on it)" },
|
|
67213
68128
|
args: { ref: { type: "positional", description: "Staged ref (gtm_temp_*) or target", required: false } },
|
|
67214
68129
|
run: async ({ args: args5 }) => {
|
|
@@ -67217,7 +68132,7 @@ var draftCommand5 = defineCommand219({
|
|
|
67217
68132
|
});
|
|
67218
68133
|
}
|
|
67219
68134
|
}),
|
|
67220
|
-
clear:
|
|
68135
|
+
clear: defineCommand220({
|
|
67221
68136
|
meta: { name: "clear", description: "Discard all Tag Manager changes staged on this chat" },
|
|
67222
68137
|
run: async () => {
|
|
67223
68138
|
await draftAction3("/api/tag-manager/draft/clear", {});
|
|
@@ -67227,7 +68142,7 @@ var draftCommand5 = defineCommand219({
|
|
|
67227
68142
|
});
|
|
67228
68143
|
|
|
67229
68144
|
// src/commands/tag-manager/read.ts
|
|
67230
|
-
import { defineCommand as
|
|
68145
|
+
import { defineCommand as defineCommand221 } from "citty";
|
|
67231
68146
|
registerSchema({
|
|
67232
68147
|
command: "tagManager.containers",
|
|
67233
68148
|
description: "List the Google Tag Manager containers this company's connection can reach. Every container the company connected is flagged `connected: true` \u2014 there can be several, and Baker may read and change all of them. Start here to confirm which containers you are managing.",
|
|
@@ -67268,7 +68183,7 @@ function containersHints(containers) {
|
|
|
67268
68183
|
}))
|
|
67269
68184
|
});
|
|
67270
68185
|
}
|
|
67271
|
-
var containersCommand =
|
|
68186
|
+
var containersCommand = defineCommand221({
|
|
67272
68187
|
meta: {
|
|
67273
68188
|
name: "containers",
|
|
67274
68189
|
description: `List Tag Manager containers reachable by this company's connection.
|
|
@@ -67285,7 +68200,7 @@ Start here:
|
|
|
67285
68200
|
}
|
|
67286
68201
|
}
|
|
67287
68202
|
});
|
|
67288
|
-
var readCommand =
|
|
68203
|
+
var readCommand = defineCommand221({
|
|
67289
68204
|
meta: {
|
|
67290
68205
|
name: "read",
|
|
67291
68206
|
description: `Read the current contents of the Tag Manager container \u2014 always do this before staging changes.
|
|
@@ -67327,7 +68242,7 @@ Examples:
|
|
|
67327
68242
|
});
|
|
67328
68243
|
|
|
67329
68244
|
// src/commands/tag-manager/write-commands.ts
|
|
67330
|
-
import { defineCommand as
|
|
68245
|
+
import { defineCommand as defineCommand222 } from "citty";
|
|
67331
68246
|
var CONTAINER_ARG_DESCRIPTION = "Numeric container id (optional only when one container is connected \u2014 run `baker tag-manager containers`)";
|
|
67332
68247
|
var ENTITIES = [
|
|
67333
68248
|
{
|
|
@@ -67383,10 +68298,10 @@ for (const { entity, noun, createHint } of ENTITIES) {
|
|
|
67383
68298
|
});
|
|
67384
68299
|
}
|
|
67385
68300
|
function entityCommand(entity, noun, example) {
|
|
67386
|
-
return
|
|
68301
|
+
return defineCommand222({
|
|
67387
68302
|
meta: { name: entity, description: `Stage ${noun} changes on this chat's Tag Manager draft` },
|
|
67388
68303
|
subCommands: {
|
|
67389
|
-
create:
|
|
68304
|
+
create: defineCommand222({
|
|
67390
68305
|
meta: {
|
|
67391
68306
|
name: "create",
|
|
67392
68307
|
description: `Stage a new ${noun}
|
|
@@ -67408,7 +68323,7 @@ Examples:
|
|
|
67408
68323
|
});
|
|
67409
68324
|
}
|
|
67410
68325
|
}),
|
|
67411
|
-
update:
|
|
68326
|
+
update: defineCommand222({
|
|
67412
68327
|
meta: {
|
|
67413
68328
|
name: "update",
|
|
67414
68329
|
description: `Stage an update to an existing ${noun} (pass its id or path)`
|
|
@@ -67428,7 +68343,7 @@ Examples:
|
|
|
67428
68343
|
});
|
|
67429
68344
|
}
|
|
67430
68345
|
}),
|
|
67431
|
-
delete:
|
|
68346
|
+
delete: defineCommand222({
|
|
67432
68347
|
meta: { name: "delete", description: `Stage the deletion of a ${noun} (pass its id or path)` },
|
|
67433
68348
|
args: {
|
|
67434
68349
|
id: { type: "positional", description: `${noun} id or path`, required: false },
|
|
@@ -67469,7 +68384,7 @@ function builtinTypes(args5) {
|
|
|
67469
68384
|
}
|
|
67470
68385
|
return raw.split(",").map((entry) => entry.trim());
|
|
67471
68386
|
}
|
|
67472
|
-
var builtinCommand =
|
|
68387
|
+
var builtinCommand = defineCommand222({
|
|
67473
68388
|
meta: {
|
|
67474
68389
|
name: "builtin",
|
|
67475
68390
|
description: `Enable or disable built-in variables
|
|
@@ -67479,7 +68394,7 @@ Examples:
|
|
|
67479
68394
|
baker tag-manager builtin disable --types formId`
|
|
67480
68395
|
},
|
|
67481
68396
|
subCommands: {
|
|
67482
|
-
enable:
|
|
68397
|
+
enable: defineCommand222({
|
|
67483
68398
|
meta: { name: "enable", description: "Stage enabling built-in variables" },
|
|
67484
68399
|
args: {
|
|
67485
68400
|
types: { type: "string", description: "Comma-separated types", required: false },
|
|
@@ -67493,7 +68408,7 @@ Examples:
|
|
|
67493
68408
|
});
|
|
67494
68409
|
}
|
|
67495
68410
|
}),
|
|
67496
|
-
disable:
|
|
68411
|
+
disable: defineCommand222({
|
|
67497
68412
|
meta: { name: "disable", description: "Stage disabling built-in variables" },
|
|
67498
68413
|
args: {
|
|
67499
68414
|
types: { type: "string", description: "Comma-separated types", required: false },
|
|
@@ -67511,7 +68426,7 @@ Examples:
|
|
|
67511
68426
|
});
|
|
67512
68427
|
|
|
67513
68428
|
// src/commands/tag-manager/index.ts
|
|
67514
|
-
var tagManagerCommand =
|
|
68429
|
+
var tagManagerCommand = defineCommand223({
|
|
67515
68430
|
meta: {
|
|
67516
68431
|
name: "tag-manager",
|
|
67517
68432
|
description: `Read and change what lives inside the client's Google Tag Manager container \u2014 tags, triggers, variables, folders and built-in variables.
|
|
@@ -67548,7 +68463,7 @@ Full guide: __tooling__/docs/tools/baker/tag-manager.md`
|
|
|
67548
68463
|
});
|
|
67549
68464
|
|
|
67550
68465
|
// src/commands/tags/index.ts
|
|
67551
|
-
import { defineCommand as
|
|
68466
|
+
import { defineCommand as defineCommand224 } from "citty";
|
|
67552
68467
|
|
|
67553
68468
|
// src/commands/tags/shared.ts
|
|
67554
68469
|
function failApi3(err) {
|
|
@@ -67628,7 +68543,7 @@ async function listTags(json) {
|
|
|
67628
68543
|
var listArgs11 = {
|
|
67629
68544
|
json: { type: "boolean", description: "Print the raw JSON envelope instead of the readable list" }
|
|
67630
68545
|
};
|
|
67631
|
-
var listCommand19 =
|
|
68546
|
+
var listCommand19 = defineCommand224({
|
|
67632
68547
|
meta: {
|
|
67633
68548
|
name: "list",
|
|
67634
68549
|
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"
|
|
@@ -67647,7 +68562,7 @@ async function listDraft4(chat) {
|
|
|
67647
68562
|
failApi3(err);
|
|
67648
68563
|
}
|
|
67649
68564
|
}
|
|
67650
|
-
var draftCommand6 =
|
|
68565
|
+
var draftCommand6 = defineCommand224({
|
|
67651
68566
|
meta: {
|
|
67652
68567
|
name: "draft",
|
|
67653
68568
|
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."
|
|
@@ -67657,7 +68572,7 @@ var draftCommand6 = defineCommand223({
|
|
|
67657
68572
|
await listDraft4(args5.chat);
|
|
67658
68573
|
}
|
|
67659
68574
|
});
|
|
67660
|
-
var tagsCommand4 =
|
|
68575
|
+
var tagsCommand4 = defineCommand224({
|
|
67661
68576
|
meta: {
|
|
67662
68577
|
name: "tags",
|
|
67663
68578
|
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.
|
|
@@ -67686,10 +68601,10 @@ Full guide: __tooling__/docs/tools/baker/tags.md`
|
|
|
67686
68601
|
});
|
|
67687
68602
|
|
|
67688
68603
|
// src/commands/testimonials/index.ts
|
|
67689
|
-
import { defineCommand as
|
|
68604
|
+
import { defineCommand as defineCommand228 } from "citty";
|
|
67690
68605
|
|
|
67691
68606
|
// src/commands/testimonials/get.ts
|
|
67692
|
-
import { defineCommand as
|
|
68607
|
+
import { defineCommand as defineCommand225 } from "citty";
|
|
67693
68608
|
registerSchema({
|
|
67694
68609
|
command: "testimonials.get",
|
|
67695
68610
|
description: "Get a single testimonial by ID",
|
|
@@ -67697,7 +68612,7 @@ registerSchema({
|
|
|
67697
68612
|
id: { type: "string", description: "Testimonial ID", required: true }
|
|
67698
68613
|
}
|
|
67699
68614
|
});
|
|
67700
|
-
var getCommand6 =
|
|
68615
|
+
var getCommand6 = defineCommand225({
|
|
67701
68616
|
meta: { name: "get", description: "Get a single testimonial by ID. Example: baker testimonials get j571abc123" },
|
|
67702
68617
|
args: {
|
|
67703
68618
|
id: { type: "positional", description: "Testimonial ID", required: false },
|
|
@@ -67734,7 +68649,7 @@ var getCommand6 = defineCommand224({
|
|
|
67734
68649
|
});
|
|
67735
68650
|
|
|
67736
68651
|
// src/commands/testimonials/list.ts
|
|
67737
|
-
import { defineCommand as
|
|
68652
|
+
import { defineCommand as defineCommand226 } from "citty";
|
|
67738
68653
|
|
|
67739
68654
|
// src/commands/testimonials/emptyCorpusHints.ts
|
|
67740
68655
|
function resolveEmptyReason({
|
|
@@ -67883,15 +68798,15 @@ var PARAM_NAMES = {
|
|
|
67883
68798
|
};
|
|
67884
68799
|
function buildListParams(args5) {
|
|
67885
68800
|
const params2 = {};
|
|
67886
|
-
for (const [
|
|
67887
|
-
const value = args5[
|
|
68801
|
+
for (const [flag3, param] of Object.entries(PARAM_NAMES)) {
|
|
68802
|
+
const value = args5[flag3];
|
|
67888
68803
|
if (value !== void 0 && value !== "") {
|
|
67889
68804
|
params2[param] = String(value);
|
|
67890
68805
|
}
|
|
67891
68806
|
}
|
|
67892
68807
|
return params2;
|
|
67893
68808
|
}
|
|
67894
|
-
var listCommand20 =
|
|
68809
|
+
var listCommand20 = defineCommand226({
|
|
67895
68810
|
meta: {
|
|
67896
68811
|
name: "list",
|
|
67897
68812
|
description: "List testimonials with optional filters. Example: baker testimonials list --source google --sentiment positive"
|
|
@@ -67951,7 +68866,7 @@ var listCommand20 = defineCommand225({
|
|
|
67951
68866
|
});
|
|
67952
68867
|
|
|
67953
68868
|
// src/commands/testimonials/search.ts
|
|
67954
|
-
import { defineCommand as
|
|
68869
|
+
import { defineCommand as defineCommand227 } from "citty";
|
|
67955
68870
|
var FILTER_FLAGS2 = ["source", "rating-min", "rating-max", "status", "sentiment", "language", "tags"];
|
|
67956
68871
|
function languageBiasHint(results, requestedLanguage) {
|
|
67957
68872
|
if (requestedLanguage) {
|
|
@@ -68030,7 +68945,7 @@ function buildSearchRequest(query, args5) {
|
|
|
68030
68945
|
}
|
|
68031
68946
|
return body;
|
|
68032
68947
|
}
|
|
68033
|
-
var searchCommand3 =
|
|
68948
|
+
var searchCommand3 = defineCommand227({
|
|
68034
68949
|
meta: {
|
|
68035
68950
|
name: "search",
|
|
68036
68951
|
description: "Semantic search testimonials by text query. Uses hybrid BM25 + vector + reranking. Example: baker testimonials search 'great service' --rating-min 4"
|
|
@@ -68094,7 +69009,7 @@ var searchCommand3 = defineCommand226({
|
|
|
68094
69009
|
var tagsCommand5 = makeTagsCommand("testimonials", "testimonial", "/api/testimonials/tags");
|
|
68095
69010
|
|
|
68096
69011
|
// src/commands/testimonials/index.ts
|
|
68097
|
-
var testimonialsCommand =
|
|
69012
|
+
var testimonialsCommand = defineCommand228({
|
|
68098
69013
|
meta: {
|
|
68099
69014
|
name: "testimonials",
|
|
68100
69015
|
description: `Find and browse testimonials in Baker. Subcommands: search, get, list, tags.
|
|
@@ -68116,10 +69031,10 @@ Full guide: __tooling__/docs/tools/baker/testimonials.md`
|
|
|
68116
69031
|
});
|
|
68117
69032
|
|
|
68118
69033
|
// src/commands/videos/index.ts
|
|
68119
|
-
import { defineCommand as
|
|
69034
|
+
import { defineCommand as defineCommand236 } from "citty";
|
|
68120
69035
|
|
|
68121
69036
|
// src/commands/videos/delete.ts
|
|
68122
|
-
import { defineCommand as
|
|
69037
|
+
import { defineCommand as defineCommand229 } from "citty";
|
|
68123
69038
|
registerSchema({
|
|
68124
69039
|
command: "videos.delete",
|
|
68125
69040
|
description: "Delete a video by ID",
|
|
@@ -68133,7 +69048,7 @@ registerSchema({
|
|
|
68133
69048
|
}
|
|
68134
69049
|
}
|
|
68135
69050
|
});
|
|
68136
|
-
var deleteCommand4 =
|
|
69051
|
+
var deleteCommand4 = defineCommand229({
|
|
68137
69052
|
meta: {
|
|
68138
69053
|
name: "delete",
|
|
68139
69054
|
description: "Delete a video by ID. Use --dry-run to preview. Example: baker videos delete j571abc123 --dry-run"
|
|
@@ -68174,7 +69089,7 @@ var deleteCommand4 = defineCommand228({
|
|
|
68174
69089
|
});
|
|
68175
69090
|
|
|
68176
69091
|
// src/commands/videos/describe.ts
|
|
68177
|
-
import { defineCommand as
|
|
69092
|
+
import { defineCommand as defineCommand230 } from "citty";
|
|
68178
69093
|
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.";
|
|
68179
69094
|
registerSchema({
|
|
68180
69095
|
command: "videos.describe",
|
|
@@ -68191,7 +69106,7 @@ registerSchema({
|
|
|
68191
69106
|
full: { type: "boolean", description: "Return the whole library row, not just what changed", required: false }
|
|
68192
69107
|
}
|
|
68193
69108
|
});
|
|
68194
|
-
var describeCommand2 =
|
|
69109
|
+
var describeCommand2 = defineCommand230({
|
|
68195
69110
|
meta: {
|
|
68196
69111
|
name: "describe",
|
|
68197
69112
|
description: `Correct what the library says a video is. Only the fields you pass change; the rest keep their current values.
|
|
@@ -68249,7 +69164,7 @@ Example: baker videos describe j571abc123 --description "Customer explaining how
|
|
|
68249
69164
|
});
|
|
68250
69165
|
|
|
68251
69166
|
// src/commands/videos/get.ts
|
|
68252
|
-
import { defineCommand as
|
|
69167
|
+
import { defineCommand as defineCommand231 } from "citty";
|
|
68253
69168
|
registerSchema({
|
|
68254
69169
|
command: "videos.get",
|
|
68255
69170
|
description: "Get a single video by ID",
|
|
@@ -68257,7 +69172,7 @@ registerSchema({
|
|
|
68257
69172
|
id: { type: "string", description: "Video ID", required: true }
|
|
68258
69173
|
}
|
|
68259
69174
|
});
|
|
68260
|
-
var getCommand7 =
|
|
69175
|
+
var getCommand7 = defineCommand231({
|
|
68261
69176
|
meta: { name: "get", description: "Get a single video by ID. Example: baker videos get j571abc123" },
|
|
68262
69177
|
args: {
|
|
68263
69178
|
id: { type: "positional", description: "Video ID", required: false },
|
|
@@ -68294,7 +69209,7 @@ var getCommand7 = defineCommand230({
|
|
|
68294
69209
|
});
|
|
68295
69210
|
|
|
68296
69211
|
// src/commands/videos/group.ts
|
|
68297
|
-
import { defineCommand as
|
|
69212
|
+
import { defineCommand as defineCommand232 } from "citty";
|
|
68298
69213
|
registerSchema({
|
|
68299
69214
|
command: "videos.group",
|
|
68300
69215
|
description: "List every clip and image that arrived in the same set as this video (carousel slides, one page)",
|
|
@@ -68303,7 +69218,7 @@ registerSchema({
|
|
|
68303
69218
|
"group-key": { type: "string", description: "The set key directly, when you already have it", required: false }
|
|
68304
69219
|
}
|
|
68305
69220
|
});
|
|
68306
|
-
var groupCommand2 =
|
|
69221
|
+
var groupCommand2 = defineCommand232({
|
|
68307
69222
|
meta: {
|
|
68308
69223
|
name: "group",
|
|
68309
69224
|
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>"
|
|
@@ -68326,7 +69241,7 @@ var groupCommand2 = defineCommand231({
|
|
|
68326
69241
|
import { mkdtemp as mkdtemp3, rm as rm9, stat as stat11 } from "fs/promises";
|
|
68327
69242
|
import { tmpdir as tmpdir4 } from "os";
|
|
68328
69243
|
import path47 from "path";
|
|
68329
|
-
import { defineCommand as
|
|
69244
|
+
import { defineCommand as defineCommand233 } from "citty";
|
|
68330
69245
|
|
|
68331
69246
|
// src/lib/streamUpload.ts
|
|
68332
69247
|
import { createHash as createHash2 } from "crypto";
|
|
@@ -68490,7 +69405,7 @@ registerSchema({
|
|
|
68490
69405
|
"dry-run": { type: "boolean", description: "Preview the operation without executing", required: false }
|
|
68491
69406
|
}
|
|
68492
69407
|
});
|
|
68493
|
-
var ingestCommand2 =
|
|
69408
|
+
var ingestCommand2 = defineCommand233({
|
|
68494
69409
|
meta: {
|
|
68495
69410
|
name: "ingest",
|
|
68496
69411
|
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"
|
|
@@ -68752,7 +69667,7 @@ async function uploadToAssetStore(filePath, sizeBytes) {
|
|
|
68752
69667
|
}
|
|
68753
69668
|
|
|
68754
69669
|
// src/commands/videos/search.ts
|
|
68755
|
-
import { defineCommand as
|
|
69670
|
+
import { defineCommand as defineCommand234 } from "citty";
|
|
68756
69671
|
registerSchema({
|
|
68757
69672
|
command: "videos.search",
|
|
68758
69673
|
description: "Search videos by text query. Only returns ready videos.",
|
|
@@ -68762,7 +69677,7 @@ registerSchema({
|
|
|
68762
69677
|
tags: { type: "string", description: "Comma-separated tags to filter by", required: false }
|
|
68763
69678
|
}
|
|
68764
69679
|
});
|
|
68765
|
-
var searchCommand4 =
|
|
69680
|
+
var searchCommand4 = defineCommand234({
|
|
68766
69681
|
meta: {
|
|
68767
69682
|
name: "search",
|
|
68768
69683
|
description: "Semantic search videos by text query. Uses hybrid BM25 + vector + reranking. Example: baker videos search 'product demo' --tags tutorial"
|
|
@@ -68814,7 +69729,7 @@ var tagsCommand6 = makeTagsCommand("videos", "video", "/api/videos/tags");
|
|
|
68814
69729
|
// src/commands/videos/upload.ts
|
|
68815
69730
|
import { readFile as readFile33, stat as stat12 } from "fs/promises";
|
|
68816
69731
|
import { basename as basename3, extname as extname4 } from "path";
|
|
68817
|
-
import { defineCommand as
|
|
69732
|
+
import { defineCommand as defineCommand235 } from "citty";
|
|
68818
69733
|
var MIME_MAP = {
|
|
68819
69734
|
".mp4": "video/mp4",
|
|
68820
69735
|
".mov": "video/quicktime",
|
|
@@ -68856,7 +69771,7 @@ function detectContentType(filePath) {
|
|
|
68856
69771
|
function isRemoteUrl3(value) {
|
|
68857
69772
|
return /^https?:\/\//i.test(value);
|
|
68858
69773
|
}
|
|
68859
|
-
var uploadCommand2 =
|
|
69774
|
+
var uploadCommand2 = defineCommand235({
|
|
68860
69775
|
meta: {
|
|
68861
69776
|
name: "upload",
|
|
68862
69777
|
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"
|
|
@@ -68940,7 +69855,7 @@ var uploadCommand2 = defineCommand234({
|
|
|
68940
69855
|
});
|
|
68941
69856
|
|
|
68942
69857
|
// src/commands/videos/index.ts
|
|
68943
|
-
var videosCommand =
|
|
69858
|
+
var videosCommand = defineCommand236({
|
|
68944
69859
|
meta: {
|
|
68945
69860
|
name: "videos",
|
|
68946
69861
|
description: `Find and manage videos in Baker. Subcommands: search, get, upload, ingest, describe, delete, tags.
|
|
@@ -68969,10 +69884,10 @@ Full guide: __tooling__/docs/tools/baker/videos.md`
|
|
|
68969
69884
|
});
|
|
68970
69885
|
|
|
68971
69886
|
// src/commands/winning-ads/index.ts
|
|
68972
|
-
import { defineCommand as
|
|
69887
|
+
import { defineCommand as defineCommand249 } from "citty";
|
|
68973
69888
|
|
|
68974
69889
|
// src/commands/winning-ads/advertisers.ts
|
|
68975
|
-
import { defineCommand as
|
|
69890
|
+
import { defineCommand as defineCommand237 } from "citty";
|
|
68976
69891
|
|
|
68977
69892
|
// src/commands/winning-ads/shared.ts
|
|
68978
69893
|
function splitList3(value) {
|
|
@@ -69025,7 +69940,7 @@ function advertiserNormalizer(record, full) {
|
|
|
69025
69940
|
last_synced_at: record.last_synced_at ?? null
|
|
69026
69941
|
};
|
|
69027
69942
|
}
|
|
69028
|
-
var advertisersCommand2 =
|
|
69943
|
+
var advertisersCommand2 = defineCommand237({
|
|
69029
69944
|
meta: {
|
|
69030
69945
|
name: "advertisers",
|
|
69031
69946
|
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'
|
|
@@ -69083,7 +69998,7 @@ var advertisersCommand2 = defineCommand236({
|
|
|
69083
69998
|
});
|
|
69084
69999
|
|
|
69085
70000
|
// src/commands/winning-ads/brief.ts
|
|
69086
|
-
import { defineCommand as
|
|
70001
|
+
import { defineCommand as defineCommand238 } from "citty";
|
|
69087
70002
|
registerSchema({
|
|
69088
70003
|
command: "winning-ads.brief",
|
|
69089
70004
|
description: "Generate a creative brief grounded in strategically-similar winning ads. Optionally describe the target creative with --dna (JSON) and steer with --notes.",
|
|
@@ -69129,7 +70044,7 @@ function parseDna(raw) {
|
|
|
69129
70044
|
}
|
|
69130
70045
|
return parsed;
|
|
69131
70046
|
}
|
|
69132
|
-
var briefCommand =
|
|
70047
|
+
var briefCommand = defineCommand238({
|
|
69133
70048
|
meta: {
|
|
69134
70049
|
name: "brief",
|
|
69135
70050
|
description: `Generate a creative brief from winning references. Example: baker winning-ads brief --dna '{"angle":"cost savings"}' --notes "B2B, LinkedIn video" --k 8`
|
|
@@ -69165,7 +70080,7 @@ var briefCommand = defineCommand237({
|
|
|
69165
70080
|
});
|
|
69166
70081
|
|
|
69167
70082
|
// src/commands/winning-ads/content.ts
|
|
69168
|
-
import { defineCommand as
|
|
70083
|
+
import { defineCommand as defineCommand239 } from "citty";
|
|
69169
70084
|
registerSchema({
|
|
69170
70085
|
command: "winning-ads.content",
|
|
69171
70086
|
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. Add --full for speech, pacing, and soundtrack detail. Video ads carry the transcript/on-screen text; static ads carry only the copy.",
|
|
@@ -69178,7 +70093,7 @@ registerSchema({
|
|
|
69178
70093
|
}
|
|
69179
70094
|
}
|
|
69180
70095
|
});
|
|
69181
|
-
var contentCommand =
|
|
70096
|
+
var contentCommand = defineCommand239({
|
|
69182
70097
|
meta: {
|
|
69183
70098
|
name: "content",
|
|
69184
70099
|
description: "Read the transcript + on-screen text + copy of one winning ad. Example: baker winning-ads content adg_123 --platform meta --full --output md"
|
|
@@ -69227,7 +70142,7 @@ var contentCommand = defineCommand238({
|
|
|
69227
70142
|
});
|
|
69228
70143
|
|
|
69229
70144
|
// src/commands/winning-ads/feed.ts
|
|
69230
|
-
import { defineCommand as
|
|
70145
|
+
import { defineCommand as defineCommand240 } from "citty";
|
|
69231
70146
|
function buildFeedParams(input) {
|
|
69232
70147
|
const params2 = {};
|
|
69233
70148
|
const advertiser = splitList3(input.advertiser);
|
|
@@ -69279,7 +70194,7 @@ registerSchema({
|
|
|
69279
70194
|
format: { type: "string", description: "Comma-separated formats to include (e.g. static,video)", required: false }
|
|
69280
70195
|
}
|
|
69281
70196
|
});
|
|
69282
|
-
var feedCommand2 =
|
|
70197
|
+
var feedCommand2 = defineCommand240({
|
|
69283
70198
|
meta: {
|
|
69284
70199
|
name: "feed",
|
|
69285
70200
|
description: "Winners across every brand you follow (browse, then trim per advertiser). Example: baker winning-ads feed --per-advertiser 5 --output md"
|
|
@@ -69364,7 +70279,7 @@ var feedCommand2 = defineCommand239({
|
|
|
69364
70279
|
});
|
|
69365
70280
|
|
|
69366
70281
|
// src/commands/winning-ads/follow.ts
|
|
69367
|
-
import { defineCommand as
|
|
70282
|
+
import { defineCommand as defineCommand241 } from "citty";
|
|
69368
70283
|
var PLATFORMS = adLibraryPlatformSchema.options;
|
|
69369
70284
|
registerSchema({
|
|
69370
70285
|
command: "winning-ads.follow",
|
|
@@ -69379,7 +70294,7 @@ registerSchema({
|
|
|
69379
70294
|
label: { type: "string", description: "Optional display label (defaults to the resolved name)", required: false }
|
|
69380
70295
|
}
|
|
69381
70296
|
});
|
|
69382
|
-
var followCommand =
|
|
70297
|
+
var followCommand = defineCommand241({
|
|
69383
70298
|
meta: {
|
|
69384
70299
|
name: "follow",
|
|
69385
70300
|
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'
|
|
@@ -69426,7 +70341,7 @@ var followCommand = defineCommand240({
|
|
|
69426
70341
|
});
|
|
69427
70342
|
|
|
69428
70343
|
// src/commands/winning-ads/follow-competitors.ts
|
|
69429
|
-
import { defineCommand as
|
|
70344
|
+
import { defineCommand as defineCommand242 } from "citty";
|
|
69430
70345
|
var PLATFORMS2 = adLibraryPlatformSchema.options;
|
|
69431
70346
|
var BATCH_TIMEOUT_MS = 3e5;
|
|
69432
70347
|
function buildFollowBatchBody(input) {
|
|
@@ -69459,7 +70374,7 @@ registerSchema({
|
|
|
69459
70374
|
}
|
|
69460
70375
|
}
|
|
69461
70376
|
});
|
|
69462
|
-
var followCompetitorsCommand =
|
|
70377
|
+
var followCompetitorsCommand = defineCommand242({
|
|
69463
70378
|
meta: {
|
|
69464
70379
|
name: "follow-competitors",
|
|
69465
70380
|
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"'
|
|
@@ -69534,7 +70449,7 @@ var followCompetitorsCommand = defineCommand241({
|
|
|
69534
70449
|
});
|
|
69535
70450
|
|
|
69536
70451
|
// src/commands/winning-ads/following.ts
|
|
69537
|
-
import { defineCommand as
|
|
70452
|
+
import { defineCommand as defineCommand243 } from "citty";
|
|
69538
70453
|
registerSchema({
|
|
69539
70454
|
command: "winning-ads.following",
|
|
69540
70455
|
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.",
|
|
@@ -69588,7 +70503,7 @@ function followingNormalizer(record, full) {
|
|
|
69588
70503
|
platforms: Array.isArray(record.platforms) ? record.platforms : []
|
|
69589
70504
|
};
|
|
69590
70505
|
}
|
|
69591
|
-
var followingCommand =
|
|
70506
|
+
var followingCommand = defineCommand243({
|
|
69592
70507
|
meta: {
|
|
69593
70508
|
name: "following",
|
|
69594
70509
|
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"
|
|
@@ -69624,7 +70539,7 @@ var followingCommand = defineCommand242({
|
|
|
69624
70539
|
});
|
|
69625
70540
|
|
|
69626
70541
|
// src/commands/winning-ads/patterns.ts
|
|
69627
|
-
import { defineCommand as
|
|
70542
|
+
import { defineCommand as defineCommand244 } from "citty";
|
|
69628
70543
|
registerSchema({
|
|
69629
70544
|
command: "winning-ads.patterns",
|
|
69630
70545
|
description: "Mine what separates two cohorts of ads: pass a comma-list of winning ad ids (--winners) and a comma-list of weaker ad ids (--duds). Returns the discriminating DNA fields.",
|
|
@@ -69663,7 +70578,7 @@ function discriminatorRow(record) {
|
|
|
69663
70578
|
top_values_duds: Array.isArray(record.top_values_b) ? record.top_values_b.join(", ") : ""
|
|
69664
70579
|
};
|
|
69665
70580
|
}
|
|
69666
|
-
var patternsCommand =
|
|
70581
|
+
var patternsCommand = defineCommand244({
|
|
69667
70582
|
meta: {
|
|
69668
70583
|
name: "patterns",
|
|
69669
70584
|
description: "Discover what separates winning ads from weak ones. Example: baker winning-ads patterns --winners a_1,a_2,a_3 --duds a_9,a_8 --output md"
|
|
@@ -69719,7 +70634,7 @@ var patternsCommand = defineCommand243({
|
|
|
69719
70634
|
});
|
|
69720
70635
|
|
|
69721
70636
|
// src/commands/winning-ads/search.ts
|
|
69722
|
-
import { defineCommand as
|
|
70637
|
+
import { defineCommand as defineCommand245 } from "citty";
|
|
69723
70638
|
registerSchema({
|
|
69724
70639
|
command: "winning-ads.search",
|
|
69725
70640
|
description: "Search the ad-dna corpus of scored winning ads. Returns a lean shortlist (advertiser, summary, scores, media_url) to pick a reference to reproduce.",
|
|
@@ -69857,7 +70772,7 @@ function reportSearchError(err) {
|
|
|
69857
70772
|
}
|
|
69858
70773
|
reportError2(err);
|
|
69859
70774
|
}
|
|
69860
|
-
var searchCommand5 =
|
|
70775
|
+
var searchCommand5 = defineCommand245({
|
|
69861
70776
|
meta: {
|
|
69862
70777
|
name: "search",
|
|
69863
70778
|
description: "Search winning reference ads. Example: baker winning-ads search 'B2B SaaS before/after AI automation' --platform meta --format static --winner-category winner --exclude-advertiser adv_123 --output md"
|
|
@@ -69977,7 +70892,7 @@ var searchCommand5 = defineCommand244({
|
|
|
69977
70892
|
});
|
|
69978
70893
|
|
|
69979
70894
|
// src/commands/winning-ads/seeds.ts
|
|
69980
|
-
import { defineCommand as
|
|
70895
|
+
import { defineCommand as defineCommand246 } from "citty";
|
|
69981
70896
|
function leanRow(r) {
|
|
69982
70897
|
return {
|
|
69983
70898
|
key: r.key,
|
|
@@ -70005,7 +70920,7 @@ function makeSeedCommand(opts) {
|
|
|
70005
70920
|
limit: { type: "number", description: "Max keys 1-100 (default 20)", required: false, default: 20 }
|
|
70006
70921
|
}
|
|
70007
70922
|
});
|
|
70008
|
-
return
|
|
70923
|
+
return defineCommand246({
|
|
70009
70924
|
meta: { name: opts.name, description: opts.description },
|
|
70010
70925
|
args: {
|
|
70011
70926
|
platform: { type: "string", description: "Single platform to segment on", required: false },
|
|
@@ -70054,7 +70969,7 @@ var formatsCommand = makeSeedCommand({
|
|
|
70054
70969
|
});
|
|
70055
70970
|
|
|
70056
70971
|
// src/commands/winning-ads/unfollow.ts
|
|
70057
|
-
import { defineCommand as
|
|
70972
|
+
import { defineCommand as defineCommand247 } from "citty";
|
|
70058
70973
|
registerSchema({
|
|
70059
70974
|
command: "winning-ads.unfollow",
|
|
70060
70975
|
description: "Stop following a brand \u2014 removes it from your ad-dna library by advertiser id.",
|
|
@@ -70062,7 +70977,7 @@ registerSchema({
|
|
|
70062
70977
|
advertiser: { type: "string", description: "Advertiser id to unfollow", required: true }
|
|
70063
70978
|
}
|
|
70064
70979
|
});
|
|
70065
|
-
var unfollowCommand =
|
|
70980
|
+
var unfollowCommand = defineCommand247({
|
|
70066
70981
|
meta: {
|
|
70067
70982
|
name: "unfollow",
|
|
70068
70983
|
description: "Stop following a brand by advertiser id. Example: baker winning-ads unfollow adv_123"
|
|
@@ -70083,7 +70998,7 @@ var unfollowCommand = defineCommand246({
|
|
|
70083
70998
|
});
|
|
70084
70999
|
|
|
70085
71000
|
// src/commands/winning-ads/winners.ts
|
|
70086
|
-
import { defineCommand as
|
|
71001
|
+
import { defineCommand as defineCommand248 } from "citty";
|
|
70087
71002
|
registerSchema({
|
|
70088
71003
|
command: "winning-ads.winners",
|
|
70089
71004
|
description: "Top winning ads for one advertiser id (from `advertisers` or `following`). Returns lean winner cards; add --full for DNA + longevity.",
|
|
@@ -70093,7 +71008,7 @@ registerSchema({
|
|
|
70093
71008
|
platform: { type: "string", description: "Filter to a single platform: meta|linkedin|tiktok", required: false }
|
|
70094
71009
|
}
|
|
70095
71010
|
});
|
|
70096
|
-
var winnersCommand =
|
|
71011
|
+
var winnersCommand = defineCommand248({
|
|
70097
71012
|
meta: {
|
|
70098
71013
|
name: "winners",
|
|
70099
71014
|
description: "Top winning ads for a specific advertiser id. Example: baker winning-ads winners adv_123 --top 15 --output md"
|
|
@@ -70143,7 +71058,7 @@ var winnersCommand = defineCommand247({
|
|
|
70143
71058
|
});
|
|
70144
71059
|
|
|
70145
71060
|
// src/commands/winning-ads/index.ts
|
|
70146
|
-
var winningAdsCommand =
|
|
71061
|
+
var winningAdsCommand = defineCommand249({
|
|
70147
71062
|
meta: {
|
|
70148
71063
|
name: "winning-ads",
|
|
70149
71064
|
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.
|
|
@@ -70335,16 +71250,16 @@ function commandFor(root, argv) {
|
|
|
70335
71250
|
return resolved ? command : null;
|
|
70336
71251
|
}
|
|
70337
71252
|
function suggestFlag(unknown, command) {
|
|
70338
|
-
const
|
|
70339
|
-
if (!
|
|
71253
|
+
const flag3 = closestFlag(unknown, command);
|
|
71254
|
+
if (!flag3) {
|
|
70340
71255
|
return null;
|
|
70341
71256
|
}
|
|
70342
71257
|
const args5 = argsOf(command) ?? {};
|
|
70343
|
-
const name =
|
|
71258
|
+
const name = flag3.slice(2);
|
|
70344
71259
|
const def = args5[name] ?? args5[camel(name)];
|
|
70345
71260
|
const described = def && typeof def === "object" ? def.description : void 0;
|
|
70346
71261
|
const description = typeof described === "string" ? described.split(/(?<!\b(?:e\.g|i\.e|vs))\.\s/)[0]?.trim().replace(/\.$/, "") : "";
|
|
70347
|
-
return description ? { flag:
|
|
71262
|
+
return description ? { flag: flag3, description } : { flag: flag3 };
|
|
70348
71263
|
}
|
|
70349
71264
|
function suggestionText(suggestion) {
|
|
70350
71265
|
if (!suggestion) {
|
|
@@ -70362,7 +71277,7 @@ function unknownFlagEnvelope(unknown, commandPath, suggestion, hasSchema) {
|
|
|
70362
71277
|
message: `${list} ${unknown.length > 1 ? "are not options" : "is not an option"} of \`baker ${commandPath}\`.`,
|
|
70363
71278
|
fix: {
|
|
70364
71279
|
action: hasSchema ? "check_schema" : "check_help",
|
|
70365
|
-
explanation: (unknown.some((
|
|
71280
|
+
explanation: (unknown.some((flag3) => flag3 === "--output" || flag3.startsWith("--output=")) ? OUTPUT_EXPLANATION : "") + suggestionText(suggestion) + `Run \`${hasSchema ? `baker schema ${commandPath}` : `baker ${commandPath} --help`}\` to see every option this command takes. Do not re-run the same call: until this version an unrecognised option was accepted and ignored, so a command that looked like it had filtered or narrowed the result was in fact answering the unfiltered question.`
|
|
70366
71281
|
},
|
|
70367
71282
|
retryable: false
|
|
70368
71283
|
}
|
|
@@ -70420,13 +71335,13 @@ function declaredArg(command, name) {
|
|
|
70420
71335
|
}
|
|
70421
71336
|
function envelopeRequests(argv) {
|
|
70422
71337
|
const end = argv.indexOf("--");
|
|
70423
|
-
const
|
|
71338
|
+
const limit2 = end === -1 ? argv.length : end;
|
|
70424
71339
|
const found = [];
|
|
70425
|
-
for (let index = 0; index <
|
|
71340
|
+
for (let index = 0; index < limit2; index++) {
|
|
70426
71341
|
const token = argv[index];
|
|
70427
71342
|
if (token === `${FLAG}=${ENVELOPE}`) {
|
|
70428
71343
|
found.push({ index, length: 1 });
|
|
70429
|
-
} else if (token === FLAG && argv[index + 1] === ENVELOPE && index + 1 <
|
|
71344
|
+
} else if (token === FLAG && argv[index + 1] === ENVELOPE && index + 1 < limit2) {
|
|
70430
71345
|
found.push({ index, length: 2 });
|
|
70431
71346
|
index++;
|
|
70432
71347
|
}
|
|
@@ -70470,7 +71385,7 @@ function getCliVersion() {
|
|
|
70470
71385
|
}
|
|
70471
71386
|
|
|
70472
71387
|
// src/cli.ts
|
|
70473
|
-
var main =
|
|
71388
|
+
var main = defineCommand250({
|
|
70474
71389
|
meta: {
|
|
70475
71390
|
name: "baker",
|
|
70476
71391
|
version: getCliVersion(),
|