@koda-sl/baker-cli 0.244.0 → 0.245.0-dev.72ca35142
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 +54 -6
- package/dist/{chunk-EKLAHWSF.js → chunk-CMPAHYLB.js} +4 -4
- package/dist/chunk-CMPAHYLB.js.map +1 -0
- package/dist/cli.js +1632 -561
- package/dist/cli.js.map +1 -1
- package/dist/engine/index.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-EKLAHWSF.js.map +0 -1
package/dist/cli.js
CHANGED
|
@@ -58,7 +58,7 @@ import {
|
|
|
58
58
|
ulid,
|
|
59
59
|
validateCanvasDeep,
|
|
60
60
|
ytDlpBlockSignal
|
|
61
|
-
} from "./chunk-
|
|
61
|
+
} from "./chunk-CMPAHYLB.js";
|
|
62
62
|
import {
|
|
63
63
|
csvOrJson,
|
|
64
64
|
daysAgoIso,
|
|
@@ -108,7 +108,7 @@ import {
|
|
|
108
108
|
} from "./chunk-DZUVUGEP.js";
|
|
109
109
|
|
|
110
110
|
// src/cli.ts
|
|
111
|
-
import { defineCommand as
|
|
111
|
+
import { defineCommand as defineCommand229, runMain } from "citty";
|
|
112
112
|
|
|
113
113
|
// src/cache-flag.ts
|
|
114
114
|
var NO_CACHE_ARG = {
|
|
@@ -2122,18 +2122,6 @@ var BIDDING_STRATEGY_TYPES = [
|
|
|
2122
2122
|
"MANUAL_CPV",
|
|
2123
2123
|
"PERCENT_CPC"
|
|
2124
2124
|
];
|
|
2125
|
-
var MERGED_TARGET_STRATEGY_CHANNELS = /* @__PURE__ */ new Set(["SEARCH"]);
|
|
2126
|
-
var MERGED_TARGET_CPA_MESSAGE = "Google doesn't accept Target CPA as a bidding strategy of its own on a Search campaign \u2014 the target sits inside Maximize conversions. Choose Maximize conversions and give it the same target CPA; it bids exactly the same.";
|
|
2127
|
-
var MERGED_TARGET_ROAS_MESSAGE = "Google doesn't accept Target ROAS as a bidding strategy of its own on a Search campaign \u2014 the target sits inside Maximize conversion value. Choose Maximize conversion value and give it the same target ROAS; it bids exactly the same.";
|
|
2128
|
-
function mergedTargetStrategyRefusal(channelType, biddingType) {
|
|
2129
|
-
if (channelType === void 0 || !MERGED_TARGET_STRATEGY_CHANNELS.has(channelType)) {
|
|
2130
|
-
return void 0;
|
|
2131
|
-
}
|
|
2132
|
-
if (biddingType === "TARGET_CPA") {
|
|
2133
|
-
return MERGED_TARGET_CPA_MESSAGE;
|
|
2134
|
-
}
|
|
2135
|
-
return biddingType === "TARGET_ROAS" ? MERGED_TARGET_ROAS_MESSAGE : void 0;
|
|
2136
|
-
}
|
|
2137
2125
|
var POSITIVE_GEO_TARGET_TYPES = ["PRESENCE_OR_INTEREST", "PRESENCE"];
|
|
2138
2126
|
var NEGATIVE_GEO_TARGET_TYPES = ["PRESENCE_OR_INTEREST", "PRESENCE"];
|
|
2139
2127
|
var BUDGET_DELIVERY_METHODS = ["STANDARD", "ACCELERATED"];
|
|
@@ -2858,10 +2846,6 @@ var campaignCreateSchema2 = z4.strictObject({
|
|
|
2858
2846
|
if (p.channelType === "PERFORMANCE_MAX" && p.bidding && p.bidding.type === "MANUAL_CPC") {
|
|
2859
2847
|
ctx.addIssue({ code: "custom", path: ["bidding"], message: "Performance Max does not support Manual CPC" });
|
|
2860
2848
|
}
|
|
2861
|
-
const mergedTarget = mergedTargetStrategyRefusal(p.channelType, p.bidding?.type);
|
|
2862
|
-
if (mergedTarget) {
|
|
2863
|
-
ctx.addIssue({ code: "custom", path: ["bidding"], message: mergedTarget });
|
|
2864
|
-
}
|
|
2865
2849
|
if (p.startDate && p.endDate && p.endDate <= p.startDate) {
|
|
2866
2850
|
ctx.addIssue({ code: "custom", path: ["endDate"], message: "endDate must be after startDate" });
|
|
2867
2851
|
}
|
|
@@ -3936,10 +3920,35 @@ var capabilitySurfaceReportSchema = z9.object({
|
|
|
3936
3920
|
/** Where to look next: `baker ads google`, `baker ga4`, … */
|
|
3937
3921
|
commands: z9.string()
|
|
3938
3922
|
});
|
|
3923
|
+
var capabilityToolSchema = z9.object({
|
|
3924
|
+
/** `microsoft_clarity`, `hubspot`, or the name the user gave a custom server. */
|
|
3925
|
+
key: z9.string(),
|
|
3926
|
+
label: z9.string(),
|
|
3927
|
+
/** `mcp` — tools in the agent's own list. `platform` — signed in, read through the CLI. */
|
|
3928
|
+
kind: z9.enum(["mcp", "platform"]),
|
|
3929
|
+
/** How to actually reach it, in one phrase the agent can act on. */
|
|
3930
|
+
reach: z9.string(),
|
|
3931
|
+
/**
|
|
3932
|
+
* Whether it can be used right now.
|
|
3933
|
+
*
|
|
3934
|
+
* A connection that exists and is expired is worse than one that is absent:
|
|
3935
|
+
* it reads as available and fails at the moment it is needed, which on an
|
|
3936
|
+
* unattended run is a report with a hole in it and no explanation.
|
|
3937
|
+
*/
|
|
3938
|
+
ready: z9.boolean(),
|
|
3939
|
+
/** Why it is not ready, or what it is for. Absent when neither needs saying. */
|
|
3940
|
+
note: z9.string().optional()
|
|
3941
|
+
});
|
|
3939
3942
|
var capabilitiesResponseSchema = z9.object({
|
|
3940
3943
|
ok: z9.literal(true),
|
|
3941
3944
|
data: z9.object({
|
|
3942
|
-
surfaces: z9.array(capabilitySurfaceReportSchema)
|
|
3945
|
+
surfaces: z9.array(capabilitySurfaceReportSchema),
|
|
3946
|
+
/**
|
|
3947
|
+
* Everything else this company can reach. Shared connections only —
|
|
3948
|
+
* a teammate's private per-user connection belongs to whoever started the
|
|
3949
|
+
* session and is visible in the agent's own tool list, not here.
|
|
3950
|
+
*/
|
|
3951
|
+
tools: z9.array(capabilityToolSchema)
|
|
3943
3952
|
})
|
|
3944
3953
|
});
|
|
3945
3954
|
|
|
@@ -3986,6 +3995,8 @@ var chatChangeTypeSchema = z10.enum([
|
|
|
3986
3995
|
// what the edge captures and one held until Publish would not measure the
|
|
3987
3996
|
// campaign it was written for.
|
|
3988
3997
|
"analytics-mapping",
|
|
3998
|
+
// An A/B test between a landing and its alternative. Staged, not immediate.
|
|
3999
|
+
"experiment",
|
|
3989
4000
|
"briefs"
|
|
3990
4001
|
]);
|
|
3991
4002
|
var chatChangeActionSchema = z10.enum(["created", "updated", "deleted"]);
|
|
@@ -9779,11 +9790,11 @@ function rawTextEntries(value) {
|
|
|
9779
9790
|
const values = Array.isArray(value) ? value : typeof value === "string" ? [value] : [];
|
|
9780
9791
|
return values.filter((v) => typeof v === "string").flatMap((v) => v.split(",")).map((v) => v.trim()).filter(Boolean);
|
|
9781
9792
|
}
|
|
9782
|
-
function rawFileEntries(
|
|
9783
|
-
if (typeof
|
|
9793
|
+
function rawFileEntries(path43) {
|
|
9794
|
+
if (typeof path43 !== "string" || path43.length === 0) {
|
|
9784
9795
|
return [];
|
|
9785
9796
|
}
|
|
9786
|
-
return readFileSync2(
|
|
9797
|
+
return readFileSync2(path43, "utf8").split(/\r?\n/).map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#"));
|
|
9787
9798
|
}
|
|
9788
9799
|
function keywordEntries(args) {
|
|
9789
9800
|
const defaultMatch = typeof args["match-type"] === "string" ? args["match-type"].toUpperCase() : void 0;
|
|
@@ -9806,19 +9817,19 @@ function keywordEntries(args) {
|
|
|
9806
9817
|
}
|
|
9807
9818
|
return entries;
|
|
9808
9819
|
}
|
|
9809
|
-
function loadJsonFileArg(
|
|
9810
|
-
if (typeof
|
|
9820
|
+
function loadJsonFileArg(path43) {
|
|
9821
|
+
if (typeof path43 !== "string" || path43.length === 0) {
|
|
9811
9822
|
return {};
|
|
9812
9823
|
}
|
|
9813
9824
|
try {
|
|
9814
|
-
const parsed = JSON.parse(readFileSync2(
|
|
9825
|
+
const parsed = JSON.parse(readFileSync2(path43, "utf8"));
|
|
9815
9826
|
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
9816
|
-
failWriteValidation(`${
|
|
9827
|
+
failWriteValidation(`${path43} must contain a JSON object`);
|
|
9817
9828
|
}
|
|
9818
9829
|
return parsed;
|
|
9819
9830
|
} catch (err) {
|
|
9820
9831
|
if (err instanceof SyntaxError) {
|
|
9821
|
-
failWriteValidation(`${
|
|
9832
|
+
failWriteValidation(`${path43} is not valid JSON: ${err.message}`);
|
|
9822
9833
|
}
|
|
9823
9834
|
throw err;
|
|
9824
9835
|
}
|
|
@@ -9948,10 +9959,10 @@ async function stageUpdate(kind, customerId, target, payload, hints) {
|
|
|
9948
9959
|
async function stageTarget(kind, customerId, target, hints) {
|
|
9949
9960
|
await stageGoogleOp({ kind, customerId, target }, hints);
|
|
9950
9961
|
}
|
|
9951
|
-
async function draftAction(
|
|
9962
|
+
async function draftAction(path43, body, chat) {
|
|
9952
9963
|
try {
|
|
9953
9964
|
const chatId = resolveChatId(chat);
|
|
9954
|
-
const response = await apiPost(
|
|
9965
|
+
const response = await apiPost(path43, { chatId, ...body });
|
|
9955
9966
|
writeJsonEnvelope(response);
|
|
9956
9967
|
} catch (err) {
|
|
9957
9968
|
handleGoogleError(err);
|
|
@@ -13644,16 +13655,10 @@ var campaignsCommand = defineCommand30({
|
|
|
13644
13655
|
"budget-ref": { type: "string", description: "Budget ref (g_temp_*, id, or resource name)" },
|
|
13645
13656
|
"bidding-strategy": {
|
|
13646
13657
|
type: "string",
|
|
13647
|
-
description: "MANUAL_CPC | MAXIMIZE_CONVERSIONS |
|
|
13648
|
-
},
|
|
13649
|
-
"target-cpa": {
|
|
13650
|
-
type: "string",
|
|
13651
|
-
description: "Target CPA (major units). On a Search campaign pair it with --bidding-strategy MAXIMIZE_CONVERSIONS \u2014 Google keeps the target inside that strategy and rejects a standalone TARGET_CPA."
|
|
13652
|
-
},
|
|
13653
|
-
"target-roas": {
|
|
13654
|
-
type: "string",
|
|
13655
|
-
description: "Target ROAS (e.g. 4.0). On a Search campaign pair it with --bidding-strategy MAXIMIZE_CONVERSION_VALUE \u2014 Google keeps the target inside that strategy and rejects a standalone TARGET_ROAS."
|
|
13658
|
+
description: "MANUAL_CPC | MAXIMIZE_CONVERSIONS | TARGET_CPA | TARGET_ROAS | \u2026"
|
|
13656
13659
|
},
|
|
13660
|
+
"target-cpa": { type: "string", description: "Target CPA (major units) for TARGET_CPA" },
|
|
13661
|
+
"target-roas": { type: "string", description: "Target ROAS (e.g. 4.0) for TARGET_ROAS" },
|
|
13657
13662
|
...maxCpcArg,
|
|
13658
13663
|
...finalUrlSuffixArg,
|
|
13659
13664
|
...urlOptionsArgs,
|
|
@@ -13700,18 +13705,9 @@ var campaignsCommand = defineCommand30({
|
|
|
13700
13705
|
...fileArg,
|
|
13701
13706
|
name: { type: "string" },
|
|
13702
13707
|
"budget-ref": { type: "string" },
|
|
13703
|
-
"bidding-strategy": {
|
|
13704
|
-
|
|
13705
|
-
|
|
13706
|
-
},
|
|
13707
|
-
"target-cpa": {
|
|
13708
|
-
type: "string",
|
|
13709
|
-
description: "Target CPA (major units). On a Search campaign pair it with --bidding-strategy MAXIMIZE_CONVERSIONS \u2014 Google keeps the target inside that strategy and rejects a standalone TARGET_CPA."
|
|
13710
|
-
},
|
|
13711
|
-
"target-roas": {
|
|
13712
|
-
type: "string",
|
|
13713
|
-
description: "Target ROAS (e.g. 4.0). On a Search campaign pair it with --bidding-strategy MAXIMIZE_CONVERSION_VALUE \u2014 Google keeps the target inside that strategy and rejects a standalone TARGET_ROAS."
|
|
13714
|
-
},
|
|
13708
|
+
"bidding-strategy": { type: "string" },
|
|
13709
|
+
"target-cpa": { type: "string" },
|
|
13710
|
+
"target-roas": { type: "string" },
|
|
13715
13711
|
...maxCpcArg,
|
|
13716
13712
|
...finalUrlSuffixArg,
|
|
13717
13713
|
...urlOptionsArgs,
|
|
@@ -14456,8 +14452,7 @@ function requireAudienceLevel(value) {
|
|
|
14456
14452
|
return level;
|
|
14457
14453
|
}
|
|
14458
14454
|
var AUDIENCE_ATTACH_HINTS = [
|
|
14459
|
-
'Read back what the parent ended up with before you report: `baker ads google query "SELECT campaign.id, campaign.targeting_setting.target_restrictions FROM campaign"` (or the ad_group equivalent). A missing AUDIENCE row means TARGETING \u2014 Google does not serialize an empty setting, so absence is a value, not a gap.'
|
|
14460
|
-
"Staging checks the list is big enough to serve on this campaign's network (1,000 users on search, 100 on display) and refuses a targeting attach that is not. It does NOT check delivery afterwards, and Google reports no error when an audience stops a campaign serving \u2014 after publishing, read the campaign's impressions for the hour that follows and say what you saw."
|
|
14455
|
+
'Read back what the parent ended up with before you report: `baker ads google query "SELECT campaign.id, campaign.targeting_setting.target_restrictions FROM campaign"` (or the ad_group equivalent). A missing AUDIENCE row means TARGETING \u2014 Google does not serialize an empty setting, so absence is a value, not a gap.'
|
|
14461
14456
|
];
|
|
14462
14457
|
var audiencesAttachCommand = defineCommand30({
|
|
14463
14458
|
meta: {
|
|
@@ -14916,19 +14911,19 @@ function failWriteValidation2(message) {
|
|
|
14916
14911
|
writeJsonEnvelope({ ok: false, error: { code: "VALIDATION_ERROR", message } });
|
|
14917
14912
|
process.exit(1);
|
|
14918
14913
|
}
|
|
14919
|
-
function loadJsonFileArg2(
|
|
14920
|
-
if (typeof
|
|
14914
|
+
function loadJsonFileArg2(path43) {
|
|
14915
|
+
if (typeof path43 !== "string" || path43.length === 0) {
|
|
14921
14916
|
return {};
|
|
14922
14917
|
}
|
|
14923
14918
|
try {
|
|
14924
|
-
const parsed = JSON.parse(readFileSync4(
|
|
14919
|
+
const parsed = JSON.parse(readFileSync4(path43, "utf8"));
|
|
14925
14920
|
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
14926
|
-
failWriteValidation2(`${
|
|
14921
|
+
failWriteValidation2(`${path43} must contain a JSON object`);
|
|
14927
14922
|
}
|
|
14928
14923
|
return parsed;
|
|
14929
14924
|
} catch (err) {
|
|
14930
14925
|
if (err instanceof SyntaxError) {
|
|
14931
|
-
failWriteValidation2(`${
|
|
14926
|
+
failWriteValidation2(`${path43} is not valid JSON: ${err.message}`);
|
|
14932
14927
|
}
|
|
14933
14928
|
throw err;
|
|
14934
14929
|
}
|
|
@@ -15013,15 +15008,15 @@ function parseLocaleFlag(value) {
|
|
|
15013
15008
|
}
|
|
15014
15009
|
return { language: match[1], country: match[2].toUpperCase() };
|
|
15015
15010
|
}
|
|
15016
|
-
function loadTargetingFileArg(
|
|
15017
|
-
if (typeof
|
|
15011
|
+
function loadTargetingFileArg(path43) {
|
|
15012
|
+
if (typeof path43 !== "string" || path43.length === 0) {
|
|
15018
15013
|
return void 0;
|
|
15019
15014
|
}
|
|
15020
|
-
const parsed = loadJsonFileArg2(
|
|
15015
|
+
const parsed = loadJsonFileArg2(path43);
|
|
15021
15016
|
const criteria = parsed.targetingCriteria ?? parsed;
|
|
15022
15017
|
if (!criteria.include) {
|
|
15023
15018
|
failWriteValidation2(
|
|
15024
|
-
`${
|
|
15019
|
+
`${path43} must contain targeting criteria with an "include" block (see baker schema ads.linkedin.campaigns.create)`
|
|
15025
15020
|
);
|
|
15026
15021
|
}
|
|
15027
15022
|
return criteria;
|
|
@@ -15056,14 +15051,14 @@ function parseCsvLine(line) {
|
|
|
15056
15051
|
cells.push(current);
|
|
15057
15052
|
return cells.map((cell2) => cell2.trim());
|
|
15058
15053
|
}
|
|
15059
|
-
function parseListFileArg(
|
|
15060
|
-
if (typeof
|
|
15054
|
+
function parseListFileArg(path43, maxRows) {
|
|
15055
|
+
if (typeof path43 !== "string" || path43.length === 0) {
|
|
15061
15056
|
return void 0;
|
|
15062
15057
|
}
|
|
15063
|
-
const raw = readFileSync4(
|
|
15058
|
+
const raw = readFileSync4(path43, "utf8");
|
|
15064
15059
|
const lines = raw.split(/\r?\n/).filter((line) => line.trim().length > 0);
|
|
15065
15060
|
if (lines.length < 2) {
|
|
15066
|
-
failWriteValidation2(`${
|
|
15061
|
+
failWriteValidation2(`${path43} needs a header row and at least one data row`);
|
|
15067
15062
|
}
|
|
15068
15063
|
const columns = parseCsvLine(lines[0]).map((column) => column.trim());
|
|
15069
15064
|
const rows = [];
|
|
@@ -15082,7 +15077,7 @@ function parseListFileArg(path40, maxRows) {
|
|
|
15082
15077
|
}
|
|
15083
15078
|
}
|
|
15084
15079
|
if (rows.length > maxRows) {
|
|
15085
|
-
failWriteValidation2(`${
|
|
15080
|
+
failWriteValidation2(`${path43} has ${rows.length} rows \u2014 the inline limit is ${maxRows}. Split the list.`);
|
|
15086
15081
|
}
|
|
15087
15082
|
return { columns, rows };
|
|
15088
15083
|
}
|
|
@@ -15178,11 +15173,11 @@ function readPositionals(args) {
|
|
|
15178
15173
|
function splitIdList(raw) {
|
|
15179
15174
|
return raw.split(",").map((id) => id.trim()).filter(Boolean);
|
|
15180
15175
|
}
|
|
15181
|
-
function idsFileEntries(
|
|
15182
|
-
if (typeof
|
|
15176
|
+
function idsFileEntries(path43) {
|
|
15177
|
+
if (typeof path43 !== "string" || path43.length === 0) {
|
|
15183
15178
|
return [];
|
|
15184
15179
|
}
|
|
15185
|
-
return readFileSync4(
|
|
15180
|
+
return readFileSync4(path43, "utf8").split(/\r?\n/).map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#")).flatMap(splitIdList);
|
|
15186
15181
|
}
|
|
15187
15182
|
function requireTargets(args, entity) {
|
|
15188
15183
|
const positionals = readPositionals(args);
|
|
@@ -17803,9 +17798,9 @@ function compactRow(row) {
|
|
|
17803
17798
|
...destination.postUrn ? { postUrn: destination.postUrn } : {}
|
|
17804
17799
|
};
|
|
17805
17800
|
}
|
|
17806
|
-
function readPath(row,
|
|
17801
|
+
function readPath(row, path43) {
|
|
17807
17802
|
let current = row;
|
|
17808
|
-
for (const segment of
|
|
17803
|
+
for (const segment of path43.split(".")) {
|
|
17809
17804
|
const record = asRecord2(current);
|
|
17810
17805
|
if (!record) return void 0;
|
|
17811
17806
|
current = record[segment];
|
|
@@ -17815,10 +17810,10 @@ function readPath(row, path40) {
|
|
|
17815
17810
|
function projectFields(rows, paths) {
|
|
17816
17811
|
return rows.map((row) => {
|
|
17817
17812
|
const projected = {};
|
|
17818
|
-
for (const
|
|
17819
|
-
const value = readPath(row,
|
|
17813
|
+
for (const path43 of paths) {
|
|
17814
|
+
const value = readPath(row, path43);
|
|
17820
17815
|
if (value !== void 0) {
|
|
17821
|
-
projected[
|
|
17816
|
+
projected[path43] = value;
|
|
17822
17817
|
}
|
|
17823
17818
|
}
|
|
17824
17819
|
return projected;
|
|
@@ -19118,11 +19113,11 @@ var updateStatusSchema = z25.enum(UPDATE_STATUSES);
|
|
|
19118
19113
|
function currencyMinimums2(currencyCode) {
|
|
19119
19114
|
return CURRENCY_MINIMUMS2[currencyCode] ?? DEFAULT_CURRENCY_MINIMUM2;
|
|
19120
19115
|
}
|
|
19121
|
-
function validateDailyBudgetFloor(money, ctx,
|
|
19116
|
+
function validateDailyBudgetFloor(money, ctx, path43) {
|
|
19122
19117
|
if (money?.currencyCode) {
|
|
19123
19118
|
const min = currencyMinimums2(money.currencyCode).dailyBudgetMin;
|
|
19124
19119
|
if (Number(money.amount) < min) {
|
|
19125
|
-
ctx.addIssue({ code: "custom", path:
|
|
19120
|
+
ctx.addIssue({ code: "custom", path: path43, message: `below the ${min} ${money.currencyCode} daily minimum` });
|
|
19126
19121
|
}
|
|
19127
19122
|
}
|
|
19128
19123
|
}
|
|
@@ -19791,19 +19786,19 @@ function failWriteValidation3(message) {
|
|
|
19791
19786
|
writeJsonEnvelope({ ok: false, error: { code: "VALIDATION_ERROR", message } });
|
|
19792
19787
|
process.exit(1);
|
|
19793
19788
|
}
|
|
19794
|
-
function loadJsonFileArg3(
|
|
19795
|
-
if (typeof
|
|
19789
|
+
function loadJsonFileArg3(path43) {
|
|
19790
|
+
if (typeof path43 !== "string" || path43.length === 0) {
|
|
19796
19791
|
return {};
|
|
19797
19792
|
}
|
|
19798
19793
|
try {
|
|
19799
|
-
const parsed = JSON.parse(readFileSync8(
|
|
19794
|
+
const parsed = JSON.parse(readFileSync8(path43, "utf8"));
|
|
19800
19795
|
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
19801
|
-
failWriteValidation3(`${
|
|
19796
|
+
failWriteValidation3(`${path43} must contain a JSON object`);
|
|
19802
19797
|
}
|
|
19803
19798
|
return parsed;
|
|
19804
19799
|
} catch (err) {
|
|
19805
19800
|
if (err instanceof SyntaxError) {
|
|
19806
|
-
failWriteValidation3(`${
|
|
19801
|
+
failWriteValidation3(`${path43} is not valid JSON: ${err.message}`);
|
|
19807
19802
|
}
|
|
19808
19803
|
throw err;
|
|
19809
19804
|
}
|
|
@@ -24401,11 +24396,11 @@ function unwrap(response) {
|
|
|
24401
24396
|
}
|
|
24402
24397
|
return response.data;
|
|
24403
24398
|
}
|
|
24404
|
-
async function readAvatars(
|
|
24405
|
-
return unwrap(await apiGet(
|
|
24399
|
+
async function readAvatars(path43, params) {
|
|
24400
|
+
return unwrap(await apiGet(path43, params));
|
|
24406
24401
|
}
|
|
24407
|
-
async function writeAvatars(
|
|
24408
|
-
return unwrap(await apiPost(
|
|
24402
|
+
async function writeAvatars(path43, body) {
|
|
24403
|
+
return unwrap(await apiPost(path43, body));
|
|
24409
24404
|
}
|
|
24410
24405
|
|
|
24411
24406
|
// src/commands/avatars/create.ts
|
|
@@ -25239,12 +25234,12 @@ function missingFontFiles(urls, available) {
|
|
|
25239
25234
|
function planFontAdoption(sources, families) {
|
|
25240
25235
|
const wanted = new Map(families.map((family) => [normalizeFamily(family), family]));
|
|
25241
25236
|
const byFamily = /* @__PURE__ */ new Map();
|
|
25242
|
-
for (const { path:
|
|
25243
|
-
const dir = posix.dirname(
|
|
25237
|
+
for (const { path: path43, source } of sources) {
|
|
25238
|
+
const dir = posix.dirname(path43);
|
|
25244
25239
|
for (const face of declaredFontFaces(source)) {
|
|
25245
25240
|
if (!wanted.has(face.family)) continue;
|
|
25246
25241
|
const perFile = byFamily.get(face.family) ?? /* @__PURE__ */ new Map();
|
|
25247
|
-
perFile.set(
|
|
25242
|
+
perFile.set(path43, [...perFile.get(path43) ?? [], rebaseFontFaceSrc(face.block, dir)]);
|
|
25248
25243
|
byFamily.set(face.family, perFile);
|
|
25249
25244
|
}
|
|
25250
25245
|
}
|
|
@@ -26268,10 +26263,10 @@ import { defineCommand as defineCommand100 } from "citty";
|
|
|
26268
26263
|
import { existsSync as existsSync3, realpathSync } from "fs";
|
|
26269
26264
|
import { writeFile as writeFile2 } from "fs/promises";
|
|
26270
26265
|
import path5 from "path";
|
|
26271
|
-
function findWorkspaceRoot(startDir,
|
|
26266
|
+
function findWorkspaceRoot(startDir, exists2 = existsSync3, maxDepth = 12) {
|
|
26272
26267
|
let dir = path5.resolve(startDir);
|
|
26273
26268
|
for (let i = 0; i < maxDepth; i++) {
|
|
26274
|
-
if (
|
|
26269
|
+
if (exists2(path5.join(dir, "package.json"))) return dir;
|
|
26275
26270
|
const parent = path5.dirname(dir);
|
|
26276
26271
|
if (parent === dir) break;
|
|
26277
26272
|
dir = parent;
|
|
@@ -30218,10 +30213,10 @@ function runDirsToPrune(entries, keep, currentRunId) {
|
|
|
30218
30213
|
return runs.slice(0, Math.max(0, runs.length - keep));
|
|
30219
30214
|
}
|
|
30220
30215
|
async function pruneOldRuns(outputsDir, keep, currentRunId, log) {
|
|
30221
|
-
const { readdir:
|
|
30216
|
+
const { readdir: readdir11 } = await import("fs/promises");
|
|
30222
30217
|
let entries;
|
|
30223
30218
|
try {
|
|
30224
|
-
entries = await
|
|
30219
|
+
entries = await readdir11(outputsDir);
|
|
30225
30220
|
} catch {
|
|
30226
30221
|
return;
|
|
30227
30222
|
}
|
|
@@ -32013,12 +32008,12 @@ async function detectSceneCutsPySceneDetect(filePath, opts = {}) {
|
|
|
32013
32008
|
// src/commands/canvas/composition-path.ts
|
|
32014
32009
|
import { existsSync as existsSync4 } from "fs";
|
|
32015
32010
|
import path21 from "path";
|
|
32016
|
-
function resolveShippedCanvasDir(name, startDir,
|
|
32011
|
+
function resolveShippedCanvasDir(name, startDir, exists2 = existsSync4, maxDepth = 8) {
|
|
32017
32012
|
const rel = path21.join("canvas", name);
|
|
32018
32013
|
let dir = startDir;
|
|
32019
32014
|
for (let i = 0; i < maxDepth; i++) {
|
|
32020
32015
|
const candidate = path21.join(dir, rel);
|
|
32021
|
-
if (
|
|
32016
|
+
if (exists2(path21.join(candidate, "meta.json"))) return candidate;
|
|
32022
32017
|
const parent = path21.dirname(dir);
|
|
32023
32018
|
if (parent === dir) break;
|
|
32024
32019
|
dir = parent;
|
|
@@ -32844,7 +32839,7 @@ registerSchema({
|
|
|
32844
32839
|
}
|
|
32845
32840
|
}
|
|
32846
32841
|
});
|
|
32847
|
-
function capabilityHints(surfaces) {
|
|
32842
|
+
function capabilityHints(surfaces, tools = []) {
|
|
32848
32843
|
const hints = [];
|
|
32849
32844
|
const immediate = surfaces.filter((surface) => surface.writeMode === "applies-at-publish");
|
|
32850
32845
|
if (immediate.length > 0) {
|
|
@@ -32867,6 +32862,18 @@ function capabilityHints(surfaces) {
|
|
|
32867
32862
|
"Naming one surface (`baker capabilities google-ads`) adds `ops` \u2014 the exact fields and accepted values of every change there. Read that instead of guessing a field name."
|
|
32868
32863
|
);
|
|
32869
32864
|
}
|
|
32865
|
+
const usable = tools.filter((tool) => tool.ready);
|
|
32866
|
+
if (usable.length > 0) {
|
|
32867
|
+
hints.push(
|
|
32868
|
+
`ALSO AVAILABLE, and not on any surface above: ${usable.map((tool) => tool.label).join(", ")}. These answer WHY a page behaves as it does, which no surface here can. See \`tools[].reach\` for how to call each one.`
|
|
32869
|
+
);
|
|
32870
|
+
}
|
|
32871
|
+
const broken = tools.filter((tool) => !tool.ready);
|
|
32872
|
+
if (broken.length > 0) {
|
|
32873
|
+
hints.push(
|
|
32874
|
+
`CONNECTED BUT UNUSABLE: ${broken.map((tool) => `${tool.label} \u2014 ${tool.note ?? "not reachable"}`).join(" ")} Tell the user rather than planning around it silently.`
|
|
32875
|
+
);
|
|
32876
|
+
}
|
|
32870
32877
|
return hints;
|
|
32871
32878
|
}
|
|
32872
32879
|
var runCapabilities = defineCommand108({
|
|
@@ -32876,6 +32883,8 @@ var runCapabilities = defineCommand108({
|
|
|
32876
32883
|
|
|
32877
32884
|
Reports, per surface: what is connected and with what access, what a write there does when it lands, the entities that can be changed, and \u2014 the part nothing else tells you \u2014 what cannot be changed and why.
|
|
32878
32885
|
|
|
32886
|
+
Also returns \`tools\`: everything else this company can reach that is not one of those surfaces \u2014 Microsoft Clarity and any other MCP toolkit, custom servers, HubSpot. This is the one call that answers "what do I actually have".
|
|
32887
|
+
|
|
32879
32888
|
Examples:
|
|
32880
32889
|
baker capabilities
|
|
32881
32890
|
baker capabilities google-ads
|
|
@@ -32911,7 +32920,7 @@ Full guide: __tooling__/docs/tools/baker/capabilities.md`
|
|
|
32911
32920
|
body.full = true;
|
|
32912
32921
|
}
|
|
32913
32922
|
const response = await apiPost("/api/capabilities", body);
|
|
32914
|
-
const hints = capabilityHints(response.data.surfaces);
|
|
32923
|
+
const hints = capabilityHints(response.data.surfaces, response.data.tools);
|
|
32915
32924
|
writeJson({
|
|
32916
32925
|
...response,
|
|
32917
32926
|
meta: { count: response.data.surfaces.length },
|
|
@@ -33381,12 +33390,793 @@ Full guide: __tooling__/docs/tools/baker/creatives.md`
|
|
|
33381
33390
|
}
|
|
33382
33391
|
});
|
|
33383
33392
|
|
|
33393
|
+
// src/commands/experiment/index.ts
|
|
33394
|
+
import { defineCommand as defineCommand112 } from "citty";
|
|
33395
|
+
|
|
33396
|
+
// src/commands/landing/import-graph.ts
|
|
33397
|
+
import { readdir as readdir8, readFile as readFile20, stat as stat4 } from "fs/promises";
|
|
33398
|
+
import path26 from "path";
|
|
33399
|
+
var SOURCE_EXT = /\.(astro|ts|tsx|js|jsx|mjs|cjs|vue|svelte)$/;
|
|
33400
|
+
var RESOLVE_CANDIDATES = [
|
|
33401
|
+
"",
|
|
33402
|
+
".astro",
|
|
33403
|
+
".ts",
|
|
33404
|
+
".tsx",
|
|
33405
|
+
".js",
|
|
33406
|
+
".jsx",
|
|
33407
|
+
".mjs",
|
|
33408
|
+
".cjs",
|
|
33409
|
+
"/index.astro",
|
|
33410
|
+
"/index.ts",
|
|
33411
|
+
"/index.js"
|
|
33412
|
+
];
|
|
33413
|
+
var IMPORT_RE = /(?<![A-Za-z0-9_$])(?:import|export)\s*(?:[\s\S]*?\bfrom\s*)?["']([^"']+)["']/g;
|
|
33414
|
+
var DYNAMIC_IMPORT_RE = /\bimport\s*\(\s*["']([^"']+)["']\s*\)/g;
|
|
33415
|
+
async function isFile(p) {
|
|
33416
|
+
try {
|
|
33417
|
+
return (await stat4(p)).isFile();
|
|
33418
|
+
} catch {
|
|
33419
|
+
return false;
|
|
33420
|
+
}
|
|
33421
|
+
}
|
|
33422
|
+
async function walkFiles(dir) {
|
|
33423
|
+
let entries;
|
|
33424
|
+
try {
|
|
33425
|
+
entries = await readdir8(dir, { withFileTypes: true });
|
|
33426
|
+
} catch {
|
|
33427
|
+
return [];
|
|
33428
|
+
}
|
|
33429
|
+
const out = [];
|
|
33430
|
+
for (const entry of entries) {
|
|
33431
|
+
const full = path26.join(dir, entry.name);
|
|
33432
|
+
if (entry.isDirectory()) {
|
|
33433
|
+
if (entry.name === "node_modules" || entry.name === "dist") continue;
|
|
33434
|
+
out.push(...await walkFiles(full));
|
|
33435
|
+
} else if (SOURCE_EXT.test(entry.name)) {
|
|
33436
|
+
out.push(full);
|
|
33437
|
+
}
|
|
33438
|
+
}
|
|
33439
|
+
return out;
|
|
33440
|
+
}
|
|
33441
|
+
async function resolveSpecifier(specifier, fromFile, root) {
|
|
33442
|
+
const bare = specifier.split("?")[0] ?? "";
|
|
33443
|
+
if (bare.includes("*")) return null;
|
|
33444
|
+
let base;
|
|
33445
|
+
if (bare.startsWith("/")) base = path26.join(root, bare.slice(1));
|
|
33446
|
+
else if (bare.startsWith(".")) base = path26.resolve(path26.dirname(fromFile), bare);
|
|
33447
|
+
else return null;
|
|
33448
|
+
for (const suffix of RESOLVE_CANDIDATES) {
|
|
33449
|
+
const candidate = base + suffix;
|
|
33450
|
+
if (SOURCE_EXT.test(candidate) && await isFile(candidate)) return candidate;
|
|
33451
|
+
}
|
|
33452
|
+
return null;
|
|
33453
|
+
}
|
|
33454
|
+
async function importedFiles(text2, file, root) {
|
|
33455
|
+
const out = [];
|
|
33456
|
+
for (const re2 of [IMPORT_RE, DYNAMIC_IMPORT_RE]) {
|
|
33457
|
+
for (const match of text2.matchAll(re2)) {
|
|
33458
|
+
const spec = match[1];
|
|
33459
|
+
if (spec === void 0) continue;
|
|
33460
|
+
const target = await resolveSpecifier(spec, file, root);
|
|
33461
|
+
if (target !== null) out.push(target);
|
|
33462
|
+
}
|
|
33463
|
+
}
|
|
33464
|
+
return out;
|
|
33465
|
+
}
|
|
33466
|
+
async function landingGraphFiles(root, slug) {
|
|
33467
|
+
const seen = /* @__PURE__ */ new Set();
|
|
33468
|
+
const queue = await walkFiles(path26.join(root, "src/pages", slug));
|
|
33469
|
+
while (queue.length > 0) {
|
|
33470
|
+
const file = queue.pop();
|
|
33471
|
+
if (file === void 0 || seen.has(file)) continue;
|
|
33472
|
+
let text2;
|
|
33473
|
+
try {
|
|
33474
|
+
text2 = await readFile20(file, "utf8");
|
|
33475
|
+
} catch {
|
|
33476
|
+
continue;
|
|
33477
|
+
}
|
|
33478
|
+
seen.add(file);
|
|
33479
|
+
for (const target of await importedFiles(text2, file, root)) {
|
|
33480
|
+
if (!seen.has(target)) queue.push(target);
|
|
33481
|
+
}
|
|
33482
|
+
}
|
|
33483
|
+
return new Set([...seen].map((abs) => path26.relative(root, abs).split(path26.sep).join("/")));
|
|
33484
|
+
}
|
|
33485
|
+
|
|
33486
|
+
// src/commands/experiment/composition.ts
|
|
33487
|
+
function sectionsUnder(files, slug) {
|
|
33488
|
+
const prefix = `src/pages/${slug}/`;
|
|
33489
|
+
return files.filter((file) => file.startsWith(prefix) && !file.slice(prefix.length).startsWith("index.")).sort();
|
|
33490
|
+
}
|
|
33491
|
+
function compositionOf(controlSlug, variantSlug, variantGraph) {
|
|
33492
|
+
return {
|
|
33493
|
+
forked: sectionsUnder(variantGraph, variantSlug),
|
|
33494
|
+
shared: sectionsUnder(variantGraph, controlSlug)
|
|
33495
|
+
};
|
|
33496
|
+
}
|
|
33497
|
+
async function readComposition(root, controlSlug, variantSlug) {
|
|
33498
|
+
try {
|
|
33499
|
+
const graph = await landingGraphFiles(root, variantSlug);
|
|
33500
|
+
return compositionOf(controlSlug, variantSlug, [...graph]);
|
|
33501
|
+
} catch {
|
|
33502
|
+
return void 0;
|
|
33503
|
+
}
|
|
33504
|
+
}
|
|
33505
|
+
|
|
33506
|
+
// src/commands/experiment/goal.ts
|
|
33507
|
+
function parseEventGoal(argument) {
|
|
33508
|
+
const [name, filter] = argument.split("/");
|
|
33509
|
+
if (!name) return { error: "Name the event: --goal event:request_demo" };
|
|
33510
|
+
if (!filter) return { kind: "custom_event", name };
|
|
33511
|
+
const separator = filter.indexOf("=");
|
|
33512
|
+
if (separator === -1) {
|
|
33513
|
+
return { error: `Write the property as key=value: --goal event:${name}/section=pricing` };
|
|
33514
|
+
}
|
|
33515
|
+
return { kind: "custom_event", name, property: filter.slice(0, separator), value: filter.slice(separator + 1) };
|
|
33516
|
+
}
|
|
33517
|
+
function parseGoal(raw) {
|
|
33518
|
+
if (raw === void 0 || raw === "") return void 0;
|
|
33519
|
+
const separator = raw.indexOf(":");
|
|
33520
|
+
const kind = separator === -1 ? raw : raw.slice(0, separator);
|
|
33521
|
+
const argument = separator === -1 ? "" : raw.slice(separator + 1);
|
|
33522
|
+
if (kind === "leads") return argument ? { kind: "conversion", flowSlug: argument } : { kind: "conversion" };
|
|
33523
|
+
if (kind === "click") return argument ? { kind: "outbound_click", targetHost: argument } : { kind: "outbound_click" };
|
|
33524
|
+
if (kind === "event") return parseEventGoal(argument);
|
|
33525
|
+
return {
|
|
33526
|
+
error: `Unknown goal "${raw}". Use leads, leads:<form>, event:<name>, event:<name>/<key>=<value>, click, or click:<host>`
|
|
33527
|
+
};
|
|
33528
|
+
}
|
|
33529
|
+
|
|
33530
|
+
// src/commands/experiment/hints.ts
|
|
33531
|
+
var RUNNING_HINT = {
|
|
33532
|
+
keep_running: (row) => `${row.experimentId} cannot yet tell the two pages apart \u2014 do not read its numbers as a result, and do not end it hoping for one. ${describeWait(row)}`,
|
|
33533
|
+
winner: (row) => `${row.experimentId} has a winner. Run \`baker experiment finish --id ${row.experimentId}\` to send all the traffic to it. That only re-routes between two pages already published \u2014 it does not edit either.`,
|
|
33534
|
+
no_difference: (row) => `${row.experimentId} measured the two pages as alike \u2014 it collected enough traffic to see the change it was looking for, and there was none. Finish it (the page you have stays live) and test a bigger change \u2014 a headline, an offer, the shape of the page, not a button colour.`,
|
|
33535
|
+
stopped_early_harmful: (row) => `${row.experimentId} is doing damage. Finish it now \u2014 traffic goes back to the original page.`,
|
|
33536
|
+
// Unreachable in practice — only `finish --abandon` produces it, and that
|
|
33537
|
+
// leaves the test finished. The Record is exhaustive by type so a verdict
|
|
33538
|
+
// added upstream fails to compile here rather than printing nothing.
|
|
33539
|
+
abandoned: (row) => `${row.experimentId} was stopped before it concluded and found nothing.`,
|
|
33540
|
+
invalid: (row) => `${row.experimentId} cannot be read: ${row.invalidReason === "crossover" ? "too many visitors saw both versions" : "the traffic did not split evenly"}. Finish it and start again rather than reporting anything from it.`
|
|
33541
|
+
};
|
|
33542
|
+
function describeWait(row) {
|
|
33543
|
+
if (row.willNotConclude) {
|
|
33544
|
+
return "This page's traffic will not settle it at any point, so abandon it with `--abandon` and test a bigger change.";
|
|
33545
|
+
}
|
|
33546
|
+
if (row.earliestDecisionAt === null) {
|
|
33547
|
+
return "This page has produced no visitors yet, so there is nothing to project a finish date from.";
|
|
33548
|
+
}
|
|
33549
|
+
return `Earliest it could say anything: ${new Date(row.earliestDecisionAt).toISOString().slice(0, 10)}.`;
|
|
33550
|
+
}
|
|
33551
|
+
function buildStatusHints(experiments) {
|
|
33552
|
+
const hints = experiments.filter((row) => row.status === "running").map((row) => RUNNING_HINT[row.verdict](row));
|
|
33553
|
+
if (experiments.length === 0) {
|
|
33554
|
+
hints.push(
|
|
33555
|
+
"No tests yet. `baker experiment plan --landing <slug> --variant <slug>` says whether a page has enough traffic for one before you build the alternative."
|
|
33556
|
+
);
|
|
33557
|
+
}
|
|
33558
|
+
return hints;
|
|
33559
|
+
}
|
|
33560
|
+
function asLift(value) {
|
|
33561
|
+
return `+${Math.round(value * 100)}%`;
|
|
33562
|
+
}
|
|
33563
|
+
function windowHint(days, detectable) {
|
|
33564
|
+
if (days === void 0) return null;
|
|
33565
|
+
if (detectable === null || detectable === void 0) {
|
|
33566
|
+
return `In ${days} days this page would not gather enough visitors to settle anything, at any size of change. The constraint is the traffic, not the test.`;
|
|
33567
|
+
}
|
|
33568
|
+
return `In ${days} days this page could only settle a change of ${asLift(detectable)} or more. Design the alternative to be at least that different \u2014 a smaller change needs traffic these ${days} days will not deliver, and the test would end saying nothing.`;
|
|
33569
|
+
}
|
|
33570
|
+
function buildPlanHints(plan, days) {
|
|
33571
|
+
const window2 = windowHint(days, plan.detectableLiftInDays);
|
|
33572
|
+
if (!plan.canRun) {
|
|
33573
|
+
return [
|
|
33574
|
+
plan.reason ?? "This test cannot run on this page.",
|
|
33575
|
+
...window2 ? [window2] : [],
|
|
33576
|
+
"A test that cannot conclude is worse than no test \u2014 it still produces a number, and somebody acts on it."
|
|
33577
|
+
];
|
|
33578
|
+
}
|
|
33579
|
+
const hints = [
|
|
33580
|
+
`This test measures ${plan.goalLabel}. That is fixed when it starts and cannot be changed later.`,
|
|
33581
|
+
// Fires at the exact moment the hypothesis gets invented, whether or not
|
|
33582
|
+
// the agent read the family doc. `plan` says a question CAN be settled here
|
|
33583
|
+
// and says nothing about which question is worth asking — and a well-run
|
|
33584
|
+
// test of a bad idea costs three weeks and returns `no_difference`.
|
|
33585
|
+
"Now decide WHAT to change, from evidence rather than taste. These numbers say which page and which step; they cannot say why. A second opinion answers why \u2014 Microsoft Clarity for what visitors actually do on the page, `baker gsc` for what they searched, `baker ads <platform>` for what the ad promised, `baker ga4` for the client's own measurement. `baker capabilities` lists everything this client has in one call \u2014 the surfaces, and under `tools` everything else that is reachable. Message match \u2014 what the ad or the search promised against what the page opens with \u2014 is the cheapest hypothesis worth testing and is wrong surprisingly often.",
|
|
33586
|
+
// The base always works, so a missing integration is never a reason to
|
|
33587
|
+
// stop. Naming what it would have shown is worth more to the client than
|
|
33588
|
+
// either a silent gap or a run that waited.
|
|
33589
|
+
"If none of that is connected, this page's own drop-off is enough to test on \u2014 say which source you lacked and what it would have told you, then run the test anyway. A test never waits on an integration."
|
|
33590
|
+
];
|
|
33591
|
+
if (plan.estimatedDays !== null && plan.estimatedDays > 60) {
|
|
33592
|
+
hints.push(
|
|
33593
|
+
`At this page's traffic it would take about ${plan.estimatedDays} days. Consider testing a bigger change, which needs less traffic to detect.`
|
|
33594
|
+
);
|
|
33595
|
+
}
|
|
33596
|
+
if (window2) hints.push(window2);
|
|
33597
|
+
return hints;
|
|
33598
|
+
}
|
|
33599
|
+
|
|
33600
|
+
// ../api/src/experiments/composition.ts
|
|
33601
|
+
import { z as z29 } from "zod";
|
|
33602
|
+
var experimentCompositionSchema = z29.object({
|
|
33603
|
+
/** Components the variant owns its own copy of — the thing under test. */
|
|
33604
|
+
forked: z29.array(z29.string().max(300)).max(50),
|
|
33605
|
+
/** Components both pages render from one file, so an edit reaches both arms. */
|
|
33606
|
+
shared: z29.array(z29.string().max(300)).max(200)
|
|
33607
|
+
});
|
|
33608
|
+
|
|
33609
|
+
// ../api/src/experiments/goal.ts
|
|
33610
|
+
import { z as z30 } from "zod";
|
|
33611
|
+
var eventNameSchema = z30.string().min(1).max(120);
|
|
33612
|
+
var experimentGoalSchema = z30.discriminatedUnion("kind", [
|
|
33613
|
+
z30.object({
|
|
33614
|
+
kind: z30.literal("conversion"),
|
|
33615
|
+
/**
|
|
33616
|
+
* Narrow to one Form, by the slug that is its identity.
|
|
33617
|
+
*
|
|
33618
|
+
* Absent means every marked conversion on the page counts, which is the
|
|
33619
|
+
* right default for a landing with one Form and the wrong one for a page
|
|
33620
|
+
* carrying both a newsletter signup and a demo request.
|
|
33621
|
+
*/
|
|
33622
|
+
flowSlug: z30.string().max(120).optional()
|
|
33623
|
+
}),
|
|
33624
|
+
z30.object({
|
|
33625
|
+
kind: z30.literal("custom_event"),
|
|
33626
|
+
name: eventNameSchema,
|
|
33627
|
+
/**
|
|
33628
|
+
* Narrow to one value of one of the event's own properties.
|
|
33629
|
+
*
|
|
33630
|
+
* `section_view` on its own is every section; `section_view` where
|
|
33631
|
+
* `section = pricing` is the one that matters. Without this a page would
|
|
33632
|
+
* have to encode the answer into the event name to be testable against it,
|
|
33633
|
+
* which is precisely what the properties map exists to stop.
|
|
33634
|
+
*/
|
|
33635
|
+
property: z30.string().max(64).optional(),
|
|
33636
|
+
value: z30.string().max(255).optional()
|
|
33637
|
+
}),
|
|
33638
|
+
z30.object({
|
|
33639
|
+
kind: z30.literal("outbound_click"),
|
|
33640
|
+
/**
|
|
33641
|
+
* The destination host, as the row records it. Absent means any exit.
|
|
33642
|
+
*
|
|
33643
|
+
* A host rather than a full URL because that is what `event_name` carries
|
|
33644
|
+
* on an `outbound_click` row — `target_url` has the rest, and matching on
|
|
33645
|
+
* it would make the goal depend on query parameters a campaign rewrites.
|
|
33646
|
+
*/
|
|
33647
|
+
targetHost: z30.string().max(255).optional()
|
|
33648
|
+
})
|
|
33649
|
+
]);
|
|
33650
|
+
|
|
33651
|
+
// ../api/src/experiments/hypothesis.ts
|
|
33652
|
+
import { z as z31 } from "zod";
|
|
33653
|
+
var experimentEvidenceSourceSchema = z31.enum([
|
|
33654
|
+
"baker_analytics",
|
|
33655
|
+
"clarity",
|
|
33656
|
+
"ads",
|
|
33657
|
+
"search_console",
|
|
33658
|
+
"research",
|
|
33659
|
+
"inspiration",
|
|
33660
|
+
"client",
|
|
33661
|
+
"hunch"
|
|
33662
|
+
]);
|
|
33663
|
+
var experimentEvidenceSchema = z31.object({
|
|
33664
|
+
source: experimentEvidenceSourceSchema,
|
|
33665
|
+
note: z31.string().min(1).max(200),
|
|
33666
|
+
/**
|
|
33667
|
+
* Where to go and look. Not a `z.url()` — a report inside Baker is reached by
|
|
33668
|
+
* a relative path, and rejecting those would push every in-product citation
|
|
33669
|
+
* into the note where nothing can link it.
|
|
33670
|
+
*/
|
|
33671
|
+
href: z31.string().max(500).optional()
|
|
33672
|
+
});
|
|
33673
|
+
var experimentHypothesisSchema = z31.object({
|
|
33674
|
+
/**
|
|
33675
|
+
* The observation. The one genuinely new field, and the reason for the rest.
|
|
33676
|
+
*
|
|
33677
|
+
* Without it a lost test teaches nothing, because there is no belief for the
|
|
33678
|
+
* result to be evidence against — which is how a testing programme ends up as
|
|
33679
|
+
* a list of tests instead of a list of things learned.
|
|
33680
|
+
*/
|
|
33681
|
+
because: z31.string().min(1).max(300),
|
|
33682
|
+
/**
|
|
33683
|
+
* The change, in the client's words.
|
|
33684
|
+
*
|
|
33685
|
+
* The machine-checkable truth lives in `plan.composition.forked`; this is what
|
|
33686
|
+
* a person reads. The two are allowed to differ in wording and cannot differ
|
|
33687
|
+
* in fact, because the brief shows both.
|
|
33688
|
+
*/
|
|
33689
|
+
change: z31.string().min(1).max(200),
|
|
33690
|
+
/** Which way the goal is expected to move. See the note at the top of this file. */
|
|
33691
|
+
expect: z31.enum(["increase", "decrease"]).default("increase"),
|
|
33692
|
+
evidence: z31.array(experimentEvidenceSchema).max(5).optional()
|
|
33693
|
+
});
|
|
33694
|
+
var experimentHypothesisOutcomeSchema = z31.enum([
|
|
33695
|
+
"supported",
|
|
33696
|
+
"refuted_opposite",
|
|
33697
|
+
"refuted_no_effect",
|
|
33698
|
+
"unanswered"
|
|
33699
|
+
]);
|
|
33700
|
+
|
|
33701
|
+
// ../api/src/experiments/wire.ts
|
|
33702
|
+
import { z as z32 } from "zod";
|
|
33703
|
+
var slugSchema = z32.string().min(1).max(120).regex(/^[a-z0-9]+(?:-[a-z0-9]+)*$/, "Use a page slug, like `oferta`");
|
|
33704
|
+
var experimentPlanRequestSchema = z32.object({
|
|
33705
|
+
landingSlug: slugSchema,
|
|
33706
|
+
variantSlug: slugSchema,
|
|
33707
|
+
goal: experimentGoalSchema.optional(),
|
|
33708
|
+
/**
|
|
33709
|
+
* The smallest lift worth detecting, relative to the baseline. `0.2` is +20%.
|
|
33710
|
+
*
|
|
33711
|
+
* The single most consequential number a caller supplies, and the one it is
|
|
33712
|
+
* most tempting to set small. A test sized for +5% on a 3% page needs a
|
|
33713
|
+
* quarter of a million visitors per arm; `plan` will say so rather than
|
|
33714
|
+
* running it.
|
|
33715
|
+
*/
|
|
33716
|
+
minDetectableRelativeLift: z32.number().gt(0).max(10).optional(),
|
|
33717
|
+
/**
|
|
33718
|
+
* "I have this many days — what could I even see?"
|
|
33719
|
+
*
|
|
33720
|
+
* Read-only, and deliberately not a setting. It does NOT shorten the test:
|
|
33721
|
+
* the finish line is a number of visitors fixed before the test starts, and a
|
|
33722
|
+
* horizon that can be moved is not a horizon. What it does is answer the
|
|
33723
|
+
* question a refusal leaves hanging — `plan` says a +20% lift needs three
|
|
33724
|
+
* weeks this page will not deliver, and this says what those weeks WOULD
|
|
33725
|
+
* settle, which is a change somebody can actually go and make.
|
|
33726
|
+
*/
|
|
33727
|
+
days: z32.number().int().gt(0).max(365).optional()
|
|
33728
|
+
});
|
|
33729
|
+
var experimentStartRequestSchema = experimentPlanRequestSchema.extend({
|
|
33730
|
+
hypothesis: experimentHypothesisSchema,
|
|
33731
|
+
/**
|
|
33732
|
+
* What the two arms are made of, read off the import graph by the caller at
|
|
33733
|
+
* the moment the test starts.
|
|
33734
|
+
*
|
|
33735
|
+
* Optional on the wire because the backend cannot compute it — only the
|
|
33736
|
+
* Company Workspace has the files — and a workspace that cannot answer must
|
|
33737
|
+
* not be blocked from running a test. The brief degrades to naming the two
|
|
33738
|
+
* pages instead of naming the section under test.
|
|
33739
|
+
*/
|
|
33740
|
+
composition: experimentCompositionSchema.optional()
|
|
33741
|
+
});
|
|
33742
|
+
var experimentFinishRequestSchema = z32.object({
|
|
33743
|
+
experimentId: z32.string().min(1).max(64),
|
|
33744
|
+
/**
|
|
33745
|
+
* Stop the test, keep the page that is live, and record no result.
|
|
33746
|
+
*
|
|
33747
|
+
* The only way a person or an agent can end a test that is still
|
|
33748
|
+
* `keep_running`, and it is deliberately its own field rather than a
|
|
33749
|
+
* `survivor` they get to pick: an abandoned test has no winner, keeps the
|
|
33750
|
+
* control, and is recorded as `abandoned` so it can never be reported later
|
|
33751
|
+
* as a result. Without the separate flag, "stop this" and "the variant won"
|
|
33752
|
+
* would be the same call.
|
|
33753
|
+
*
|
|
33754
|
+
* **It overrides the verdict, and does not defer to it.** The only outcome it
|
|
33755
|
+
* can actually disagree with is a variant win — every other one keeps the
|
|
33756
|
+
* control anyway — so reading it as "apply only when the numbers have nothing
|
|
33757
|
+
* to say" meant that abandoning a won test handed all the traffic to the
|
|
33758
|
+
* variant instead of keeping the original: the opposite of what this field
|
|
33759
|
+
* promises, on live visitors, from a flag whose whole purpose is to be the
|
|
33760
|
+
* conservative choice. The response reports `discardedVerdict` when it did
|
|
33761
|
+
* override one, because throwing away a real finding should be said out loud.
|
|
33762
|
+
*/
|
|
33763
|
+
abandon: z32.boolean().optional()
|
|
33764
|
+
});
|
|
33765
|
+
var experimentStatusRequestSchema = z32.object({
|
|
33766
|
+
experimentId: z32.string().max(64).optional(),
|
|
33767
|
+
full: z32.boolean().optional()
|
|
33768
|
+
});
|
|
33769
|
+
var experimentPlanResponseSchema = z32.object({
|
|
33770
|
+
canRun: z32.boolean(),
|
|
33771
|
+
/** Present when `canRun` is false. Product language, ready to show. */
|
|
33772
|
+
reason: z32.string().optional(),
|
|
33773
|
+
baselineRate: z32.number(),
|
|
33774
|
+
minDetectableRelativeLift: z32.number(),
|
|
33775
|
+
/** `null` when no amount of traffic could settle the hypothesis. */
|
|
33776
|
+
requiredPerVariant: z32.number().nullable(),
|
|
33777
|
+
/** Visitors a day the page has been getting, per arm once split. */
|
|
33778
|
+
dailyVisitorsPerVariant: z32.number(),
|
|
33779
|
+
/** `null` when the page has no traffic to project from. */
|
|
33780
|
+
estimatedDays: z32.number().nullable(),
|
|
33781
|
+
goal: experimentGoalSchema,
|
|
33782
|
+
goalLabel: z32.string(),
|
|
33783
|
+
/**
|
|
33784
|
+
* The smallest lift `days` of this page's traffic could detect, when `days`
|
|
33785
|
+
* was asked for. `null` when it was not, or when no lift at all would be
|
|
33786
|
+
* detectable in that window.
|
|
33787
|
+
*/
|
|
33788
|
+
detectableLiftInDays: z32.number().nullable().optional()
|
|
33789
|
+
});
|
|
33790
|
+
var experimentVerdictSchema = z32.enum([
|
|
33791
|
+
"keep_running",
|
|
33792
|
+
"winner",
|
|
33793
|
+
"no_difference",
|
|
33794
|
+
"invalid",
|
|
33795
|
+
"stopped_early_harmful",
|
|
33796
|
+
/**
|
|
33797
|
+
* Stopped by a person before it concluded. Never produced by the stopping
|
|
33798
|
+
* rule — only by `finish --abandon`.
|
|
33799
|
+
*
|
|
33800
|
+
* Its own verdict rather than folded into `no_difference`, because they are
|
|
33801
|
+
* opposite claims: one says the two versions were measured and found alike,
|
|
33802
|
+
* the other says nobody ever found out. Collapsing them would manufacture the
|
|
33803
|
+
* exact finding this feature exists to refuse.
|
|
33804
|
+
*/
|
|
33805
|
+
"abandoned"
|
|
33806
|
+
]);
|
|
33807
|
+
var experimentArmSchema = z32.object({
|
|
33808
|
+
slug: z32.string(),
|
|
33809
|
+
visitors: z32.number(),
|
|
33810
|
+
conversions: z32.number(),
|
|
33811
|
+
rate: z32.number()
|
|
33812
|
+
});
|
|
33813
|
+
var experimentStatusRowSchema = z32.object({
|
|
33814
|
+
experimentId: z32.string(),
|
|
33815
|
+
landingSlug: z32.string(),
|
|
33816
|
+
variantSlug: z32.string(),
|
|
33817
|
+
hypothesis: experimentHypothesisSchema,
|
|
33818
|
+
/**
|
|
33819
|
+
* The hypothesis as one sentence, built from its parts plus the goal and the
|
|
33820
|
+
* lift on this same row. Rendered backend-side so what a client reads in chat
|
|
33821
|
+
* and what they read on the dashboard can never be two different sentences.
|
|
33822
|
+
*/
|
|
33823
|
+
hypothesisStatement: z32.string(),
|
|
33824
|
+
/**
|
|
33825
|
+
* What the test found out about the belief — derived from `verdict` and
|
|
33826
|
+
* `winner`, never entered.
|
|
33827
|
+
*
|
|
33828
|
+
* Its own field because the verdict and the outcome come apart in exactly the
|
|
33829
|
+
* case worth catching: a `winner` the *control* won is not a win, it is the
|
|
33830
|
+
* change having made things worse.
|
|
33831
|
+
*/
|
|
33832
|
+
hypothesisOutcome: experimentHypothesisOutcomeSchema,
|
|
33833
|
+
/** What the two arms differ by. Absent for a test started before this was recorded. */
|
|
33834
|
+
composition: experimentCompositionSchema.optional(),
|
|
33835
|
+
/**
|
|
33836
|
+
* The finish line this test was sized for. On the row rather than only inside
|
|
33837
|
+
* the plan because the claim is not readable without it — "leads should go up"
|
|
33838
|
+
* is not a hypothesis, "leads should go up by at least 20%" is.
|
|
33839
|
+
*/
|
|
33840
|
+
minDetectableRelativeLift: z32.number(),
|
|
33841
|
+
goalLabel: z32.string(),
|
|
33842
|
+
status: z32.enum(["running", "finished"]),
|
|
33843
|
+
startedAt: z32.number(),
|
|
33844
|
+
finishedAt: z32.number().nullable(),
|
|
33845
|
+
verdict: experimentVerdictSchema,
|
|
33846
|
+
/** Set only on `winner`. Which page to keep. */
|
|
33847
|
+
winner: z32.enum(["control", "variant"]).nullable(),
|
|
33848
|
+
/** Set only on `invalid`. Why the numbers cannot be read. */
|
|
33849
|
+
invalidReason: z32.enum(["sample_ratio_mismatch", "crossover"]).nullable(),
|
|
33850
|
+
/** What a person should be told, in one sentence, in product language. */
|
|
33851
|
+
summary: z32.string(),
|
|
33852
|
+
control: experimentArmSchema,
|
|
33853
|
+
variant: experimentArmSchema,
|
|
33854
|
+
/**
|
|
33855
|
+
* How close this test is to being able to say anything at all, 0 to 1.
|
|
33856
|
+
*
|
|
33857
|
+
* Not progress toward a planned sample — there is no longer one to progress
|
|
33858
|
+
* toward. It is the traffic behind the test as a share of the traffic its own
|
|
33859
|
+
* measured variance says it needs, so it moves when the page gets noisier or
|
|
33860
|
+
* steadier rather than only when visitors arrive.
|
|
33861
|
+
*/
|
|
33862
|
+
precision: z32.number(),
|
|
33863
|
+
/** When the test could first say something. `null` with no traffic to project from. */
|
|
33864
|
+
earliestDecisionAt: z32.number().nullable(),
|
|
33865
|
+
/**
|
|
33866
|
+
* True when no amount of this page's traffic would settle the comparison.
|
|
33867
|
+
* A different thing from a page that is merely slow, and the reason to
|
|
33868
|
+
* abandon a test rather than leave it running.
|
|
33869
|
+
*/
|
|
33870
|
+
willNotConclude: z32.boolean(),
|
|
33871
|
+
/** Printing only. Never a reason to act — `verdict` is. */
|
|
33872
|
+
evidence: z32.object({
|
|
33873
|
+
relativeLift: z32.number(),
|
|
33874
|
+
probabilityVariantBetter: z32.number(),
|
|
33875
|
+
controlInterval: z32.tuple([z32.number(), z32.number()]),
|
|
33876
|
+
variantInterval: z32.tuple([z32.number(), z32.number()]),
|
|
33877
|
+
/** The anytime-valid interval on the relative lift — what the verdict is read from. */
|
|
33878
|
+
liftInterval: z32.tuple([z32.number(), z32.number()])
|
|
33879
|
+
}).optional()
|
|
33880
|
+
});
|
|
33881
|
+
var experimentStatusResponseSchema = z32.object({
|
|
33882
|
+
experiments: z32.array(experimentStatusRowSchema)
|
|
33883
|
+
});
|
|
33884
|
+
|
|
33885
|
+
// src/commands/experiment/hypothesis.ts
|
|
33886
|
+
var SOURCES = experimentEvidenceSourceSchema.options.join(", ");
|
|
33887
|
+
function parseOne(raw) {
|
|
33888
|
+
const separator = raw.indexOf(":");
|
|
33889
|
+
if (separator === -1) {
|
|
33890
|
+
return { error: `Write the evidence as source:note \u2014 for example \`${raw}:what you saw\`. Sources: ${SOURCES}` };
|
|
33891
|
+
}
|
|
33892
|
+
const source = experimentEvidenceSourceSchema.safeParse(raw.slice(0, separator).trim());
|
|
33893
|
+
if (!source.success) {
|
|
33894
|
+
return { error: `\u201C${raw.slice(0, separator).trim()}\u201D is not one of the evidence sources. Use one of: ${SOURCES}` };
|
|
33895
|
+
}
|
|
33896
|
+
const rest = raw.slice(separator + 1).trim();
|
|
33897
|
+
const pipe = rest.lastIndexOf("|");
|
|
33898
|
+
const note = (pipe === -1 ? rest : rest.slice(0, pipe)).trim();
|
|
33899
|
+
const href = pipe === -1 ? void 0 : rest.slice(pipe + 1).trim();
|
|
33900
|
+
if (note === "") {
|
|
33901
|
+
return { error: `Say what ${source.data} showed: --evidence "${source.data}:what you saw"` };
|
|
33902
|
+
}
|
|
33903
|
+
return href ? { source: source.data, note, href } : { source: source.data, note };
|
|
33904
|
+
}
|
|
33905
|
+
function parseEvidence(raw) {
|
|
33906
|
+
if (raw === void 0) return void 0;
|
|
33907
|
+
const entries = (Array.isArray(raw) ? raw : [raw]).filter((entry) => entry !== "");
|
|
33908
|
+
if (entries.length === 0) return void 0;
|
|
33909
|
+
const parsed = [];
|
|
33910
|
+
for (const entry of entries) {
|
|
33911
|
+
const one = parseOne(entry);
|
|
33912
|
+
if ("error" in one) return one;
|
|
33913
|
+
parsed.push(one);
|
|
33914
|
+
}
|
|
33915
|
+
return parsed;
|
|
33916
|
+
}
|
|
33917
|
+
|
|
33918
|
+
// src/commands/experiment/index.ts
|
|
33919
|
+
var GOAL_ARG = {
|
|
33920
|
+
type: "string",
|
|
33921
|
+
description: "What counts as success, and it is fixed for the life of the test. `leads` (default \u2014 whatever this company already marks as a conversion), `leads:<form>`, `event:<name>` for an event the page declares itself (see `baker analytics events`), `event:<name>/<key>=<value>` to narrow it, `click` or `click:<host>` for leaving the site. Pick the thing the business actually wants; a test that measures the wrong outcome can have the variant win and the revenue fall.",
|
|
33922
|
+
required: false
|
|
33923
|
+
};
|
|
33924
|
+
var LIFT_ARG = {
|
|
33925
|
+
type: "string",
|
|
33926
|
+
description: "The smallest improvement worth detecting, relative \u2014 0.2 is +20% (default). The single most consequential number here: halving it roughly quadruples the traffic needed. Ask for the smallest lift that would actually change what the client does, not the smallest one you would like to see.",
|
|
33927
|
+
required: false
|
|
33928
|
+
};
|
|
33929
|
+
var DAYS_ARG = {
|
|
33930
|
+
type: "string",
|
|
33931
|
+
description: "How many days you have, if that is the constraint. This does NOT shorten the test \u2014 the finish line is a visitor count fixed before it starts \u2014 it answers the other question: what a window that long could settle on this page's traffic. Use it when `plan` refuses and you need to know what WOULD fit.",
|
|
33932
|
+
required: false
|
|
33933
|
+
};
|
|
33934
|
+
var BECAUSE_ARG = {
|
|
33935
|
+
type: "string",
|
|
33936
|
+
description: "What you SAW that makes this worth testing \u2014 the observation, not the change. \u201C60% of visitors never scroll past the hero\u201D, \u201CCPL doubled after the new headline\u201D. This is the field that makes a losing test worth something: without it there is no belief for the result to be evidence against, and the next test is no better informed than this one.",
|
|
33937
|
+
required: true
|
|
33938
|
+
};
|
|
33939
|
+
var CHANGE_ARG = {
|
|
33940
|
+
type: "string",
|
|
33941
|
+
description: "What is different about the alternative version, in the client's words \u2014 \u201Cput the booking form in the hero\u201D. One thing. Say it as the reader of the report would.",
|
|
33942
|
+
required: true
|
|
33943
|
+
};
|
|
33944
|
+
var EXPECT_ARG = {
|
|
33945
|
+
type: "string",
|
|
33946
|
+
description: "Which way you expect the goal to move: `increase` (default) or `decrease`. This is what lets Baker report whether your hypothesis HELD, rather than only which page won \u2014 a test the original page wins is not a win, it is the change having made things worse, and that is the most useful thing a test can tell you.",
|
|
33947
|
+
required: false
|
|
33948
|
+
};
|
|
33949
|
+
var EVIDENCE_ARG = {
|
|
33950
|
+
type: "string",
|
|
33951
|
+
description: "Where the observation came from, as `source:what it showed`, optionally `|link`. Repeatable. Sources: baker_analytics, clarity, ads, search_console, research, inspiration, client, hunch. Use `hunch` honestly when there is no data \u2014 a stated guess is worth more than a guess dressed as analytics.",
|
|
33952
|
+
required: false
|
|
33953
|
+
};
|
|
33954
|
+
function fail5(message) {
|
|
33955
|
+
writeJsonEnvelope({ ok: false, error: { code: "VALIDATION_ERROR", message } });
|
|
33956
|
+
process.exit(1);
|
|
33957
|
+
}
|
|
33958
|
+
function reportApiError(error) {
|
|
33959
|
+
if (error instanceof ApiError) {
|
|
33960
|
+
writeJsonEnvelope({ ok: false, error: { code: error.code ?? "ERROR", message: error.message } });
|
|
33961
|
+
process.exit(1);
|
|
33962
|
+
}
|
|
33963
|
+
throw error;
|
|
33964
|
+
}
|
|
33965
|
+
registerSchema({
|
|
33966
|
+
command: "experiment.plan",
|
|
33967
|
+
description: "Whether a page has enough traffic to settle a question, before anything is built",
|
|
33968
|
+
args: {
|
|
33969
|
+
landing: { type: "string", description: "The page under test, by slug", required: true },
|
|
33970
|
+
variant: { type: "string", description: "The alternative page's slug", required: true },
|
|
33971
|
+
goal: GOAL_ARG,
|
|
33972
|
+
lift: LIFT_ARG,
|
|
33973
|
+
days: DAYS_ARG
|
|
33974
|
+
}
|
|
33975
|
+
});
|
|
33976
|
+
var planCommand = defineCommand112({
|
|
33977
|
+
meta: {
|
|
33978
|
+
name: "plan",
|
|
33979
|
+
description: "Start here, BEFORE building the alternative page. Says whether this page gets enough traffic to settle the question, how long it would take, and whether the thing you want to measure has ever happened on it. Most landing pages cannot run most tests \u2014 finding that out now costs one call, and finding it out later costs three weeks and a decision made on noise."
|
|
33980
|
+
},
|
|
33981
|
+
args: {
|
|
33982
|
+
landing: { type: "string", description: "The page under test, by slug", required: true },
|
|
33983
|
+
variant: { type: "string", description: "The alternative page's slug", required: true },
|
|
33984
|
+
goal: GOAL_ARG,
|
|
33985
|
+
lift: LIFT_ARG,
|
|
33986
|
+
days: DAYS_ARG
|
|
33987
|
+
},
|
|
33988
|
+
run: async ({ args }) => {
|
|
33989
|
+
const goal = parseGoal(args.goal ? String(args.goal) : void 0);
|
|
33990
|
+
if (goal && "error" in goal) fail5(goal.error);
|
|
33991
|
+
try {
|
|
33992
|
+
const response = await apiPost("/api/experiments/plan", {
|
|
33993
|
+
landingSlug: String(args.landing),
|
|
33994
|
+
variantSlug: String(args.variant),
|
|
33995
|
+
...goal ? { goal } : {},
|
|
33996
|
+
...args.lift ? { minDetectableRelativeLift: Number(args.lift) } : {},
|
|
33997
|
+
...args.days ? { days: Number(args.days) } : {}
|
|
33998
|
+
});
|
|
33999
|
+
writeJsonEnvelope({
|
|
34000
|
+
ok: true,
|
|
34001
|
+
data: response.data,
|
|
34002
|
+
hints: buildPlanHints(response.data, args.days ? Number(args.days) : void 0)
|
|
34003
|
+
});
|
|
34004
|
+
} catch (error) {
|
|
34005
|
+
reportApiError(error);
|
|
34006
|
+
}
|
|
34007
|
+
}
|
|
34008
|
+
});
|
|
34009
|
+
registerSchema({
|
|
34010
|
+
command: "experiment.start",
|
|
34011
|
+
description: "Stage an A/B test between a page and its alternative",
|
|
34012
|
+
args: {
|
|
34013
|
+
landing: { type: "string", description: "The page under test, by slug", required: true },
|
|
34014
|
+
variant: { type: "string", description: "The alternative page's slug", required: true },
|
|
34015
|
+
because: BECAUSE_ARG,
|
|
34016
|
+
change: CHANGE_ARG,
|
|
34017
|
+
expect: EXPECT_ARG,
|
|
34018
|
+
evidence: EVIDENCE_ARG,
|
|
34019
|
+
goal: GOAL_ARG,
|
|
34020
|
+
lift: LIFT_ARG
|
|
34021
|
+
}
|
|
34022
|
+
});
|
|
34023
|
+
var startCommand = defineCommand112({
|
|
34024
|
+
meta: {
|
|
34025
|
+
name: "start",
|
|
34026
|
+
description: "Stage a test between the page and its alternative. Both are ordinary published pages; visitors are split 50/50 at the edge, so there is no flicker and nothing for an ad blocker to suppress. Nothing goes live until the session is published \u2014 the person publishing is the review of the alternative page. Re-runs `plan` first and refuses on the same grounds."
|
|
34027
|
+
},
|
|
34028
|
+
args: {
|
|
34029
|
+
landing: { type: "string", description: "The page under test, by slug", required: true },
|
|
34030
|
+
variant: { type: "string", description: "The alternative page's slug", required: true },
|
|
34031
|
+
because: BECAUSE_ARG,
|
|
34032
|
+
change: CHANGE_ARG,
|
|
34033
|
+
expect: EXPECT_ARG,
|
|
34034
|
+
evidence: EVIDENCE_ARG,
|
|
34035
|
+
goal: GOAL_ARG,
|
|
34036
|
+
lift: LIFT_ARG
|
|
34037
|
+
},
|
|
34038
|
+
run: async ({ args }) => {
|
|
34039
|
+
const goal = parseGoal(args.goal ? String(args.goal) : void 0);
|
|
34040
|
+
if (goal && "error" in goal) fail5(goal.error);
|
|
34041
|
+
const expect = args.expect === void 0 ? "increase" : String(args.expect);
|
|
34042
|
+
if (expect !== "increase" && expect !== "decrease") {
|
|
34043
|
+
fail5("`--expect` is either `increase` or `decrease` \u2014 which way should the goal move if you are right?");
|
|
34044
|
+
}
|
|
34045
|
+
const evidence = parseEvidence(args.evidence);
|
|
34046
|
+
if (evidence && "error" in evidence) fail5(evidence.error);
|
|
34047
|
+
const composition = await readComposition(process.cwd(), String(args.landing), String(args.variant));
|
|
34048
|
+
try {
|
|
34049
|
+
const response = await apiPost("/api/experiments/start", {
|
|
34050
|
+
landingSlug: String(args.landing),
|
|
34051
|
+
variantSlug: String(args.variant),
|
|
34052
|
+
hypothesis: {
|
|
34053
|
+
because: String(args.because),
|
|
34054
|
+
change: String(args.change),
|
|
34055
|
+
expect,
|
|
34056
|
+
...evidence ? { evidence } : {}
|
|
34057
|
+
},
|
|
34058
|
+
...composition ? { composition } : {},
|
|
34059
|
+
...goal ? { goal } : {},
|
|
34060
|
+
...args.lift ? { minDetectableRelativeLift: Number(args.lift) } : {}
|
|
34061
|
+
});
|
|
34062
|
+
writeJsonEnvelope({
|
|
34063
|
+
ok: true,
|
|
34064
|
+
data: response.data,
|
|
34065
|
+
hints: [
|
|
34066
|
+
// The one composition worth interrupting for: `baker landing variant`
|
|
34067
|
+
// with no `--fork` succeeds and produces a page that renders exactly
|
|
34068
|
+
// the control, so the test would run for weeks against itself.
|
|
34069
|
+
...composition && composition.forked.length === 0 ? [
|
|
34070
|
+
`\u201C${String(args.variant)}\u201D does not have its own copy of any section \u2014 it renders exactly the same page as \u201C${String(args.landing)}\u201D, so this test cannot find anything. Fork the section you want to test (\`baker landing variant ${String(args.landing)} ${String(args.variant)} --fork Hero.astro\`) and edit only that file.`
|
|
34071
|
+
] : [],
|
|
34072
|
+
"Staged. The split starts when this session is published \u2014 publishing is the review of the alternative page.",
|
|
34073
|
+
"Check it as often as you like \u2014 `baker experiment status` is safe to read at any moment and holds its error rate however often you ask. What is never safe is reading the numbers instead of the verdict: `keep_running` means this test cannot yet tell the two pages apart, whatever the rates happen to say today."
|
|
34074
|
+
]
|
|
34075
|
+
});
|
|
34076
|
+
} catch (error) {
|
|
34077
|
+
reportApiError(error);
|
|
34078
|
+
}
|
|
34079
|
+
}
|
|
34080
|
+
});
|
|
34081
|
+
registerSchema({
|
|
34082
|
+
command: "experiment.status",
|
|
34083
|
+
description: "The verdict on every A/B test, running and finished",
|
|
34084
|
+
args: {
|
|
34085
|
+
id: { type: "string", description: "One test, by its id", required: false },
|
|
34086
|
+
full: { type: "boolean", description: "Include the posteriors behind the verdict", required: false }
|
|
34087
|
+
}
|
|
34088
|
+
});
|
|
34089
|
+
var statusCommand4 = defineCommand112({
|
|
34090
|
+
meta: {
|
|
34091
|
+
name: "status",
|
|
34092
|
+
description: "The verdict on each test. Read `verdict` and nothing else to decide: keep_running | winner | no_difference | invalid | stopped_early_harmful. `summary` is one sentence you can show the client. The numbers under `--full` are for printing, never for deciding \u2014 a test that says keep_running has not finished, however good its numbers look."
|
|
34093
|
+
},
|
|
34094
|
+
args: {
|
|
34095
|
+
id: { type: "string", description: "One test, by its id", required: false },
|
|
34096
|
+
full: { type: "boolean", description: "Include the posteriors behind the verdict", required: false }
|
|
34097
|
+
},
|
|
34098
|
+
run: async ({ args }) => {
|
|
34099
|
+
try {
|
|
34100
|
+
const response = await apiPost("/api/experiments/status", {
|
|
34101
|
+
...args.id ? { experimentId: String(args.id) } : {},
|
|
34102
|
+
...args.full === true ? { full: true } : {}
|
|
34103
|
+
});
|
|
34104
|
+
writeJsonEnvelope({
|
|
34105
|
+
ok: true,
|
|
34106
|
+
data: response.data,
|
|
34107
|
+
hints: buildStatusHints(response.data.experiments)
|
|
34108
|
+
});
|
|
34109
|
+
} catch (error) {
|
|
34110
|
+
reportApiError(error);
|
|
34111
|
+
}
|
|
34112
|
+
}
|
|
34113
|
+
});
|
|
34114
|
+
registerSchema({
|
|
34115
|
+
command: "experiment.finish",
|
|
34116
|
+
description: "End a test and send all its traffic to the surviving page",
|
|
34117
|
+
args: {
|
|
34118
|
+
id: { type: "string", description: "The test, by its id", required: true },
|
|
34119
|
+
abandon: {
|
|
34120
|
+
type: "boolean",
|
|
34121
|
+
description: "Stop the test, keep the original page, and record no result. The only way to end a test that has not concluded \u2014 and it OVERRIDES a verdict if there is one, throwing that finding away. Do not pass it by reflex.",
|
|
34122
|
+
required: false
|
|
34123
|
+
}
|
|
34124
|
+
}
|
|
34125
|
+
});
|
|
34126
|
+
var finishCommand = defineCommand112({
|
|
34127
|
+
meta: {
|
|
34128
|
+
name: "finish",
|
|
34129
|
+
description: "End a test and send all its traffic to whichever page survived. Only a test whose verdict has concluded can be finished with a result \u2014 a `keep_running` test can only be `--abandon`ed, which keeps the original page and records no finding. Promotion is a traffic change over two pages that were both already published and reviewed; it does not edit either page."
|
|
34130
|
+
},
|
|
34131
|
+
args: {
|
|
34132
|
+
id: { type: "string", description: "The test, by its id", required: true },
|
|
34133
|
+
abandon: {
|
|
34134
|
+
type: "boolean",
|
|
34135
|
+
description: "Stop the test, keep the original page, and record no result. The only way to end a test that has not concluded \u2014 and it OVERRIDES a verdict if there is one, throwing that finding away. Do not pass it by reflex.",
|
|
34136
|
+
required: false
|
|
34137
|
+
}
|
|
34138
|
+
},
|
|
34139
|
+
run: async ({ args }) => {
|
|
34140
|
+
try {
|
|
34141
|
+
const response = await apiPost("/api/experiments/finish", {
|
|
34142
|
+
experimentId: String(args.id),
|
|
34143
|
+
...args.abandon === true ? { abandon: true } : {}
|
|
34144
|
+
});
|
|
34145
|
+
writeJsonEnvelope({
|
|
34146
|
+
ok: true,
|
|
34147
|
+
data: response.data,
|
|
34148
|
+
hints: [
|
|
34149
|
+
`Staged. All the traffic goes to the ${response.data.survivor === "variant" ? "alternative" : "original"} page when this session is published.`,
|
|
34150
|
+
...response.data.discardedVerdict ? [
|
|
34151
|
+
`You abandoned a test that HAD concluded (${response.data.discardedVerdict}). The original page keeps the traffic and no result is recorded \u2014 the finding is gone. If that was not what you meant, run this again without --abandon.`
|
|
34152
|
+
] : [],
|
|
34153
|
+
"If the alternative won and you want it to become the page itself, that is an ordinary page edit afterwards \u2014 copy its content over and delete the alternative."
|
|
34154
|
+
]
|
|
34155
|
+
});
|
|
34156
|
+
} catch (error) {
|
|
34157
|
+
reportApiError(error);
|
|
34158
|
+
}
|
|
34159
|
+
}
|
|
34160
|
+
});
|
|
34161
|
+
var experimentCommand = defineCommand112({
|
|
34162
|
+
meta: {
|
|
34163
|
+
name: "experiment",
|
|
34164
|
+
description: "Run an A/B test between a landing page and an alternative version of it, and be told which one is better. Always `plan` first: most pages do not get enough traffic to settle most questions, and a test that cannot conclude still produces a number somebody will act on."
|
|
34165
|
+
},
|
|
34166
|
+
subCommands: {
|
|
34167
|
+
plan: planCommand,
|
|
34168
|
+
start: startCommand,
|
|
34169
|
+
status: statusCommand4,
|
|
34170
|
+
finish: finishCommand
|
|
34171
|
+
}
|
|
34172
|
+
});
|
|
34173
|
+
|
|
33384
34174
|
// src/commands/flows/index.ts
|
|
33385
|
-
import { defineCommand as
|
|
34175
|
+
import { defineCommand as defineCommand117 } from "citty";
|
|
33386
34176
|
|
|
33387
34177
|
// src/commands/flows/add.ts
|
|
33388
34178
|
import { randomUUID } from "crypto";
|
|
33389
|
-
import { defineCommand as
|
|
34179
|
+
import { defineCommand as defineCommand113 } from "citty";
|
|
33390
34180
|
|
|
33391
34181
|
// src/commands/flows/shared.ts
|
|
33392
34182
|
import { existsSync as existsSync5, readdirSync as readdirSync2, readFileSync as readFileSync9, writeFileSync as writeFileSync3 } from "fs";
|
|
@@ -33439,12 +34229,12 @@ function collectSideEffects(tree) {
|
|
|
33439
34229
|
);
|
|
33440
34230
|
}
|
|
33441
34231
|
function readFlowTree(slug) {
|
|
33442
|
-
const
|
|
33443
|
-
if (!existsSync5(
|
|
34232
|
+
const path43 = join3(flowsDir(), slug, "_data.json");
|
|
34233
|
+
if (!existsSync5(path43)) {
|
|
33444
34234
|
failLocal(`No form "${slug}". Run "baker flows list" to see the forms in this workspace.`);
|
|
33445
34235
|
}
|
|
33446
34236
|
try {
|
|
33447
|
-
return JSON.parse(readFileSync9(
|
|
34237
|
+
return JSON.parse(readFileSync9(path43, "utf-8"));
|
|
33448
34238
|
} catch (error) {
|
|
33449
34239
|
failLocal(`Could not read form "${slug}": ${error instanceof Error ? error.message : String(error)}`);
|
|
33450
34240
|
}
|
|
@@ -33596,7 +34386,7 @@ registerSchema({
|
|
|
33596
34386
|
parent: { type: "string", description: "Parent node id. Omit to append to the root's children.", required: false }
|
|
33597
34387
|
}
|
|
33598
34388
|
});
|
|
33599
|
-
var addNodeCommand =
|
|
34389
|
+
var addNodeCommand = defineCommand113({
|
|
33600
34390
|
meta: {
|
|
33601
34391
|
name: "add-node",
|
|
33602
34392
|
description: 'Add a step to a form, with the skeleton its type requires. Example: baker flows add-node contact --type customForm --name "Contact Info"'
|
|
@@ -33692,7 +34482,7 @@ registerSchema({
|
|
|
33692
34482
|
}
|
|
33693
34483
|
}
|
|
33694
34484
|
});
|
|
33695
|
-
var addSideEffectCommand =
|
|
34485
|
+
var addSideEffectCommand = defineCommand113({
|
|
33696
34486
|
meta: {
|
|
33697
34487
|
name: "add-side-effect",
|
|
33698
34488
|
description: "Add a side effect to a step, with the skeleton its type requires. Example: baker flows add-side-effect contact --node <id> --type googleSpreadsheet"
|
|
@@ -33774,7 +34564,7 @@ var addSideEffectCommand = defineCommand112({
|
|
|
33774
34564
|
|
|
33775
34565
|
// src/commands/flows/map.ts
|
|
33776
34566
|
import { readFileSync as readFileSync11 } from "fs";
|
|
33777
|
-
import { defineCommand as
|
|
34567
|
+
import { defineCommand as defineCommand114 } from "citty";
|
|
33778
34568
|
|
|
33779
34569
|
// src/commands/flows/value-expression.ts
|
|
33780
34570
|
import { existsSync as existsSync6, readFileSync as readFileSync10 } from "fs";
|
|
@@ -33836,10 +34626,10 @@ function parseValueExpression(raw) {
|
|
|
33836
34626
|
return parts.map(parsePart);
|
|
33837
34627
|
}
|
|
33838
34628
|
function trackingFieldIds() {
|
|
33839
|
-
const
|
|
33840
|
-
if (!existsSync6(
|
|
34629
|
+
const path43 = join4(flowsDir(), "..", "tracking.ts");
|
|
34630
|
+
if (!existsSync6(path43)) return null;
|
|
33841
34631
|
try {
|
|
33842
|
-
const source = readFileSync10(
|
|
34632
|
+
const source = readFileSync10(path43, "utf-8");
|
|
33843
34633
|
const block2 = source.match(/TRACKING_FIELD_IDS\s*=\s*\[([\s\S]*?)\]\s*as const/)?.[1];
|
|
33844
34634
|
if (!block2) return null;
|
|
33845
34635
|
const ids = [...block2.matchAll(/"(tracking\.[a-z0-9_]+)"/g)].map((match) => match[1]);
|
|
@@ -34391,13 +35181,13 @@ function specsFromFile(parsed) {
|
|
|
34391
35181
|
return `${destField}${type}=${entry?.value ?? ""}`;
|
|
34392
35182
|
});
|
|
34393
35183
|
}
|
|
34394
|
-
function readSpecFile(
|
|
35184
|
+
function readSpecFile(path43) {
|
|
34395
35185
|
let raw;
|
|
34396
35186
|
try {
|
|
34397
|
-
raw =
|
|
35187
|
+
raw = path43 === "-" ? readFileSync11(0, "utf-8") : readFileSync11(path43, "utf-8");
|
|
34398
35188
|
} catch (error) {
|
|
34399
35189
|
refuse(
|
|
34400
|
-
`Could not read ${
|
|
35190
|
+
`Could not read ${path43 === "-" ? "the mapping from stdin" : `"${path43}"`}: ${error instanceof Error ? error.message : String(error)}`
|
|
34401
35191
|
);
|
|
34402
35192
|
}
|
|
34403
35193
|
let parsed;
|
|
@@ -34405,7 +35195,7 @@ function readSpecFile(path40) {
|
|
|
34405
35195
|
parsed = JSON.parse(raw);
|
|
34406
35196
|
} catch (error) {
|
|
34407
35197
|
refuse(
|
|
34408
|
-
`${
|
|
35198
|
+
`${path43 === "-" ? "stdin" : `"${path43}"`} is not valid JSON: ${error instanceof Error ? error.message : String(error)}`,
|
|
34409
35199
|
'Expected { "map": { "<destField>": "<value>", \u2026 } }'
|
|
34410
35200
|
);
|
|
34411
35201
|
}
|
|
@@ -34547,7 +35337,7 @@ async function mapOptions(args, fromFile, into) {
|
|
|
34547
35337
|
destinationFields: hubspotForm ? await hubspotFieldCatalog(hubspotForm) : void 0
|
|
34548
35338
|
};
|
|
34549
35339
|
}
|
|
34550
|
-
var mapCommand2 =
|
|
35340
|
+
var mapCommand2 = defineCommand114({
|
|
34551
35341
|
meta: {
|
|
34552
35342
|
name: "map",
|
|
34553
35343
|
description: "Map form fields onto any value a side effect sends \u2014 never hand-write a mapping. Start here: baker flows map contact --from map.json --write"
|
|
@@ -34645,7 +35435,7 @@ var mapCommand2 = defineCommand113({
|
|
|
34645
35435
|
});
|
|
34646
35436
|
|
|
34647
35437
|
// src/commands/flows/normalize.ts
|
|
34648
|
-
import { defineCommand as
|
|
35438
|
+
import { defineCommand as defineCommand115 } from "citty";
|
|
34649
35439
|
var OPTIONAL_STRINGS = ["oauthProviderId"];
|
|
34650
35440
|
var ARRAY_FIELDS = [
|
|
34651
35441
|
"fieldMapping",
|
|
@@ -34659,18 +35449,18 @@ var ARRAY_FIELDS = [
|
|
|
34659
35449
|
"tagIds"
|
|
34660
35450
|
];
|
|
34661
35451
|
var ARRAY_OWNERS = ["", "body"];
|
|
34662
|
-
function dropUnsetOptionals(sideEffect,
|
|
35452
|
+
function dropUnsetOptionals(sideEffect, path43) {
|
|
34663
35453
|
return OPTIONAL_STRINGS.flatMap((key) => {
|
|
34664
35454
|
if (!(key in sideEffect) || sideEffect[key] !== null && sideEffect[key] !== "") return [];
|
|
34665
35455
|
delete sideEffect[key];
|
|
34666
|
-
return [{ path:
|
|
35456
|
+
return [{ path: path43, change: `dropped \`${key}\` (an optional string is absent, never null)` }];
|
|
34667
35457
|
});
|
|
34668
35458
|
}
|
|
34669
|
-
function fillNulledArrays(target, prefix,
|
|
35459
|
+
function fillNulledArrays(target, prefix, path43) {
|
|
34670
35460
|
return ARRAY_FIELDS.flatMap((key) => {
|
|
34671
35461
|
if (!(key in target) || target[key] !== null) return [];
|
|
34672
35462
|
target[key] = [];
|
|
34673
|
-
return [{ path:
|
|
35463
|
+
return [{ path: path43, change: `\`${prefix}${key}: null\` \u2192 \`[]\`` }];
|
|
34674
35464
|
});
|
|
34675
35465
|
}
|
|
34676
35466
|
function sideEffectsOf(node) {
|
|
@@ -34680,13 +35470,13 @@ function sideEffectsOf(node) {
|
|
|
34680
35470
|
);
|
|
34681
35471
|
}
|
|
34682
35472
|
function normalizeSideEffect(sideEffect, where) {
|
|
34683
|
-
const
|
|
35473
|
+
const path43 = `${where} \u2192 ${String(sideEffect.id ?? "side effect")}`;
|
|
34684
35474
|
const arrays = ARRAY_OWNERS.flatMap((owner) => {
|
|
34685
35475
|
const target = owner ? sideEffect[owner] : sideEffect;
|
|
34686
35476
|
if (!target || typeof target !== "object") return [];
|
|
34687
|
-
return fillNulledArrays(target, owner ? `${owner}.` : "",
|
|
35477
|
+
return fillNulledArrays(target, owner ? `${owner}.` : "", path43);
|
|
34688
35478
|
});
|
|
34689
|
-
return [...dropUnsetOptionals(sideEffect,
|
|
35479
|
+
return [...dropUnsetOptionals(sideEffect, path43), ...arrays];
|
|
34690
35480
|
}
|
|
34691
35481
|
function normalizeFlowTree(tree) {
|
|
34692
35482
|
const changes = [];
|
|
@@ -34714,7 +35504,7 @@ registerSchema({
|
|
|
34714
35504
|
}
|
|
34715
35505
|
}
|
|
34716
35506
|
});
|
|
34717
|
-
var normalizeCommand =
|
|
35507
|
+
var normalizeCommand = defineCommand115({
|
|
34718
35508
|
meta: {
|
|
34719
35509
|
name: "normalize",
|
|
34720
35510
|
description: "Repair the shapes a form file is allowed to get slightly wrong. Example: baker flows normalize contact --write"
|
|
@@ -34745,7 +35535,7 @@ var normalizeCommand = defineCommand114({
|
|
|
34745
35535
|
});
|
|
34746
35536
|
|
|
34747
35537
|
// src/commands/flows/schema.ts
|
|
34748
|
-
import { defineCommand as
|
|
35538
|
+
import { defineCommand as defineCommand116 } from "citty";
|
|
34749
35539
|
registerSchema({
|
|
34750
35540
|
command: "flows.schema",
|
|
34751
35541
|
description: "The shape of one node or side-effect type: a pasteable skeleton, who fills each part, and the reference file with the craft. Start here before authoring anything in a form's _data.json.",
|
|
@@ -34770,7 +35560,7 @@ function triggerHints(triggers) {
|
|
|
34770
35560
|
`A side effect on this step carries \`triggerId: "${triggers.completion}"\`, and \`baker flows add-side-effect\` writes it for you. The trigger belongs to the STEP, not to the side-effect type: one that does not match is valid, ships, and never runs.` + (others.length > 0 ? ` Other moments this step raises: ${others.join(", ")}.` : "")
|
|
34771
35561
|
];
|
|
34772
35562
|
}
|
|
34773
|
-
var schemaCommand =
|
|
35563
|
+
var schemaCommand = defineCommand116({
|
|
34774
35564
|
meta: {
|
|
34775
35565
|
name: "schema",
|
|
34776
35566
|
description: "Show a node or side-effect type's shape. Start here: baker flows schema node customForm \xB7 baker flows schema side-effect googleSpreadsheet \xB7 baker flows schema (lists both)"
|
|
@@ -34904,7 +35694,7 @@ function displayName(slug) {
|
|
|
34904
35694
|
const name = tree.displayName;
|
|
34905
35695
|
return typeof name === "string" && name.trim() ? name.trim() : slug;
|
|
34906
35696
|
}
|
|
34907
|
-
var listCommand12 =
|
|
35697
|
+
var listCommand12 = defineCommand117({
|
|
34908
35698
|
meta: {
|
|
34909
35699
|
name: "list",
|
|
34910
35700
|
description: "List Forms in this workspace with the count of confidential fields still needing setup. Example: baker flows list"
|
|
@@ -34925,7 +35715,7 @@ var listCommand12 = defineCommand116({
|
|
|
34925
35715
|
});
|
|
34926
35716
|
}
|
|
34927
35717
|
});
|
|
34928
|
-
var showCommand3 =
|
|
35718
|
+
var showCommand3 = defineCommand117({
|
|
34929
35719
|
meta: {
|
|
34930
35720
|
name: "show",
|
|
34931
35721
|
description: "Show a Form's confidential fields and their configuration status (never secret values). Example: baker flows show contact"
|
|
@@ -34953,7 +35743,7 @@ var showCommand3 = defineCommand116({
|
|
|
34953
35743
|
});
|
|
34954
35744
|
}
|
|
34955
35745
|
});
|
|
34956
|
-
var flowsCommand =
|
|
35746
|
+
var flowsCommand = defineCommand117({
|
|
34957
35747
|
meta: {
|
|
34958
35748
|
name: "flows",
|
|
34959
35749
|
description: `Read and shape this workspace's Forms (flows).
|
|
@@ -34987,10 +35777,10 @@ Full guide: __tooling__/docs/tools/baker/flows.md`
|
|
|
34987
35777
|
});
|
|
34988
35778
|
|
|
34989
35779
|
// src/commands/ga4/index.ts
|
|
34990
|
-
import { defineCommand as
|
|
35780
|
+
import { defineCommand as defineCommand124 } from "citty";
|
|
34991
35781
|
|
|
34992
35782
|
// src/commands/ga4/audit.ts
|
|
34993
|
-
import { defineCommand as
|
|
35783
|
+
import { defineCommand as defineCommand118 } from "citty";
|
|
34994
35784
|
|
|
34995
35785
|
// src/commands/ga4/resolve.ts
|
|
34996
35786
|
async function fetchProperties(useCache = true) {
|
|
@@ -35056,7 +35846,7 @@ registerSchema({
|
|
|
35056
35846
|
"no-cache": { type: "boolean", description: "Skip cache, hit API directly", required: false }
|
|
35057
35847
|
}
|
|
35058
35848
|
});
|
|
35059
|
-
var auditCommand2 =
|
|
35849
|
+
var auditCommand2 = defineCommand118({
|
|
35060
35850
|
meta: {
|
|
35061
35851
|
name: "audit",
|
|
35062
35852
|
description: `Run all GA4 admin health checks. Returns property config with playbook warnings.
|
|
@@ -35111,7 +35901,7 @@ Examples:
|
|
|
35111
35901
|
});
|
|
35112
35902
|
|
|
35113
35903
|
// src/commands/ga4/config.ts
|
|
35114
|
-
import { defineCommand as
|
|
35904
|
+
import { defineCommand as defineCommand119 } from "citty";
|
|
35115
35905
|
|
|
35116
35906
|
// src/commands/ga4/shared.ts
|
|
35117
35907
|
import { readFileSync as readFileSync12 } from "fs";
|
|
@@ -35224,10 +36014,10 @@ async function stageOps(ops) {
|
|
|
35224
36014
|
handleError2(err);
|
|
35225
36015
|
}
|
|
35226
36016
|
}
|
|
35227
|
-
async function draftAction2(
|
|
36017
|
+
async function draftAction2(path43, body, chat) {
|
|
35228
36018
|
const chatId = resolveChatId(chat);
|
|
35229
36019
|
try {
|
|
35230
|
-
const data = await apiPost(
|
|
36020
|
+
const data = await apiPost(path43, { chatId, ...body });
|
|
35231
36021
|
writeJsonEnvelope({ ok: true, data });
|
|
35232
36022
|
return data;
|
|
35233
36023
|
} catch (err) {
|
|
@@ -35307,7 +36097,7 @@ function configHints(config) {
|
|
|
35307
36097
|
}
|
|
35308
36098
|
return hints;
|
|
35309
36099
|
}
|
|
35310
|
-
var configCommand =
|
|
36100
|
+
var configCommand = defineCommand119({
|
|
35311
36101
|
meta: {
|
|
35312
36102
|
name: "config",
|
|
35313
36103
|
description: `Read how the property is configured to measure \u2014 key events, custom definitions, custom events, retention
|
|
@@ -35339,7 +36129,7 @@ Examples:
|
|
|
35339
36129
|
});
|
|
35340
36130
|
|
|
35341
36131
|
// src/commands/ga4/draft.ts
|
|
35342
|
-
import { defineCommand as
|
|
36132
|
+
import { defineCommand as defineCommand120 } from "citty";
|
|
35343
36133
|
registerSchema({
|
|
35344
36134
|
command: "ga4.draft",
|
|
35345
36135
|
description: "Review and undo the Google Analytics changes staged on this chat. Use `list` to see everything staged, `show` to inspect one change in full before the chat completes, `amend` to correct one in place, and `remove`/`clear` to drop them. `list` and `show` take --chat <id> to read an earlier chat's changes instead.",
|
|
@@ -35348,13 +36138,13 @@ registerSchema({
|
|
|
35348
36138
|
chat: { type: "string", description: CHAT_READ_ARG.description, required: false }
|
|
35349
36139
|
}
|
|
35350
36140
|
});
|
|
35351
|
-
var draftCommand3 =
|
|
36141
|
+
var draftCommand3 = defineCommand120({
|
|
35352
36142
|
meta: {
|
|
35353
36143
|
name: "draft",
|
|
35354
36144
|
description: "List, show, amend, remove or clear the Google Analytics changes staged on this chat. `list` and `show` take --chat <id> to read an earlier chat's changes instead."
|
|
35355
36145
|
},
|
|
35356
36146
|
subCommands: {
|
|
35357
|
-
list:
|
|
36147
|
+
list: defineCommand120({
|
|
35358
36148
|
meta: { name: "list", description: "Review everything staged on this chat (--json for the raw envelope)" },
|
|
35359
36149
|
args: {
|
|
35360
36150
|
json: { type: "boolean", description: "Print the raw JSON envelope", required: false },
|
|
@@ -35364,7 +36154,7 @@ var draftCommand3 = defineCommand119({
|
|
|
35364
36154
|
await draftList(args.json === true, args.chat);
|
|
35365
36155
|
}
|
|
35366
36156
|
}),
|
|
35367
|
-
show:
|
|
36157
|
+
show: defineCommand120({
|
|
35368
36158
|
meta: {
|
|
35369
36159
|
name: "show",
|
|
35370
36160
|
description: "Print the full staged payload for one change, alongside how the property looks today \u2014 the receipt to verify it before the chat completes (never truncated)."
|
|
@@ -35381,7 +36171,7 @@ var draftCommand3 = defineCommand119({
|
|
|
35381
36171
|
);
|
|
35382
36172
|
}
|
|
35383
36173
|
}),
|
|
35384
|
-
amend:
|
|
36174
|
+
amend: defineCommand120({
|
|
35385
36175
|
meta: {
|
|
35386
36176
|
name: "amend",
|
|
35387
36177
|
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-runs every check. Use this instead of remove + re-create."
|
|
@@ -35398,7 +36188,7 @@ var draftCommand3 = defineCommand119({
|
|
|
35398
36188
|
});
|
|
35399
36189
|
}
|
|
35400
36190
|
}),
|
|
35401
|
-
remove:
|
|
36191
|
+
remove: defineCommand120({
|
|
35402
36192
|
meta: { name: "remove", description: "Remove one staged change" },
|
|
35403
36193
|
args: { ref: { type: "positional", description: "Staged ref or target id", required: false } },
|
|
35404
36194
|
run: async ({ args }) => {
|
|
@@ -35407,7 +36197,7 @@ var draftCommand3 = defineCommand119({
|
|
|
35407
36197
|
});
|
|
35408
36198
|
}
|
|
35409
36199
|
}),
|
|
35410
|
-
clear:
|
|
36200
|
+
clear: defineCommand120({
|
|
35411
36201
|
meta: { name: "clear", description: "Discard all Google Analytics changes staged on this chat" },
|
|
35412
36202
|
run: async () => {
|
|
35413
36203
|
await draftAction2("/api/ga4/draft/clear", {});
|
|
@@ -35417,7 +36207,7 @@ var draftCommand3 = defineCommand119({
|
|
|
35417
36207
|
});
|
|
35418
36208
|
|
|
35419
36209
|
// src/commands/ga4/properties.ts
|
|
35420
|
-
import { defineCommand as
|
|
36210
|
+
import { defineCommand as defineCommand121 } from "citty";
|
|
35421
36211
|
registerSchema({
|
|
35422
36212
|
command: "ga4.properties",
|
|
35423
36213
|
description: "List the GA4 properties this company connected \u2014 there can be several, and every one of them is yours to query. Returns the property IDs the query and audit commands take. Run this first to find property IDs.",
|
|
@@ -35433,7 +36223,7 @@ function propertyHints(properties) {
|
|
|
35433
36223
|
resources: properties.map((property) => ({ id: property.externalId, label: property.name }))
|
|
35434
36224
|
});
|
|
35435
36225
|
}
|
|
35436
|
-
var propertiesCommand =
|
|
36226
|
+
var propertiesCommand = defineCommand121({
|
|
35437
36227
|
meta: {
|
|
35438
36228
|
name: "properties",
|
|
35439
36229
|
description: `List accessible GA4 properties.
|
|
@@ -35483,7 +36273,7 @@ Examples:
|
|
|
35483
36273
|
// src/commands/ga4/query.ts
|
|
35484
36274
|
import { appendFileSync as appendFileSync2, existsSync as existsSync7, readFileSync as readFileSync13, writeFileSync as writeFileSync4 } from "fs";
|
|
35485
36275
|
import { resolve as resolve2 } from "path";
|
|
35486
|
-
import { defineCommand as
|
|
36276
|
+
import { defineCommand as defineCommand122 } from "citty";
|
|
35487
36277
|
|
|
35488
36278
|
// src/commands/ga4/presets.ts
|
|
35489
36279
|
var GA4_PRESETS = [
|
|
@@ -35618,7 +36408,7 @@ function handleError3(err) {
|
|
|
35618
36408
|
});
|
|
35619
36409
|
process.exit(1);
|
|
35620
36410
|
}
|
|
35621
|
-
var queryCommand2 =
|
|
36411
|
+
var queryCommand2 = defineCommand122({
|
|
35622
36412
|
meta: {
|
|
35623
36413
|
name: "query",
|
|
35624
36414
|
description: `Run GA4 Data API reports. Preset-first with free-form escape hatch.
|
|
@@ -35689,7 +36479,7 @@ Free-form (escape hatch):
|
|
|
35689
36479
|
});
|
|
35690
36480
|
|
|
35691
36481
|
// src/commands/ga4/write-commands.ts
|
|
35692
|
-
import { defineCommand as
|
|
36482
|
+
import { defineCommand as defineCommand123 } from "citty";
|
|
35693
36483
|
var PROPERTY_ARG_DESCRIPTION = "GA4 property id (optional only when one property is connected \u2014 run `baker ga4 properties`)";
|
|
35694
36484
|
var propertyArg = { type: "string", description: PROPERTY_ARG_DESCRIPTION, required: false };
|
|
35695
36485
|
function createJsonDescription(noun) {
|
|
@@ -35707,7 +36497,7 @@ registerSchema({
|
|
|
35707
36497
|
"property-id": { type: "string", description: PROPERTY_ARG_DESCRIPTION, required: false }
|
|
35708
36498
|
}
|
|
35709
36499
|
});
|
|
35710
|
-
var keyEventCommand =
|
|
36500
|
+
var keyEventCommand = defineCommand123({
|
|
35711
36501
|
meta: {
|
|
35712
36502
|
name: "key-event",
|
|
35713
36503
|
description: `Stage key-event (conversion) changes
|
|
@@ -35720,7 +36510,7 @@ Examples:
|
|
|
35720
36510
|
baker ga4 key-event delete 4185`
|
|
35721
36511
|
},
|
|
35722
36512
|
subCommands: {
|
|
35723
|
-
create:
|
|
36513
|
+
create: defineCommand123({
|
|
35724
36514
|
meta: {
|
|
35725
36515
|
name: "create",
|
|
35726
36516
|
description: "Stage a new key event. Needs eventName and countingMethod (ONCE_PER_EVENT or ONCE_PER_SESSION); defaultValue sets a monetary value when the event does not send one."
|
|
@@ -35741,7 +36531,7 @@ Examples:
|
|
|
35741
36531
|
);
|
|
35742
36532
|
}
|
|
35743
36533
|
}),
|
|
35744
|
-
update:
|
|
36534
|
+
update: defineCommand123({
|
|
35745
36535
|
meta: {
|
|
35746
36536
|
name: "update",
|
|
35747
36537
|
description: "Stage a change to an existing key event (pass its id). Only countingMethod and defaultValue can change \u2014 Google will not rename a key event."
|
|
@@ -35761,7 +36551,7 @@ Examples:
|
|
|
35761
36551
|
});
|
|
35762
36552
|
}
|
|
35763
36553
|
}),
|
|
35764
|
-
delete:
|
|
36554
|
+
delete: defineCommand123({
|
|
35765
36555
|
meta: {
|
|
35766
36556
|
name: "delete",
|
|
35767
36557
|
description: "Stage removing a key event, so the event stops counting as a conversion. The event itself keeps being collected. Irreversible once the chat completes \u2014 confirm with the user first."
|
|
@@ -35809,7 +36599,7 @@ for (const { kind, noun, createHint } of DEFINITIONS) {
|
|
|
35809
36599
|
}
|
|
35810
36600
|
function definitionCommand(definition) {
|
|
35811
36601
|
const { command, kind, noun, example } = definition;
|
|
35812
|
-
return
|
|
36602
|
+
return defineCommand123({
|
|
35813
36603
|
meta: {
|
|
35814
36604
|
name: command,
|
|
35815
36605
|
description: `Stage ${noun} changes
|
|
@@ -35821,7 +36611,7 @@ Examples:
|
|
|
35821
36611
|
baker ga4 ${command} archive 12`
|
|
35822
36612
|
},
|
|
35823
36613
|
subCommands: {
|
|
35824
|
-
create:
|
|
36614
|
+
create: defineCommand123({
|
|
35825
36615
|
meta: { name: "create", description: `Stage a new ${noun} (or a JSON array of them, staged together)` },
|
|
35826
36616
|
args: {
|
|
35827
36617
|
json: { type: "string", description: createJsonDescription(noun), required: false },
|
|
@@ -35839,7 +36629,7 @@ Examples:
|
|
|
35839
36629
|
);
|
|
35840
36630
|
}
|
|
35841
36631
|
}),
|
|
35842
|
-
update:
|
|
36632
|
+
update: defineCommand123({
|
|
35843
36633
|
meta: {
|
|
35844
36634
|
name: "update",
|
|
35845
36635
|
description: `Stage a change to an existing ${noun} (pass its id). The parameter name and scope cannot change \u2014 archive and recreate for that.`
|
|
@@ -35859,7 +36649,7 @@ Examples:
|
|
|
35859
36649
|
});
|
|
35860
36650
|
}
|
|
35861
36651
|
}),
|
|
35862
|
-
archive:
|
|
36652
|
+
archive: defineCommand123({
|
|
35863
36653
|
meta: {
|
|
35864
36654
|
name: "archive",
|
|
35865
36655
|
description: `Stage archiving a ${noun} (pass its id). It stops collecting and leaves reporting; past data stays. Irreversible once the chat completes \u2014 confirm with the user first.`
|
|
@@ -35904,7 +36694,7 @@ var dataStreamArg = {
|
|
|
35904
36694
|
function withStream(args) {
|
|
35905
36695
|
return typeof args["data-stream"] === "string" ? { dataStream: args["data-stream"] } : {};
|
|
35906
36696
|
}
|
|
35907
|
-
var customEventCommand =
|
|
36697
|
+
var customEventCommand = defineCommand123({
|
|
35908
36698
|
meta: {
|
|
35909
36699
|
name: "custom-event",
|
|
35910
36700
|
description: `Stage custom events \u2014 new events built from events the site already sends
|
|
@@ -35915,7 +36705,7 @@ Examples:
|
|
|
35915
36705
|
baker ga4 custom-event delete 7`
|
|
35916
36706
|
},
|
|
35917
36707
|
subCommands: {
|
|
35918
|
-
create:
|
|
36708
|
+
create: defineCommand123({
|
|
35919
36709
|
meta: {
|
|
35920
36710
|
name: "create",
|
|
35921
36711
|
description: "Stage a new custom event. Conditions match the source event: use field `event_name` to match the event itself, or any parameter name to match its value."
|
|
@@ -35938,7 +36728,7 @@ Examples:
|
|
|
35938
36728
|
);
|
|
35939
36729
|
}
|
|
35940
36730
|
}),
|
|
35941
|
-
update:
|
|
36731
|
+
update: defineCommand123({
|
|
35942
36732
|
meta: { name: "update", description: "Stage a change to an existing custom event (pass its id)" },
|
|
35943
36733
|
args: {
|
|
35944
36734
|
id: { type: "positional", description: "Custom event rule id", required: false },
|
|
@@ -35957,7 +36747,7 @@ Examples:
|
|
|
35957
36747
|
});
|
|
35958
36748
|
}
|
|
35959
36749
|
}),
|
|
35960
|
-
delete:
|
|
36750
|
+
delete: defineCommand123({
|
|
35961
36751
|
meta: {
|
|
35962
36752
|
name: "delete",
|
|
35963
36753
|
description: "Stage removing a custom event (pass its id). The event stops being created from then on; data already collected under it stays. Confirm with the user first."
|
|
@@ -36012,7 +36802,7 @@ function booleanArg(input, flag) {
|
|
|
36012
36802
|
if (input === "false") return false;
|
|
36013
36803
|
return failValidation3(`--${flag} must be true or false`);
|
|
36014
36804
|
}
|
|
36015
|
-
var dataRetentionCommand =
|
|
36805
|
+
var dataRetentionCommand = defineCommand123({
|
|
36016
36806
|
meta: {
|
|
36017
36807
|
name: "data-retention",
|
|
36018
36808
|
description: `Stage a change to how long Google Analytics keeps data
|
|
@@ -36022,7 +36812,7 @@ Examples:
|
|
|
36022
36812
|
baker ga4 data-retention set --event-data 14 --user-data 14 --reset-on-activity true`
|
|
36023
36813
|
},
|
|
36024
36814
|
subCommands: {
|
|
36025
|
-
set:
|
|
36815
|
+
set: defineCommand123({
|
|
36026
36816
|
meta: { name: "set", description: "Stage the retention window (months)" },
|
|
36027
36817
|
args: {
|
|
36028
36818
|
"event-data": { type: "string", description: "2, 14, 26, 38 or 50", required: false },
|
|
@@ -36058,7 +36848,7 @@ registerSchema({
|
|
|
36058
36848
|
"property-id": { type: "string", description: PROPERTY_ARG_DESCRIPTION, required: false }
|
|
36059
36849
|
}
|
|
36060
36850
|
});
|
|
36061
|
-
var audienceCommand =
|
|
36851
|
+
var audienceCommand = defineCommand123({
|
|
36062
36852
|
meta: {
|
|
36063
36853
|
name: "audience",
|
|
36064
36854
|
description: `Stage audience changes (saved groups of users, for retargeting and analysis)
|
|
@@ -36090,7 +36880,7 @@ Examples:
|
|
|
36090
36880
|
baker ga4 audience archive 8813`
|
|
36091
36881
|
},
|
|
36092
36882
|
subCommands: {
|
|
36093
|
-
create:
|
|
36883
|
+
create: defineCommand123({
|
|
36094
36884
|
meta: { name: "create", description: createJsonDescription("audience") },
|
|
36095
36885
|
args: {
|
|
36096
36886
|
json: { type: "string", description: createJsonDescription("audience"), required: false },
|
|
@@ -36108,7 +36898,7 @@ Examples:
|
|
|
36108
36898
|
);
|
|
36109
36899
|
}
|
|
36110
36900
|
}),
|
|
36111
|
-
update:
|
|
36901
|
+
update: defineCommand123({
|
|
36112
36902
|
meta: {
|
|
36113
36903
|
name: "update",
|
|
36114
36904
|
description: "Stage a new name or description (pass the audience id). Who is in it cannot be changed."
|
|
@@ -36129,7 +36919,7 @@ Examples:
|
|
|
36129
36919
|
});
|
|
36130
36920
|
}
|
|
36131
36921
|
}),
|
|
36132
|
-
archive:
|
|
36922
|
+
archive: defineCommand123({
|
|
36133
36923
|
meta: {
|
|
36134
36924
|
name: "archive",
|
|
36135
36925
|
description: "Stage retiring an audience. Cannot be undone \u2014 the users it gathered do not come back."
|
|
@@ -36158,7 +36948,7 @@ registerSchema({
|
|
|
36158
36948
|
"property-id": { type: "string", description: PROPERTY_ARG_DESCRIPTION, required: false }
|
|
36159
36949
|
}
|
|
36160
36950
|
});
|
|
36161
|
-
var propertyCommand =
|
|
36951
|
+
var propertyCommand = defineCommand123({
|
|
36162
36952
|
meta: {
|
|
36163
36953
|
name: "property",
|
|
36164
36954
|
description: `Stage a change to the property's own settings
|
|
@@ -36168,7 +36958,7 @@ Examples:
|
|
|
36168
36958
|
baker ga4 property set --name "Acme \u2014 Web" --industry REAL_ESTATE`
|
|
36169
36959
|
},
|
|
36170
36960
|
subCommands: {
|
|
36171
|
-
set:
|
|
36961
|
+
set: defineCommand123({
|
|
36172
36962
|
meta: { name: "set", description: "Stage the property name and/or industry category" },
|
|
36173
36963
|
args: {
|
|
36174
36964
|
name: { type: "string", description: "New property display name", required: false },
|
|
@@ -36208,7 +36998,7 @@ registerSchema({
|
|
|
36208
36998
|
"property-id": { type: "string", description: PROPERTY_ARG_DESCRIPTION, required: false }
|
|
36209
36999
|
}
|
|
36210
37000
|
});
|
|
36211
|
-
var adsLinkCommand =
|
|
37001
|
+
var adsLinkCommand = defineCommand123({
|
|
36212
37002
|
meta: {
|
|
36213
37003
|
name: "ads-link",
|
|
36214
37004
|
description: `Stage linking this property to a Google Ads account
|
|
@@ -36223,7 +37013,7 @@ Examples:
|
|
|
36223
37013
|
baker ga4 ads-link delete L1234`
|
|
36224
37014
|
},
|
|
36225
37015
|
subCommands: {
|
|
36226
|
-
create:
|
|
37016
|
+
create: defineCommand123({
|
|
36227
37017
|
meta: { name: "create", description: "Stage linking the property to a Google Ads account" },
|
|
36228
37018
|
args: {
|
|
36229
37019
|
"customer-id": { type: "string", description: "Google Ads customer id (123-456-7890)", required: false },
|
|
@@ -36242,7 +37032,7 @@ Examples:
|
|
|
36242
37032
|
await stageOp3({ kind: "ga4.googleAdsLink.create", payload, ...withProperty(args) });
|
|
36243
37033
|
}
|
|
36244
37034
|
}),
|
|
36245
|
-
update:
|
|
37035
|
+
update: defineCommand123({
|
|
36246
37036
|
meta: {
|
|
36247
37037
|
name: "update",
|
|
36248
37038
|
description: "Stage the ads-personalisation setting on an existing link (pass its id)"
|
|
@@ -36264,7 +37054,7 @@ Examples:
|
|
|
36264
37054
|
});
|
|
36265
37055
|
}
|
|
36266
37056
|
}),
|
|
36267
|
-
delete:
|
|
37057
|
+
delete: defineCommand123({
|
|
36268
37058
|
meta: {
|
|
36269
37059
|
name: "delete",
|
|
36270
37060
|
description: "Stage unlinking. Stops conversion imports and strands every audience on this property."
|
|
@@ -36315,7 +37105,7 @@ var MEASUREMENT_FLAGS = {
|
|
|
36315
37105
|
"page-changes": "pageChangesEnabled",
|
|
36316
37106
|
"form-interactions": "formInteractionsEnabled"
|
|
36317
37107
|
};
|
|
36318
|
-
var enhancedMeasurementCommand =
|
|
37108
|
+
var enhancedMeasurementCommand = defineCommand123({
|
|
36319
37109
|
meta: {
|
|
36320
37110
|
name: "enhanced-measurement",
|
|
36321
37111
|
description: `Stage the automatic events GA4 collects on a website stream
|
|
@@ -36329,7 +37119,7 @@ Examples:
|
|
|
36329
37119
|
baker ga4 enhanced-measurement set --video false`
|
|
36330
37120
|
},
|
|
36331
37121
|
subCommands: {
|
|
36332
|
-
set:
|
|
37122
|
+
set: defineCommand123({
|
|
36333
37123
|
meta: { name: "set", description: "Stage which automatic events the website stream collects" },
|
|
36334
37124
|
args: {
|
|
36335
37125
|
scrolls: { type: "string", description: "true|false", required: false },
|
|
@@ -36372,7 +37162,7 @@ Examples:
|
|
|
36372
37162
|
});
|
|
36373
37163
|
|
|
36374
37164
|
// src/commands/ga4/index.ts
|
|
36375
|
-
var ga4Command =
|
|
37165
|
+
var ga4Command = defineCommand124({
|
|
36376
37166
|
meta: {
|
|
36377
37167
|
name: "ga4",
|
|
36378
37168
|
description: `Google Analytics 4. Report on a property, audit its config, and change what it measures.
|
|
@@ -36421,12 +37211,12 @@ Full guide: __tooling__/docs/tools/baker/ga4.md`
|
|
|
36421
37211
|
});
|
|
36422
37212
|
|
|
36423
37213
|
// src/commands/gsc/index.ts
|
|
36424
|
-
import { defineCommand as
|
|
37214
|
+
import { defineCommand as defineCommand128 } from "citty";
|
|
36425
37215
|
|
|
36426
37216
|
// src/commands/gsc/query.ts
|
|
36427
37217
|
import { appendFileSync as appendFileSync3, existsSync as existsSync8, readFileSync as readFileSync14, writeFileSync as writeFileSync5 } from "fs";
|
|
36428
37218
|
import { resolve as resolve3 } from "path";
|
|
36429
|
-
import { defineCommand as
|
|
37219
|
+
import { defineCommand as defineCommand125 } from "citty";
|
|
36430
37220
|
|
|
36431
37221
|
// src/commands/gsc/presets.ts
|
|
36432
37222
|
var GSC_PRESETS = [
|
|
@@ -36620,7 +37410,7 @@ function handleError4(err) {
|
|
|
36620
37410
|
});
|
|
36621
37411
|
process.exit(1);
|
|
36622
37412
|
}
|
|
36623
|
-
var queryCommand3 =
|
|
37413
|
+
var queryCommand3 = defineCommand125({
|
|
36624
37414
|
meta: {
|
|
36625
37415
|
name: "query",
|
|
36626
37416
|
description: `Run GSC Search Analytics queries. Preset-first with free-form escape hatch.
|
|
@@ -36698,7 +37488,7 @@ Free-form (escape hatch):
|
|
|
36698
37488
|
});
|
|
36699
37489
|
|
|
36700
37490
|
// src/commands/gsc/sitemaps.ts
|
|
36701
|
-
import { defineCommand as
|
|
37491
|
+
import { defineCommand as defineCommand126 } from "citty";
|
|
36702
37492
|
registerSchema({
|
|
36703
37493
|
command: "gsc.sitemaps",
|
|
36704
37494
|
description: "List sitemaps for a Search Console site. Check sitemap health and errors.",
|
|
@@ -36707,7 +37497,7 @@ registerSchema({
|
|
|
36707
37497
|
"no-cache": { type: "boolean", description: "Skip cache, hit API directly", required: false }
|
|
36708
37498
|
}
|
|
36709
37499
|
});
|
|
36710
|
-
var sitemapsCommand =
|
|
37500
|
+
var sitemapsCommand = defineCommand126({
|
|
36711
37501
|
meta: {
|
|
36712
37502
|
name: "sitemaps",
|
|
36713
37503
|
description: `List sitemaps for a site. Check health and errors.
|
|
@@ -36760,7 +37550,7 @@ Examples:
|
|
|
36760
37550
|
});
|
|
36761
37551
|
|
|
36762
37552
|
// src/commands/gsc/sites.ts
|
|
36763
|
-
import { defineCommand as
|
|
37553
|
+
import { defineCommand as defineCommand127 } from "citty";
|
|
36764
37554
|
registerSchema({
|
|
36765
37555
|
command: "gsc.sites",
|
|
36766
37556
|
description: "List the Search Console sites this company connected \u2014 there can be several, and every one of them is yours to query. Returns the site URLs the query and sitemaps commands take.",
|
|
@@ -36776,7 +37566,7 @@ function siteHints(sites) {
|
|
|
36776
37566
|
resources: sites.map((site) => ({ id: site.siteUrl, label: site.permissionLevel }))
|
|
36777
37567
|
});
|
|
36778
37568
|
}
|
|
36779
|
-
var sitesCommand =
|
|
37569
|
+
var sitesCommand = defineCommand127({
|
|
36780
37570
|
meta: {
|
|
36781
37571
|
name: "sites",
|
|
36782
37572
|
description: `List verified Search Console sites.
|
|
@@ -36824,7 +37614,7 @@ Examples:
|
|
|
36824
37614
|
});
|
|
36825
37615
|
|
|
36826
37616
|
// src/commands/gsc/index.ts
|
|
36827
|
-
var gscCommand =
|
|
37617
|
+
var gscCommand = defineCommand128({
|
|
36828
37618
|
meta: {
|
|
36829
37619
|
name: "gsc",
|
|
36830
37620
|
description: `Google Search Console commands. PPC-SEO arbitrage, brand halo analysis, negative keyword discovery.
|
|
@@ -36848,7 +37638,7 @@ Full guide: __tooling__/docs/tools/baker/gsc.md`
|
|
|
36848
37638
|
});
|
|
36849
37639
|
|
|
36850
37640
|
// src/commands/history/index.ts
|
|
36851
|
-
import { defineCommand as
|
|
37641
|
+
import { defineCommand as defineCommand129 } from "citty";
|
|
36852
37642
|
registerSchema({
|
|
36853
37643
|
command: "history.list",
|
|
36854
37644
|
description: "Start here: unified account history (audit log) \u2014 everything that changed on this account, newest first: publishes, chat lifecycle, backlog actions, team changes, setup links, tags, schedules, ad-platform writes, followed advertisers, media, creatives, reports, domains, and integrations. Use it to see what happened recently before planning work. Compact by default; add --full for raw metadata per entry.",
|
|
@@ -36903,7 +37693,7 @@ function parseBoundedInt2(raw, name, min, max) {
|
|
|
36903
37693
|
}
|
|
36904
37694
|
return value;
|
|
36905
37695
|
}
|
|
36906
|
-
var listCommand13 =
|
|
37696
|
+
var listCommand13 = defineCommand129({
|
|
36907
37697
|
meta: {
|
|
36908
37698
|
name: "list",
|
|
36909
37699
|
description: "List recent account changes (unified audit log), newest first."
|
|
@@ -36949,7 +37739,7 @@ var listCommand13 = defineCommand128({
|
|
|
36949
37739
|
}
|
|
36950
37740
|
}
|
|
36951
37741
|
});
|
|
36952
|
-
var historyCommand =
|
|
37742
|
+
var historyCommand = defineCommand129({
|
|
36953
37743
|
meta: {
|
|
36954
37744
|
name: "history",
|
|
36955
37745
|
description: `Unified account history (audit log): what changed, who did it, and when.
|
|
@@ -36959,7 +37749,7 @@ Full guide: __tooling__/docs/tools/baker/history.md`
|
|
|
36959
37749
|
});
|
|
36960
37750
|
|
|
36961
37751
|
// src/commands/hubspot/index.ts
|
|
36962
|
-
import { defineCommand as
|
|
37752
|
+
import { defineCommand as defineCommand130 } from "citty";
|
|
36963
37753
|
var EMBED_TYPES = ["legacy", "v4", "unknown"];
|
|
36964
37754
|
function failNotConnected(err) {
|
|
36965
37755
|
if (err instanceof ApiError && err.code === "FORBIDDEN" && err.message.includes(HUBSPOT_MISSING_GRANT_MARKER)) {
|
|
@@ -37113,7 +37903,7 @@ registerSchema({
|
|
|
37113
37903
|
}
|
|
37114
37904
|
}
|
|
37115
37905
|
});
|
|
37116
|
-
var formsListCommand =
|
|
37906
|
+
var formsListCommand = defineCommand130({
|
|
37117
37907
|
meta: {
|
|
37118
37908
|
name: "list",
|
|
37119
37909
|
description: "List HubSpot forms with embed type and post-submit action. Example: baker hubspot forms list --redirecting-only"
|
|
@@ -37158,7 +37948,7 @@ var formsListCommand = defineCommand129({
|
|
|
37158
37948
|
}
|
|
37159
37949
|
}
|
|
37160
37950
|
});
|
|
37161
|
-
var formsViewCommand =
|
|
37951
|
+
var formsViewCommand = defineCommand130({
|
|
37162
37952
|
meta: {
|
|
37163
37953
|
name: "view",
|
|
37164
37954
|
description: "Show one HubSpot form's fields and configuration. Example: baker hubspot forms view 1a2b3c"
|
|
@@ -37210,7 +38000,7 @@ var formsViewCommand = defineCommand129({
|
|
|
37210
38000
|
}
|
|
37211
38001
|
}
|
|
37212
38002
|
});
|
|
37213
|
-
var meetingsListCommand =
|
|
38003
|
+
var meetingsListCommand = defineCommand130({
|
|
37214
38004
|
meta: {
|
|
37215
38005
|
name: "list",
|
|
37216
38006
|
description: "List HubSpot meeting links (calendars). Example: baker hubspot meetings list"
|
|
@@ -37252,7 +38042,7 @@ var meetingsListCommand = defineCommand129({
|
|
|
37252
38042
|
}
|
|
37253
38043
|
}
|
|
37254
38044
|
});
|
|
37255
|
-
var meetingsViewCommand =
|
|
38045
|
+
var meetingsViewCommand = defineCommand130({
|
|
37256
38046
|
meta: {
|
|
37257
38047
|
name: "view",
|
|
37258
38048
|
description: "Show one HubSpot meeting link's booking fields. Example: baker hubspot meetings view discovery-call"
|
|
@@ -37296,7 +38086,7 @@ var meetingsViewCommand = defineCommand129({
|
|
|
37296
38086
|
}
|
|
37297
38087
|
}
|
|
37298
38088
|
});
|
|
37299
|
-
var formsSubmissionsCommand =
|
|
38089
|
+
var formsSubmissionsCommand = defineCommand130({
|
|
37300
38090
|
meta: {
|
|
37301
38091
|
name: "submissions",
|
|
37302
38092
|
description: "How many leads a form received, and when. Example: baker hubspot forms submissions <formId> --days 30"
|
|
@@ -37345,7 +38135,7 @@ var formsSubmissionsCommand = defineCommand129({
|
|
|
37345
38135
|
}
|
|
37346
38136
|
}
|
|
37347
38137
|
});
|
|
37348
|
-
var workflowsListCommand =
|
|
38138
|
+
var workflowsListCommand = defineCommand130({
|
|
37349
38139
|
meta: {
|
|
37350
38140
|
name: "list",
|
|
37351
38141
|
description: "List HubSpot workflows. Example: baker hubspot workflows list --enabled-only"
|
|
@@ -37410,7 +38200,7 @@ function workflowViewHints(workflow) {
|
|
|
37410
38200
|
}
|
|
37411
38201
|
return hints;
|
|
37412
38202
|
}
|
|
37413
|
-
var workflowsViewCommand =
|
|
38203
|
+
var workflowsViewCommand = defineCommand130({
|
|
37414
38204
|
meta: {
|
|
37415
38205
|
name: "view",
|
|
37416
38206
|
description: "Read one workflow's real configuration \u2014 enrolment, branches in order, and what each step writes. Example: baker hubspot workflows view 121183594 --full"
|
|
@@ -37437,7 +38227,7 @@ var workflowsViewCommand = defineCommand129({
|
|
|
37437
38227
|
}
|
|
37438
38228
|
}
|
|
37439
38229
|
});
|
|
37440
|
-
var pipelinesListCommand =
|
|
38230
|
+
var pipelinesListCommand = defineCommand130({
|
|
37441
38231
|
meta: {
|
|
37442
38232
|
name: "list",
|
|
37443
38233
|
description: "List HubSpot deal pipelines and their stages. Example: baker hubspot pipelines list"
|
|
@@ -37454,7 +38244,7 @@ var pipelinesListCommand = defineCommand129({
|
|
|
37454
38244
|
}
|
|
37455
38245
|
}
|
|
37456
38246
|
});
|
|
37457
|
-
var contactsSummaryCommand =
|
|
38247
|
+
var contactsSummaryCommand = defineCommand130({
|
|
37458
38248
|
meta: {
|
|
37459
38249
|
name: "summary",
|
|
37460
38250
|
description: "Whether recent leads are being worked, as counts. Example: baker hubspot contacts summary --days 30"
|
|
@@ -37524,7 +38314,7 @@ function contactLookupHints(data) {
|
|
|
37524
38314
|
}
|
|
37525
38315
|
return hints;
|
|
37526
38316
|
}
|
|
37527
|
-
var contactsLookupCommand =
|
|
38317
|
+
var contactsLookupCommand = defineCommand130({
|
|
37528
38318
|
meta: {
|
|
37529
38319
|
name: "lookup",
|
|
37530
38320
|
description: "Find one contact by email and see whether it was worked. Example: baker hubspot contacts lookup a@b.com"
|
|
@@ -37548,27 +38338,27 @@ var contactsLookupCommand = defineCommand129({
|
|
|
37548
38338
|
}
|
|
37549
38339
|
}
|
|
37550
38340
|
});
|
|
37551
|
-
var contactsCommand =
|
|
38341
|
+
var contactsCommand = defineCommand130({
|
|
37552
38342
|
meta: { name: "contacts", description: "Contacts on the connected HubSpot account." },
|
|
37553
38343
|
subCommands: { summary: contactsSummaryCommand, lookup: contactsLookupCommand }
|
|
37554
38344
|
});
|
|
37555
|
-
var formsCommand =
|
|
38345
|
+
var formsCommand = defineCommand130({
|
|
37556
38346
|
meta: { name: "forms", description: "HubSpot forms on the connected account." },
|
|
37557
38347
|
subCommands: { list: formsListCommand, view: formsViewCommand, submissions: formsSubmissionsCommand }
|
|
37558
38348
|
});
|
|
37559
|
-
var workflowsCommand =
|
|
38349
|
+
var workflowsCommand = defineCommand130({
|
|
37560
38350
|
meta: { name: "workflows", description: "HubSpot workflows on the connected account." },
|
|
37561
38351
|
subCommands: { list: workflowsListCommand, view: workflowsViewCommand }
|
|
37562
38352
|
});
|
|
37563
|
-
var pipelinesCommand =
|
|
38353
|
+
var pipelinesCommand = defineCommand130({
|
|
37564
38354
|
meta: { name: "pipelines", description: "HubSpot deal pipelines on the connected account." },
|
|
37565
38355
|
subCommands: { list: pipelinesListCommand }
|
|
37566
38356
|
});
|
|
37567
|
-
var meetingsCommand =
|
|
38357
|
+
var meetingsCommand = defineCommand130({
|
|
37568
38358
|
meta: { name: "meetings", description: "HubSpot meeting links (calendars) on the connected account." },
|
|
37569
38359
|
subCommands: { list: meetingsListCommand, view: meetingsViewCommand }
|
|
37570
38360
|
});
|
|
37571
|
-
var hubspotCommand =
|
|
38361
|
+
var hubspotCommand = defineCommand130({
|
|
37572
38362
|
meta: {
|
|
37573
38363
|
name: "hubspot",
|
|
37574
38364
|
description: `Read the connected HubSpot account \u2014 forms, the leads they received, workflows, deal pipelines and
|
|
@@ -37606,10 +38396,10 @@ Full guide: __tooling__/docs/tools/baker/hubspot.md`
|
|
|
37606
38396
|
});
|
|
37607
38397
|
|
|
37608
38398
|
// src/commands/images/index.ts
|
|
37609
|
-
import { defineCommand as
|
|
38399
|
+
import { defineCommand as defineCommand156 } from "citty";
|
|
37610
38400
|
|
|
37611
38401
|
// src/commands/images/crop.ts
|
|
37612
|
-
import { defineCommand as
|
|
38402
|
+
import { defineCommand as defineCommand131 } from "citty";
|
|
37613
38403
|
|
|
37614
38404
|
// src/lib/image/crop-sprite.ts
|
|
37615
38405
|
import sharp from "sharp";
|
|
@@ -37624,7 +38414,7 @@ function cropSprite(input, region) {
|
|
|
37624
38414
|
|
|
37625
38415
|
// src/lib/image/io.ts
|
|
37626
38416
|
import { randomBytes } from "crypto";
|
|
37627
|
-
import { glob as fsGlob, readFile as
|
|
38417
|
+
import { glob as fsGlob, readFile as readFile21, rename, stat as stat5, writeFile as writeFile11 } from "fs/promises";
|
|
37628
38418
|
import { dirname as dirname2, extname as extname2, join as join5, resolve as resolve4 } from "path";
|
|
37629
38419
|
var REMOTE_RE = /^https?:\/\//i;
|
|
37630
38420
|
var GLOB_RE = /[*?[\]{}]/;
|
|
@@ -37657,11 +38447,11 @@ async function readImageBuffer(pathOrUrl) {
|
|
|
37657
38447
|
const { buffer } = await fetchExternalBytes(pathOrUrl, { maxBytes: MAX_REMOTE_IMAGE_BYTES });
|
|
37658
38448
|
return buffer;
|
|
37659
38449
|
}
|
|
37660
|
-
return
|
|
38450
|
+
return readFile21(pathOrUrl);
|
|
37661
38451
|
}
|
|
37662
|
-
async function isDirectory(
|
|
38452
|
+
async function isDirectory(path43) {
|
|
37663
38453
|
try {
|
|
37664
|
-
const s = await
|
|
38454
|
+
const s = await stat5(path43);
|
|
37665
38455
|
return s.isDirectory();
|
|
37666
38456
|
} catch {
|
|
37667
38457
|
return false;
|
|
@@ -37731,7 +38521,7 @@ function emitError2(err) {
|
|
|
37731
38521
|
}
|
|
37732
38522
|
process.exit(1);
|
|
37733
38523
|
}
|
|
37734
|
-
var cropCommand =
|
|
38524
|
+
var cropCommand = defineCommand131({
|
|
37735
38525
|
meta: {
|
|
37736
38526
|
name: "crop",
|
|
37737
38527
|
description: "Crop a rectangular region from an image.\n\nExample: baker images crop sprite.png --x 0 --y 0 --width 64 --height 64 --output icon.png"
|
|
@@ -37767,7 +38557,7 @@ var cropCommand = defineCommand130({
|
|
|
37767
38557
|
});
|
|
37768
38558
|
|
|
37769
38559
|
// src/commands/images/delete.ts
|
|
37770
|
-
import { defineCommand as
|
|
38560
|
+
import { defineCommand as defineCommand132 } from "citty";
|
|
37771
38561
|
registerSchema({
|
|
37772
38562
|
command: "images.delete",
|
|
37773
38563
|
description: "Delete an image by ID",
|
|
@@ -37781,7 +38571,7 @@ registerSchema({
|
|
|
37781
38571
|
}
|
|
37782
38572
|
}
|
|
37783
38573
|
});
|
|
37784
|
-
var deleteCommand2 =
|
|
38574
|
+
var deleteCommand2 = defineCommand132({
|
|
37785
38575
|
meta: {
|
|
37786
38576
|
name: "delete",
|
|
37787
38577
|
description: "Delete an image by ID. Use --dry-run to preview. Example: baker images delete j571abc123 --dry-run"
|
|
@@ -37822,7 +38612,7 @@ var deleteCommand2 = defineCommand131({
|
|
|
37822
38612
|
});
|
|
37823
38613
|
|
|
37824
38614
|
// src/commands/images/dimensions.ts
|
|
37825
|
-
import { defineCommand as
|
|
38615
|
+
import { defineCommand as defineCommand133 } from "citty";
|
|
37826
38616
|
|
|
37827
38617
|
// src/lib/image/dimensions.ts
|
|
37828
38618
|
import { imageSize } from "image-size";
|
|
@@ -37851,7 +38641,7 @@ registerSchema({
|
|
|
37851
38641
|
}
|
|
37852
38642
|
}
|
|
37853
38643
|
});
|
|
37854
|
-
var dimensionsCommand =
|
|
38644
|
+
var dimensionsCommand = defineCommand133({
|
|
37855
38645
|
meta: {
|
|
37856
38646
|
name: "dimensions",
|
|
37857
38647
|
description: "Read image dimensions without decoding the full file.\n\nExample: baker images dimensions ./logo.png\nExample: baker images dimensions https://acme.com/hero.png"
|
|
@@ -37907,7 +38697,7 @@ var dimensionsCommand = defineCommand132({
|
|
|
37907
38697
|
});
|
|
37908
38698
|
|
|
37909
38699
|
// src/commands/images/download.ts
|
|
37910
|
-
import { defineCommand as
|
|
38700
|
+
import { defineCommand as defineCommand134 } from "citty";
|
|
37911
38701
|
|
|
37912
38702
|
// src/commands/images/downloadPaths.ts
|
|
37913
38703
|
import { basename as basename2, extname as extname3, join as join6 } from "path";
|
|
@@ -37963,13 +38753,13 @@ function resolveDownloadPath({ baseName, extension, out, outIsDirectory: outIsDi
|
|
|
37963
38753
|
}
|
|
37964
38754
|
function disambiguate(paths) {
|
|
37965
38755
|
const taken = /* @__PURE__ */ new Set();
|
|
37966
|
-
return paths.map((
|
|
37967
|
-
if (!taken.has(
|
|
37968
|
-
taken.add(
|
|
37969
|
-
return
|
|
38756
|
+
return paths.map((path43) => {
|
|
38757
|
+
if (!taken.has(path43)) {
|
|
38758
|
+
taken.add(path43);
|
|
38759
|
+
return path43;
|
|
37970
38760
|
}
|
|
37971
|
-
const ext = extname3(
|
|
37972
|
-
const stem =
|
|
38761
|
+
const ext = extname3(path43);
|
|
38762
|
+
const stem = path43.slice(0, path43.length - ext.length);
|
|
37973
38763
|
let n = 2;
|
|
37974
38764
|
while (taken.has(`${stem}-${n}${ext}`)) n += 1;
|
|
37975
38765
|
const unique = `${stem}-${n}${ext}`;
|
|
@@ -38096,10 +38886,10 @@ async function runDownloads(plan) {
|
|
|
38096
38886
|
const paths = disambiguate(fetched.map((item) => item.path));
|
|
38097
38887
|
const downloaded = [];
|
|
38098
38888
|
for (const [index, item] of fetched.entries()) {
|
|
38099
|
-
const
|
|
38889
|
+
const path43 = paths[index] ?? item.path;
|
|
38100
38890
|
try {
|
|
38101
|
-
await atomicWrite(
|
|
38102
|
-
downloaded.push({ input: item.input, output:
|
|
38891
|
+
await atomicWrite(path43, item.buffer);
|
|
38892
|
+
downloaded.push({ input: item.input, output: path43, bytes: item.buffer.length, contentType: item.contentType });
|
|
38103
38893
|
} catch (err) {
|
|
38104
38894
|
failed.push({ input: item.input, error: failureMessage(err, "Write failed") });
|
|
38105
38895
|
}
|
|
@@ -38132,7 +38922,7 @@ function emitError3(err) {
|
|
|
38132
38922
|
writeJson({ ok: false, error: { code: "INTERNAL_ERROR", message: "Unexpected error" } });
|
|
38133
38923
|
process.exit(1);
|
|
38134
38924
|
}
|
|
38135
|
-
var downloadCommand =
|
|
38925
|
+
var downloadCommand = defineCommand134({
|
|
38136
38926
|
meta: {
|
|
38137
38927
|
name: "download",
|
|
38138
38928
|
description: "Download image URLs and/or library images to local files \u2014 the missing first half of `source \u2192 download \u2192 normalize \u2192 place`. Never use `curl` for this.\n\nExamples:\n baker images download https://media.withbaker.com/\u2026/logo.webp\n baker images download j57abc123 j57def456 --out src/pages/pricing/_images/\n baker images download https://\u2026/hero.png --out ./hero.png"
|
|
@@ -38165,7 +38955,7 @@ var downloadCommand = defineCommand133({
|
|
|
38165
38955
|
});
|
|
38166
38956
|
|
|
38167
38957
|
// src/commands/images/extract.ts
|
|
38168
|
-
import { defineCommand as
|
|
38958
|
+
import { defineCommand as defineCommand135 } from "citty";
|
|
38169
38959
|
|
|
38170
38960
|
// src/commands/images/autoIngest.ts
|
|
38171
38961
|
var AUTO_INGEST_MAX = {
|
|
@@ -38212,7 +39002,7 @@ registerSchema({
|
|
|
38212
39002
|
}
|
|
38213
39003
|
}
|
|
38214
39004
|
});
|
|
38215
|
-
var extractCommand =
|
|
39005
|
+
var extractCommand = defineCommand135({
|
|
38216
39006
|
meta: {
|
|
38217
39007
|
name: "extract",
|
|
38218
39008
|
description: "Pull every image from a single URL via Firecrawl. ~$0.001/scrape. Cap auto-ingest at 20.\n\nExample: baker images extract https://stripe.com --auto-ingest 5"
|
|
@@ -38267,7 +39057,7 @@ var extractCommand = defineCommand134({
|
|
|
38267
39057
|
});
|
|
38268
39058
|
|
|
38269
39059
|
// src/commands/images/find.ts
|
|
38270
|
-
import { defineCommand as
|
|
39060
|
+
import { defineCommand as defineCommand136 } from "citty";
|
|
38271
39061
|
|
|
38272
39062
|
// src/commands/images/providerHits.ts
|
|
38273
39063
|
function asRecord3(value) {
|
|
@@ -38405,7 +39195,7 @@ registerSchema({
|
|
|
38405
39195
|
full: { type: "boolean", description: "Include full metadata", required: false, default: false }
|
|
38406
39196
|
}
|
|
38407
39197
|
});
|
|
38408
|
-
var findCommand =
|
|
39198
|
+
var findCommand = defineCommand136({
|
|
38409
39199
|
meta: {
|
|
38410
39200
|
name: "find",
|
|
38411
39201
|
description: "Library-first fanout image search. Opt in to providers with --sources. `--fallback` short-circuits to externals only when library is thin. With --auto-ingest, ingested external hits return Baker-owned URLs.\n\nExample: baker images find 'office' --sources library,magnific --limit 20"
|
|
@@ -38478,7 +39268,7 @@ var findCommand = defineCommand135({
|
|
|
38478
39268
|
});
|
|
38479
39269
|
|
|
38480
39270
|
// src/commands/images/get.ts
|
|
38481
|
-
import { defineCommand as
|
|
39271
|
+
import { defineCommand as defineCommand137 } from "citty";
|
|
38482
39272
|
registerSchema({
|
|
38483
39273
|
command: "images.get",
|
|
38484
39274
|
description: "Get a single image by ID",
|
|
@@ -38486,7 +39276,7 @@ registerSchema({
|
|
|
38486
39276
|
id: { type: "string", description: "Image ID", required: true }
|
|
38487
39277
|
}
|
|
38488
39278
|
});
|
|
38489
|
-
var getCommand3 =
|
|
39279
|
+
var getCommand3 = defineCommand137({
|
|
38490
39280
|
meta: { name: "get", description: "Get a single image by ID. Example: baker images get j571abc123" },
|
|
38491
39281
|
args: {
|
|
38492
39282
|
id: { type: "positional", description: "Image ID", required: false },
|
|
@@ -38522,7 +39312,7 @@ var getCommand3 = defineCommand136({
|
|
|
38522
39312
|
});
|
|
38523
39313
|
|
|
38524
39314
|
// src/commands/images/gif.ts
|
|
38525
|
-
import { defineCommand as
|
|
39315
|
+
import { defineCommand as defineCommand138 } from "citty";
|
|
38526
39316
|
registerSchema({
|
|
38527
39317
|
command: "images.gif",
|
|
38528
39318
|
description: "Search Giphy for GIFs / reaction memes (paid social creative).",
|
|
@@ -38554,7 +39344,7 @@ registerSchema({
|
|
|
38554
39344
|
}
|
|
38555
39345
|
}
|
|
38556
39346
|
});
|
|
38557
|
-
var gifCommand =
|
|
39347
|
+
var gifCommand = defineCommand138({
|
|
38558
39348
|
meta: {
|
|
38559
39349
|
name: "gif",
|
|
38560
39350
|
description: "Search Giphy for GIFs / reaction memes \u2014 built for paid-social creative (Meta, TikTok, LinkedIn, X). Free API. Each hit carries WebP + GIF + MP4 URLs in providerMeta so you can pick the right format per platform.\n\nExample: baker images gif 'this is fine' --limit 10\nExample: baker images gif 'office reaction' --rating pg --auto-ingest 2\nExample: baker images gif --trending --limit 25"
|
|
@@ -38601,7 +39391,7 @@ var gifCommand = defineCommand137({
|
|
|
38601
39391
|
});
|
|
38602
39392
|
|
|
38603
39393
|
// src/commands/images/google.ts
|
|
38604
|
-
import { defineCommand as
|
|
39394
|
+
import { defineCommand as defineCommand139 } from "citty";
|
|
38605
39395
|
var GOOGLE_ERROR_FIX = {
|
|
38606
39396
|
action: "use_different_resource",
|
|
38607
39397
|
explanation: "Generate the asset instead of retrying Google. Google is the last-resort image provider. Run `baker studio generate` to make the asset. Never sleep-and-retry this command \u2014 the CLI already backs off on rate limits. If no image can be sourced, continue the rest of the task with a placeholder rather than aborting it."
|
|
@@ -38641,7 +39431,7 @@ registerSchema({
|
|
|
38641
39431
|
}
|
|
38642
39432
|
}
|
|
38643
39433
|
});
|
|
38644
|
-
var googleCommand2 =
|
|
39434
|
+
var googleCommand2 = defineCommand139({
|
|
38645
39435
|
meta: {
|
|
38646
39436
|
name: "google",
|
|
38647
39437
|
description: "Google Images via the official Custom Search JSON API ($0.005/query, free 100/day). \u26A0 Source unverified \u2014 watermarks, low-res, mislabeled results are common. Use as last resort. With --auto-ingest, ingested hits return Baker-owned URLs.\n\nExample: baker images google 'industrial workshop' --type photo --size large --limit 20"
|
|
@@ -38714,7 +39504,7 @@ var googleCommand2 = defineCommand138({
|
|
|
38714
39504
|
});
|
|
38715
39505
|
|
|
38716
39506
|
// src/commands/images/group.ts
|
|
38717
|
-
import { defineCommand as
|
|
39507
|
+
import { defineCommand as defineCommand140 } from "citty";
|
|
38718
39508
|
|
|
38719
39509
|
// src/commands/mediaGroup.ts
|
|
38720
39510
|
async function runMediaGroupLookup(input) {
|
|
@@ -38767,7 +39557,7 @@ registerSchema({
|
|
|
38767
39557
|
"group-key": { type: "string", description: "The set key directly, when you already have it", required: false }
|
|
38768
39558
|
}
|
|
38769
39559
|
});
|
|
38770
|
-
var groupCommand =
|
|
39560
|
+
var groupCommand = defineCommand140({
|
|
38771
39561
|
meta: {
|
|
38772
39562
|
name: "group",
|
|
38773
39563
|
description: "List every asset that arrived in the same set \u2014 the slides of one Instagram carousel, the images off one scraped page. Start here whenever a hit looks like part of a sequence: carousel slides are authored to be read in order and usually only make sense together. Takes an image or a video id, since one carousel can contain both. Example: baker images group <imageId>"
|
|
@@ -38787,7 +39577,7 @@ var groupCommand = defineCommand139({
|
|
|
38787
39577
|
});
|
|
38788
39578
|
|
|
38789
39579
|
// src/commands/images/icon.ts
|
|
38790
|
-
import { defineCommand as
|
|
39580
|
+
import { defineCommand as defineCommand141 } from "citty";
|
|
38791
39581
|
|
|
38792
39582
|
// src/commands/images/brandVerification.ts
|
|
38793
39583
|
function brandToken(domain) {
|
|
@@ -38884,7 +39674,7 @@ registerSchema({
|
|
|
38884
39674
|
full: { type: "boolean", description: "Include full metadata", required: false, default: false }
|
|
38885
39675
|
}
|
|
38886
39676
|
});
|
|
38887
|
-
var iconCommand =
|
|
39677
|
+
var iconCommand = defineCommand141({
|
|
38888
39678
|
meta: {
|
|
38889
39679
|
name: "icon",
|
|
38890
39680
|
description: "Icon via Iconify (simple-icons, logos, lucide, devicon, heroicons, tabler, phosphor, material-symbols, \u2026). Free CDN, no API key.\n\nExample: baker images icon react --set devicon\nExample: baker images icon lucide:check --color '#0a0a0a'"
|
|
@@ -38954,7 +39744,7 @@ var iconCommand = defineCommand140({
|
|
|
38954
39744
|
});
|
|
38955
39745
|
|
|
38956
39746
|
// src/commands/images/ingest.ts
|
|
38957
|
-
import { defineCommand as
|
|
39747
|
+
import { defineCommand as defineCommand142 } from "citty";
|
|
38958
39748
|
var SOURCE_VALUES = imageSourceSchema.options.join(" | ");
|
|
38959
39749
|
registerSchema({
|
|
38960
39750
|
command: "images.ingest",
|
|
@@ -38969,7 +39759,7 @@ registerSchema({
|
|
|
38969
39759
|
fields: { type: "string", description: "Comma-separated field names to include", required: false }
|
|
38970
39760
|
}
|
|
38971
39761
|
});
|
|
38972
|
-
var ingestCommand =
|
|
39762
|
+
var ingestCommand = defineCommand142({
|
|
38973
39763
|
meta: {
|
|
38974
39764
|
name: "ingest",
|
|
38975
39765
|
description: "Download a remote URL and store it in the library. Hash-deduped on bytes + externalId.\n\nExample: baker images ingest https://img.freepik.com/free-photo/xyz.jpg --source magnific --external-id 12345"
|
|
@@ -39038,7 +39828,7 @@ var ingestCommand = defineCommand141({
|
|
|
39038
39828
|
});
|
|
39039
39829
|
|
|
39040
39830
|
// src/commands/images/layerize.ts
|
|
39041
|
-
import { defineCommand as
|
|
39831
|
+
import { defineCommand as defineCommand143 } from "citty";
|
|
39042
39832
|
registerSchema({
|
|
39043
39833
|
command: "images.layerize",
|
|
39044
39834
|
description: "Split a library image into editable layers: transparent PNG cutouts for each element, plus any headline recovered as EDITABLE TEXT with its font, size, colour and position. Waits for completion by default. Costs credits.",
|
|
@@ -39102,7 +39892,7 @@ async function pollUntilSettled(imageId, maxWait) {
|
|
|
39102
39892
|
}
|
|
39103
39893
|
return null;
|
|
39104
39894
|
}
|
|
39105
|
-
var layerizeCommand =
|
|
39895
|
+
var layerizeCommand = defineCommand143({
|
|
39106
39896
|
meta: {
|
|
39107
39897
|
name: "layerize",
|
|
39108
39898
|
description: "Split a library image into editable layers \u2014 transparent cutouts per element, plus any baked-in headline recovered as editable text with its typography.\n\nStart here: baker images layerize j571abc123def\nExample: baker images layerize j571abc123def --full\nExample: baker images layerize j571abc123def --instructions 'keep the product and its shadow together'"
|
|
@@ -39171,7 +39961,7 @@ registerSchema({
|
|
|
39171
39961
|
full: { type: "boolean", description: "Include geometry and typography for every layer", required: false }
|
|
39172
39962
|
}
|
|
39173
39963
|
});
|
|
39174
|
-
var layersCommand =
|
|
39964
|
+
var layersCommand = defineCommand143({
|
|
39175
39965
|
meta: {
|
|
39176
39966
|
name: "layers",
|
|
39177
39967
|
description: "Read the layers of an image that has already been split. Free \u2014 no provider call.\n\nStart here: baker images layers j571abc123def\nExample: baker images layers j571abc123def --full"
|
|
@@ -39211,7 +40001,7 @@ var layersCommand = defineCommand142({
|
|
|
39211
40001
|
});
|
|
39212
40002
|
|
|
39213
40003
|
// src/commands/images/library.ts
|
|
39214
|
-
import { defineCommand as
|
|
40004
|
+
import { defineCommand as defineCommand144 } from "citty";
|
|
39215
40005
|
registerSchema({
|
|
39216
40006
|
command: "images.library",
|
|
39217
40007
|
description: "Search the company image library. Returns only ready images.",
|
|
@@ -39237,7 +40027,7 @@ registerSchema({
|
|
|
39237
40027
|
}
|
|
39238
40028
|
}
|
|
39239
40029
|
});
|
|
39240
|
-
var libraryCommand =
|
|
40030
|
+
var libraryCommand = defineCommand144({
|
|
39241
40031
|
meta: {
|
|
39242
40032
|
name: "library",
|
|
39243
40033
|
description: "Search the company image library (hybrid BM25 + vector + Cohere rerank). Use this BEFORE any external provider.\n\nExample: baker images library 'hero banner' --aspect-ratio 16:9 --source magnific"
|
|
@@ -39300,7 +40090,7 @@ var libraryCommand = defineCommand143({
|
|
|
39300
40090
|
});
|
|
39301
40091
|
|
|
39302
40092
|
// src/commands/images/logo.ts
|
|
39303
|
-
import { defineCommand as
|
|
40093
|
+
import { defineCommand as defineCommand145 } from "citty";
|
|
39304
40094
|
registerSchema({
|
|
39305
40095
|
command: "images.logo",
|
|
39306
40096
|
description: "Brand logo lookup via Brandfetch. Auto-ingests by default. Returns `brandMatch` \u2014 Brandfetch's own verdict on whose brand the domain is (confirmed | mismatch | unverified). Branch on it: `mismatch` means the mark is another company's, so do not place it. `confirmed` verifies the record, not the artwork \u2014 read the ingested row back to check the mark itself.",
|
|
@@ -39328,7 +40118,7 @@ registerSchema({
|
|
|
39328
40118
|
full: { type: "boolean", description: "Include full metadata", required: false, default: false }
|
|
39329
40119
|
}
|
|
39330
40120
|
});
|
|
39331
|
-
var logoCommand =
|
|
40121
|
+
var logoCommand = defineCommand145({
|
|
39332
40122
|
meta: {
|
|
39333
40123
|
name: "logo",
|
|
39334
40124
|
description: "Brand logo via Brandfetch. Returns up to 5 variants (icon, light/dark logo, light/dark symbol) plus `brandMatch`. Auto-ingests the first variant.\n\nStart here: check `brandMatch.verdict`.\n mismatch \u2192 the mark belongs to `brandMatch.name`, a different company. Do not place it.\n unverified \u2192 nothing confirmed whose logo this is. Treat as unchecked.\n confirmed \u2192 Brandfetch has this brand's record. That verifies the record, NOT the artwork \u2014 a confirmed domain has served another company's logo before.\n\n\u26A0 Whatever the verdict, read the ingested row back with `baker images get <imageId>` and check `textInImage`/`subject` before placing it. That is the only check that looks at the mark.\n\nExample: baker images logo stripe.com --variant logo"
|
|
@@ -39401,7 +40191,7 @@ var logoCommand = defineCommand144({
|
|
|
39401
40191
|
});
|
|
39402
40192
|
|
|
39403
40193
|
// src/commands/images/normalize.ts
|
|
39404
|
-
import { defineCommand as
|
|
40194
|
+
import { defineCommand as defineCommand146 } from "citty";
|
|
39405
40195
|
|
|
39406
40196
|
// src/lib/image/color-changer.ts
|
|
39407
40197
|
import quantize from "quantize";
|
|
@@ -40133,7 +40923,7 @@ function coerceRawArgs(args) {
|
|
|
40133
40923
|
"dry-run": bool(args["dry-run"])
|
|
40134
40924
|
};
|
|
40135
40925
|
}
|
|
40136
|
-
var normalizeCommand2 =
|
|
40926
|
+
var normalizeCommand2 = defineCommand146({
|
|
40137
40927
|
meta: {
|
|
40138
40928
|
name: "normalize",
|
|
40139
40929
|
description: `Normalize logos / images: declarative recolor + bg removal + trim + resize. Operates on local files; writes in-place by default.
|
|
@@ -40188,7 +40978,7 @@ Examples:
|
|
|
40188
40978
|
});
|
|
40189
40979
|
|
|
40190
40980
|
// src/commands/images/pinterest.ts
|
|
40191
|
-
import { defineCommand as
|
|
40981
|
+
import { defineCommand as defineCommand147 } from "citty";
|
|
40192
40982
|
registerSchema({
|
|
40193
40983
|
command: "images.pinterest",
|
|
40194
40984
|
description: "Pinterest image search via ScrapeCreators. Reference-grade real-world photography, product styling, interiors, fashion, food, and aesthetic mood boards. Inspect before placing \u2014 Pinterest is unverified, trademark-bearing web content.",
|
|
@@ -40208,7 +40998,7 @@ registerSchema({
|
|
|
40208
40998
|
}
|
|
40209
40999
|
}
|
|
40210
41000
|
});
|
|
40211
|
-
var pinterestCommand =
|
|
41001
|
+
var pinterestCommand = defineCommand147({
|
|
40212
41002
|
meta: {
|
|
40213
41003
|
name: "pinterest",
|
|
40214
41004
|
description: "Pinterest image search via ScrapeCreators ($0.00188/request). Best for photo-realistic reference imagery \u2014 lifestyle, interiors, fashion, food, product styling, and mood boards to brief AI generation against. \u26A0 Unverified, trademark-bearing web content \u2014 inspect and respect rights before placing on a customer page. Browse first; auto-ingest only the pins you commit to.\n\nExamples:\n baker images pinterest 'scandinavian living room'\n baker images pinterest 'minimalist skincare product photography' --limit 20\n baker images pinterest 'cozy coffee shop interior' --auto-ingest 2 --context 'Mood reference for hero photography'"
|
|
@@ -40265,7 +41055,7 @@ var pinterestCommand = defineCommand146({
|
|
|
40265
41055
|
});
|
|
40266
41056
|
|
|
40267
41057
|
// src/commands/images/screenshot.ts
|
|
40268
|
-
import { defineCommand as
|
|
41058
|
+
import { defineCommand as defineCommand148 } from "citty";
|
|
40269
41059
|
registerSchema({
|
|
40270
41060
|
command: "images.screenshot",
|
|
40271
41061
|
description: "Capture a website screenshot via ScreenshotOne. Auto-ingests on success.",
|
|
@@ -40284,7 +41074,7 @@ registerSchema({
|
|
|
40284
41074
|
full: { type: "boolean", description: "Include full metadata", required: false, default: false }
|
|
40285
41075
|
}
|
|
40286
41076
|
});
|
|
40287
|
-
var screenshotCommand =
|
|
41077
|
+
var screenshotCommand = defineCommand148({
|
|
40288
41078
|
meta: {
|
|
40289
41079
|
name: "screenshot",
|
|
40290
41080
|
description: "Screenshot a URL via ScreenshotOne. $0.009/capture. Auto-ingests to library.\n\nExample: baker images screenshot https://stripe.com --full-page"
|
|
@@ -40348,7 +41138,7 @@ var screenshotCommand = defineCommand147({
|
|
|
40348
41138
|
});
|
|
40349
41139
|
|
|
40350
41140
|
// src/commands/images/search.ts
|
|
40351
|
-
import { defineCommand as
|
|
41141
|
+
import { defineCommand as defineCommand149 } from "citty";
|
|
40352
41142
|
registerSchema({
|
|
40353
41143
|
command: "images.search",
|
|
40354
41144
|
description: "Search images by text query. Only returns ready images.",
|
|
@@ -40364,7 +41154,7 @@ registerSchema({
|
|
|
40364
41154
|
tags: { type: "string", description: "Comma-separated tags to filter by", required: false }
|
|
40365
41155
|
}
|
|
40366
41156
|
});
|
|
40367
|
-
var searchCommand =
|
|
41157
|
+
var searchCommand = defineCommand149({
|
|
40368
41158
|
meta: {
|
|
40369
41159
|
name: "search",
|
|
40370
41160
|
description: "Semantic search images by text query. Uses hybrid BM25 + vector + reranking. Example: baker images search 'hero banner' --aspect-ratio 16:9 --tags logo"
|
|
@@ -40424,7 +41214,7 @@ var searchCommand = defineCommand148({
|
|
|
40424
41214
|
});
|
|
40425
41215
|
|
|
40426
41216
|
// src/commands/images/sticker.ts
|
|
40427
|
-
import { defineCommand as
|
|
41217
|
+
import { defineCommand as defineCommand150 } from "citty";
|
|
40428
41218
|
registerSchema({
|
|
40429
41219
|
command: "images.sticker",
|
|
40430
41220
|
description: "Search Giphy stickers \u2014 transparent-background overlays for ad creative.",
|
|
@@ -40456,7 +41246,7 @@ registerSchema({
|
|
|
40456
41246
|
}
|
|
40457
41247
|
}
|
|
40458
41248
|
});
|
|
40459
|
-
var stickerCommand =
|
|
41249
|
+
var stickerCommand = defineCommand150({
|
|
40460
41250
|
meta: {
|
|
40461
41251
|
name: "sticker",
|
|
40462
41252
|
description: "Search Giphy's sticker corpus \u2014 transparent-background WebPs / GIFs ideal for overlaying on ad creative (Meta, TikTok, Stories). Same Giphy free API as `baker images gif`; results carry WebP + GIF + MP4 URLs in providerMeta.\n\nExample: baker images sticker 'thumbs up' --limit 10\nExample: baker images sticker celebration --rating g --auto-ingest 3\nExample: baker images sticker --trending --limit 25"
|
|
@@ -40503,7 +41293,7 @@ var stickerCommand = defineCommand149({
|
|
|
40503
41293
|
});
|
|
40504
41294
|
|
|
40505
41295
|
// src/commands/images/stock.ts
|
|
40506
|
-
import { defineCommand as
|
|
41296
|
+
import { defineCommand as defineCommand151 } from "citty";
|
|
40507
41297
|
var STOCK_ERROR_FIX = {
|
|
40508
41298
|
action: "use_different_resource",
|
|
40509
41299
|
explanation: "Switch provider instead of retrying stock search. Stock search is one of several image sources. Run `baker images find <query> --sources library,pinterest,google` (`--sources` is required \u2014 `find` alone searches the library only) or `baker studio generate` to make the asset. Never sleep-and-retry this command \u2014 the CLI already backs off on rate limits. If no image can be sourced, continue the rest of the task with a placeholder rather than aborting it."
|
|
@@ -40580,7 +41370,7 @@ function buildStockRequest(query, args) {
|
|
|
40580
41370
|
if (args.context) body.descriptionContext = args.context;
|
|
40581
41371
|
return body;
|
|
40582
41372
|
}
|
|
40583
|
-
var stockCommand =
|
|
41373
|
+
var stockCommand = defineCommand151({
|
|
40584
41374
|
meta: {
|
|
40585
41375
|
name: "stock",
|
|
40586
41376
|
description: "Stock search via Magnific \u2014 Freepik's developer API (~250M assets: photos, vectors, illustrations, icons, PSDs). $0.002/req. With --auto-ingest, ingested hits return Baker-owned URLs.\n\nExamples:\n baker images stock 'minimalist office'\n baker images stock 'flat office workers' --type vector\n baker images stock 'hero photo of a kitchen' --type photo --orientation landscape --ai exclude\n baker images stock 'brand pattern' --color '#0a0a0a' --license freemium --auto-ingest 2"
|
|
@@ -40653,7 +41443,7 @@ var stockCommand = defineCommand150({
|
|
|
40653
41443
|
});
|
|
40654
41444
|
|
|
40655
41445
|
// src/lib/tags-command.ts
|
|
40656
|
-
import { defineCommand as
|
|
41446
|
+
import { defineCommand as defineCommand152 } from "citty";
|
|
40657
41447
|
function makeTagsCommand(command, label, endpoint) {
|
|
40658
41448
|
registerSchema({
|
|
40659
41449
|
command: `${command}.tags`,
|
|
@@ -40662,7 +41452,7 @@ function makeTagsCommand(command, label, endpoint) {
|
|
|
40662
41452
|
output: { type: "string", description: "Output format: md|json", required: false, default: "md" }
|
|
40663
41453
|
}
|
|
40664
41454
|
});
|
|
40665
|
-
return
|
|
41455
|
+
return defineCommand152({
|
|
40666
41456
|
meta: {
|
|
40667
41457
|
name: "tags",
|
|
40668
41458
|
description: `List the available ${label} tag names (defaults + company custom tags). Use before filtering with --tags. Example: baker ${command} tags`
|
|
@@ -40698,7 +41488,7 @@ function makeTagsCommand(command, label, endpoint) {
|
|
|
40698
41488
|
var tagsCommand3 = makeTagsCommand("images", "image", "/api/images/tags");
|
|
40699
41489
|
|
|
40700
41490
|
// src/commands/images/upload.ts
|
|
40701
|
-
import { defineCommand as
|
|
41491
|
+
import { defineCommand as defineCommand153 } from "citty";
|
|
40702
41492
|
registerSchema({
|
|
40703
41493
|
command: "images.upload",
|
|
40704
41494
|
description: "Upload an image to the library \u2014 local file path or remote http(s) URL.",
|
|
@@ -40736,7 +41526,7 @@ registerSchema({
|
|
|
40736
41526
|
function isRemoteUrl2(value) {
|
|
40737
41527
|
return /^https?:\/\//i.test(value);
|
|
40738
41528
|
}
|
|
40739
|
-
var uploadCommand =
|
|
41529
|
+
var uploadCommand = defineCommand153({
|
|
40740
41530
|
meta: {
|
|
40741
41531
|
name: "upload",
|
|
40742
41532
|
description: "Upload an image to the library \u2014 accepts a local file path OR a remote http(s) URL.\n\nLocal: reads bytes, sends to /api/images/upload, content-type auto-detected from extension.\nRemote: dispatches to /api/images/ingest with hash-dedup on bytes + externalId.\n\nExamples:\n baker images upload ./logo.png --source uploaded\n baker images upload ./cert.png --context 'ISO 27001 badge \u2014 enterprise tier'\n baker images upload https://acme.com/hero.png --source firecrawl --context 'Acme competitor pricing hero'"
|
|
@@ -40829,7 +41619,7 @@ async function uploadLocal(target, args) {
|
|
|
40829
41619
|
}
|
|
40830
41620
|
|
|
40831
41621
|
// src/commands/images/upscale.ts
|
|
40832
|
-
import { defineCommand as
|
|
41622
|
+
import { defineCommand as defineCommand154 } from "citty";
|
|
40833
41623
|
registerSchema({
|
|
40834
41624
|
command: "images.upscale",
|
|
40835
41625
|
description: "Upscale a library image via the backend (Replicate, cost-tracked). Waits for completion by default. The image must be status 'ready' and raster (not SVG/AVIF).",
|
|
@@ -40844,7 +41634,7 @@ registerSchema({
|
|
|
40844
41634
|
}
|
|
40845
41635
|
});
|
|
40846
41636
|
var POLL_INTERVAL_MS4 = 1500;
|
|
40847
|
-
var upscaleCommand =
|
|
41637
|
+
var upscaleCommand = defineCommand154({
|
|
40848
41638
|
meta: {
|
|
40849
41639
|
name: "upscale",
|
|
40850
41640
|
description: "Upscale a library image via the Convex backend (Replicate, cost-tracked at $0.05/image). Waits for completion by default.\n\nExample: baker images upscale j571abc123def\nExample: baker images upscale j571abc123def --max-wait 0 # fire-and-forget"
|
|
@@ -40899,7 +41689,7 @@ var upscaleCommand = defineCommand153({
|
|
|
40899
41689
|
});
|
|
40900
41690
|
|
|
40901
41691
|
// src/commands/images/use.ts
|
|
40902
|
-
import { defineCommand as
|
|
41692
|
+
import { defineCommand as defineCommand155 } from "citty";
|
|
40903
41693
|
registerSchema({
|
|
40904
41694
|
command: "images.use",
|
|
40905
41695
|
description: "Ingest a URL and wait for the library record to be ready.",
|
|
@@ -40928,7 +41718,7 @@ function emitReady(ingestResult, doc, args) {
|
|
|
40928
41718
|
args.full === true
|
|
40929
41719
|
);
|
|
40930
41720
|
}
|
|
40931
|
-
var useCommand =
|
|
41721
|
+
var useCommand = defineCommand155({
|
|
40932
41722
|
meta: {
|
|
40933
41723
|
name: "use",
|
|
40934
41724
|
description: "Sugar over `ingest`: download \u2192 store \u2192 wait until describe + embed complete \u2192 return ready library record.\n\nExample: baker images use https://cdn.example.com/hero.png --source uploaded"
|
|
@@ -40977,7 +41767,7 @@ var useCommand = defineCommand154({
|
|
|
40977
41767
|
});
|
|
40978
41768
|
|
|
40979
41769
|
// src/commands/images/index.ts
|
|
40980
|
-
var imagesCommand =
|
|
41770
|
+
var imagesCommand = defineCommand156({
|
|
40981
41771
|
meta: {
|
|
40982
41772
|
name: "images",
|
|
40983
41773
|
description: `Find, source, and normalize images. Subcommands route by provider so cost + license are explicit.
|
|
@@ -41055,12 +41845,12 @@ Full guide: __tooling__/docs/tools/baker/images.md`
|
|
|
41055
41845
|
});
|
|
41056
41846
|
|
|
41057
41847
|
// src/commands/landing/index.ts
|
|
41058
|
-
import { defineCommand as
|
|
41848
|
+
import { defineCommand as defineCommand168 } from "citty";
|
|
41059
41849
|
|
|
41060
41850
|
// src/commands/landing/critique.ts
|
|
41061
41851
|
import { readdir as readdir9, stat as stat6 } from "fs/promises";
|
|
41062
|
-
import
|
|
41063
|
-
import { defineCommand as
|
|
41852
|
+
import path30 from "path";
|
|
41853
|
+
import { defineCommand as defineCommand157 } from "citty";
|
|
41064
41854
|
|
|
41065
41855
|
// src/engine/landing/lib/constants.ts
|
|
41066
41856
|
var OVERUSED_FONTS = /* @__PURE__ */ new Set([
|
|
@@ -42099,13 +42889,13 @@ function describeCounts(findings) {
|
|
|
42099
42889
|
}
|
|
42100
42890
|
|
|
42101
42891
|
// src/engine/landing/lib/referenceStore.ts
|
|
42102
|
-
import { mkdir as mkdir8, readFile as
|
|
42103
|
-
import
|
|
42892
|
+
import { mkdir as mkdir8, readFile as readFile22, writeFile as writeFile12 } from "fs/promises";
|
|
42893
|
+
import path27 from "path";
|
|
42104
42894
|
var REFERENCES_FILE = ".cache/inspiration-refs.json";
|
|
42105
42895
|
var REFERENCE_TTL_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
42106
42896
|
async function readReferences(projectRoot) {
|
|
42107
42897
|
try {
|
|
42108
|
-
const raw = await
|
|
42898
|
+
const raw = await readFile22(path27.join(projectRoot, REFERENCES_FILE), "utf8");
|
|
42109
42899
|
const parsed = JSON.parse(raw);
|
|
42110
42900
|
if (!Array.isArray(parsed)) return [];
|
|
42111
42901
|
const cutoff = Date.now() - REFERENCE_TTL_MS;
|
|
@@ -42129,8 +42919,8 @@ async function recordReferences(projectRoot, references) {
|
|
|
42129
42919
|
const existing = await readReferences(projectRoot);
|
|
42130
42920
|
const replaced = new Set(references.map((reference) => reference.sectionId));
|
|
42131
42921
|
const merged = [...existing.filter((entry) => !replaced.has(entry.sectionId)), ...references];
|
|
42132
|
-
const file =
|
|
42133
|
-
await mkdir8(
|
|
42922
|
+
const file = path27.join(projectRoot, REFERENCES_FILE);
|
|
42923
|
+
await mkdir8(path27.dirname(file), { recursive: true });
|
|
42134
42924
|
await writeFile12(file, `${JSON.stringify(merged, null, 2)}
|
|
42135
42925
|
`);
|
|
42136
42926
|
return true;
|
|
@@ -42141,13 +42931,13 @@ async function recordReferences(projectRoot, references) {
|
|
|
42141
42931
|
|
|
42142
42932
|
// src/commands/landing/snapshot.ts
|
|
42143
42933
|
import { mkdir as mkdir9, rename as rename2, writeFile as writeFile13 } from "fs/promises";
|
|
42144
|
-
import
|
|
42934
|
+
import path28 from "path";
|
|
42145
42935
|
var CRITIC_VERSION = "2";
|
|
42146
42936
|
function critiqueCacheDir(projectRoot) {
|
|
42147
|
-
return
|
|
42937
|
+
return path28.join(projectRoot, ".cache", "landing-critique");
|
|
42148
42938
|
}
|
|
42149
42939
|
function snapshotPath(projectRoot, slug) {
|
|
42150
|
-
return
|
|
42940
|
+
return path28.join(critiqueCacheDir(projectRoot), `${slug}.json`);
|
|
42151
42941
|
}
|
|
42152
42942
|
async function writeCritiqueSnapshot(projectRoot, snapshot) {
|
|
42153
42943
|
await mkdir9(critiqueCacheDir(projectRoot), { recursive: true });
|
|
@@ -42159,30 +42949,26 @@ async function writeCritiqueSnapshot(projectRoot, snapshot) {
|
|
|
42159
42949
|
}
|
|
42160
42950
|
|
|
42161
42951
|
// src/commands/landing/source-version.ts
|
|
42162
|
-
import {
|
|
42163
|
-
import
|
|
42164
|
-
|
|
42165
|
-
|
|
42166
|
-
|
|
42167
|
-
|
|
42168
|
-
for (const abs of await walkAstro(componentsDir)) {
|
|
42169
|
-
rel.push(path28.relative(landingDir, abs).split(path28.sep).join("/"));
|
|
42170
|
-
}
|
|
42171
|
-
return rel.sort((a, b) => a < b ? -1 : a > b ? 1 : 0);
|
|
42952
|
+
import { readFile as readFile23 } from "fs/promises";
|
|
42953
|
+
import path29 from "path";
|
|
42954
|
+
var CRITIQUED_ROOTS = ["src/pages/", "src/components/"];
|
|
42955
|
+
async function landingSourceRelPaths(root, slug) {
|
|
42956
|
+
const files = await landingGraphFiles(root, slug);
|
|
42957
|
+
return [...files].filter((rel) => rel.endsWith(".astro") && CRITIQUED_ROOTS.some((r) => rel.startsWith(r))).sort((a, b) => a < b ? -1 : a > b ? 1 : 0);
|
|
42172
42958
|
}
|
|
42173
|
-
async function readLandingSources(
|
|
42174
|
-
const rel = await landingSourceRelPaths(
|
|
42959
|
+
async function readLandingSources(root, slug) {
|
|
42960
|
+
const rel = await landingSourceRelPaths(root, slug);
|
|
42175
42961
|
const out = [];
|
|
42176
|
-
for (const r of rel) out.push({ path: r, text: await
|
|
42962
|
+
for (const r of rel) out.push({ path: r, text: await readFile23(path29.join(root, r), "utf8") });
|
|
42177
42963
|
return out;
|
|
42178
42964
|
}
|
|
42179
|
-
async function computeLandingSourceSha(
|
|
42180
|
-
const rel = await landingSourceRelPaths(
|
|
42965
|
+
async function computeLandingSourceSha(root, slug) {
|
|
42966
|
+
const rel = await landingSourceRelPaths(root, slug);
|
|
42181
42967
|
const parts = [];
|
|
42182
42968
|
for (const r of rel) {
|
|
42183
42969
|
let bytes;
|
|
42184
42970
|
try {
|
|
42185
|
-
bytes = await
|
|
42971
|
+
bytes = await readFile23(path29.join(root, r));
|
|
42186
42972
|
} catch {
|
|
42187
42973
|
bytes = Buffer.alloc(0);
|
|
42188
42974
|
}
|
|
@@ -42190,28 +42976,6 @@ async function computeLandingSourceSha(landingDir) {
|
|
|
42190
42976
|
}
|
|
42191
42977
|
return sha256Hex(Buffer.concat(parts));
|
|
42192
42978
|
}
|
|
42193
|
-
async function isFile(p) {
|
|
42194
|
-
try {
|
|
42195
|
-
return (await stat5(p)).isFile();
|
|
42196
|
-
} catch {
|
|
42197
|
-
return false;
|
|
42198
|
-
}
|
|
42199
|
-
}
|
|
42200
|
-
async function walkAstro(dir) {
|
|
42201
|
-
let entries;
|
|
42202
|
-
try {
|
|
42203
|
-
entries = await readdir8(dir, { withFileTypes: true });
|
|
42204
|
-
} catch {
|
|
42205
|
-
return [];
|
|
42206
|
-
}
|
|
42207
|
-
const out = [];
|
|
42208
|
-
for (const entry of entries) {
|
|
42209
|
-
const abs = path28.join(dir, entry.name);
|
|
42210
|
-
if (entry.isDirectory()) out.push(...await walkAstro(abs));
|
|
42211
|
-
else if (entry.isFile() && entry.name.endsWith(".astro")) out.push(abs);
|
|
42212
|
-
}
|
|
42213
|
-
return out;
|
|
42214
|
-
}
|
|
42215
42979
|
|
|
42216
42980
|
// src/commands/landing/critique.ts
|
|
42217
42981
|
registerSchema({
|
|
@@ -42236,14 +43000,14 @@ function parseCritiqueSlugs(args) {
|
|
|
42236
43000
|
const all = [args.slug, ...rest].filter((s) => typeof s === "string" && s.length > 0);
|
|
42237
43001
|
return [...new Set(all.map(String))];
|
|
42238
43002
|
}
|
|
42239
|
-
function
|
|
43003
|
+
function fail6(code, message, fix) {
|
|
42240
43004
|
process.stderr.write(
|
|
42241
43005
|
`${JSON.stringify({ ok: false, error: { code, message, ...fix ? { fix } : {} } }, null, 2)}
|
|
42242
43006
|
`
|
|
42243
43007
|
);
|
|
42244
43008
|
process.exit(2);
|
|
42245
43009
|
}
|
|
42246
|
-
var critiqueCommand2 =
|
|
43010
|
+
var critiqueCommand2 = defineCommand157({
|
|
42247
43011
|
meta: {
|
|
42248
43012
|
name: "critique",
|
|
42249
43013
|
description: "Start here: `baker landing critique <slug>` after building or editing a landing. Deterministic design-quality critic (ADVISORY \u2014 findings never fail it). Flags the known AI design tells (gradient text, overused fonts, side-tab borders, cream palettes, buzzword copy, broken images) tiered block/warn/advisory, respecting the client's BRAND.md as the allowlist. Also records the critique that publishing requires \u2014 run it before finishing a landing."
|
|
@@ -42261,14 +43025,14 @@ var critiqueCommand2 = defineCommand156({
|
|
|
42261
43025
|
const projectRoot = process.cwd();
|
|
42262
43026
|
for (const slug of slugs) {
|
|
42263
43027
|
if (!SLUG_RE.test(slug) || slug.includes("..")) {
|
|
42264
|
-
|
|
43028
|
+
fail6(
|
|
42265
43029
|
"INVALID_SLUG",
|
|
42266
43030
|
`"${slug}" is not a landing slug \u2014 use the folder name directly under src/pages/ (letters, digits, dashes; not a path, not a _-private folder).`,
|
|
42267
43031
|
{ availableSlugs: await listLandingSlugs(projectRoot) }
|
|
42268
43032
|
);
|
|
42269
43033
|
}
|
|
42270
|
-
if (!await isDir(
|
|
42271
|
-
|
|
43034
|
+
if (!await isDir(path30.resolve(projectRoot, "src", "pages", slug))) {
|
|
43035
|
+
fail6("NOT_FOUND", `No landing at src/pages/${slug}/`, {
|
|
42272
43036
|
availableSlugs: await listLandingSlugs(projectRoot)
|
|
42273
43037
|
});
|
|
42274
43038
|
}
|
|
@@ -42306,8 +43070,10 @@ var critiqueCommand2 = defineCommand156({
|
|
|
42306
43070
|
}
|
|
42307
43071
|
});
|
|
42308
43072
|
async function critiqueOne(projectRoot, slug, brand, references) {
|
|
42309
|
-
const
|
|
42310
|
-
|
|
43073
|
+
const [sources, sourceSha] = await Promise.all([
|
|
43074
|
+
readLandingSources(projectRoot, slug),
|
|
43075
|
+
computeLandingSourceSha(projectRoot, slug)
|
|
43076
|
+
]);
|
|
42311
43077
|
const report = critiqueLanding({ slug, sources, brand, references });
|
|
42312
43078
|
let snapshotFailed = false;
|
|
42313
43079
|
try {
|
|
@@ -42326,7 +43092,7 @@ async function critiqueOne(projectRoot, slug, brand, references) {
|
|
|
42326
43092
|
}
|
|
42327
43093
|
async function listLandingSlugs(projectRoot) {
|
|
42328
43094
|
try {
|
|
42329
|
-
const entries = await readdir9(
|
|
43095
|
+
const entries = await readdir9(path30.join(projectRoot, "src", "pages"), { withFileTypes: true });
|
|
42330
43096
|
return entries.filter((e) => e.isDirectory() && !e.name.startsWith("_") && !e.name.startsWith(".")).map((e) => e.name).sort();
|
|
42331
43097
|
} catch {
|
|
42332
43098
|
return [];
|
|
@@ -42352,10 +43118,10 @@ async function isDir(p) {
|
|
|
42352
43118
|
}
|
|
42353
43119
|
|
|
42354
43120
|
// src/commands/landing/inspiration/index.ts
|
|
42355
|
-
import { defineCommand as
|
|
43121
|
+
import { defineCommand as defineCommand166 } from "citty";
|
|
42356
43122
|
|
|
42357
43123
|
// src/commands/landing/inspiration/add.ts
|
|
42358
|
-
import { defineCommand as
|
|
43124
|
+
import { defineCommand as defineCommand158 } from "citty";
|
|
42359
43125
|
|
|
42360
43126
|
// src/commands/landing/inspiration/shared.ts
|
|
42361
43127
|
var INSPIRATION_HINTS = {
|
|
@@ -42437,7 +43203,7 @@ registerSchema({
|
|
|
42437
43203
|
note: { type: "string", description: "Why this page is worth keeping", required: false }
|
|
42438
43204
|
}
|
|
42439
43205
|
});
|
|
42440
|
-
var addCommand =
|
|
43206
|
+
var addCommand = defineCommand158({
|
|
42441
43207
|
meta: {
|
|
42442
43208
|
name: "add",
|
|
42443
43209
|
description: "Add someone else's landing page to the reference library. Example: baker landing inspiration add https://linear.app --note 'the client likes this density'"
|
|
@@ -42478,8 +43244,8 @@ var addCommand = defineCommand157({
|
|
|
42478
43244
|
|
|
42479
43245
|
// src/commands/landing/inspiration/code.ts
|
|
42480
43246
|
import { mkdir as mkdir10, writeFile as writeFile14 } from "fs/promises";
|
|
42481
|
-
import
|
|
42482
|
-
import { defineCommand as
|
|
43247
|
+
import path31 from "path";
|
|
43248
|
+
import { defineCommand as defineCommand159 } from "citty";
|
|
42483
43249
|
registerSchema({
|
|
42484
43250
|
command: "landing.inspiration.code",
|
|
42485
43251
|
description: "Write a reference section's standalone HTML+CSS to .baker/inspiration/<id>/ so you can read how it is built. Reference only \u2014 the structure is the lesson, the words are not yours to reuse. Consulting a section records it, and `baker landing critique` blocks a publish that ships its copy verbatim.",
|
|
@@ -42488,7 +43254,7 @@ registerSchema({
|
|
|
42488
43254
|
full: { type: "boolean", description: "Print the markup inline as well as writing it", required: false }
|
|
42489
43255
|
}
|
|
42490
43256
|
});
|
|
42491
|
-
var codeCommand =
|
|
43257
|
+
var codeCommand = defineCommand159({
|
|
42492
43258
|
meta: {
|
|
42493
43259
|
name: "code",
|
|
42494
43260
|
description: "Write one reference section's standalone markup to disk. Example: baker landing inspiration code k57abc\u2026 \u2014 read it for structure, then build your own."
|
|
@@ -42501,9 +43267,9 @@ var codeCommand = defineCommand158({
|
|
|
42501
43267
|
try {
|
|
42502
43268
|
const id = args.id;
|
|
42503
43269
|
const data = await apiGet("/api/landing-inspiration/section-code", { id });
|
|
42504
|
-
const dir =
|
|
43270
|
+
const dir = path31.join(process.cwd(), ".baker", "inspiration", id);
|
|
42505
43271
|
await mkdir10(dir, { recursive: true });
|
|
42506
|
-
const file =
|
|
43272
|
+
const file = path31.join(dir, "section.html");
|
|
42507
43273
|
await writeFile14(file, data.html);
|
|
42508
43274
|
const recorded = await recordReference(process.cwd(), {
|
|
42509
43275
|
sectionId: id,
|
|
@@ -42524,7 +43290,7 @@ var codeCommand = defineCommand158({
|
|
|
42524
43290
|
ok: true,
|
|
42525
43291
|
data: {
|
|
42526
43292
|
id,
|
|
42527
|
-
file:
|
|
43293
|
+
file: path31.relative(process.cwd(), file),
|
|
42528
43294
|
bytes: data.html.length,
|
|
42529
43295
|
fidelity: data.fidelity,
|
|
42530
43296
|
reproduction_notes: data.reproductionNotes,
|
|
@@ -42545,7 +43311,7 @@ var codeCommand = defineCommand158({
|
|
|
42545
43311
|
});
|
|
42546
43312
|
|
|
42547
43313
|
// src/commands/landing/inspiration/favorites.ts
|
|
42548
|
-
import { defineCommand as
|
|
43314
|
+
import { defineCommand as defineCommand160 } from "citty";
|
|
42549
43315
|
registerSchema({
|
|
42550
43316
|
command: "landing.inspiration.favorites",
|
|
42551
43317
|
description: "List the reference sections this company has saved. This is what `search` looks at by default, so it is the client's own taste profile \u2014 read it before proposing a direction.",
|
|
@@ -42559,7 +43325,7 @@ registerSchema({
|
|
|
42559
43325
|
}
|
|
42560
43326
|
}
|
|
42561
43327
|
});
|
|
42562
|
-
var favoritesCommand =
|
|
43328
|
+
var favoritesCommand = defineCommand160({
|
|
42563
43329
|
meta: {
|
|
42564
43330
|
name: "favorites",
|
|
42565
43331
|
description: "List this company's saved reference sections. Example: baker landing inspiration favorites --type hero,pricing"
|
|
@@ -42639,7 +43405,7 @@ registerSchema({
|
|
|
42639
43405
|
note: { type: "string", description: "Why this is worth keeping", required: false }
|
|
42640
43406
|
}
|
|
42641
43407
|
});
|
|
42642
|
-
var favoriteCommand =
|
|
43408
|
+
var favoriteCommand = defineCommand160({
|
|
42643
43409
|
meta: {
|
|
42644
43410
|
name: "favorite",
|
|
42645
43411
|
description: "Save a reference section to this company. Example: baker landing inspiration favorite k57abc\u2026"
|
|
@@ -42677,7 +43443,7 @@ registerSchema({
|
|
|
42677
43443
|
page: { type: "boolean", description: "Treat the id as a page rather than a section", required: false }
|
|
42678
43444
|
}
|
|
42679
43445
|
});
|
|
42680
|
-
var unfavoriteCommand =
|
|
43446
|
+
var unfavoriteCommand = defineCommand160({
|
|
42681
43447
|
meta: {
|
|
42682
43448
|
name: "unfavorite",
|
|
42683
43449
|
description: "Remove a reference section from this company's saved set. Example: baker landing inspiration unfavorite k57abc\u2026"
|
|
@@ -42699,7 +43465,7 @@ var unfavoriteCommand = defineCommand159({
|
|
|
42699
43465
|
});
|
|
42700
43466
|
|
|
42701
43467
|
// src/commands/landing/inspiration/page.ts
|
|
42702
|
-
import { defineCommand as
|
|
43468
|
+
import { defineCommand as defineCommand161 } from "citty";
|
|
42703
43469
|
registerSchema({
|
|
42704
43470
|
command: "landing.inspiration.page",
|
|
42705
43471
|
description: "Show a whole reference page as a sequence: every section top to bottom with its type and the idea behind it. This is the view to use when the question is how a good page is ORDERED rather than what one section looks like.",
|
|
@@ -42716,7 +43482,7 @@ registerSchema({
|
|
|
42716
43482
|
}
|
|
42717
43483
|
}
|
|
42718
43484
|
});
|
|
42719
|
-
var pageCommand2 =
|
|
43485
|
+
var pageCommand2 = defineCommand161({
|
|
42720
43486
|
meta: {
|
|
42721
43487
|
name: "page",
|
|
42722
43488
|
description: "Show how a reference page sequences its sections. Example: baker landing inspiration page j91xyz\u2026 \u2014 the blueprint, not the pixels."
|
|
@@ -42779,9 +43545,9 @@ var pageCommand2 = defineCommand160({
|
|
|
42779
43545
|
});
|
|
42780
43546
|
|
|
42781
43547
|
// src/commands/landing/inspiration/scrape.ts
|
|
42782
|
-
import { readFile as
|
|
42783
|
-
import
|
|
42784
|
-
import { defineCommand as
|
|
43548
|
+
import { readFile as readFile24 } from "fs/promises";
|
|
43549
|
+
import path35 from "path";
|
|
43550
|
+
import { defineCommand as defineCommand162 } from "citty";
|
|
42785
43551
|
|
|
42786
43552
|
// src/engine/landing/lib/capturedReferences.ts
|
|
42787
43553
|
var MAX_STRINGS_PER_SECTION = 40;
|
|
@@ -42973,7 +43739,7 @@ function classifyCaptureFailure(error) {
|
|
|
42973
43739
|
|
|
42974
43740
|
// src/engine/landing-library/run.ts
|
|
42975
43741
|
import { mkdir as mkdir11, writeFile as writeFile16 } from "fs/promises";
|
|
42976
|
-
import
|
|
43742
|
+
import path33 from "path";
|
|
42977
43743
|
|
|
42978
43744
|
// ../proxy/src/preflight.ts
|
|
42979
43745
|
import http from "http";
|
|
@@ -44389,9 +45155,9 @@ async function renderBundleToPng(browser, html, viewportWidth, options = {}) {
|
|
|
44389
45155
|
|
|
44390
45156
|
// src/engine/landing-library/report.ts
|
|
44391
45157
|
import { writeFile as writeFile15 } from "fs/promises";
|
|
44392
|
-
import
|
|
45158
|
+
import path32 from "path";
|
|
44393
45159
|
async function writeCaptureReport(manifest, outDir) {
|
|
44394
|
-
const file =
|
|
45160
|
+
const file = path32.join(outDir, "report.html");
|
|
44395
45161
|
await writeFile15(file, renderReport(manifest));
|
|
44396
45162
|
return file;
|
|
44397
45163
|
}
|
|
@@ -44570,32 +45336,32 @@ async function reproducePage(args) {
|
|
|
44570
45336
|
const { browser, page, outDir, pageUrl, livePageShot } = args;
|
|
44571
45337
|
const built = await buildSectionBundle(page, "body", pageUrl).catch(() => null);
|
|
44572
45338
|
if (!built) return { bundle: null, fidelity: null };
|
|
44573
|
-
await writeFile16(
|
|
45339
|
+
await writeFile16(path33.join(outDir, "page.html"), built.html);
|
|
44574
45340
|
const rendered = await renderBundleToPng(browser, built.html, DESKTOP_VIEWPORT.width, {
|
|
44575
45341
|
wholePage: true,
|
|
44576
45342
|
timeoutMs: 6e4
|
|
44577
45343
|
});
|
|
44578
45344
|
if (!rendered || !livePageShot) return { bundle: "page.html", fidelity: null };
|
|
44579
|
-
await writeFile16(
|
|
45345
|
+
await writeFile16(path33.join(outDir, "page-rendered.png"), rendered);
|
|
44580
45346
|
const { score, note } = await scoreFidelity(livePageShot, rendered);
|
|
44581
45347
|
return { bundle: "page.html", fidelity: score, ...note ? { fidelityNote: note } : {} };
|
|
44582
45348
|
}
|
|
44583
45349
|
async function captureOneSection(args) {
|
|
44584
45350
|
const { browser, page, candidate, sectionsDir, outDir, pageUrl, withCode } = args;
|
|
44585
|
-
const dir =
|
|
45351
|
+
const dir = path33.join(sectionsDir, String(candidate.index).padStart(2, "0"));
|
|
44586
45352
|
await mkdir11(dir, { recursive: true });
|
|
44587
45353
|
const desktop = await captureSection(page, candidate);
|
|
44588
|
-
if (desktop) await writeFile16(
|
|
45354
|
+
if (desktop) await writeFile16(path33.join(dir, "desktop.png"), desktop);
|
|
44589
45355
|
const visualHash = desktop ? await perceptualHash(desktop) : null;
|
|
44590
45356
|
const motion = await collectMotion(page, candidate.selector);
|
|
44591
45357
|
const built = withCode ? await buildSectionBundle(page, candidate.selector, pageUrl) : null;
|
|
44592
45358
|
let fidelity = null;
|
|
44593
45359
|
let fidelityNote;
|
|
44594
45360
|
if (built) {
|
|
44595
|
-
await writeFile16(
|
|
45361
|
+
await writeFile16(path33.join(dir, "section.html"), built.html);
|
|
44596
45362
|
const rendered = await renderBundleToPng(browser, built.html, DESKTOP_VIEWPORT.width);
|
|
44597
45363
|
if (rendered && desktop) {
|
|
44598
|
-
await writeFile16(
|
|
45364
|
+
await writeFile16(path33.join(dir, "section-rendered.png"), rendered);
|
|
44599
45365
|
const result = await scoreFidelity(desktop, rendered);
|
|
44600
45366
|
fidelity = result.score;
|
|
44601
45367
|
fidelityNote = result.note;
|
|
@@ -44603,9 +45369,9 @@ async function captureOneSection(args) {
|
|
|
44603
45369
|
}
|
|
44604
45370
|
return {
|
|
44605
45371
|
...candidate,
|
|
44606
|
-
desktopShot: desktop ?
|
|
45372
|
+
desktopShot: desktop ? path33.relative(outDir, path33.join(dir, "desktop.png")) : null,
|
|
44607
45373
|
mobileShot: null,
|
|
44608
|
-
bundle: built ?
|
|
45374
|
+
bundle: built ? path33.relative(outDir, path33.join(dir, "section.html")) : null,
|
|
44609
45375
|
fidelity,
|
|
44610
45376
|
...fidelityNote ? { fidelityNote } : {},
|
|
44611
45377
|
...built ? { cssStats: built.stats } : {},
|
|
@@ -44624,9 +45390,9 @@ async function captureMobileShots(args) {
|
|
|
44624
45390
|
for (const section of sections) {
|
|
44625
45391
|
const shot = await captureSectionOnMobile(mobile.page, section);
|
|
44626
45392
|
if (!shot) continue;
|
|
44627
|
-
const file =
|
|
45393
|
+
const file = path33.join(sectionsDir, String(section.index).padStart(2, "0"), "mobile.png");
|
|
44628
45394
|
await writeFile16(file, shot);
|
|
44629
|
-
section.mobileShot =
|
|
45395
|
+
section.mobileShot = path33.relative(outDir, file);
|
|
44630
45396
|
}
|
|
44631
45397
|
} finally {
|
|
44632
45398
|
await mobile.context.close();
|
|
@@ -44641,10 +45407,10 @@ async function captureMotionTakes(args) {
|
|
|
44641
45407
|
const filmOne = async (section) => {
|
|
44642
45408
|
const take = await captureMotionTake(browser, pageUrl, section.selector).catch(() => null);
|
|
44643
45409
|
if (!take) return;
|
|
44644
|
-
const dir =
|
|
44645
|
-
const file =
|
|
45410
|
+
const dir = path33.join(sectionsDir, String(section.index).padStart(2, "0"));
|
|
45411
|
+
const file = path33.join(dir, "motion-filmstrip.png");
|
|
44646
45412
|
await writeFile16(file, take.filmstrip);
|
|
44647
|
-
section.motionFilmstrip =
|
|
45413
|
+
section.motionFilmstrip = path33.relative(outDir, file);
|
|
44648
45414
|
log(` [${section.index}] ${section.motion.summary}`);
|
|
44649
45415
|
};
|
|
44650
45416
|
const queue = [...moving];
|
|
@@ -44701,7 +45467,7 @@ async function captureAlternateViews(args) {
|
|
|
44701
45467
|
async function reproduceWholePage(args) {
|
|
44702
45468
|
const { browser, page, outDir, pageUrl, withCode, log } = args;
|
|
44703
45469
|
const fullPage = await page.screenshot({ type: "png", fullPage: true }).catch(() => null);
|
|
44704
|
-
if (fullPage) await writeFile16(
|
|
45470
|
+
if (fullPage) await writeFile16(path33.join(outDir, "full-page.png"), fullPage);
|
|
44705
45471
|
if (!withCode) return { bundle: null, fidelity: null };
|
|
44706
45472
|
const reproduction = await reproducePage({ browser, page, outDir, pageUrl, livePageShot: fullPage });
|
|
44707
45473
|
log(`page reproduction: ${reproduction.fidelity === null ? "unavailable" : reproduction.fidelity.toFixed(2)}`);
|
|
@@ -44772,7 +45538,7 @@ async function openViaLadder(args) {
|
|
|
44772
45538
|
async function scrapeLanding(options) {
|
|
44773
45539
|
const timeoutMs = options.timeoutMs ?? 45e3;
|
|
44774
45540
|
const log = options.onProgress ?? (() => void 0);
|
|
44775
|
-
const sectionsDir =
|
|
45541
|
+
const sectionsDir = path33.join(options.outDir, "sections");
|
|
44776
45542
|
const nonPublic = refuseNonPublicUrl(options.url);
|
|
44777
45543
|
if (nonPublic) {
|
|
44778
45544
|
throw new BlockedPageError({
|
|
@@ -44834,7 +45600,7 @@ async function scrapeLanding(options) {
|
|
|
44834
45600
|
security: prepared.security,
|
|
44835
45601
|
captureTier: tier
|
|
44836
45602
|
};
|
|
44837
|
-
await writeFile16(
|
|
45603
|
+
await writeFile16(path33.join(options.outDir, "manifest.json"), `${JSON.stringify(manifest, null, 2)}
|
|
44838
45604
|
`);
|
|
44839
45605
|
if (options.report !== false) {
|
|
44840
45606
|
const reportPath = await writeCaptureReport(manifest, options.outDir);
|
|
@@ -44849,28 +45615,28 @@ async function scrapeLanding(options) {
|
|
|
44849
45615
|
|
|
44850
45616
|
// src/commands/landing/inspiration/captureOut.ts
|
|
44851
45617
|
import { existsSync as existsSync9 } from "fs";
|
|
44852
|
-
import
|
|
45618
|
+
import path34 from "path";
|
|
44853
45619
|
var SCRATCH_DIR = ".baker";
|
|
44854
45620
|
function isWithin(parent, target) {
|
|
44855
|
-
const relative =
|
|
44856
|
-
return relative === "" || !relative.startsWith("..") && !
|
|
45621
|
+
const relative = path34.relative(parent, target);
|
|
45622
|
+
return relative === "" || !relative.startsWith("..") && !path34.isAbsolute(relative);
|
|
44857
45623
|
}
|
|
44858
45624
|
function findRepoRoot(from) {
|
|
44859
|
-
let dir =
|
|
45625
|
+
let dir = path34.resolve(from);
|
|
44860
45626
|
for (; ; ) {
|
|
44861
|
-
if (existsSync9(
|
|
44862
|
-
const parent =
|
|
45627
|
+
if (existsSync9(path34.join(dir, ".git"))) return dir;
|
|
45628
|
+
const parent = path34.dirname(dir);
|
|
44863
45629
|
if (parent === dir) return null;
|
|
44864
45630
|
dir = parent;
|
|
44865
45631
|
}
|
|
44866
45632
|
}
|
|
44867
45633
|
function checkCaptureOut(out, options) {
|
|
44868
45634
|
const { cwd, repoRoot } = options;
|
|
44869
|
-
const resolved =
|
|
45635
|
+
const resolved = path34.resolve(cwd, out);
|
|
44870
45636
|
if (repoRoot === null || !isWithin(repoRoot, resolved)) return { ok: true };
|
|
44871
|
-
const scratch =
|
|
45637
|
+
const scratch = path34.join(repoRoot, SCRATCH_DIR);
|
|
44872
45638
|
if (isWithin(scratch, resolved)) return { ok: true };
|
|
44873
|
-
const suggestion =
|
|
45639
|
+
const suggestion = path34.posix.join(SCRATCH_DIR, "teardowns", path34.basename(resolved) || "capture");
|
|
44874
45640
|
return {
|
|
44875
45641
|
ok: false,
|
|
44876
45642
|
error: {
|
|
@@ -44899,7 +45665,7 @@ async function recordCapture(manifest, outDir) {
|
|
|
44899
45665
|
for (const section of manifest.sections) {
|
|
44900
45666
|
if (!section.bundle) continue;
|
|
44901
45667
|
try {
|
|
44902
|
-
const markup = await
|
|
45668
|
+
const markup = await readFile24(path35.join(outDir, section.bundle), "utf8");
|
|
44903
45669
|
const copyStrings = capturedCopyStrings(markup);
|
|
44904
45670
|
if (copyStrings.length === 0) continue;
|
|
44905
45671
|
references.push({
|
|
@@ -44960,7 +45726,7 @@ registerSchema({
|
|
|
44960
45726
|
report: { type: "boolean", description: "Write report.html. `--no-report` to skip", required: false }
|
|
44961
45727
|
}
|
|
44962
45728
|
});
|
|
44963
|
-
var scrapeCommand =
|
|
45729
|
+
var scrapeCommand = defineCommand162({
|
|
44964
45730
|
meta: {
|
|
44965
45731
|
name: "scrape",
|
|
44966
45732
|
description: "Capture a landing page to a directory, now. Example: baker landing inspiration scrape https://linear.app --out .baker/inspiration/linear.app"
|
|
@@ -45069,12 +45835,12 @@ var scrapeCommand = defineCommand161({
|
|
|
45069
45835
|
});
|
|
45070
45836
|
|
|
45071
45837
|
// src/commands/landing/inspiration/search.ts
|
|
45072
|
-
import
|
|
45073
|
-
import { defineCommand as
|
|
45838
|
+
import path37 from "path";
|
|
45839
|
+
import { defineCommand as defineCommand163 } from "citty";
|
|
45074
45840
|
|
|
45075
45841
|
// src/commands/landing/inspiration/shot.ts
|
|
45076
45842
|
import { mkdir as mkdir12, writeFile as writeFile17 } from "fs/promises";
|
|
45077
|
-
import
|
|
45843
|
+
import path36 from "path";
|
|
45078
45844
|
import sharp6 from "sharp";
|
|
45079
45845
|
var READABLE_SHOT = {
|
|
45080
45846
|
maxWidth: 1440,
|
|
@@ -45100,9 +45866,9 @@ async function downloadReadableShot(url, file) {
|
|
|
45100
45866
|
const response = await fetch(url);
|
|
45101
45867
|
if (!response.ok) return null;
|
|
45102
45868
|
const shot = await toReadableShot(Buffer.from(await response.arrayBuffer()));
|
|
45103
|
-
await mkdir12(
|
|
45869
|
+
await mkdir12(path36.dirname(file), { recursive: true });
|
|
45104
45870
|
await writeFile17(file, shot);
|
|
45105
|
-
return
|
|
45871
|
+
return path36.relative(process.cwd(), file);
|
|
45106
45872
|
} catch {
|
|
45107
45873
|
return null;
|
|
45108
45874
|
}
|
|
@@ -45194,20 +45960,20 @@ function buildSearchBody(args) {
|
|
|
45194
45960
|
return body;
|
|
45195
45961
|
}
|
|
45196
45962
|
async function downloadShots(results) {
|
|
45197
|
-
const dir =
|
|
45963
|
+
const dir = path37.join(process.cwd(), ".baker", "inspiration");
|
|
45198
45964
|
const saved = /* @__PURE__ */ new Map();
|
|
45199
45965
|
await Promise.all(
|
|
45200
45966
|
results.map(async (result) => {
|
|
45201
45967
|
const file = await downloadReadableShot(
|
|
45202
45968
|
result.desktopShotUrl,
|
|
45203
|
-
|
|
45969
|
+
path37.join(dir, `${result.id}.${READABLE_SHOT.extension}`)
|
|
45204
45970
|
);
|
|
45205
45971
|
if (file) saved.set(result.id, file);
|
|
45206
45972
|
})
|
|
45207
45973
|
);
|
|
45208
45974
|
return saved;
|
|
45209
45975
|
}
|
|
45210
|
-
var searchCommand2 =
|
|
45976
|
+
var searchCommand2 = defineCommand163({
|
|
45211
45977
|
meta: {
|
|
45212
45978
|
name: "search",
|
|
45213
45979
|
description: "Search real landing-page sections for reference. Example: baker landing inspiration search 'dark developer hero with a terminal' --register dev-tool-minimal --scope all"
|
|
@@ -45305,7 +46071,7 @@ var searchCommand2 = defineCommand162({
|
|
|
45305
46071
|
});
|
|
45306
46072
|
|
|
45307
46073
|
// src/commands/landing/inspiration/sequences.ts
|
|
45308
|
-
import { defineCommand as
|
|
46074
|
+
import { defineCommand as defineCommand164 } from "citty";
|
|
45309
46075
|
var COMPACT_TRANSITIONS = 12;
|
|
45310
46076
|
var COMPACT_ENDS = 5;
|
|
45311
46077
|
var MAX_PAGES = 50;
|
|
@@ -45379,7 +46145,7 @@ function sequencesHints(data, { scope, full }) {
|
|
|
45379
46145
|
if (scope === "favorites") hints.push(...favoritesScopeHints(data.favoritesHealth, data.pagesReturned));
|
|
45380
46146
|
return hints;
|
|
45381
46147
|
}
|
|
45382
|
-
var sequencesCommand =
|
|
46148
|
+
var sequencesCommand = defineCommand164({
|
|
45383
46149
|
meta: {
|
|
45384
46150
|
name: "sequences",
|
|
45385
46151
|
description: "What section follows what, across many real pages at once. Example: baker landing inspiration sequences 'developer tool pricing page' --scope all \u2014 the evidence for how to order a page you are about to build."
|
|
@@ -45428,8 +46194,8 @@ var sequencesCommand = defineCommand163({
|
|
|
45428
46194
|
});
|
|
45429
46195
|
|
|
45430
46196
|
// src/commands/landing/inspiration/view.ts
|
|
45431
|
-
import
|
|
45432
|
-
import { defineCommand as
|
|
46197
|
+
import path38 from "path";
|
|
46198
|
+
import { defineCommand as defineCommand165 } from "citty";
|
|
45433
46199
|
registerSchema({
|
|
45434
46200
|
command: "landing.inspiration.view",
|
|
45435
46201
|
description: "Everything known about one section: composition, motion, design tokens, the copy it uses, why it works, and what must change to make it yours. Downloads the desktop and mobile screenshots plus the motion filmstrip so you can look at them.",
|
|
@@ -45442,7 +46208,7 @@ registerSchema({
|
|
|
45442
46208
|
}
|
|
45443
46209
|
}
|
|
45444
46210
|
});
|
|
45445
|
-
var viewCommand2 =
|
|
46211
|
+
var viewCommand2 = defineCommand165({
|
|
45446
46212
|
meta: {
|
|
45447
46213
|
name: "view",
|
|
45448
46214
|
description: "Full detail for one reference section. Example: baker landing inspiration view k57abc\u2026 \u2014 read the screenshots it saves before you build."
|
|
@@ -45461,12 +46227,12 @@ var viewCommand2 = defineCommand164({
|
|
|
45461
46227
|
const id = args.id;
|
|
45462
46228
|
const data = await apiGet("/api/landing-inspiration/section", { id });
|
|
45463
46229
|
const section = data.section;
|
|
45464
|
-
const dir =
|
|
46230
|
+
const dir = path38.join(process.cwd(), ".baker", "inspiration", id);
|
|
45465
46231
|
const ext = READABLE_SHOT.extension;
|
|
45466
46232
|
const [desktop, mobile, filmstrip] = await Promise.all([
|
|
45467
|
-
downloadReadableShot(section.desktopShotUrl,
|
|
45468
|
-
downloadReadableShot(section.mobileShotUrl,
|
|
45469
|
-
downloadReadableShot(section.motionFilmstripUrl,
|
|
46233
|
+
downloadReadableShot(section.desktopShotUrl, path38.join(dir, `desktop.${ext}`)),
|
|
46234
|
+
downloadReadableShot(section.mobileShotUrl, path38.join(dir, `mobile.${ext}`)),
|
|
46235
|
+
downloadReadableShot(section.motionFilmstripUrl, path38.join(dir, `motion-filmstrip.${ext}`))
|
|
45470
46236
|
]);
|
|
45471
46237
|
const full = args.full;
|
|
45472
46238
|
const hints = [INSPIRATION_HINTS.structureNotCopy, INSPIRATION_HINTS.adapt];
|
|
@@ -45526,7 +46292,7 @@ var viewCommand2 = defineCommand164({
|
|
|
45526
46292
|
});
|
|
45527
46293
|
|
|
45528
46294
|
// src/commands/landing/inspiration/index.ts
|
|
45529
|
-
var inspirationCommand =
|
|
46295
|
+
var inspirationCommand = defineCommand166({
|
|
45530
46296
|
meta: {
|
|
45531
46297
|
name: "inspiration",
|
|
45532
46298
|
description: `Reference library of real landing-page sections \u2014 look at how good pages actually solve a problem before you design one.
|
|
@@ -45570,8 +46336,310 @@ Full guide: __tooling__/docs/tools/baker/landing.md`
|
|
|
45570
46336
|
}
|
|
45571
46337
|
});
|
|
45572
46338
|
|
|
46339
|
+
// src/commands/landing/variant.ts
|
|
46340
|
+
import { randomUUID as randomUUID2 } from "crypto";
|
|
46341
|
+
import { mkdir as mkdir13, readdir as readdir10, readFile as readFile25, stat as stat7, writeFile as writeFile18 } from "fs/promises";
|
|
46342
|
+
import path40 from "path";
|
|
46343
|
+
import { defineCommand as defineCommand167 } from "citty";
|
|
46344
|
+
|
|
46345
|
+
// src/commands/landing/repoint.ts
|
|
46346
|
+
import path39 from "path";
|
|
46347
|
+
var QUOTED_RELATIVE = /(['"])(\.\.?\/[^'"]*)\1/g;
|
|
46348
|
+
function isInside3(abs, dir) {
|
|
46349
|
+
return abs === dir || abs.startsWith(dir + path39.sep);
|
|
46350
|
+
}
|
|
46351
|
+
function toSpecifier(from, to) {
|
|
46352
|
+
const rel = path39.relative(from, to).split(path39.sep).join("/");
|
|
46353
|
+
return rel.startsWith(".") ? rel : `./${rel}`;
|
|
46354
|
+
}
|
|
46355
|
+
function repointSpecifier(spec, opts) {
|
|
46356
|
+
if (!spec.startsWith(".")) return spec;
|
|
46357
|
+
const abs = path39.resolve(opts.fromDir, spec);
|
|
46358
|
+
if (opts.forkedTargets?.has(abs)) {
|
|
46359
|
+
const withinControl = path39.relative(opts.controlDir, abs);
|
|
46360
|
+
return toSpecifier(opts.toDir, path39.resolve(opts.variantDir, withinControl));
|
|
46361
|
+
}
|
|
46362
|
+
if (!isInside3(abs, opts.controlDir)) return spec;
|
|
46363
|
+
return toSpecifier(opts.toDir, abs);
|
|
46364
|
+
}
|
|
46365
|
+
function repointFile(text2, opts) {
|
|
46366
|
+
return text2.replace(QUOTED_RELATIVE, (_match, quote, spec) => {
|
|
46367
|
+
return `${quote}${repointSpecifier(spec, opts)}${quote}`;
|
|
46368
|
+
});
|
|
46369
|
+
}
|
|
46370
|
+
|
|
46371
|
+
// src/commands/landing/variant.ts
|
|
46372
|
+
registerSchema({
|
|
46373
|
+
command: "landing.variant",
|
|
46374
|
+
description: "Create the alternative version of a landing for an A/B test. The new page shares the control's sections instead of copying them, so the only difference between the two is the one you fork \u2014 which is the only way the test measures what you think it measures. Run `baker experiment plan` FIRST: most pages cannot settle most questions.",
|
|
46375
|
+
args: {
|
|
46376
|
+
control: { type: "string", description: "The page under test, by slug", required: true },
|
|
46377
|
+
slug: { type: "string", description: "Slug for the alternative page (convention: <control>-b)", required: true },
|
|
46378
|
+
fork: {
|
|
46379
|
+
type: "string",
|
|
46380
|
+
description: "The component the variant owns its own copy of, named as it appears under _components/ (`Hero.astro`, or `hero/Card.astro`). Repeat for several. Everything else is shared with the control, so a later edit to the control reaches both arms and cannot skew the test.",
|
|
46381
|
+
required: false
|
|
46382
|
+
},
|
|
46383
|
+
because: {
|
|
46384
|
+
type: "string",
|
|
46385
|
+
description: "What you SAW that makes this worth building \u2014 the observation, not the change. Required here, before the page exists, because a reason written afterwards is a caption for a page rather than the reason for one.",
|
|
46386
|
+
required: true
|
|
46387
|
+
},
|
|
46388
|
+
change: {
|
|
46389
|
+
type: "string",
|
|
46390
|
+
description: "What is different about this version, in the client's words \u2014 \u201Cput the booking form in the hero\u201D.",
|
|
46391
|
+
required: true
|
|
46392
|
+
}
|
|
46393
|
+
}
|
|
46394
|
+
});
|
|
46395
|
+
var SLUG_RE2 = /^[a-z0-9][a-z0-9-]*$/;
|
|
46396
|
+
function fail7(code, message, fix) {
|
|
46397
|
+
process.stderr.write(
|
|
46398
|
+
`${JSON.stringify({ ok: false, error: { code, message, ...fix ? { fix } : {} } }, null, 2)}
|
|
46399
|
+
`
|
|
46400
|
+
);
|
|
46401
|
+
process.exit(2);
|
|
46402
|
+
}
|
|
46403
|
+
async function isDir2(p) {
|
|
46404
|
+
try {
|
|
46405
|
+
return (await stat7(p)).isDirectory();
|
|
46406
|
+
} catch {
|
|
46407
|
+
return false;
|
|
46408
|
+
}
|
|
46409
|
+
}
|
|
46410
|
+
async function exists(p) {
|
|
46411
|
+
try {
|
|
46412
|
+
await stat7(p);
|
|
46413
|
+
return true;
|
|
46414
|
+
} catch {
|
|
46415
|
+
return false;
|
|
46416
|
+
}
|
|
46417
|
+
}
|
|
46418
|
+
async function listLandingSlugs2(root) {
|
|
46419
|
+
try {
|
|
46420
|
+
const entries = await readdir10(path40.resolve(root, "src", "pages"), { withFileTypes: true });
|
|
46421
|
+
return entries.filter((e) => e.isDirectory() && !e.name.startsWith("_") && !e.name.startsWith(".")).map((e) => e.name).sort();
|
|
46422
|
+
} catch {
|
|
46423
|
+
return [];
|
|
46424
|
+
}
|
|
46425
|
+
}
|
|
46426
|
+
async function listComponents(componentsDir, prefix = "") {
|
|
46427
|
+
let entries;
|
|
46428
|
+
try {
|
|
46429
|
+
entries = await readdir10(componentsDir, { withFileTypes: true });
|
|
46430
|
+
} catch {
|
|
46431
|
+
return [];
|
|
46432
|
+
}
|
|
46433
|
+
const out = [];
|
|
46434
|
+
for (const entry of entries) {
|
|
46435
|
+
const rel = prefix ? `${prefix}/${entry.name}` : entry.name;
|
|
46436
|
+
if (entry.isDirectory()) out.push(...await listComponents(path40.join(componentsDir, entry.name), rel));
|
|
46437
|
+
else if (entry.name.endsWith(".astro")) out.push(rel);
|
|
46438
|
+
}
|
|
46439
|
+
return out.sort();
|
|
46440
|
+
}
|
|
46441
|
+
function resolveForkName(requested, available) {
|
|
46442
|
+
const cleaned = requested.replace(/^\.?\/?(_components\/)?/, "");
|
|
46443
|
+
for (const candidate of [cleaned, `${cleaned}.astro`]) {
|
|
46444
|
+
if (available.includes(candidate)) return candidate;
|
|
46445
|
+
}
|
|
46446
|
+
const basename4 = (p) => p.slice(p.lastIndexOf("/") + 1);
|
|
46447
|
+
const matches = available.filter((a) => basename4(a) === cleaned || basename4(a) === `${cleaned}.astro`);
|
|
46448
|
+
return matches.length === 1 ? matches[0] ?? null : null;
|
|
46449
|
+
}
|
|
46450
|
+
function parseForks(fork) {
|
|
46451
|
+
const list = Array.isArray(fork) ? fork : fork === void 0 || fork === null ? [] : [fork];
|
|
46452
|
+
const names = list.filter((s) => typeof s === "string").flatMap((s) => s.split(",")).map((s) => s.trim()).filter((s) => s.length > 0);
|
|
46453
|
+
return [...new Set(names)];
|
|
46454
|
+
}
|
|
46455
|
+
function buildVariantDefinition(controlDefinition, opts) {
|
|
46456
|
+
let out = controlDefinition;
|
|
46457
|
+
out = out.replace(/^internalId:.*$/m, `internalId: "${opts.internalId}"`);
|
|
46458
|
+
out = out.replace(/^internalTitle:\s*(.*)$/m, (_m, title) => `internalTitle: ${title.trim()} (variant)`);
|
|
46459
|
+
const shared = opts.forks.length > 0 ? opts.forks.join(", ") : "nothing yet";
|
|
46460
|
+
const note = [
|
|
46461
|
+
"",
|
|
46462
|
+
`## A/B test \u2014 alternative version of \`${opts.controlSlug}\``,
|
|
46463
|
+
"",
|
|
46464
|
+
`This page is the alternative arm of a test against \`${opts.controlSlug}\`. Visitors are split at the edge and the address bar keeps saying \`/${opts.controlSlug}/\`, so both arms are the same page seen by different people.`,
|
|
46465
|
+
"",
|
|
46466
|
+
`**Only this differs:** ${shared}. Every other section is imported from \`${opts.controlSlug}\`, on purpose \u2014 an edit to the control reaches both arms, so the test keeps measuring the one difference rather than a drift between two copies.`,
|
|
46467
|
+
...opts.because ? ["", `**Because:** ${opts.because}`] : [],
|
|
46468
|
+
...opts.change ? ["", `**We changed:** ${opts.change}`] : [],
|
|
46469
|
+
"",
|
|
46470
|
+
`Run \`baker experiment start --landing ${opts.controlSlug} --variant ${opts.variantSlug} --because "\u2026" --change "\u2026"\` to begin, and read the result with \`baker experiment status\`.`,
|
|
46471
|
+
""
|
|
46472
|
+
].join("\n");
|
|
46473
|
+
return `${out.trimEnd()}
|
|
46474
|
+
${note}`;
|
|
46475
|
+
}
|
|
46476
|
+
async function resolveTargets(projectRoot, opts) {
|
|
46477
|
+
const { controlSlug, variantSlug } = opts;
|
|
46478
|
+
for (const [label, slug] of [
|
|
46479
|
+
["control", controlSlug],
|
|
46480
|
+
["slug", variantSlug]
|
|
46481
|
+
]) {
|
|
46482
|
+
if (!SLUG_RE2.test(slug)) {
|
|
46483
|
+
fail7(
|
|
46484
|
+
"INVALID_SLUG",
|
|
46485
|
+
`"${slug}" is not a landing slug \u2014 use the folder name directly under src/pages/ (lowercase letters, digits and dashes).`,
|
|
46486
|
+
{ argument: label, availableSlugs: await listLandingSlugs2(projectRoot) }
|
|
46487
|
+
);
|
|
46488
|
+
}
|
|
46489
|
+
}
|
|
46490
|
+
if (controlSlug === variantSlug) {
|
|
46491
|
+
fail7("INVALID_SLUG", `The alternative needs its own slug \u2014 try \`${controlSlug}-b\`.`);
|
|
46492
|
+
}
|
|
46493
|
+
const controlDir = path40.resolve(projectRoot, "src", "pages", controlSlug);
|
|
46494
|
+
const variantDir = path40.resolve(projectRoot, "src", "pages", variantSlug);
|
|
46495
|
+
if (!await isDir2(controlDir)) {
|
|
46496
|
+
fail7("NOT_FOUND", `No landing at src/pages/${controlSlug}/`, {
|
|
46497
|
+
availableSlugs: await listLandingSlugs2(projectRoot)
|
|
46498
|
+
});
|
|
46499
|
+
}
|
|
46500
|
+
if (await exists(variantDir)) {
|
|
46501
|
+
fail7(
|
|
46502
|
+
"ALREADY_EXISTS",
|
|
46503
|
+
`src/pages/${variantSlug}/ already exists \u2014 pick another slug, or delete that page if it was a false start.`
|
|
46504
|
+
);
|
|
46505
|
+
}
|
|
46506
|
+
if (!await exists(path40.join(controlDir, "index.astro"))) {
|
|
46507
|
+
fail7("NOT_FOUND", `src/pages/${controlSlug}/index.astro is missing, so there is no page to make a variant of.`);
|
|
46508
|
+
}
|
|
46509
|
+
const available = await listComponents(path40.join(controlDir, "_components"));
|
|
46510
|
+
const forks = [];
|
|
46511
|
+
for (const name of parseForks(opts.fork)) {
|
|
46512
|
+
const resolved = resolveForkName(name, available);
|
|
46513
|
+
if (resolved === null) {
|
|
46514
|
+
fail7("NOT_FOUND", `"${name}" is not a component of ${controlSlug}.`, {
|
|
46515
|
+
availableComponents: available,
|
|
46516
|
+
hint: "Name it as it appears under _components/ \u2014 `Hero.astro`, or `hero/Card.astro`."
|
|
46517
|
+
});
|
|
46518
|
+
}
|
|
46519
|
+
forks.push(resolved);
|
|
46520
|
+
}
|
|
46521
|
+
return { controlDir, variantDir, available, forks };
|
|
46522
|
+
}
|
|
46523
|
+
async function writeVariant(opts) {
|
|
46524
|
+
const { controlDir, variantDir, controlSlug, variantSlug, forks } = opts;
|
|
46525
|
+
const forkedTargets = new Set(forks.map((f) => path40.join(controlDir, "_components", f)));
|
|
46526
|
+
const written = [];
|
|
46527
|
+
const indexText = await readFile25(path40.join(controlDir, "index.astro"), "utf8");
|
|
46528
|
+
await mkdir13(variantDir, { recursive: true });
|
|
46529
|
+
await writeFile18(
|
|
46530
|
+
path40.join(variantDir, "index.astro"),
|
|
46531
|
+
repointFile(indexText, { fromDir: controlDir, toDir: variantDir, controlDir, variantDir, forkedTargets }),
|
|
46532
|
+
"utf8"
|
|
46533
|
+
);
|
|
46534
|
+
written.push(`src/pages/${variantSlug}/index.astro`);
|
|
46535
|
+
for (const fork of forks) {
|
|
46536
|
+
const fromFile = path40.join(controlDir, "_components", fork);
|
|
46537
|
+
const toFile = path40.join(variantDir, "_components", fork);
|
|
46538
|
+
const text2 = await readFile25(fromFile, "utf8");
|
|
46539
|
+
await mkdir13(path40.dirname(toFile), { recursive: true });
|
|
46540
|
+
await writeFile18(
|
|
46541
|
+
toFile,
|
|
46542
|
+
repointFile(text2, {
|
|
46543
|
+
fromDir: path40.dirname(fromFile),
|
|
46544
|
+
toDir: path40.dirname(toFile),
|
|
46545
|
+
controlDir,
|
|
46546
|
+
variantDir,
|
|
46547
|
+
forkedTargets
|
|
46548
|
+
}),
|
|
46549
|
+
"utf8"
|
|
46550
|
+
);
|
|
46551
|
+
written.push(`src/pages/${variantSlug}/_components/${fork}`);
|
|
46552
|
+
}
|
|
46553
|
+
const controlDefinitionPath = path40.join(controlDir, "_definition.md");
|
|
46554
|
+
if (await exists(controlDefinitionPath)) {
|
|
46555
|
+
const definition = buildVariantDefinition(await readFile25(controlDefinitionPath, "utf8"), {
|
|
46556
|
+
internalId: randomUUID2().replace(/-/g, "").slice(0, 8),
|
|
46557
|
+
variantSlug,
|
|
46558
|
+
controlSlug,
|
|
46559
|
+
forks,
|
|
46560
|
+
...opts.because ? { because: opts.because } : {},
|
|
46561
|
+
...opts.change ? { change: opts.change } : {}
|
|
46562
|
+
});
|
|
46563
|
+
await writeFile18(path40.join(variantDir, "_definition.md"), definition, "utf8");
|
|
46564
|
+
written.push(`src/pages/${variantSlug}/_definition.md`);
|
|
46565
|
+
}
|
|
46566
|
+
await mkdir13(path40.join(variantDir, "_images"), { recursive: true });
|
|
46567
|
+
await writeFile18(path40.join(variantDir, "_images", ".gitkeep"), "", "utf8");
|
|
46568
|
+
return written;
|
|
46569
|
+
}
|
|
46570
|
+
var variantCommand = defineCommand167({
|
|
46571
|
+
meta: {
|
|
46572
|
+
name: "variant",
|
|
46573
|
+
description: "Create the alternative version of a landing for an A/B test. The new page SHARES the control's sections and forks only the component you name, so the two arms differ by exactly the thing you are testing. Run `baker experiment plan` first \u2014 most pages do not have the traffic to settle most questions."
|
|
46574
|
+
},
|
|
46575
|
+
args: {
|
|
46576
|
+
control: { type: "positional", required: true, description: "The page under test, by slug" },
|
|
46577
|
+
slug: {
|
|
46578
|
+
type: "positional",
|
|
46579
|
+
required: true,
|
|
46580
|
+
description: "Slug for the alternative page (convention: <control>-b)"
|
|
46581
|
+
},
|
|
46582
|
+
fork: {
|
|
46583
|
+
type: "string",
|
|
46584
|
+
description: "Component the variant owns its own copy of (`Hero.astro`). Repeat for several."
|
|
46585
|
+
},
|
|
46586
|
+
because: {
|
|
46587
|
+
type: "string",
|
|
46588
|
+
required: true,
|
|
46589
|
+
description: "What you SAW that makes this worth building \u2014 the observation, not the change. Required here, before the page exists, because a reason written afterwards is a caption for a page rather than the reason for one, and a test with no stated belief teaches nothing whichever way it lands. Recorded in the page's notes and passed to `baker experiment start`."
|
|
46590
|
+
},
|
|
46591
|
+
change: {
|
|
46592
|
+
type: "string",
|
|
46593
|
+
required: true,
|
|
46594
|
+
description: "What is different about this version, in the client's words \u2014 \u201Cput the booking form in the hero\u201D."
|
|
46595
|
+
}
|
|
46596
|
+
},
|
|
46597
|
+
async run({ args }) {
|
|
46598
|
+
const projectRoot = process.cwd();
|
|
46599
|
+
const controlSlug = String(args.control);
|
|
46600
|
+
const variantSlug = String(args.slug);
|
|
46601
|
+
const { controlDir, variantDir, available, forks } = await resolveTargets(projectRoot, {
|
|
46602
|
+
controlSlug,
|
|
46603
|
+
variantSlug,
|
|
46604
|
+
fork: args.fork
|
|
46605
|
+
});
|
|
46606
|
+
const written = await writeVariant({
|
|
46607
|
+
controlDir,
|
|
46608
|
+
variantDir,
|
|
46609
|
+
controlSlug,
|
|
46610
|
+
variantSlug,
|
|
46611
|
+
forks,
|
|
46612
|
+
because: String(args.because),
|
|
46613
|
+
change: String(args.change)
|
|
46614
|
+
});
|
|
46615
|
+
const hints = [
|
|
46616
|
+
forks.length === 0 ? `MISSING --fork: ${variantSlug} currently renders exactly ${controlSlug}, so a test between them cannot conclude anything. Re-run with --fork <Component>, or copy the one section your change touches into src/pages/${variantSlug}/_components/ and repoint that import.` : `Edit ONLY src/pages/${variantSlug}/_components/${forks.join(", ")} \u2014 every other section is shared with ${controlSlug}, and editing a shared one changes both arms at once.`,
|
|
46617
|
+
`Preview both at http://localhost:4321/${controlSlug}/ and http://localhost:4321/${variantSlug}/ and check they differ only where you meant.`,
|
|
46618
|
+
`Then: baker experiment start --landing ${controlSlug} --variant ${variantSlug} --because "${String(args.because)}" --change "${String(args.change)}". Nothing is split until the session is published \u2014 the person publishing is the review of this page.`
|
|
46619
|
+
];
|
|
46620
|
+
process.stdout.write(
|
|
46621
|
+
`${JSON.stringify(
|
|
46622
|
+
{
|
|
46623
|
+
ok: true,
|
|
46624
|
+
data: {
|
|
46625
|
+
control: controlSlug,
|
|
46626
|
+
slug: variantSlug,
|
|
46627
|
+
forked: forks,
|
|
46628
|
+
shared: available.filter((c) => !forks.includes(c)),
|
|
46629
|
+
written
|
|
46630
|
+
},
|
|
46631
|
+
hints
|
|
46632
|
+
},
|
|
46633
|
+
null,
|
|
46634
|
+
2
|
|
46635
|
+
)}
|
|
46636
|
+
`
|
|
46637
|
+
);
|
|
46638
|
+
}
|
|
46639
|
+
});
|
|
46640
|
+
|
|
45573
46641
|
// src/commands/landing/index.ts
|
|
45574
|
-
var landingCommand =
|
|
46642
|
+
var landingCommand = defineCommand168({
|
|
45575
46643
|
meta: {
|
|
45576
46644
|
name: "landing",
|
|
45577
46645
|
description: `Design-quality tools for landing pages (src/pages/<slug>/).
|
|
@@ -45580,16 +46648,18 @@ Start here: \`baker landing critique <slug>\` after building or editing a landin
|
|
|
45580
46648
|
|
|
45581
46649
|
Subcommands:
|
|
45582
46650
|
baker landing inspiration \u2014 reference library of real landing-page sections; search it during research, BEFORE writing the Direction Contract. Inspiration only: take the mechanism, never the words.
|
|
46651
|
+
baker landing variant <control> <slug> \u2014 the alternative version of a page for an A/B test: shares the control's sections, forks only the component you name, so the two arms differ by exactly your hypothesis. Run \`baker experiment plan\` first.
|
|
45583
46652
|
baker landing critique <slug> \u2014 deterministic design-quality critic (advisory): flags the known AI 'slop' tells (gradient text, overused fonts, side-tab borders, cream palettes, buzzword copy, broken images) tiered block/warn/advisory, respecting the client's BRAND.md. Records the critique the publish quality gate requires \u2014 run it before finishing a landing.`
|
|
45584
46653
|
},
|
|
45585
46654
|
subCommands: {
|
|
45586
46655
|
inspiration: inspirationCommand,
|
|
45587
|
-
critique: critiqueCommand2
|
|
46656
|
+
critique: critiqueCommand2,
|
|
46657
|
+
variant: variantCommand
|
|
45588
46658
|
}
|
|
45589
46659
|
});
|
|
45590
46660
|
|
|
45591
46661
|
// src/commands/mcp/index.ts
|
|
45592
|
-
import { defineCommand as
|
|
46662
|
+
import { defineCommand as defineCommand169 } from "citty";
|
|
45593
46663
|
|
|
45594
46664
|
// src/commands/mcp/platforms.ts
|
|
45595
46665
|
function readsKey(label) {
|
|
@@ -45644,7 +46714,7 @@ function parseHeaders(raw) {
|
|
|
45644
46714
|
}
|
|
45645
46715
|
return Object.keys(headers).length > 0 ? headers : void 0;
|
|
45646
46716
|
}
|
|
45647
|
-
function
|
|
46717
|
+
function fail8(err) {
|
|
45648
46718
|
if (err instanceof ApiError) {
|
|
45649
46719
|
writeJson({ ok: false, error: { code: err.code, message: err.message } });
|
|
45650
46720
|
process.exit(1);
|
|
@@ -45658,7 +46728,7 @@ registerSchema({
|
|
|
45658
46728
|
description: "List everything this chat can reach: managed integrations (Attio, Slack, Gmail, Google Sheets, \u2026), custom MCP servers, and the platforms the company signed in to (HubSpot, Google Ads, GA4, Search Console, Tag Manager) which you read through their own `baker` commands. Start here when the user mentions an external tool or platform.",
|
|
45659
46729
|
args: {}
|
|
45660
46730
|
});
|
|
45661
|
-
var connectedCommand =
|
|
46731
|
+
var connectedCommand = defineCommand169({
|
|
45662
46732
|
meta: {
|
|
45663
46733
|
name: "connected",
|
|
45664
46734
|
description: `Everything this chat can reach \u2014 managed integrations, custom MCP servers, and connected platforms.
|
|
@@ -45708,7 +46778,7 @@ A tool or platform the user names that is NOT listed anywhere here is simply not
|
|
|
45708
46778
|
hints
|
|
45709
46779
|
});
|
|
45710
46780
|
} catch (err) {
|
|
45711
|
-
|
|
46781
|
+
fail8(err);
|
|
45712
46782
|
}
|
|
45713
46783
|
}
|
|
45714
46784
|
});
|
|
@@ -45717,7 +46787,7 @@ registerSchema({
|
|
|
45717
46787
|
description: "List the custom MCP servers this company's chats see (org + company + your own user scope).",
|
|
45718
46788
|
args: {}
|
|
45719
46789
|
});
|
|
45720
|
-
var listCommand14 =
|
|
46790
|
+
var listCommand14 = defineCommand169({
|
|
45721
46791
|
meta: { name: "list", description: "List custom MCP servers visible to this company's chats." },
|
|
45722
46792
|
run: async () => {
|
|
45723
46793
|
try {
|
|
@@ -45736,7 +46806,7 @@ var listCommand14 = defineCommand167({
|
|
|
45736
46806
|
} : {}
|
|
45737
46807
|
});
|
|
45738
46808
|
} catch (err) {
|
|
45739
|
-
|
|
46809
|
+
fail8(err);
|
|
45740
46810
|
}
|
|
45741
46811
|
}
|
|
45742
46812
|
});
|
|
@@ -45754,7 +46824,7 @@ registerSchema({
|
|
|
45754
46824
|
header: { type: "string", description: 'Auth header "Key: Value" (repeatable)', required: false }
|
|
45755
46825
|
}
|
|
45756
46826
|
});
|
|
45757
|
-
var addCommand2 =
|
|
46827
|
+
var addCommand2 = defineCommand169({
|
|
45758
46828
|
meta: {
|
|
45759
46829
|
name: "add",
|
|
45760
46830
|
description: `Register a custom MCP server. Tools appear as mcp__<name>__* on the NEXT message.
|
|
@@ -45797,7 +46867,7 @@ Examples:
|
|
|
45797
46867
|
]
|
|
45798
46868
|
});
|
|
45799
46869
|
} catch (err) {
|
|
45800
|
-
|
|
46870
|
+
fail8(err);
|
|
45801
46871
|
}
|
|
45802
46872
|
}
|
|
45803
46873
|
});
|
|
@@ -45806,7 +46876,7 @@ registerSchema({
|
|
|
45806
46876
|
description: "Remove a company custom MCP server by name.",
|
|
45807
46877
|
args: { name: { type: "string", description: "Server name to remove", required: true } }
|
|
45808
46878
|
});
|
|
45809
|
-
var removeCommand4 =
|
|
46879
|
+
var removeCommand4 = defineCommand169({
|
|
45810
46880
|
meta: {
|
|
45811
46881
|
name: "remove",
|
|
45812
46882
|
description: `Remove a company custom MCP server by name.
|
|
@@ -45824,11 +46894,11 @@ Example:
|
|
|
45824
46894
|
});
|
|
45825
46895
|
writeJson({ ok: true, data });
|
|
45826
46896
|
} catch (err) {
|
|
45827
|
-
|
|
46897
|
+
fail8(err);
|
|
45828
46898
|
}
|
|
45829
46899
|
}
|
|
45830
46900
|
});
|
|
45831
|
-
var mcpCommand =
|
|
46901
|
+
var mcpCommand = defineCommand169({
|
|
45832
46902
|
meta: {
|
|
45833
46903
|
name: "mcp",
|
|
45834
46904
|
description: `Third-party tools for this company \u2014 see what's connected, register custom HTTPS MCP endpoints.
|
|
@@ -45854,10 +46924,10 @@ Full guide: __tooling__/docs/tools/baker/mcp.md`
|
|
|
45854
46924
|
});
|
|
45855
46925
|
|
|
45856
46926
|
// src/commands/research/index.ts
|
|
45857
|
-
import { defineCommand as
|
|
46927
|
+
import { defineCommand as defineCommand181 } from "citty";
|
|
45858
46928
|
|
|
45859
46929
|
// src/commands/research/advertisers.ts
|
|
45860
|
-
import { defineCommand as
|
|
46930
|
+
import { defineCommand as defineCommand170 } from "citty";
|
|
45861
46931
|
|
|
45862
46932
|
// src/commands/research/hints.ts
|
|
45863
46933
|
var AD_COPY_ROUTE = 'This returns competing DOMAINS and their SERP economics \u2014 no ad copy, no headlines, no creative. For the actual copy of a competitor\'s ads: `baker winning-ads advertisers "<brand>"` \u2192 `baker winning-ads search "<brief>" --advertiser-id <id>` \u2192 `baker winning-ads content <adId>` (`primary_text` / `headline` / `cta`, plus the spoken transcript and on-screen text for video). That corpus is Meta and LinkedIn only \u2014 Google SERP ad copy is not available through any Baker command, so do not keep querying for it here.';
|
|
@@ -46040,7 +47110,7 @@ var FIELDS3 = {
|
|
|
46040
47110
|
etv: "Estimated traffic value (USD)",
|
|
46041
47111
|
visibility: "SERP visibility score (0-1)"
|
|
46042
47112
|
};
|
|
46043
|
-
var advertisersCommand =
|
|
47113
|
+
var advertisersCommand = defineCommand170({
|
|
46044
47114
|
meta: {
|
|
46045
47115
|
name: "advertisers",
|
|
46046
47116
|
description: `Domains competing for a keyword in Google SERPs, with position, relevance, traffic value and visibility. Returns NO ad copy \u2014 for a competitor's headlines and body copy use \`baker winning-ads content <adId>\` (Meta/LinkedIn only).
|
|
@@ -46096,7 +47166,7 @@ Examples:
|
|
|
46096
47166
|
});
|
|
46097
47167
|
|
|
46098
47168
|
// src/commands/research/autocomplete.ts
|
|
46099
|
-
import { defineCommand as
|
|
47169
|
+
import { defineCommand as defineCommand171 } from "citty";
|
|
46100
47170
|
registerSchema({
|
|
46101
47171
|
command: "research.autocomplete",
|
|
46102
47172
|
description: "Get Google Autocomplete suggestions for a seed keyword. Useful for keyword expansion and discovering what people actually search for. IMPORTANT: If --location and --language are omitted, defaults to United States (us) and English (en).",
|
|
@@ -46119,7 +47189,7 @@ registerSchema({
|
|
|
46119
47189
|
var FIELDS4 = {
|
|
46120
47190
|
suggestion: "Autocomplete suggestion from Google"
|
|
46121
47191
|
};
|
|
46122
|
-
var autocompleteCommand =
|
|
47192
|
+
var autocompleteCommand = defineCommand171({
|
|
46123
47193
|
meta: {
|
|
46124
47194
|
name: "autocomplete",
|
|
46125
47195
|
description: `Get Google Autocomplete suggestions for keyword expansion.
|
|
@@ -46174,7 +47244,7 @@ Examples:
|
|
|
46174
47244
|
});
|
|
46175
47245
|
|
|
46176
47246
|
// src/commands/research/countries.ts
|
|
46177
|
-
import { defineCommand as
|
|
47247
|
+
import { defineCommand as defineCommand172 } from "citty";
|
|
46178
47248
|
registerSchema({
|
|
46179
47249
|
command: "research.countries",
|
|
46180
47250
|
description: "List all supported country codes for --location flag in research commands.",
|
|
@@ -46231,7 +47301,7 @@ var FIELDS5 = {
|
|
|
46231
47301
|
code: "Country code to pass as --location",
|
|
46232
47302
|
name: "Country name"
|
|
46233
47303
|
};
|
|
46234
|
-
var countriesCommand =
|
|
47304
|
+
var countriesCommand = defineCommand172({
|
|
46235
47305
|
meta: {
|
|
46236
47306
|
name: "countries",
|
|
46237
47307
|
description: "List all supported country codes for --location flag."
|
|
@@ -46242,7 +47312,7 @@ var countriesCommand = defineCommand170({
|
|
|
46242
47312
|
});
|
|
46243
47313
|
|
|
46244
47314
|
// src/commands/research/fetch.ts
|
|
46245
|
-
import { defineCommand as
|
|
47315
|
+
import { defineCommand as defineCommand173 } from "citty";
|
|
46246
47316
|
var CONTENT_PREVIEW_CHARS = 2e4;
|
|
46247
47317
|
var TIMEOUT_MS = 18e4;
|
|
46248
47318
|
registerSchema({
|
|
@@ -46315,7 +47385,7 @@ function fetchFix(code) {
|
|
|
46315
47385
|
explanation: "This read failed. Don't build a retry ladder around it \u2014 finish the rest of the job with what you can reach and name this page as a gap."
|
|
46316
47386
|
};
|
|
46317
47387
|
}
|
|
46318
|
-
var fetchCommand =
|
|
47388
|
+
var fetchCommand = defineCommand173({
|
|
46319
47389
|
meta: {
|
|
46320
47390
|
name: "fetch",
|
|
46321
47391
|
description: `Read a page an ordinary web fetch could not. Bot walls and JavaScript-rendered pages are resolved for you \u2014 you never have to retry, wait, or drive a browser yourself.
|
|
@@ -46392,7 +47462,7 @@ Examples:
|
|
|
46392
47462
|
});
|
|
46393
47463
|
|
|
46394
47464
|
// src/commands/research/intent.ts
|
|
46395
|
-
import { defineCommand as
|
|
47465
|
+
import { defineCommand as defineCommand174 } from "citty";
|
|
46396
47466
|
registerSchema({
|
|
46397
47467
|
command: "research.intent",
|
|
46398
47468
|
description: "Classify Google Search intent for keywords. Determines if someone searching is looking to buy, research, or navigate. IMPORTANT: If --language is omitted, defaults to English (en). The response includes a query_context object showing which language was used.",
|
|
@@ -46415,7 +47485,7 @@ var FIELDS7 = {
|
|
|
46415
47485
|
intent: "Primary Google Search intent: informational, navigational, commercial, transactional",
|
|
46416
47486
|
probability: "Confidence score 0.0-1.0"
|
|
46417
47487
|
};
|
|
46418
|
-
var intentCommand =
|
|
47488
|
+
var intentCommand = defineCommand174({
|
|
46419
47489
|
meta: {
|
|
46420
47490
|
name: "intent",
|
|
46421
47491
|
description: `Classify Google Search intent for keywords. Returns intent type and confidence.
|
|
@@ -46463,7 +47533,7 @@ Examples:
|
|
|
46463
47533
|
});
|
|
46464
47534
|
|
|
46465
47535
|
// src/commands/research/keyword-gap.ts
|
|
46466
|
-
import { defineCommand as
|
|
47536
|
+
import { defineCommand as defineCommand175 } from "citty";
|
|
46467
47537
|
registerSchema({
|
|
46468
47538
|
command: "research.keyword-gap",
|
|
46469
47539
|
description: "Find keywords a competitor ranks for (organic or paid) that you don't. Discovers expansion opportunities. IMPORTANT: If --location and --language are omitted, defaults to United States (us) and English (en). The response includes a query_context object showing which location/language were used.",
|
|
@@ -46492,7 +47562,7 @@ var FIELDS8 = {
|
|
|
46492
47562
|
cpc: "Cost per click USD",
|
|
46493
47563
|
their_position: "Competitor's ranking position"
|
|
46494
47564
|
};
|
|
46495
|
-
var keywordGapCommand =
|
|
47565
|
+
var keywordGapCommand = defineCommand175({
|
|
46496
47566
|
meta: {
|
|
46497
47567
|
name: "keyword-gap",
|
|
46498
47568
|
description: `Find keywords a competitor has that you don't. Supports pagination via --offset.
|
|
@@ -46567,7 +47637,7 @@ Examples:
|
|
|
46567
47637
|
});
|
|
46568
47638
|
|
|
46569
47639
|
// src/commands/research/keywords-for-site.ts
|
|
46570
|
-
import { defineCommand as
|
|
47640
|
+
import { defineCommand as defineCommand176 } from "citty";
|
|
46571
47641
|
registerSchema({
|
|
46572
47642
|
command: "research.keywords-for-site",
|
|
46573
47643
|
description: "Get keywords a competitor targets in Google. Use --type paid to see only paid keywords, --type organic for organic only. IMPORTANT: If --location and --language are omitted, defaults to United States (us) and English (en). The response includes a query_context object showing which location/language were used.",
|
|
@@ -46600,7 +47670,7 @@ var FIELDS9 = {
|
|
|
46600
47670
|
competition: "LOW, MEDIUM, or HIGH",
|
|
46601
47671
|
competition_index: "Competition score 0-100"
|
|
46602
47672
|
};
|
|
46603
|
-
var keywordsForSiteCommand =
|
|
47673
|
+
var keywordsForSiteCommand = defineCommand176({
|
|
46604
47674
|
meta: {
|
|
46605
47675
|
name: "keywords-for-site",
|
|
46606
47676
|
description: `Get keywords a competitor targets in Google. Use --type to filter paid/organic.
|
|
@@ -46662,7 +47732,7 @@ Examples:
|
|
|
46662
47732
|
});
|
|
46663
47733
|
|
|
46664
47734
|
// src/commands/research/languages.ts
|
|
46665
|
-
import { defineCommand as
|
|
47735
|
+
import { defineCommand as defineCommand177 } from "citty";
|
|
46666
47736
|
registerSchema({
|
|
46667
47737
|
command: "research.languages",
|
|
46668
47738
|
description: "List all supported language codes for --language flag in research commands.",
|
|
@@ -46692,7 +47762,7 @@ var FIELDS10 = {
|
|
|
46692
47762
|
code: "Language code to pass as --language",
|
|
46693
47763
|
name: "Language name (also accepted by --language)"
|
|
46694
47764
|
};
|
|
46695
|
-
var languagesCommand2 =
|
|
47765
|
+
var languagesCommand2 = defineCommand177({
|
|
46696
47766
|
meta: {
|
|
46697
47767
|
name: "languages",
|
|
46698
47768
|
description: "List all supported language codes for --language flag."
|
|
@@ -46703,7 +47773,7 @@ var languagesCommand2 = defineCommand175({
|
|
|
46703
47773
|
});
|
|
46704
47774
|
|
|
46705
47775
|
// src/commands/research/lighthouse.ts
|
|
46706
|
-
import { defineCommand as
|
|
47776
|
+
import { defineCommand as defineCommand178 } from "citty";
|
|
46707
47777
|
registerSchema({
|
|
46708
47778
|
command: "research.lighthouse",
|
|
46709
47779
|
description: "Landing page performance audit. Returns metrics that affect Google Ads Quality Score and CPC.",
|
|
@@ -46722,7 +47792,7 @@ var FIELDS11 = {
|
|
|
46722
47792
|
speed_index_ms: "Speed Index in ms (good: < 3400)",
|
|
46723
47793
|
interactive_ms: "Time to Interactive in ms (good: < 3800)"
|
|
46724
47794
|
};
|
|
46725
|
-
var lighthouseCommand =
|
|
47795
|
+
var lighthouseCommand = defineCommand178({
|
|
46726
47796
|
meta: {
|
|
46727
47797
|
name: "lighthouse",
|
|
46728
47798
|
description: `Landing page performance audit. Metrics affecting Google Ads Quality Score.
|
|
@@ -46760,7 +47830,7 @@ Examples:
|
|
|
46760
47830
|
});
|
|
46761
47831
|
|
|
46762
47832
|
// src/commands/research/relevant-pages.ts
|
|
46763
|
-
import { defineCommand as
|
|
47833
|
+
import { defineCommand as defineCommand179 } from "citty";
|
|
46764
47834
|
registerSchema({
|
|
46765
47835
|
command: "research.relevant-pages",
|
|
46766
47836
|
description: "Get the top pages of a competitor domain with organic traffic and ranking data. Shows which pages drive the most traffic. IMPORTANT: If --location and --language are omitted, defaults to United States (us) and English (en).",
|
|
@@ -46786,7 +47856,7 @@ var FIELDS12 = {
|
|
|
46786
47856
|
keywords: "Total organic keywords the page ranks for",
|
|
46787
47857
|
top_10: "Keywords in positions 1-10"
|
|
46788
47858
|
};
|
|
46789
|
-
var relevantPagesCommand =
|
|
47859
|
+
var relevantPagesCommand = defineCommand179({
|
|
46790
47860
|
meta: {
|
|
46791
47861
|
name: "relevant-pages",
|
|
46792
47862
|
description: `Get the top pages of a competitor domain with traffic data.
|
|
@@ -46833,7 +47903,7 @@ Examples:
|
|
|
46833
47903
|
});
|
|
46834
47904
|
|
|
46835
47905
|
// src/commands/research/web.ts
|
|
46836
|
-
import { defineCommand as
|
|
47906
|
+
import { defineCommand as defineCommand180 } from "citty";
|
|
46837
47907
|
registerSchema({
|
|
46838
47908
|
command: "research.web",
|
|
46839
47909
|
description: "Search the web with AI to answer marketing questions \u2014 competitors, ICP, pricing, pain points, market trends. Three depth levels: medium (quick, default), high (thorough), xhigh (exhaustive deep research).",
|
|
@@ -46884,7 +47954,7 @@ async function runDeepResearch(question) {
|
|
|
46884
47954
|
}
|
|
46885
47955
|
throw new Error("Deep research timed out");
|
|
46886
47956
|
}
|
|
46887
|
-
var webCommand =
|
|
47957
|
+
var webCommand = defineCommand180({
|
|
46888
47958
|
meta: {
|
|
46889
47959
|
name: "web",
|
|
46890
47960
|
description: `Search the web with AI to answer any open-ended marketing question. Uses live internet data via Google Search.
|
|
@@ -46946,7 +48016,7 @@ Examples:
|
|
|
46946
48016
|
});
|
|
46947
48017
|
|
|
46948
48018
|
// src/commands/research/index.ts
|
|
46949
|
-
var researchCommand =
|
|
48019
|
+
var researchCommand = defineCommand181({
|
|
46950
48020
|
meta: {
|
|
46951
48021
|
name: "research",
|
|
46952
48022
|
description: `Competitive intelligence and AI-powered research commands.
|
|
@@ -46990,10 +48060,10 @@ Full guide: __tooling__/docs/tools/baker/research.md`
|
|
|
46990
48060
|
});
|
|
46991
48061
|
|
|
46992
48062
|
// src/commands/scheduled-actions/index.ts
|
|
46993
|
-
import { defineCommand as
|
|
48063
|
+
import { defineCommand as defineCommand189 } from "citty";
|
|
46994
48064
|
|
|
46995
48065
|
// src/commands/scheduled-actions/create.ts
|
|
46996
|
-
import { defineCommand as
|
|
48066
|
+
import { defineCommand as defineCommand182 } from "citty";
|
|
46997
48067
|
|
|
46998
48068
|
// src/commands/scheduled-actions/shared.ts
|
|
46999
48069
|
var TEMP_SCHEDULED_ACTION_PREFIX = "temp_sched_";
|
|
@@ -47140,7 +48210,7 @@ registerSchema({
|
|
|
47140
48210
|
}
|
|
47141
48211
|
}
|
|
47142
48212
|
});
|
|
47143
|
-
var createCommand3 =
|
|
48213
|
+
var createCommand3 = defineCommand182({
|
|
47144
48214
|
meta: {
|
|
47145
48215
|
name: "create",
|
|
47146
48216
|
description: 'Stage a scheduled action. Example: baker scheduled-actions create --name "Weekly report" --description "..." --cron "0 9 * * MON"'
|
|
@@ -47199,7 +48269,7 @@ var createCommand3 = defineCommand180({
|
|
|
47199
48269
|
});
|
|
47200
48270
|
|
|
47201
48271
|
// src/commands/scheduled-actions/delete.ts
|
|
47202
|
-
import { defineCommand as
|
|
48272
|
+
import { defineCommand as defineCommand183 } from "citty";
|
|
47203
48273
|
registerSchema({
|
|
47204
48274
|
command: "scheduled-actions.delete",
|
|
47205
48275
|
description: "Stage deletion of a published scheduled action or cancellation of a temp_sched_* draft creation.",
|
|
@@ -47207,7 +48277,7 @@ registerSchema({
|
|
|
47207
48277
|
id: { type: "string", description: "Published scheduled action ID or temp_sched_* draft ID", required: true }
|
|
47208
48278
|
}
|
|
47209
48279
|
});
|
|
47210
|
-
var deleteCommand3 =
|
|
48280
|
+
var deleteCommand3 = defineCommand183({
|
|
47211
48281
|
meta: {
|
|
47212
48282
|
name: "delete",
|
|
47213
48283
|
description: "Stage scheduled action deletion. Example: baker scheduled-actions delete <id-or-temp_sched_id>"
|
|
@@ -47236,7 +48306,7 @@ var deleteCommand3 = defineCommand181({
|
|
|
47236
48306
|
});
|
|
47237
48307
|
|
|
47238
48308
|
// src/commands/scheduled-actions/get.ts
|
|
47239
|
-
import { defineCommand as
|
|
48309
|
+
import { defineCommand as defineCommand184 } from "citty";
|
|
47240
48310
|
registerSchema({
|
|
47241
48311
|
command: "scheduled-actions.get",
|
|
47242
48312
|
description: "Get a published scheduled action or a temp_sched_* draft-created scheduled action.",
|
|
@@ -47245,7 +48315,7 @@ registerSchema({
|
|
|
47245
48315
|
chat: { type: "string", description: CHAT_READ_ARG.description, required: false }
|
|
47246
48316
|
}
|
|
47247
48317
|
});
|
|
47248
|
-
var getCommand4 =
|
|
48318
|
+
var getCommand4 = defineCommand184({
|
|
47249
48319
|
meta: {
|
|
47250
48320
|
name: "get",
|
|
47251
48321
|
description: "Get a scheduled action. Example: baker scheduled-actions get <id-or-temp_sched_id>"
|
|
@@ -47284,7 +48354,7 @@ var getCommand4 = defineCommand182({
|
|
|
47284
48354
|
});
|
|
47285
48355
|
|
|
47286
48356
|
// src/commands/scheduled-actions/list.ts
|
|
47287
|
-
import { defineCommand as
|
|
48357
|
+
import { defineCommand as defineCommand185 } from "citty";
|
|
47288
48358
|
registerSchema({
|
|
47289
48359
|
command: "scheduled-actions.list",
|
|
47290
48360
|
description: "List published scheduled actions. Includes draft state when BAKER_CHAT_ID is set, or --chat <id> to read an earlier chat's staged schedules instead.",
|
|
@@ -47292,7 +48362,7 @@ registerSchema({
|
|
|
47292
48362
|
chat: { type: "string", description: CHAT_READ_ARG.description, required: false }
|
|
47293
48363
|
}
|
|
47294
48364
|
});
|
|
47295
|
-
var listCommand15 =
|
|
48365
|
+
var listCommand15 = defineCommand185({
|
|
47296
48366
|
meta: {
|
|
47297
48367
|
name: "list",
|
|
47298
48368
|
description: "List scheduled actions. Includes staged draft ops when BAKER_CHAT_ID is set, or --chat <id> to read an earlier chat's staged schedules instead."
|
|
@@ -47315,9 +48385,9 @@ var listCommand15 = defineCommand183({
|
|
|
47315
48385
|
});
|
|
47316
48386
|
|
|
47317
48387
|
// src/commands/scheduled-actions/templates.ts
|
|
47318
|
-
import { readFile as
|
|
47319
|
-
import
|
|
47320
|
-
import { defineCommand as
|
|
48388
|
+
import { readFile as readFile26 } from "fs/promises";
|
|
48389
|
+
import path41 from "path";
|
|
48390
|
+
import { defineCommand as defineCommand186 } from "citty";
|
|
47321
48391
|
registerSchema({
|
|
47322
48392
|
command: "scheduled-actions.templates",
|
|
47323
48393
|
description: "The recipes available to this company: the ones Baker ships plus the ones they wrote themselves, each with its id, what it produces and how often it is meant to run. Read this before proposing a company's automation, so every recipe you name is one that exists. Also how a company gets a recipe of its own \u2014 save a brief, prove it runs, then publish it.",
|
|
@@ -47372,7 +48442,7 @@ registerSchema({
|
|
|
47372
48442
|
}
|
|
47373
48443
|
}
|
|
47374
48444
|
});
|
|
47375
|
-
var templatesCommand =
|
|
48445
|
+
var templatesCommand = defineCommand186({
|
|
47376
48446
|
meta: {
|
|
47377
48447
|
name: "templates",
|
|
47378
48448
|
description: `The recipes this company can run \u2014 Baker's own plus theirs, with what each one produces.
|
|
@@ -47419,7 +48489,7 @@ Full guide: __tooling__/docs/tools/baker/scheduled-actions.md`
|
|
|
47419
48489
|
}
|
|
47420
48490
|
if (save.length > 0) {
|
|
47421
48491
|
const briefFile = flag("brief-file");
|
|
47422
|
-
const brief = briefFile.length > 0 ? await
|
|
48492
|
+
const brief = briefFile.length > 0 ? await readFile26(path41.resolve(briefFile), "utf8") : flag("brief");
|
|
47423
48493
|
if (brief.trim().length === 0) {
|
|
47424
48494
|
failValidation4("--brief-file (preferred) or --brief is required: the brief is the recipe.");
|
|
47425
48495
|
}
|
|
@@ -47464,7 +48534,7 @@ Full guide: __tooling__/docs/tools/baker/scheduled-actions.md`
|
|
|
47464
48534
|
});
|
|
47465
48535
|
|
|
47466
48536
|
// src/commands/scheduled-actions/trigger.ts
|
|
47467
|
-
import { defineCommand as
|
|
48537
|
+
import { defineCommand as defineCommand187 } from "citty";
|
|
47468
48538
|
registerSchema({
|
|
47469
48539
|
command: "scheduled-actions.trigger",
|
|
47470
48540
|
description: "Immediately trigger a published scheduled action. Does not require BAKER_CHAT_ID and rejects temp_sched_* IDs.",
|
|
@@ -47472,7 +48542,7 @@ registerSchema({
|
|
|
47472
48542
|
id: { type: "string", description: "Published scheduled action ID", required: true }
|
|
47473
48543
|
}
|
|
47474
48544
|
});
|
|
47475
|
-
var triggerCommand =
|
|
48545
|
+
var triggerCommand = defineCommand187({
|
|
47476
48546
|
meta: {
|
|
47477
48547
|
name: "trigger",
|
|
47478
48548
|
description: "Immediately trigger a published scheduled action. Example: baker scheduled-actions trigger <id>"
|
|
@@ -47509,7 +48579,7 @@ var triggerCommand = defineCommand185({
|
|
|
47509
48579
|
});
|
|
47510
48580
|
|
|
47511
48581
|
// src/commands/scheduled-actions/update.ts
|
|
47512
|
-
import { defineCommand as
|
|
48582
|
+
import { defineCommand as defineCommand188 } from "citty";
|
|
47513
48583
|
registerSchema({
|
|
47514
48584
|
command: "scheduled-actions.update",
|
|
47515
48585
|
description: "Stage an update to a published scheduled action or temp_sched_* draft-created scheduled action.",
|
|
@@ -47540,7 +48610,7 @@ registerSchema({
|
|
|
47540
48610
|
prompt: { type: "string", description: "Replacement additional spawned-agent instructions", required: false }
|
|
47541
48611
|
}
|
|
47542
48612
|
});
|
|
47543
|
-
var updateCommand3 =
|
|
48613
|
+
var updateCommand3 = defineCommand188({
|
|
47544
48614
|
meta: {
|
|
47545
48615
|
name: "update",
|
|
47546
48616
|
description: "Stage a scheduled action update. Examples: baker scheduled-actions update <id> --enabled false | baker scheduled-actions update <id> --mode publish"
|
|
@@ -47618,7 +48688,7 @@ var updateCommand3 = defineCommand186({
|
|
|
47618
48688
|
});
|
|
47619
48689
|
|
|
47620
48690
|
// src/commands/scheduled-actions/index.ts
|
|
47621
|
-
var scheduledActionsCommand =
|
|
48691
|
+
var scheduledActionsCommand = defineCommand189({
|
|
47622
48692
|
meta: {
|
|
47623
48693
|
name: "scheduled-actions",
|
|
47624
48694
|
description: `Manage Scheduled Actions. Subcommands: list, get, create, update, delete, trigger, templates.
|
|
@@ -47647,14 +48717,14 @@ Full guide: __tooling__/docs/tools/baker/scheduled-actions.md`
|
|
|
47647
48717
|
});
|
|
47648
48718
|
|
|
47649
48719
|
// src/commands/schema.ts
|
|
47650
|
-
import { defineCommand as
|
|
48720
|
+
import { defineCommand as defineCommand190 } from "citty";
|
|
47651
48721
|
function narrowToFamily(commandName, available) {
|
|
47652
48722
|
const segments = commandName.split(".");
|
|
47653
48723
|
const prefix = segments[0] === "ads" && segments[1] ? `ads.${segments[1]}.` : `${segments[0]}.`;
|
|
47654
48724
|
const siblings = available.filter((name) => name.startsWith(prefix));
|
|
47655
48725
|
return siblings.length > 0 ? siblings : available;
|
|
47656
48726
|
}
|
|
47657
|
-
var schemaCommand2 =
|
|
48727
|
+
var schemaCommand2 = defineCommand190({
|
|
47658
48728
|
meta: {
|
|
47659
48729
|
name: "schema",
|
|
47660
48730
|
description: "Inspect command argument schemas (for AI agent introspection). Lists all commands if no argument given. Example: baker schema images.search"
|
|
@@ -47698,10 +48768,10 @@ var schemaCommand2 = defineCommand188({
|
|
|
47698
48768
|
});
|
|
47699
48769
|
|
|
47700
48770
|
// src/commands/studio/index.ts
|
|
47701
|
-
import { defineCommand as
|
|
48771
|
+
import { defineCommand as defineCommand199 } from "citty";
|
|
47702
48772
|
|
|
47703
48773
|
// src/commands/studio/animate.ts
|
|
47704
|
-
import { defineCommand as
|
|
48774
|
+
import { defineCommand as defineCommand191 } from "citty";
|
|
47705
48775
|
|
|
47706
48776
|
// src/commands/studio/batch.ts
|
|
47707
48777
|
function projectBatch(generation, full) {
|
|
@@ -47890,7 +48960,7 @@ function parseImageRefs(spec) {
|
|
|
47890
48960
|
}
|
|
47891
48961
|
var defaultDeps = {
|
|
47892
48962
|
ingest: (url) => apiPost("/api/images/ingest", { url, source: "uploaded" }),
|
|
47893
|
-
upload: (
|
|
48963
|
+
upload: (path43) => uploadLocalImage({ file: path43, contentType: detectImageContentType(path43), source: "uploaded" })
|
|
47894
48964
|
};
|
|
47895
48965
|
async function resolveLibraryImageIds(spec, limit, deps = defaultDeps) {
|
|
47896
48966
|
const refs = parseImageRefs(spec);
|
|
@@ -47910,10 +48980,10 @@ async function resolveLibraryImageIds(spec, limit, deps = defaultDeps) {
|
|
|
47910
48980
|
}
|
|
47911
48981
|
return { imageIds, added };
|
|
47912
48982
|
}
|
|
47913
|
-
function uploadFailure(
|
|
48983
|
+
function uploadFailure(path43) {
|
|
47914
48984
|
return (error) => {
|
|
47915
48985
|
if (error instanceof ApiError) throw error;
|
|
47916
|
-
throw new ApiError("VALIDATION_ERROR", `Could not read "${
|
|
48986
|
+
throw new ApiError("VALIDATION_ERROR", `Could not read "${path43}" as an image.`);
|
|
47917
48987
|
};
|
|
47918
48988
|
}
|
|
47919
48989
|
|
|
@@ -48152,7 +49222,7 @@ function costHintsFor(body) {
|
|
|
48152
49222
|
}
|
|
48153
49223
|
return hints;
|
|
48154
49224
|
}
|
|
48155
|
-
var animateCommand =
|
|
49225
|
+
var animateCommand = defineCommand191({
|
|
48156
49226
|
meta: {
|
|
48157
49227
|
name: "animate",
|
|
48158
49228
|
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 '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"
|
|
@@ -48271,7 +49341,7 @@ var animateCommand = defineCommand189({
|
|
|
48271
49341
|
});
|
|
48272
49342
|
|
|
48273
49343
|
// src/commands/studio/generate.ts
|
|
48274
|
-
import { defineCommand as
|
|
49344
|
+
import { defineCommand as defineCommand192 } from "citty";
|
|
48275
49345
|
var MODEL_LIST2 = IMAGE_MODEL_IDS;
|
|
48276
49346
|
var DEFAULT_MAX_WAIT_MS2 = 24e4;
|
|
48277
49347
|
registerSchema({
|
|
@@ -48407,7 +49477,7 @@ function buildGenerateBody(args, prompt) {
|
|
|
48407
49477
|
}
|
|
48408
49478
|
return body;
|
|
48409
49479
|
}
|
|
48410
|
-
var generateCommand =
|
|
49480
|
+
var generateCommand = defineCommand192({
|
|
48411
49481
|
meta: {
|
|
48412
49482
|
name: "generate",
|
|
48413
49483
|
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]]'"
|
|
@@ -48485,7 +49555,7 @@ var generateCommand = defineCommand190({
|
|
|
48485
49555
|
});
|
|
48486
49556
|
|
|
48487
49557
|
// src/commands/studio/get.ts
|
|
48488
|
-
import { defineCommand as
|
|
49558
|
+
import { defineCommand as defineCommand193 } from "citty";
|
|
48489
49559
|
registerSchema({
|
|
48490
49560
|
command: "studio.get",
|
|
48491
49561
|
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.",
|
|
@@ -48494,7 +49564,7 @@ registerSchema({
|
|
|
48494
49564
|
full: { type: "boolean", description: "Include settings, references and attribution", required: false }
|
|
48495
49565
|
}
|
|
48496
49566
|
});
|
|
48497
|
-
var getCommand5 =
|
|
49567
|
+
var getCommand5 = defineCommand193({
|
|
48498
49568
|
meta: {
|
|
48499
49569
|
name: "get",
|
|
48500
49570
|
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"
|
|
@@ -48523,7 +49593,7 @@ var getCommand5 = defineCommand191({
|
|
|
48523
49593
|
});
|
|
48524
49594
|
|
|
48525
49595
|
// src/commands/studio/improve.ts
|
|
48526
|
-
import { defineCommand as
|
|
49596
|
+
import { defineCommand as defineCommand194 } from "citty";
|
|
48527
49597
|
var DESCRIPTION = "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.";
|
|
48528
49598
|
registerSchema({
|
|
48529
49599
|
command: "studio.improve",
|
|
@@ -48548,7 +49618,7 @@ registerSchema({
|
|
|
48548
49618
|
}
|
|
48549
49619
|
}
|
|
48550
49620
|
});
|
|
48551
|
-
var improveCommand =
|
|
49621
|
+
var improveCommand = defineCommand194({
|
|
48552
49622
|
meta: {
|
|
48553
49623
|
name: "improve",
|
|
48554
49624
|
description: `${DESCRIPTION}
|
|
@@ -48592,7 +49662,7 @@ Examples:
|
|
|
48592
49662
|
});
|
|
48593
49663
|
|
|
48594
49664
|
// src/commands/studio/keep.ts
|
|
48595
|
-
import { defineCommand as
|
|
49665
|
+
import { defineCommand as defineCommand195 } from "citty";
|
|
48596
49666
|
registerSchema({
|
|
48597
49667
|
command: "studio.keep",
|
|
48598
49668
|
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.",
|
|
@@ -48602,7 +49672,7 @@ registerSchema({
|
|
|
48602
49672
|
undo: { type: "boolean", description: "Un-star an image, or take a kept clip back out", required: false }
|
|
48603
49673
|
}
|
|
48604
49674
|
});
|
|
48605
|
-
var keepCommand =
|
|
49675
|
+
var keepCommand = defineCommand195({
|
|
48606
49676
|
meta: {
|
|
48607
49677
|
name: "keep",
|
|
48608
49678
|
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"
|
|
@@ -48653,7 +49723,7 @@ var keepCommand = defineCommand193({
|
|
|
48653
49723
|
});
|
|
48654
49724
|
|
|
48655
49725
|
// src/commands/studio/list.ts
|
|
48656
|
-
import { defineCommand as
|
|
49726
|
+
import { defineCommand as defineCommand196 } from "citty";
|
|
48657
49727
|
registerSchema({
|
|
48658
49728
|
command: "studio.list",
|
|
48659
49729
|
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.",
|
|
@@ -48664,7 +49734,7 @@ registerSchema({
|
|
|
48664
49734
|
full: { type: "boolean", description: "Include settings, references and attribution", required: false }
|
|
48665
49735
|
}
|
|
48666
49736
|
});
|
|
48667
|
-
var listCommand16 =
|
|
49737
|
+
var listCommand16 = defineCommand196({
|
|
48668
49738
|
meta: {
|
|
48669
49739
|
name: "list",
|
|
48670
49740
|
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"
|
|
@@ -48698,7 +49768,7 @@ var listCommand16 = defineCommand194({
|
|
|
48698
49768
|
});
|
|
48699
49769
|
|
|
48700
49770
|
// src/commands/studio/models.ts
|
|
48701
|
-
import { defineCommand as
|
|
49771
|
+
import { defineCommand as defineCommand197 } from "citty";
|
|
48702
49772
|
var DESCRIPTION2 = "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.";
|
|
48703
49773
|
registerSchema({
|
|
48704
49774
|
command: "studio.models",
|
|
@@ -48785,7 +49855,7 @@ function buildModelCards(kind, model) {
|
|
|
48785
49855
|
const selected = model ? ids.filter((id) => id === model) : ids;
|
|
48786
49856
|
return selected.map((id) => build(id));
|
|
48787
49857
|
}
|
|
48788
|
-
var modelsCommand =
|
|
49858
|
+
var modelsCommand = defineCommand197({
|
|
48789
49859
|
meta: {
|
|
48790
49860
|
name: "models",
|
|
48791
49861
|
description: `${DESCRIPTION2}
|
|
@@ -48832,13 +49902,13 @@ Examples:
|
|
|
48832
49902
|
});
|
|
48833
49903
|
|
|
48834
49904
|
// src/commands/studio/skills.ts
|
|
48835
|
-
import { defineCommand as
|
|
49905
|
+
import { defineCommand as defineCommand198 } from "citty";
|
|
48836
49906
|
registerSchema({
|
|
48837
49907
|
command: "studio.skills",
|
|
48838
49908
|
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.",
|
|
48839
49909
|
args: {}
|
|
48840
49910
|
});
|
|
48841
|
-
var skillsCommand =
|
|
49911
|
+
var skillsCommand = defineCommand198({
|
|
48842
49912
|
meta: {
|
|
48843
49913
|
name: "skills",
|
|
48844
49914
|
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"
|
|
@@ -48862,7 +49932,7 @@ var skillsCommand = defineCommand196({
|
|
|
48862
49932
|
});
|
|
48863
49933
|
|
|
48864
49934
|
// src/commands/studio/index.ts
|
|
48865
|
-
var studioCommand =
|
|
49935
|
+
var studioCommand = defineCommand199({
|
|
48866
49936
|
meta: {
|
|
48867
49937
|
name: "studio",
|
|
48868
49938
|
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.
|
|
@@ -48905,10 +49975,10 @@ Full guide: __tooling__/docs/tools/baker/studio.md`
|
|
|
48905
49975
|
});
|
|
48906
49976
|
|
|
48907
49977
|
// src/commands/tag-manager/index.ts
|
|
48908
|
-
import { defineCommand as
|
|
49978
|
+
import { defineCommand as defineCommand203 } from "citty";
|
|
48909
49979
|
|
|
48910
49980
|
// src/commands/tag-manager/draft.ts
|
|
48911
|
-
import { defineCommand as
|
|
49981
|
+
import { defineCommand as defineCommand200 } from "citty";
|
|
48912
49982
|
|
|
48913
49983
|
// src/commands/tag-manager/shared.ts
|
|
48914
49984
|
import { readFileSync as readFileSync15 } from "fs";
|
|
@@ -48975,10 +50045,10 @@ async function stageOp4(op) {
|
|
|
48975
50045
|
handleError5(err);
|
|
48976
50046
|
}
|
|
48977
50047
|
}
|
|
48978
|
-
async function draftAction3(
|
|
50048
|
+
async function draftAction3(path43, body, chat) {
|
|
48979
50049
|
const chatId = resolveChatId(chat);
|
|
48980
50050
|
try {
|
|
48981
|
-
const data = await apiPost(
|
|
50051
|
+
const data = await apiPost(path43, { chatId, ...body });
|
|
48982
50052
|
writeJsonEnvelope({ ok: true, data });
|
|
48983
50053
|
return data;
|
|
48984
50054
|
} catch (err) {
|
|
@@ -49031,13 +50101,13 @@ registerSchema({
|
|
|
49031
50101
|
chat: { type: "string", description: CHAT_READ_ARG.description, required: false }
|
|
49032
50102
|
}
|
|
49033
50103
|
});
|
|
49034
|
-
var draftCommand4 =
|
|
50104
|
+
var draftCommand4 = defineCommand200({
|
|
49035
50105
|
meta: {
|
|
49036
50106
|
name: "draft",
|
|
49037
50107
|
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."
|
|
49038
50108
|
},
|
|
49039
50109
|
subCommands: {
|
|
49040
|
-
list:
|
|
50110
|
+
list: defineCommand200({
|
|
49041
50111
|
meta: {
|
|
49042
50112
|
name: "list",
|
|
49043
50113
|
description: "Review everything staged on this chat (--json for the raw envelope)"
|
|
@@ -49050,7 +50120,7 @@ var draftCommand4 = defineCommand198({
|
|
|
49050
50120
|
await draftList2(args.json === true, args.chat);
|
|
49051
50121
|
}
|
|
49052
50122
|
}),
|
|
49053
|
-
show:
|
|
50123
|
+
show: defineCommand200({
|
|
49054
50124
|
meta: {
|
|
49055
50125
|
name: "show",
|
|
49056
50126
|
description: "Print the full staged payload for one change \u2014 the receipt to verify it looks right before publish (never truncated)."
|
|
@@ -49067,7 +50137,7 @@ var draftCommand4 = defineCommand198({
|
|
|
49067
50137
|
);
|
|
49068
50138
|
}
|
|
49069
50139
|
}),
|
|
49070
|
-
amend:
|
|
50140
|
+
amend: defineCommand200({
|
|
49071
50141
|
meta: {
|
|
49072
50142
|
name: "amend",
|
|
49073
50143
|
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."
|
|
@@ -49084,7 +50154,7 @@ var draftCommand4 = defineCommand198({
|
|
|
49084
50154
|
});
|
|
49085
50155
|
}
|
|
49086
50156
|
}),
|
|
49087
|
-
remove:
|
|
50157
|
+
remove: defineCommand200({
|
|
49088
50158
|
meta: { name: "remove", description: "Remove one staged change (cascades to anything depending on it)" },
|
|
49089
50159
|
args: { ref: { type: "positional", description: "Staged ref (gtm_temp_*) or target", required: false } },
|
|
49090
50160
|
run: async ({ args }) => {
|
|
@@ -49093,7 +50163,7 @@ var draftCommand4 = defineCommand198({
|
|
|
49093
50163
|
});
|
|
49094
50164
|
}
|
|
49095
50165
|
}),
|
|
49096
|
-
clear:
|
|
50166
|
+
clear: defineCommand200({
|
|
49097
50167
|
meta: { name: "clear", description: "Discard all Tag Manager changes staged on this chat" },
|
|
49098
50168
|
run: async () => {
|
|
49099
50169
|
await draftAction3("/api/tag-manager/draft/clear", {});
|
|
@@ -49103,7 +50173,7 @@ var draftCommand4 = defineCommand198({
|
|
|
49103
50173
|
});
|
|
49104
50174
|
|
|
49105
50175
|
// src/commands/tag-manager/read.ts
|
|
49106
|
-
import { defineCommand as
|
|
50176
|
+
import { defineCommand as defineCommand201 } from "citty";
|
|
49107
50177
|
registerSchema({
|
|
49108
50178
|
command: "tagManager.containers",
|
|
49109
50179
|
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.",
|
|
@@ -49144,7 +50214,7 @@ function containersHints(containers) {
|
|
|
49144
50214
|
}))
|
|
49145
50215
|
});
|
|
49146
50216
|
}
|
|
49147
|
-
var containersCommand =
|
|
50217
|
+
var containersCommand = defineCommand201({
|
|
49148
50218
|
meta: {
|
|
49149
50219
|
name: "containers",
|
|
49150
50220
|
description: `List Tag Manager containers reachable by this company's connection.
|
|
@@ -49161,7 +50231,7 @@ Start here:
|
|
|
49161
50231
|
}
|
|
49162
50232
|
}
|
|
49163
50233
|
});
|
|
49164
|
-
var readCommand =
|
|
50234
|
+
var readCommand = defineCommand201({
|
|
49165
50235
|
meta: {
|
|
49166
50236
|
name: "read",
|
|
49167
50237
|
description: `Read the current contents of the Tag Manager container \u2014 always do this before staging changes.
|
|
@@ -49203,7 +50273,7 @@ Examples:
|
|
|
49203
50273
|
});
|
|
49204
50274
|
|
|
49205
50275
|
// src/commands/tag-manager/write-commands.ts
|
|
49206
|
-
import { defineCommand as
|
|
50276
|
+
import { defineCommand as defineCommand202 } from "citty";
|
|
49207
50277
|
var CONTAINER_ARG_DESCRIPTION = "Numeric container id (optional only when one container is connected \u2014 run `baker tag-manager containers`)";
|
|
49208
50278
|
var ENTITIES = [
|
|
49209
50279
|
{
|
|
@@ -49259,10 +50329,10 @@ for (const { entity, noun, createHint } of ENTITIES) {
|
|
|
49259
50329
|
});
|
|
49260
50330
|
}
|
|
49261
50331
|
function entityCommand(entity, noun, example) {
|
|
49262
|
-
return
|
|
50332
|
+
return defineCommand202({
|
|
49263
50333
|
meta: { name: entity, description: `Stage ${noun} changes on this chat's Tag Manager draft` },
|
|
49264
50334
|
subCommands: {
|
|
49265
|
-
create:
|
|
50335
|
+
create: defineCommand202({
|
|
49266
50336
|
meta: {
|
|
49267
50337
|
name: "create",
|
|
49268
50338
|
description: `Stage a new ${noun}
|
|
@@ -49284,7 +50354,7 @@ Examples:
|
|
|
49284
50354
|
});
|
|
49285
50355
|
}
|
|
49286
50356
|
}),
|
|
49287
|
-
update:
|
|
50357
|
+
update: defineCommand202({
|
|
49288
50358
|
meta: {
|
|
49289
50359
|
name: "update",
|
|
49290
50360
|
description: `Stage an update to an existing ${noun} (pass its id or path)`
|
|
@@ -49304,7 +50374,7 @@ Examples:
|
|
|
49304
50374
|
});
|
|
49305
50375
|
}
|
|
49306
50376
|
}),
|
|
49307
|
-
delete:
|
|
50377
|
+
delete: defineCommand202({
|
|
49308
50378
|
meta: { name: "delete", description: `Stage the deletion of a ${noun} (pass its id or path)` },
|
|
49309
50379
|
args: {
|
|
49310
50380
|
id: { type: "positional", description: `${noun} id or path`, required: false },
|
|
@@ -49345,7 +50415,7 @@ function builtinTypes(args) {
|
|
|
49345
50415
|
}
|
|
49346
50416
|
return raw.split(",").map((entry) => entry.trim());
|
|
49347
50417
|
}
|
|
49348
|
-
var builtinCommand =
|
|
50418
|
+
var builtinCommand = defineCommand202({
|
|
49349
50419
|
meta: {
|
|
49350
50420
|
name: "builtin",
|
|
49351
50421
|
description: `Enable or disable built-in variables
|
|
@@ -49355,7 +50425,7 @@ Examples:
|
|
|
49355
50425
|
baker tag-manager builtin disable --types formId`
|
|
49356
50426
|
},
|
|
49357
50427
|
subCommands: {
|
|
49358
|
-
enable:
|
|
50428
|
+
enable: defineCommand202({
|
|
49359
50429
|
meta: { name: "enable", description: "Stage enabling built-in variables" },
|
|
49360
50430
|
args: {
|
|
49361
50431
|
types: { type: "string", description: "Comma-separated types", required: false },
|
|
@@ -49369,7 +50439,7 @@ Examples:
|
|
|
49369
50439
|
});
|
|
49370
50440
|
}
|
|
49371
50441
|
}),
|
|
49372
|
-
disable:
|
|
50442
|
+
disable: defineCommand202({
|
|
49373
50443
|
meta: { name: "disable", description: "Stage disabling built-in variables" },
|
|
49374
50444
|
args: {
|
|
49375
50445
|
types: { type: "string", description: "Comma-separated types", required: false },
|
|
@@ -49387,7 +50457,7 @@ Examples:
|
|
|
49387
50457
|
});
|
|
49388
50458
|
|
|
49389
50459
|
// src/commands/tag-manager/index.ts
|
|
49390
|
-
var tagManagerCommand =
|
|
50460
|
+
var tagManagerCommand = defineCommand203({
|
|
49391
50461
|
meta: {
|
|
49392
50462
|
name: "tag-manager",
|
|
49393
50463
|
description: `Read and change what lives inside the client's Google Tag Manager container \u2014 tags, triggers, variables, folders and built-in variables.
|
|
@@ -49424,7 +50494,7 @@ Full guide: __tooling__/docs/tools/baker/tag-manager.md`
|
|
|
49424
50494
|
});
|
|
49425
50495
|
|
|
49426
50496
|
// src/commands/tags/index.ts
|
|
49427
|
-
import { defineCommand as
|
|
50497
|
+
import { defineCommand as defineCommand204 } from "citty";
|
|
49428
50498
|
|
|
49429
50499
|
// src/commands/tags/shared.ts
|
|
49430
50500
|
function failApi3(err) {
|
|
@@ -49493,7 +50563,7 @@ async function listTags(json) {
|
|
|
49493
50563
|
var listArgs9 = {
|
|
49494
50564
|
json: { type: "boolean", description: "Print the raw JSON envelope instead of the readable list" }
|
|
49495
50565
|
};
|
|
49496
|
-
var listCommand17 =
|
|
50566
|
+
var listCommand17 = defineCommand204({
|
|
49497
50567
|
meta: {
|
|
49498
50568
|
name: "list",
|
|
49499
50569
|
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"
|
|
@@ -49512,7 +50582,7 @@ async function listDraft3(chat) {
|
|
|
49512
50582
|
failApi3(err);
|
|
49513
50583
|
}
|
|
49514
50584
|
}
|
|
49515
|
-
var draftCommand5 =
|
|
50585
|
+
var draftCommand5 = defineCommand204({
|
|
49516
50586
|
meta: {
|
|
49517
50587
|
name: "draft",
|
|
49518
50588
|
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."
|
|
@@ -49522,7 +50592,7 @@ var draftCommand5 = defineCommand202({
|
|
|
49522
50592
|
await listDraft3(args.chat);
|
|
49523
50593
|
}
|
|
49524
50594
|
});
|
|
49525
|
-
var tagsCommand4 =
|
|
50595
|
+
var tagsCommand4 = defineCommand204({
|
|
49526
50596
|
meta: {
|
|
49527
50597
|
name: "tags",
|
|
49528
50598
|
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.
|
|
@@ -49551,10 +50621,10 @@ Full guide: __tooling__/docs/tools/baker/tags.md`
|
|
|
49551
50621
|
});
|
|
49552
50622
|
|
|
49553
50623
|
// src/commands/testimonials/index.ts
|
|
49554
|
-
import { defineCommand as
|
|
50624
|
+
import { defineCommand as defineCommand208 } from "citty";
|
|
49555
50625
|
|
|
49556
50626
|
// src/commands/testimonials/get.ts
|
|
49557
|
-
import { defineCommand as
|
|
50627
|
+
import { defineCommand as defineCommand205 } from "citty";
|
|
49558
50628
|
registerSchema({
|
|
49559
50629
|
command: "testimonials.get",
|
|
49560
50630
|
description: "Get a single testimonial by ID",
|
|
@@ -49562,7 +50632,7 @@ registerSchema({
|
|
|
49562
50632
|
id: { type: "string", description: "Testimonial ID", required: true }
|
|
49563
50633
|
}
|
|
49564
50634
|
});
|
|
49565
|
-
var getCommand6 =
|
|
50635
|
+
var getCommand6 = defineCommand205({
|
|
49566
50636
|
meta: { name: "get", description: "Get a single testimonial by ID. Example: baker testimonials get j571abc123" },
|
|
49567
50637
|
args: {
|
|
49568
50638
|
id: { type: "positional", description: "Testimonial ID", required: false },
|
|
@@ -49599,7 +50669,7 @@ var getCommand6 = defineCommand203({
|
|
|
49599
50669
|
});
|
|
49600
50670
|
|
|
49601
50671
|
// src/commands/testimonials/list.ts
|
|
49602
|
-
import { defineCommand as
|
|
50672
|
+
import { defineCommand as defineCommand206 } from "citty";
|
|
49603
50673
|
|
|
49604
50674
|
// src/commands/testimonials/emptyCorpusHints.ts
|
|
49605
50675
|
function resolveEmptyReason({
|
|
@@ -49734,7 +50804,7 @@ function buildListParams(args) {
|
|
|
49734
50804
|
}
|
|
49735
50805
|
return params;
|
|
49736
50806
|
}
|
|
49737
|
-
var listCommand18 =
|
|
50807
|
+
var listCommand18 = defineCommand206({
|
|
49738
50808
|
meta: {
|
|
49739
50809
|
name: "list",
|
|
49740
50810
|
description: "List testimonials with optional filters. Example: baker testimonials list --source google --sentiment positive"
|
|
@@ -49786,7 +50856,7 @@ var listCommand18 = defineCommand204({
|
|
|
49786
50856
|
});
|
|
49787
50857
|
|
|
49788
50858
|
// src/commands/testimonials/search.ts
|
|
49789
|
-
import { defineCommand as
|
|
50859
|
+
import { defineCommand as defineCommand207 } from "citty";
|
|
49790
50860
|
var FILTER_FLAGS2 = ["source", "rating-min", "rating-max", "status", "sentiment", "language", "tags"];
|
|
49791
50861
|
function languageBiasHint(results, requestedLanguage) {
|
|
49792
50862
|
if (requestedLanguage) {
|
|
@@ -49865,7 +50935,7 @@ function buildSearchRequest(query, args) {
|
|
|
49865
50935
|
}
|
|
49866
50936
|
return body;
|
|
49867
50937
|
}
|
|
49868
|
-
var searchCommand3 =
|
|
50938
|
+
var searchCommand3 = defineCommand207({
|
|
49869
50939
|
meta: {
|
|
49870
50940
|
name: "search",
|
|
49871
50941
|
description: "Semantic search testimonials by text query. Uses hybrid BM25 + vector + reranking. Example: baker testimonials search 'great service' --rating-min 4"
|
|
@@ -49929,7 +50999,7 @@ var searchCommand3 = defineCommand205({
|
|
|
49929
50999
|
var tagsCommand5 = makeTagsCommand("testimonials", "testimonial", "/api/testimonials/tags");
|
|
49930
51000
|
|
|
49931
51001
|
// src/commands/testimonials/index.ts
|
|
49932
|
-
var testimonialsCommand =
|
|
51002
|
+
var testimonialsCommand = defineCommand208({
|
|
49933
51003
|
meta: {
|
|
49934
51004
|
name: "testimonials",
|
|
49935
51005
|
description: `Find and browse testimonials in Baker. Subcommands: search, get, list, tags.
|
|
@@ -49951,10 +51021,10 @@ Full guide: __tooling__/docs/tools/baker/testimonials.md`
|
|
|
49951
51021
|
});
|
|
49952
51022
|
|
|
49953
51023
|
// src/commands/videos/index.ts
|
|
49954
|
-
import { defineCommand as
|
|
51024
|
+
import { defineCommand as defineCommand215 } from "citty";
|
|
49955
51025
|
|
|
49956
51026
|
// src/commands/videos/delete.ts
|
|
49957
|
-
import { defineCommand as
|
|
51027
|
+
import { defineCommand as defineCommand209 } from "citty";
|
|
49958
51028
|
registerSchema({
|
|
49959
51029
|
command: "videos.delete",
|
|
49960
51030
|
description: "Delete a video by ID",
|
|
@@ -49968,7 +51038,7 @@ registerSchema({
|
|
|
49968
51038
|
}
|
|
49969
51039
|
}
|
|
49970
51040
|
});
|
|
49971
|
-
var deleteCommand4 =
|
|
51041
|
+
var deleteCommand4 = defineCommand209({
|
|
49972
51042
|
meta: {
|
|
49973
51043
|
name: "delete",
|
|
49974
51044
|
description: "Delete a video by ID. Use --dry-run to preview. Example: baker videos delete j571abc123 --dry-run"
|
|
@@ -50009,7 +51079,7 @@ var deleteCommand4 = defineCommand207({
|
|
|
50009
51079
|
});
|
|
50010
51080
|
|
|
50011
51081
|
// src/commands/videos/get.ts
|
|
50012
|
-
import { defineCommand as
|
|
51082
|
+
import { defineCommand as defineCommand210 } from "citty";
|
|
50013
51083
|
registerSchema({
|
|
50014
51084
|
command: "videos.get",
|
|
50015
51085
|
description: "Get a single video by ID",
|
|
@@ -50017,7 +51087,7 @@ registerSchema({
|
|
|
50017
51087
|
id: { type: "string", description: "Video ID", required: true }
|
|
50018
51088
|
}
|
|
50019
51089
|
});
|
|
50020
|
-
var getCommand7 =
|
|
51090
|
+
var getCommand7 = defineCommand210({
|
|
50021
51091
|
meta: { name: "get", description: "Get a single video by ID. Example: baker videos get j571abc123" },
|
|
50022
51092
|
args: {
|
|
50023
51093
|
id: { type: "positional", description: "Video ID", required: false },
|
|
@@ -50054,7 +51124,7 @@ var getCommand7 = defineCommand208({
|
|
|
50054
51124
|
});
|
|
50055
51125
|
|
|
50056
51126
|
// src/commands/videos/group.ts
|
|
50057
|
-
import { defineCommand as
|
|
51127
|
+
import { defineCommand as defineCommand211 } from "citty";
|
|
50058
51128
|
registerSchema({
|
|
50059
51129
|
command: "videos.group",
|
|
50060
51130
|
description: "List every clip and image that arrived in the same set as this video (carousel slides, one page)",
|
|
@@ -50063,7 +51133,7 @@ registerSchema({
|
|
|
50063
51133
|
"group-key": { type: "string", description: "The set key directly, when you already have it", required: false }
|
|
50064
51134
|
}
|
|
50065
51135
|
});
|
|
50066
|
-
var groupCommand2 =
|
|
51136
|
+
var groupCommand2 = defineCommand211({
|
|
50067
51137
|
meta: {
|
|
50068
51138
|
name: "group",
|
|
50069
51139
|
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>"
|
|
@@ -50083,10 +51153,10 @@ var groupCommand2 = defineCommand209({
|
|
|
50083
51153
|
});
|
|
50084
51154
|
|
|
50085
51155
|
// src/commands/videos/ingest.ts
|
|
50086
|
-
import { mkdtemp as mkdtemp2, rm as rm7, stat as
|
|
51156
|
+
import { mkdtemp as mkdtemp2, rm as rm7, stat as stat8 } from "fs/promises";
|
|
50087
51157
|
import { tmpdir as tmpdir3 } from "os";
|
|
50088
|
-
import
|
|
50089
|
-
import { defineCommand as
|
|
51158
|
+
import path42 from "path";
|
|
51159
|
+
import { defineCommand as defineCommand212 } from "citty";
|
|
50090
51160
|
|
|
50091
51161
|
// src/lib/streamUpload.ts
|
|
50092
51162
|
import { createHash as createHash2 } from "crypto";
|
|
@@ -50250,7 +51320,7 @@ registerSchema({
|
|
|
50250
51320
|
"dry-run": { type: "boolean", description: "Preview the operation without executing", required: false }
|
|
50251
51321
|
}
|
|
50252
51322
|
});
|
|
50253
|
-
var ingestCommand2 =
|
|
51323
|
+
var ingestCommand2 = defineCommand212({
|
|
50254
51324
|
meta: {
|
|
50255
51325
|
name: "ingest",
|
|
50256
51326
|
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"
|
|
@@ -50366,35 +51436,35 @@ async function ingestByRoute(args, direct, country) {
|
|
|
50366
51436
|
function reportYtDlpFailure(err) {
|
|
50367
51437
|
const detail = `${err.stderrTail} ${err.message}`;
|
|
50368
51438
|
if (isProxyFailure(ytDlpBlockSignal(detail))) {
|
|
50369
|
-
|
|
51439
|
+
fail9(
|
|
50370
51440
|
"Couldn't download that video: our connection to the internet was refused.",
|
|
50371
51441
|
"This is Baker's egress proxy, not the link \u2014 its credentials look wrong or expired. The link is probably fine. Report it; retrying won't help until the proxy is fixed."
|
|
50372
51442
|
);
|
|
50373
51443
|
}
|
|
50374
51444
|
if (isDrmProtected(detail)) {
|
|
50375
|
-
|
|
51445
|
+
fail9(
|
|
50376
51446
|
"That video is copy-protected, so it can't be added to the library.",
|
|
50377
51447
|
"The publisher encrypted this one against downloading. Nothing will change that \u2014 not retrying, not a different region. Ask whoever owns the video for the original file and add it with `baker videos upload <file>`."
|
|
50378
51448
|
);
|
|
50379
51449
|
}
|
|
50380
51450
|
if (isVimeoAuthFailure(detail)) {
|
|
50381
|
-
|
|
51451
|
+
fail9(
|
|
50382
51452
|
"Vimeo wouldn't hand over that video \u2014 Baker isn't signed in to Vimeo.",
|
|
50383
51453
|
"Vimeo now refuses anonymous downloads entirely, so this needs a signed-in session and the one Baker holds has either expired or was never set. Nothing is wrong with the link. Report it so the Vimeo cookie can be refreshed; meanwhile download the file and use `baker videos upload <file>`."
|
|
50384
51454
|
);
|
|
50385
51455
|
}
|
|
50386
51456
|
if (isRegionBlocked(detail)) {
|
|
50387
|
-
|
|
51457
|
+
fail9(
|
|
50388
51458
|
`That video isn't available in the region we're fetching from. ${err.stderrTail || err.message}`,
|
|
50389
51459
|
"Re-run with `--country <two-letter code>` for a country where the video plays \u2014 e.g. `--country US`. Pick the one the video belongs to (the uploader's country is the usual answer); each attempt costs a paid connection, so choose rather than work through the list."
|
|
50390
51460
|
);
|
|
50391
51461
|
}
|
|
50392
|
-
|
|
51462
|
+
fail9(
|
|
50393
51463
|
`Couldn't download that video. ${err.stderrTail || err.message}`,
|
|
50394
51464
|
isToolingOrBlockFailure(detail) ? "The site wouldn't hand over the video \u2014 usually the downloader being out of date against a site that changed, or the request being blocked. Retrying won't help, and the link is probably fine. Report it so the tool can be updated; meanwhile download the file and use `baker videos upload <file>`." : "Check the link is public and playable. Private or age-restricted videos can't be downloaded \u2014 download the file yourself and use `baker videos upload <file>`."
|
|
50395
51465
|
);
|
|
50396
51466
|
}
|
|
50397
|
-
function
|
|
51467
|
+
function fail9(message, fix, code = "INGEST_FAILED") {
|
|
50398
51468
|
writeJson({ ok: false, error: { code, message, fix } });
|
|
50399
51469
|
process.exit(1);
|
|
50400
51470
|
}
|
|
@@ -50406,13 +51476,13 @@ function reportIngestFailure(err) {
|
|
|
50406
51476
|
if (err instanceof YtDlpError) reportYtDlpFailure(err);
|
|
50407
51477
|
const detail = err instanceof Error ? err.message : String(err);
|
|
50408
51478
|
if (detail.includes("timed out after")) {
|
|
50409
|
-
|
|
51479
|
+
fail9(
|
|
50410
51480
|
"That video took too long to download and was stopped part-way.",
|
|
50411
51481
|
"Long or slow-serving videos can outrun the download window. Try a shorter clip, or download this one yourself and use `baker videos upload <file>`.",
|
|
50412
51482
|
"INTERNAL_ERROR"
|
|
50413
51483
|
);
|
|
50414
51484
|
}
|
|
50415
|
-
|
|
51485
|
+
fail9(
|
|
50416
51486
|
`Couldn't add that video. ${detail}`,
|
|
50417
51487
|
"This is a fault on Baker's side, not a problem with the link. Report it.",
|
|
50418
51488
|
"INTERNAL_ERROR"
|
|
@@ -50436,7 +51506,7 @@ function ingestUrl(args) {
|
|
|
50436
51506
|
}
|
|
50437
51507
|
async function downloadThenIngest(args, country) {
|
|
50438
51508
|
const vimeoCookie = captureVimeoCookie();
|
|
50439
|
-
const workDir = await mkdtemp2(
|
|
51509
|
+
const workDir = await mkdtemp2(path42.join(tmpdir3(), "videos-ingest-"));
|
|
50440
51510
|
try {
|
|
50441
51511
|
const probe = await probeYtDlp({ url: args.url, country, vimeoCookie, cookieDir: workDir });
|
|
50442
51512
|
if (isAudioOnly(probe.info)) {
|
|
@@ -50467,7 +51537,7 @@ async function downloadThenIngest(args, country) {
|
|
|
50467
51537
|
// we allow to fetch it cannot drift apart.
|
|
50468
51538
|
timeoutMs: downloadTimeoutMs(durationSeconds(probe.info))
|
|
50469
51539
|
});
|
|
50470
|
-
const stats = await
|
|
51540
|
+
const stats = await stat8(filePath);
|
|
50471
51541
|
if (stats.size > MAX_VIDEO_INGEST_BYTES) {
|
|
50472
51542
|
throw new ApiError(
|
|
50473
51543
|
"VALIDATION_ERROR",
|
|
@@ -50512,7 +51582,7 @@ async function uploadToAssetStore(filePath, sizeBytes) {
|
|
|
50512
51582
|
}
|
|
50513
51583
|
|
|
50514
51584
|
// src/commands/videos/search.ts
|
|
50515
|
-
import { defineCommand as
|
|
51585
|
+
import { defineCommand as defineCommand213 } from "citty";
|
|
50516
51586
|
registerSchema({
|
|
50517
51587
|
command: "videos.search",
|
|
50518
51588
|
description: "Search videos by text query. Only returns ready videos.",
|
|
@@ -50522,7 +51592,7 @@ registerSchema({
|
|
|
50522
51592
|
tags: { type: "string", description: "Comma-separated tags to filter by", required: false }
|
|
50523
51593
|
}
|
|
50524
51594
|
});
|
|
50525
|
-
var searchCommand4 =
|
|
51595
|
+
var searchCommand4 = defineCommand213({
|
|
50526
51596
|
meta: {
|
|
50527
51597
|
name: "search",
|
|
50528
51598
|
description: "Semantic search videos by text query. Uses hybrid BM25 + vector + reranking. Example: baker videos search 'product demo' --tags tutorial"
|
|
@@ -50572,9 +51642,9 @@ var searchCommand4 = defineCommand211({
|
|
|
50572
51642
|
var tagsCommand6 = makeTagsCommand("videos", "video", "/api/videos/tags");
|
|
50573
51643
|
|
|
50574
51644
|
// src/commands/videos/upload.ts
|
|
50575
|
-
import { readFile as
|
|
51645
|
+
import { readFile as readFile27, stat as stat9 } from "fs/promises";
|
|
50576
51646
|
import { basename as basename3, extname as extname4 } from "path";
|
|
50577
|
-
import { defineCommand as
|
|
51647
|
+
import { defineCommand as defineCommand214 } from "citty";
|
|
50578
51648
|
var MIME_MAP = {
|
|
50579
51649
|
".mp4": "video/mp4",
|
|
50580
51650
|
".mov": "video/quicktime",
|
|
@@ -50616,7 +51686,7 @@ function detectContentType(filePath) {
|
|
|
50616
51686
|
function isRemoteUrl3(value) {
|
|
50617
51687
|
return /^https?:\/\//i.test(value);
|
|
50618
51688
|
}
|
|
50619
|
-
var uploadCommand2 =
|
|
51689
|
+
var uploadCommand2 = defineCommand214({
|
|
50620
51690
|
meta: {
|
|
50621
51691
|
name: "upload",
|
|
50622
51692
|
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"
|
|
@@ -50656,7 +51726,7 @@ var uploadCommand2 = defineCommand212({
|
|
|
50656
51726
|
const originalFilename = basename3(filePath);
|
|
50657
51727
|
const descriptionContext = args.context;
|
|
50658
51728
|
if (args["dry-run"]) {
|
|
50659
|
-
const fileStats = await
|
|
51729
|
+
const fileStats = await stat9(filePath);
|
|
50660
51730
|
writeJson({
|
|
50661
51731
|
ok: true,
|
|
50662
51732
|
dryRun: true,
|
|
@@ -50669,7 +51739,7 @@ var uploadCommand2 = defineCommand212({
|
|
|
50669
51739
|
originalFilename,
|
|
50670
51740
|
descriptionContext
|
|
50671
51741
|
});
|
|
50672
|
-
const fileBuffer = await
|
|
51742
|
+
const fileBuffer = await readFile27(filePath);
|
|
50673
51743
|
const uploadResponse = await fetch(uploadUrl, {
|
|
50674
51744
|
method: "PUT",
|
|
50675
51745
|
headers: { "Content-Type": contentType },
|
|
@@ -50700,7 +51770,7 @@ var uploadCommand2 = defineCommand212({
|
|
|
50700
51770
|
});
|
|
50701
51771
|
|
|
50702
51772
|
// src/commands/videos/index.ts
|
|
50703
|
-
var videosCommand =
|
|
51773
|
+
var videosCommand = defineCommand215({
|
|
50704
51774
|
meta: {
|
|
50705
51775
|
name: "videos",
|
|
50706
51776
|
description: `Find and manage videos in Baker. Subcommands: search, get, upload, ingest, delete, tags.
|
|
@@ -50727,10 +51797,10 @@ Full guide: __tooling__/docs/tools/baker/videos.md`
|
|
|
50727
51797
|
});
|
|
50728
51798
|
|
|
50729
51799
|
// src/commands/winning-ads/index.ts
|
|
50730
|
-
import { defineCommand as
|
|
51800
|
+
import { defineCommand as defineCommand228 } from "citty";
|
|
50731
51801
|
|
|
50732
51802
|
// src/commands/winning-ads/advertisers.ts
|
|
50733
|
-
import { defineCommand as
|
|
51803
|
+
import { defineCommand as defineCommand216 } from "citty";
|
|
50734
51804
|
|
|
50735
51805
|
// src/commands/winning-ads/shared.ts
|
|
50736
51806
|
function splitList2(value) {
|
|
@@ -50783,7 +51853,7 @@ function advertiserNormalizer(record, full) {
|
|
|
50783
51853
|
last_synced_at: record.last_synced_at ?? null
|
|
50784
51854
|
};
|
|
50785
51855
|
}
|
|
50786
|
-
var advertisersCommand2 =
|
|
51856
|
+
var advertisersCommand2 = defineCommand216({
|
|
50787
51857
|
meta: {
|
|
50788
51858
|
name: "advertisers",
|
|
50789
51859
|
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'
|
|
@@ -50841,7 +51911,7 @@ var advertisersCommand2 = defineCommand214({
|
|
|
50841
51911
|
});
|
|
50842
51912
|
|
|
50843
51913
|
// src/commands/winning-ads/brief.ts
|
|
50844
|
-
import { defineCommand as
|
|
51914
|
+
import { defineCommand as defineCommand217 } from "citty";
|
|
50845
51915
|
registerSchema({
|
|
50846
51916
|
command: "winning-ads.brief",
|
|
50847
51917
|
description: "Generate a creative brief grounded in strategically-similar winning ads. Optionally describe the target creative with --dna (JSON) and steer with --notes.",
|
|
@@ -50887,7 +51957,7 @@ function parseDna(raw) {
|
|
|
50887
51957
|
}
|
|
50888
51958
|
return parsed;
|
|
50889
51959
|
}
|
|
50890
|
-
var briefCommand =
|
|
51960
|
+
var briefCommand = defineCommand217({
|
|
50891
51961
|
meta: {
|
|
50892
51962
|
name: "brief",
|
|
50893
51963
|
description: `Generate a creative brief from winning references. Example: baker winning-ads brief --dna '{"angle":"cost savings"}' --notes "B2B, LinkedIn video" --k 8`
|
|
@@ -50923,7 +51993,7 @@ var briefCommand = defineCommand215({
|
|
|
50923
51993
|
});
|
|
50924
51994
|
|
|
50925
51995
|
// src/commands/winning-ads/content.ts
|
|
50926
|
-
import { defineCommand as
|
|
51996
|
+
import { defineCommand as defineCommand218 } from "citty";
|
|
50927
51997
|
registerSchema({
|
|
50928
51998
|
command: "winning-ads.content",
|
|
50929
51999
|
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.",
|
|
@@ -50936,7 +52006,7 @@ registerSchema({
|
|
|
50936
52006
|
}
|
|
50937
52007
|
}
|
|
50938
52008
|
});
|
|
50939
|
-
var contentCommand =
|
|
52009
|
+
var contentCommand = defineCommand218({
|
|
50940
52010
|
meta: {
|
|
50941
52011
|
name: "content",
|
|
50942
52012
|
description: "Read the transcript + on-screen text + copy of one winning ad. Example: baker winning-ads content adg_123 --platform meta --full --output md"
|
|
@@ -50985,7 +52055,7 @@ var contentCommand = defineCommand216({
|
|
|
50985
52055
|
});
|
|
50986
52056
|
|
|
50987
52057
|
// src/commands/winning-ads/feed.ts
|
|
50988
|
-
import { defineCommand as
|
|
52058
|
+
import { defineCommand as defineCommand219 } from "citty";
|
|
50989
52059
|
function buildFeedParams(input) {
|
|
50990
52060
|
const params = {};
|
|
50991
52061
|
const advertiser = splitList2(input.advertiser);
|
|
@@ -51037,7 +52107,7 @@ registerSchema({
|
|
|
51037
52107
|
format: { type: "string", description: "Comma-separated formats to include (e.g. static,video)", required: false }
|
|
51038
52108
|
}
|
|
51039
52109
|
});
|
|
51040
|
-
var feedCommand =
|
|
52110
|
+
var feedCommand = defineCommand219({
|
|
51041
52111
|
meta: {
|
|
51042
52112
|
name: "feed",
|
|
51043
52113
|
description: "Winners across every brand you follow (browse, then trim per advertiser). Example: baker winning-ads feed --per-advertiser 5 --output md"
|
|
@@ -51122,7 +52192,7 @@ var feedCommand = defineCommand217({
|
|
|
51122
52192
|
});
|
|
51123
52193
|
|
|
51124
52194
|
// src/commands/winning-ads/follow.ts
|
|
51125
|
-
import { defineCommand as
|
|
52195
|
+
import { defineCommand as defineCommand220 } from "citty";
|
|
51126
52196
|
var PLATFORMS = ["meta", "linkedin"];
|
|
51127
52197
|
registerSchema({
|
|
51128
52198
|
command: "winning-ads.follow",
|
|
@@ -51137,7 +52207,7 @@ registerSchema({
|
|
|
51137
52207
|
label: { type: "string", description: "Optional display label (defaults to the resolved name)", required: false }
|
|
51138
52208
|
}
|
|
51139
52209
|
});
|
|
51140
|
-
var followCommand =
|
|
52210
|
+
var followCommand = defineCommand220({
|
|
51141
52211
|
meta: {
|
|
51142
52212
|
name: "follow",
|
|
51143
52213
|
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 both Meta + LinkedIn. Example: baker winning-ads follow "deel.com" --platform meta'
|
|
@@ -51184,7 +52254,7 @@ var followCommand = defineCommand218({
|
|
|
51184
52254
|
});
|
|
51185
52255
|
|
|
51186
52256
|
// src/commands/winning-ads/follow-competitors.ts
|
|
51187
|
-
import { defineCommand as
|
|
52257
|
+
import { defineCommand as defineCommand221 } from "citty";
|
|
51188
52258
|
var PLATFORMS2 = ["meta", "linkedin"];
|
|
51189
52259
|
var BATCH_TIMEOUT_MS = 3e5;
|
|
51190
52260
|
function buildFollowBatchBody(input) {
|
|
@@ -51217,7 +52287,7 @@ registerSchema({
|
|
|
51217
52287
|
}
|
|
51218
52288
|
}
|
|
51219
52289
|
});
|
|
51220
|
-
var followCompetitorsCommand =
|
|
52290
|
+
var followCompetitorsCommand = defineCommand221({
|
|
51221
52291
|
meta: {
|
|
51222
52292
|
name: "follow-competitors",
|
|
51223
52293
|
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"'
|
|
@@ -51292,7 +52362,7 @@ var followCompetitorsCommand = defineCommand219({
|
|
|
51292
52362
|
});
|
|
51293
52363
|
|
|
51294
52364
|
// src/commands/winning-ads/following.ts
|
|
51295
|
-
import { defineCommand as
|
|
52365
|
+
import { defineCommand as defineCommand222 } from "citty";
|
|
51296
52366
|
registerSchema({
|
|
51297
52367
|
command: "winning-ads.following",
|
|
51298
52368
|
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.",
|
|
@@ -51346,7 +52416,7 @@ function followingNormalizer(record, full) {
|
|
|
51346
52416
|
platforms: Array.isArray(record.platforms) ? record.platforms : []
|
|
51347
52417
|
};
|
|
51348
52418
|
}
|
|
51349
|
-
var followingCommand =
|
|
52419
|
+
var followingCommand = defineCommand222({
|
|
51350
52420
|
meta: {
|
|
51351
52421
|
name: "following",
|
|
51352
52422
|
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"
|
|
@@ -51382,7 +52452,7 @@ var followingCommand = defineCommand220({
|
|
|
51382
52452
|
});
|
|
51383
52453
|
|
|
51384
52454
|
// src/commands/winning-ads/patterns.ts
|
|
51385
|
-
import { defineCommand as
|
|
52455
|
+
import { defineCommand as defineCommand223 } from "citty";
|
|
51386
52456
|
registerSchema({
|
|
51387
52457
|
command: "winning-ads.patterns",
|
|
51388
52458
|
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.",
|
|
@@ -51421,7 +52491,7 @@ function discriminatorRow(record) {
|
|
|
51421
52491
|
top_values_duds: Array.isArray(record.top_values_b) ? record.top_values_b.join(", ") : ""
|
|
51422
52492
|
};
|
|
51423
52493
|
}
|
|
51424
|
-
var patternsCommand =
|
|
52494
|
+
var patternsCommand = defineCommand223({
|
|
51425
52495
|
meta: {
|
|
51426
52496
|
name: "patterns",
|
|
51427
52497
|
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"
|
|
@@ -51477,7 +52547,7 @@ var patternsCommand = defineCommand221({
|
|
|
51477
52547
|
});
|
|
51478
52548
|
|
|
51479
52549
|
// src/commands/winning-ads/search.ts
|
|
51480
|
-
import { defineCommand as
|
|
52550
|
+
import { defineCommand as defineCommand224 } from "citty";
|
|
51481
52551
|
registerSchema({
|
|
51482
52552
|
command: "winning-ads.search",
|
|
51483
52553
|
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.",
|
|
@@ -51585,7 +52655,7 @@ function buildSearchBody2(args) {
|
|
|
51585
52655
|
}
|
|
51586
52656
|
return body;
|
|
51587
52657
|
}
|
|
51588
|
-
var searchCommand5 =
|
|
52658
|
+
var searchCommand5 = defineCommand224({
|
|
51589
52659
|
meta: {
|
|
51590
52660
|
name: "search",
|
|
51591
52661
|
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"
|
|
@@ -51700,7 +52770,7 @@ var searchCommand5 = defineCommand222({
|
|
|
51700
52770
|
});
|
|
51701
52771
|
|
|
51702
52772
|
// src/commands/winning-ads/seeds.ts
|
|
51703
|
-
import { defineCommand as
|
|
52773
|
+
import { defineCommand as defineCommand225 } from "citty";
|
|
51704
52774
|
function leanRow(r) {
|
|
51705
52775
|
return {
|
|
51706
52776
|
key: r.key,
|
|
@@ -51728,7 +52798,7 @@ function makeSeedCommand(opts) {
|
|
|
51728
52798
|
limit: { type: "number", description: "Max keys 1-100 (default 20)", required: false, default: 20 }
|
|
51729
52799
|
}
|
|
51730
52800
|
});
|
|
51731
|
-
return
|
|
52801
|
+
return defineCommand225({
|
|
51732
52802
|
meta: { name: opts.name, description: opts.description },
|
|
51733
52803
|
args: {
|
|
51734
52804
|
platform: { type: "string", description: "Single platform to segment on", required: false },
|
|
@@ -51777,7 +52847,7 @@ var formatsCommand = makeSeedCommand({
|
|
|
51777
52847
|
});
|
|
51778
52848
|
|
|
51779
52849
|
// src/commands/winning-ads/unfollow.ts
|
|
51780
|
-
import { defineCommand as
|
|
52850
|
+
import { defineCommand as defineCommand226 } from "citty";
|
|
51781
52851
|
registerSchema({
|
|
51782
52852
|
command: "winning-ads.unfollow",
|
|
51783
52853
|
description: "Stop following a brand \u2014 removes it from your ad-dna library by advertiser id.",
|
|
@@ -51785,7 +52855,7 @@ registerSchema({
|
|
|
51785
52855
|
advertiser: { type: "string", description: "Advertiser id to unfollow", required: true }
|
|
51786
52856
|
}
|
|
51787
52857
|
});
|
|
51788
|
-
var unfollowCommand =
|
|
52858
|
+
var unfollowCommand = defineCommand226({
|
|
51789
52859
|
meta: {
|
|
51790
52860
|
name: "unfollow",
|
|
51791
52861
|
description: "Stop following a brand by advertiser id. Example: baker winning-ads unfollow adv_123"
|
|
@@ -51806,7 +52876,7 @@ var unfollowCommand = defineCommand224({
|
|
|
51806
52876
|
});
|
|
51807
52877
|
|
|
51808
52878
|
// src/commands/winning-ads/winners.ts
|
|
51809
|
-
import { defineCommand as
|
|
52879
|
+
import { defineCommand as defineCommand227 } from "citty";
|
|
51810
52880
|
registerSchema({
|
|
51811
52881
|
command: "winning-ads.winners",
|
|
51812
52882
|
description: "Top winning ads for one advertiser id (from `advertisers` or `following`). Returns lean winner cards; add --full for DNA + longevity.",
|
|
@@ -51816,7 +52886,7 @@ registerSchema({
|
|
|
51816
52886
|
platform: { type: "string", description: "Filter to a single platform: meta|linkedin", required: false }
|
|
51817
52887
|
}
|
|
51818
52888
|
});
|
|
51819
|
-
var winnersCommand =
|
|
52889
|
+
var winnersCommand = defineCommand227({
|
|
51820
52890
|
meta: {
|
|
51821
52891
|
name: "winners",
|
|
51822
52892
|
description: "Top winning ads for a specific advertiser id. Example: baker winning-ads winners adv_123 --top 15 --output md"
|
|
@@ -51866,7 +52936,7 @@ var winnersCommand = defineCommand225({
|
|
|
51866
52936
|
});
|
|
51867
52937
|
|
|
51868
52938
|
// src/commands/winning-ads/index.ts
|
|
51869
|
-
var winningAdsCommand =
|
|
52939
|
+
var winningAdsCommand = defineCommand228({
|
|
51870
52940
|
meta: {
|
|
51871
52941
|
name: "winning-ads",
|
|
51872
52942
|
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.
|
|
@@ -52056,7 +53126,7 @@ function unknownFlagEnvelope(unknown, commandPath, suggestion) {
|
|
|
52056
53126
|
};
|
|
52057
53127
|
}
|
|
52058
53128
|
function commandPathOf(root, argv) {
|
|
52059
|
-
const
|
|
53129
|
+
const path43 = [];
|
|
52060
53130
|
let command = root;
|
|
52061
53131
|
for (const token of argv) {
|
|
52062
53132
|
if (token === "--" || token.startsWith("-")) {
|
|
@@ -52067,10 +53137,10 @@ function commandPathOf(root, argv) {
|
|
|
52067
53137
|
if (next === void 0 || typeof next !== "object") {
|
|
52068
53138
|
break;
|
|
52069
53139
|
}
|
|
52070
|
-
|
|
53140
|
+
path43.push(token);
|
|
52071
53141
|
command = next;
|
|
52072
53142
|
}
|
|
52073
|
-
return
|
|
53143
|
+
return path43.join(" ");
|
|
52074
53144
|
}
|
|
52075
53145
|
function refuseUnknownFlags(root, argv) {
|
|
52076
53146
|
const unknown = findUnknownFlags(root, argv);
|
|
@@ -52104,7 +53174,7 @@ function getCliVersion() {
|
|
|
52104
53174
|
}
|
|
52105
53175
|
|
|
52106
53176
|
// src/cli.ts
|
|
52107
|
-
var main =
|
|
53177
|
+
var main = defineCommand229({
|
|
52108
53178
|
meta: {
|
|
52109
53179
|
name: "baker",
|
|
52110
53180
|
version: getCliVersion(),
|
|
@@ -52123,6 +53193,7 @@ Introspection: Run 'baker schema <command>' to inspect argument schemas.`
|
|
|
52123
53193
|
ads: adsCommand2,
|
|
52124
53194
|
brand: brandCommand,
|
|
52125
53195
|
analytics: analyticsCommand2,
|
|
53196
|
+
experiment: experimentCommand,
|
|
52126
53197
|
ga4: ga4Command,
|
|
52127
53198
|
gsc: gscCommand,
|
|
52128
53199
|
research: researchCommand,
|