@koda-sl/baker-cli 0.243.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 -0
- package/dist/{chunk-EKLAHWSF.js → chunk-CMPAHYLB.js} +4 -4
- package/dist/chunk-CMPAHYLB.js.map +1 -0
- package/dist/cli.js +1631 -559
- 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,
|
|
@@ -14915,19 +14911,19 @@ function failWriteValidation2(message) {
|
|
|
14915
14911
|
writeJsonEnvelope({ ok: false, error: { code: "VALIDATION_ERROR", message } });
|
|
14916
14912
|
process.exit(1);
|
|
14917
14913
|
}
|
|
14918
|
-
function loadJsonFileArg2(
|
|
14919
|
-
if (typeof
|
|
14914
|
+
function loadJsonFileArg2(path43) {
|
|
14915
|
+
if (typeof path43 !== "string" || path43.length === 0) {
|
|
14920
14916
|
return {};
|
|
14921
14917
|
}
|
|
14922
14918
|
try {
|
|
14923
|
-
const parsed = JSON.parse(readFileSync4(
|
|
14919
|
+
const parsed = JSON.parse(readFileSync4(path43, "utf8"));
|
|
14924
14920
|
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
14925
|
-
failWriteValidation2(`${
|
|
14921
|
+
failWriteValidation2(`${path43} must contain a JSON object`);
|
|
14926
14922
|
}
|
|
14927
14923
|
return parsed;
|
|
14928
14924
|
} catch (err) {
|
|
14929
14925
|
if (err instanceof SyntaxError) {
|
|
14930
|
-
failWriteValidation2(`${
|
|
14926
|
+
failWriteValidation2(`${path43} is not valid JSON: ${err.message}`);
|
|
14931
14927
|
}
|
|
14932
14928
|
throw err;
|
|
14933
14929
|
}
|
|
@@ -15012,15 +15008,15 @@ function parseLocaleFlag(value) {
|
|
|
15012
15008
|
}
|
|
15013
15009
|
return { language: match[1], country: match[2].toUpperCase() };
|
|
15014
15010
|
}
|
|
15015
|
-
function loadTargetingFileArg(
|
|
15016
|
-
if (typeof
|
|
15011
|
+
function loadTargetingFileArg(path43) {
|
|
15012
|
+
if (typeof path43 !== "string" || path43.length === 0) {
|
|
15017
15013
|
return void 0;
|
|
15018
15014
|
}
|
|
15019
|
-
const parsed = loadJsonFileArg2(
|
|
15015
|
+
const parsed = loadJsonFileArg2(path43);
|
|
15020
15016
|
const criteria = parsed.targetingCriteria ?? parsed;
|
|
15021
15017
|
if (!criteria.include) {
|
|
15022
15018
|
failWriteValidation2(
|
|
15023
|
-
`${
|
|
15019
|
+
`${path43} must contain targeting criteria with an "include" block (see baker schema ads.linkedin.campaigns.create)`
|
|
15024
15020
|
);
|
|
15025
15021
|
}
|
|
15026
15022
|
return criteria;
|
|
@@ -15055,14 +15051,14 @@ function parseCsvLine(line) {
|
|
|
15055
15051
|
cells.push(current);
|
|
15056
15052
|
return cells.map((cell2) => cell2.trim());
|
|
15057
15053
|
}
|
|
15058
|
-
function parseListFileArg(
|
|
15059
|
-
if (typeof
|
|
15054
|
+
function parseListFileArg(path43, maxRows) {
|
|
15055
|
+
if (typeof path43 !== "string" || path43.length === 0) {
|
|
15060
15056
|
return void 0;
|
|
15061
15057
|
}
|
|
15062
|
-
const raw = readFileSync4(
|
|
15058
|
+
const raw = readFileSync4(path43, "utf8");
|
|
15063
15059
|
const lines = raw.split(/\r?\n/).filter((line) => line.trim().length > 0);
|
|
15064
15060
|
if (lines.length < 2) {
|
|
15065
|
-
failWriteValidation2(`${
|
|
15061
|
+
failWriteValidation2(`${path43} needs a header row and at least one data row`);
|
|
15066
15062
|
}
|
|
15067
15063
|
const columns = parseCsvLine(lines[0]).map((column) => column.trim());
|
|
15068
15064
|
const rows = [];
|
|
@@ -15081,7 +15077,7 @@ function parseListFileArg(path40, maxRows) {
|
|
|
15081
15077
|
}
|
|
15082
15078
|
}
|
|
15083
15079
|
if (rows.length > maxRows) {
|
|
15084
|
-
failWriteValidation2(`${
|
|
15080
|
+
failWriteValidation2(`${path43} has ${rows.length} rows \u2014 the inline limit is ${maxRows}. Split the list.`);
|
|
15085
15081
|
}
|
|
15086
15082
|
return { columns, rows };
|
|
15087
15083
|
}
|
|
@@ -15177,11 +15173,11 @@ function readPositionals(args) {
|
|
|
15177
15173
|
function splitIdList(raw) {
|
|
15178
15174
|
return raw.split(",").map((id) => id.trim()).filter(Boolean);
|
|
15179
15175
|
}
|
|
15180
|
-
function idsFileEntries(
|
|
15181
|
-
if (typeof
|
|
15176
|
+
function idsFileEntries(path43) {
|
|
15177
|
+
if (typeof path43 !== "string" || path43.length === 0) {
|
|
15182
15178
|
return [];
|
|
15183
15179
|
}
|
|
15184
|
-
return readFileSync4(
|
|
15180
|
+
return readFileSync4(path43, "utf8").split(/\r?\n/).map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#")).flatMap(splitIdList);
|
|
15185
15181
|
}
|
|
15186
15182
|
function requireTargets(args, entity) {
|
|
15187
15183
|
const positionals = readPositionals(args);
|
|
@@ -17802,9 +17798,9 @@ function compactRow(row) {
|
|
|
17802
17798
|
...destination.postUrn ? { postUrn: destination.postUrn } : {}
|
|
17803
17799
|
};
|
|
17804
17800
|
}
|
|
17805
|
-
function readPath(row,
|
|
17801
|
+
function readPath(row, path43) {
|
|
17806
17802
|
let current = row;
|
|
17807
|
-
for (const segment of
|
|
17803
|
+
for (const segment of path43.split(".")) {
|
|
17808
17804
|
const record = asRecord2(current);
|
|
17809
17805
|
if (!record) return void 0;
|
|
17810
17806
|
current = record[segment];
|
|
@@ -17814,10 +17810,10 @@ function readPath(row, path40) {
|
|
|
17814
17810
|
function projectFields(rows, paths) {
|
|
17815
17811
|
return rows.map((row) => {
|
|
17816
17812
|
const projected = {};
|
|
17817
|
-
for (const
|
|
17818
|
-
const value = readPath(row,
|
|
17813
|
+
for (const path43 of paths) {
|
|
17814
|
+
const value = readPath(row, path43);
|
|
17819
17815
|
if (value !== void 0) {
|
|
17820
|
-
projected[
|
|
17816
|
+
projected[path43] = value;
|
|
17821
17817
|
}
|
|
17822
17818
|
}
|
|
17823
17819
|
return projected;
|
|
@@ -19117,11 +19113,11 @@ var updateStatusSchema = z25.enum(UPDATE_STATUSES);
|
|
|
19117
19113
|
function currencyMinimums2(currencyCode) {
|
|
19118
19114
|
return CURRENCY_MINIMUMS2[currencyCode] ?? DEFAULT_CURRENCY_MINIMUM2;
|
|
19119
19115
|
}
|
|
19120
|
-
function validateDailyBudgetFloor(money, ctx,
|
|
19116
|
+
function validateDailyBudgetFloor(money, ctx, path43) {
|
|
19121
19117
|
if (money?.currencyCode) {
|
|
19122
19118
|
const min = currencyMinimums2(money.currencyCode).dailyBudgetMin;
|
|
19123
19119
|
if (Number(money.amount) < min) {
|
|
19124
|
-
ctx.addIssue({ code: "custom", path:
|
|
19120
|
+
ctx.addIssue({ code: "custom", path: path43, message: `below the ${min} ${money.currencyCode} daily minimum` });
|
|
19125
19121
|
}
|
|
19126
19122
|
}
|
|
19127
19123
|
}
|
|
@@ -19790,19 +19786,19 @@ function failWriteValidation3(message) {
|
|
|
19790
19786
|
writeJsonEnvelope({ ok: false, error: { code: "VALIDATION_ERROR", message } });
|
|
19791
19787
|
process.exit(1);
|
|
19792
19788
|
}
|
|
19793
|
-
function loadJsonFileArg3(
|
|
19794
|
-
if (typeof
|
|
19789
|
+
function loadJsonFileArg3(path43) {
|
|
19790
|
+
if (typeof path43 !== "string" || path43.length === 0) {
|
|
19795
19791
|
return {};
|
|
19796
19792
|
}
|
|
19797
19793
|
try {
|
|
19798
|
-
const parsed = JSON.parse(readFileSync8(
|
|
19794
|
+
const parsed = JSON.parse(readFileSync8(path43, "utf8"));
|
|
19799
19795
|
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
19800
|
-
failWriteValidation3(`${
|
|
19796
|
+
failWriteValidation3(`${path43} must contain a JSON object`);
|
|
19801
19797
|
}
|
|
19802
19798
|
return parsed;
|
|
19803
19799
|
} catch (err) {
|
|
19804
19800
|
if (err instanceof SyntaxError) {
|
|
19805
|
-
failWriteValidation3(`${
|
|
19801
|
+
failWriteValidation3(`${path43} is not valid JSON: ${err.message}`);
|
|
19806
19802
|
}
|
|
19807
19803
|
throw err;
|
|
19808
19804
|
}
|
|
@@ -24400,11 +24396,11 @@ function unwrap(response) {
|
|
|
24400
24396
|
}
|
|
24401
24397
|
return response.data;
|
|
24402
24398
|
}
|
|
24403
|
-
async function readAvatars(
|
|
24404
|
-
return unwrap(await apiGet(
|
|
24399
|
+
async function readAvatars(path43, params) {
|
|
24400
|
+
return unwrap(await apiGet(path43, params));
|
|
24405
24401
|
}
|
|
24406
|
-
async function writeAvatars(
|
|
24407
|
-
return unwrap(await apiPost(
|
|
24402
|
+
async function writeAvatars(path43, body) {
|
|
24403
|
+
return unwrap(await apiPost(path43, body));
|
|
24408
24404
|
}
|
|
24409
24405
|
|
|
24410
24406
|
// src/commands/avatars/create.ts
|
|
@@ -25238,12 +25234,12 @@ function missingFontFiles(urls, available) {
|
|
|
25238
25234
|
function planFontAdoption(sources, families) {
|
|
25239
25235
|
const wanted = new Map(families.map((family) => [normalizeFamily(family), family]));
|
|
25240
25236
|
const byFamily = /* @__PURE__ */ new Map();
|
|
25241
|
-
for (const { path:
|
|
25242
|
-
const dir = posix.dirname(
|
|
25237
|
+
for (const { path: path43, source } of sources) {
|
|
25238
|
+
const dir = posix.dirname(path43);
|
|
25243
25239
|
for (const face of declaredFontFaces(source)) {
|
|
25244
25240
|
if (!wanted.has(face.family)) continue;
|
|
25245
25241
|
const perFile = byFamily.get(face.family) ?? /* @__PURE__ */ new Map();
|
|
25246
|
-
perFile.set(
|
|
25242
|
+
perFile.set(path43, [...perFile.get(path43) ?? [], rebaseFontFaceSrc(face.block, dir)]);
|
|
25247
25243
|
byFamily.set(face.family, perFile);
|
|
25248
25244
|
}
|
|
25249
25245
|
}
|
|
@@ -26267,10 +26263,10 @@ import { defineCommand as defineCommand100 } from "citty";
|
|
|
26267
26263
|
import { existsSync as existsSync3, realpathSync } from "fs";
|
|
26268
26264
|
import { writeFile as writeFile2 } from "fs/promises";
|
|
26269
26265
|
import path5 from "path";
|
|
26270
|
-
function findWorkspaceRoot(startDir,
|
|
26266
|
+
function findWorkspaceRoot(startDir, exists2 = existsSync3, maxDepth = 12) {
|
|
26271
26267
|
let dir = path5.resolve(startDir);
|
|
26272
26268
|
for (let i = 0; i < maxDepth; i++) {
|
|
26273
|
-
if (
|
|
26269
|
+
if (exists2(path5.join(dir, "package.json"))) return dir;
|
|
26274
26270
|
const parent = path5.dirname(dir);
|
|
26275
26271
|
if (parent === dir) break;
|
|
26276
26272
|
dir = parent;
|
|
@@ -30217,10 +30213,10 @@ function runDirsToPrune(entries, keep, currentRunId) {
|
|
|
30217
30213
|
return runs.slice(0, Math.max(0, runs.length - keep));
|
|
30218
30214
|
}
|
|
30219
30215
|
async function pruneOldRuns(outputsDir, keep, currentRunId, log) {
|
|
30220
|
-
const { readdir:
|
|
30216
|
+
const { readdir: readdir11 } = await import("fs/promises");
|
|
30221
30217
|
let entries;
|
|
30222
30218
|
try {
|
|
30223
|
-
entries = await
|
|
30219
|
+
entries = await readdir11(outputsDir);
|
|
30224
30220
|
} catch {
|
|
30225
30221
|
return;
|
|
30226
30222
|
}
|
|
@@ -32012,12 +32008,12 @@ async function detectSceneCutsPySceneDetect(filePath, opts = {}) {
|
|
|
32012
32008
|
// src/commands/canvas/composition-path.ts
|
|
32013
32009
|
import { existsSync as existsSync4 } from "fs";
|
|
32014
32010
|
import path21 from "path";
|
|
32015
|
-
function resolveShippedCanvasDir(name, startDir,
|
|
32011
|
+
function resolveShippedCanvasDir(name, startDir, exists2 = existsSync4, maxDepth = 8) {
|
|
32016
32012
|
const rel = path21.join("canvas", name);
|
|
32017
32013
|
let dir = startDir;
|
|
32018
32014
|
for (let i = 0; i < maxDepth; i++) {
|
|
32019
32015
|
const candidate = path21.join(dir, rel);
|
|
32020
|
-
if (
|
|
32016
|
+
if (exists2(path21.join(candidate, "meta.json"))) return candidate;
|
|
32021
32017
|
const parent = path21.dirname(dir);
|
|
32022
32018
|
if (parent === dir) break;
|
|
32023
32019
|
dir = parent;
|
|
@@ -32843,7 +32839,7 @@ registerSchema({
|
|
|
32843
32839
|
}
|
|
32844
32840
|
}
|
|
32845
32841
|
});
|
|
32846
|
-
function capabilityHints(surfaces) {
|
|
32842
|
+
function capabilityHints(surfaces, tools = []) {
|
|
32847
32843
|
const hints = [];
|
|
32848
32844
|
const immediate = surfaces.filter((surface) => surface.writeMode === "applies-at-publish");
|
|
32849
32845
|
if (immediate.length > 0) {
|
|
@@ -32866,6 +32862,18 @@ function capabilityHints(surfaces) {
|
|
|
32866
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."
|
|
32867
32863
|
);
|
|
32868
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
|
+
}
|
|
32869
32877
|
return hints;
|
|
32870
32878
|
}
|
|
32871
32879
|
var runCapabilities = defineCommand108({
|
|
@@ -32875,6 +32883,8 @@ var runCapabilities = defineCommand108({
|
|
|
32875
32883
|
|
|
32876
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.
|
|
32877
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
|
+
|
|
32878
32888
|
Examples:
|
|
32879
32889
|
baker capabilities
|
|
32880
32890
|
baker capabilities google-ads
|
|
@@ -32910,7 +32920,7 @@ Full guide: __tooling__/docs/tools/baker/capabilities.md`
|
|
|
32910
32920
|
body.full = true;
|
|
32911
32921
|
}
|
|
32912
32922
|
const response = await apiPost("/api/capabilities", body);
|
|
32913
|
-
const hints = capabilityHints(response.data.surfaces);
|
|
32923
|
+
const hints = capabilityHints(response.data.surfaces, response.data.tools);
|
|
32914
32924
|
writeJson({
|
|
32915
32925
|
...response,
|
|
32916
32926
|
meta: { count: response.data.surfaces.length },
|
|
@@ -33380,12 +33390,793 @@ Full guide: __tooling__/docs/tools/baker/creatives.md`
|
|
|
33380
33390
|
}
|
|
33381
33391
|
});
|
|
33382
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
|
+
|
|
33383
34174
|
// src/commands/flows/index.ts
|
|
33384
|
-
import { defineCommand as
|
|
34175
|
+
import { defineCommand as defineCommand117 } from "citty";
|
|
33385
34176
|
|
|
33386
34177
|
// src/commands/flows/add.ts
|
|
33387
34178
|
import { randomUUID } from "crypto";
|
|
33388
|
-
import { defineCommand as
|
|
34179
|
+
import { defineCommand as defineCommand113 } from "citty";
|
|
33389
34180
|
|
|
33390
34181
|
// src/commands/flows/shared.ts
|
|
33391
34182
|
import { existsSync as existsSync5, readdirSync as readdirSync2, readFileSync as readFileSync9, writeFileSync as writeFileSync3 } from "fs";
|
|
@@ -33438,12 +34229,12 @@ function collectSideEffects(tree) {
|
|
|
33438
34229
|
);
|
|
33439
34230
|
}
|
|
33440
34231
|
function readFlowTree(slug) {
|
|
33441
|
-
const
|
|
33442
|
-
if (!existsSync5(
|
|
34232
|
+
const path43 = join3(flowsDir(), slug, "_data.json");
|
|
34233
|
+
if (!existsSync5(path43)) {
|
|
33443
34234
|
failLocal(`No form "${slug}". Run "baker flows list" to see the forms in this workspace.`);
|
|
33444
34235
|
}
|
|
33445
34236
|
try {
|
|
33446
|
-
return JSON.parse(readFileSync9(
|
|
34237
|
+
return JSON.parse(readFileSync9(path43, "utf-8"));
|
|
33447
34238
|
} catch (error) {
|
|
33448
34239
|
failLocal(`Could not read form "${slug}": ${error instanceof Error ? error.message : String(error)}`);
|
|
33449
34240
|
}
|
|
@@ -33595,7 +34386,7 @@ registerSchema({
|
|
|
33595
34386
|
parent: { type: "string", description: "Parent node id. Omit to append to the root's children.", required: false }
|
|
33596
34387
|
}
|
|
33597
34388
|
});
|
|
33598
|
-
var addNodeCommand =
|
|
34389
|
+
var addNodeCommand = defineCommand113({
|
|
33599
34390
|
meta: {
|
|
33600
34391
|
name: "add-node",
|
|
33601
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"'
|
|
@@ -33691,7 +34482,7 @@ registerSchema({
|
|
|
33691
34482
|
}
|
|
33692
34483
|
}
|
|
33693
34484
|
});
|
|
33694
|
-
var addSideEffectCommand =
|
|
34485
|
+
var addSideEffectCommand = defineCommand113({
|
|
33695
34486
|
meta: {
|
|
33696
34487
|
name: "add-side-effect",
|
|
33697
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"
|
|
@@ -33773,7 +34564,7 @@ var addSideEffectCommand = defineCommand112({
|
|
|
33773
34564
|
|
|
33774
34565
|
// src/commands/flows/map.ts
|
|
33775
34566
|
import { readFileSync as readFileSync11 } from "fs";
|
|
33776
|
-
import { defineCommand as
|
|
34567
|
+
import { defineCommand as defineCommand114 } from "citty";
|
|
33777
34568
|
|
|
33778
34569
|
// src/commands/flows/value-expression.ts
|
|
33779
34570
|
import { existsSync as existsSync6, readFileSync as readFileSync10 } from "fs";
|
|
@@ -33835,10 +34626,10 @@ function parseValueExpression(raw) {
|
|
|
33835
34626
|
return parts.map(parsePart);
|
|
33836
34627
|
}
|
|
33837
34628
|
function trackingFieldIds() {
|
|
33838
|
-
const
|
|
33839
|
-
if (!existsSync6(
|
|
34629
|
+
const path43 = join4(flowsDir(), "..", "tracking.ts");
|
|
34630
|
+
if (!existsSync6(path43)) return null;
|
|
33840
34631
|
try {
|
|
33841
|
-
const source = readFileSync10(
|
|
34632
|
+
const source = readFileSync10(path43, "utf-8");
|
|
33842
34633
|
const block2 = source.match(/TRACKING_FIELD_IDS\s*=\s*\[([\s\S]*?)\]\s*as const/)?.[1];
|
|
33843
34634
|
if (!block2) return null;
|
|
33844
34635
|
const ids = [...block2.matchAll(/"(tracking\.[a-z0-9_]+)"/g)].map((match) => match[1]);
|
|
@@ -34390,13 +35181,13 @@ function specsFromFile(parsed) {
|
|
|
34390
35181
|
return `${destField}${type}=${entry?.value ?? ""}`;
|
|
34391
35182
|
});
|
|
34392
35183
|
}
|
|
34393
|
-
function readSpecFile(
|
|
35184
|
+
function readSpecFile(path43) {
|
|
34394
35185
|
let raw;
|
|
34395
35186
|
try {
|
|
34396
|
-
raw =
|
|
35187
|
+
raw = path43 === "-" ? readFileSync11(0, "utf-8") : readFileSync11(path43, "utf-8");
|
|
34397
35188
|
} catch (error) {
|
|
34398
35189
|
refuse(
|
|
34399
|
-
`Could not read ${
|
|
35190
|
+
`Could not read ${path43 === "-" ? "the mapping from stdin" : `"${path43}"`}: ${error instanceof Error ? error.message : String(error)}`
|
|
34400
35191
|
);
|
|
34401
35192
|
}
|
|
34402
35193
|
let parsed;
|
|
@@ -34404,7 +35195,7 @@ function readSpecFile(path40) {
|
|
|
34404
35195
|
parsed = JSON.parse(raw);
|
|
34405
35196
|
} catch (error) {
|
|
34406
35197
|
refuse(
|
|
34407
|
-
`${
|
|
35198
|
+
`${path43 === "-" ? "stdin" : `"${path43}"`} is not valid JSON: ${error instanceof Error ? error.message : String(error)}`,
|
|
34408
35199
|
'Expected { "map": { "<destField>": "<value>", \u2026 } }'
|
|
34409
35200
|
);
|
|
34410
35201
|
}
|
|
@@ -34546,7 +35337,7 @@ async function mapOptions(args, fromFile, into) {
|
|
|
34546
35337
|
destinationFields: hubspotForm ? await hubspotFieldCatalog(hubspotForm) : void 0
|
|
34547
35338
|
};
|
|
34548
35339
|
}
|
|
34549
|
-
var mapCommand2 =
|
|
35340
|
+
var mapCommand2 = defineCommand114({
|
|
34550
35341
|
meta: {
|
|
34551
35342
|
name: "map",
|
|
34552
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"
|
|
@@ -34644,7 +35435,7 @@ var mapCommand2 = defineCommand113({
|
|
|
34644
35435
|
});
|
|
34645
35436
|
|
|
34646
35437
|
// src/commands/flows/normalize.ts
|
|
34647
|
-
import { defineCommand as
|
|
35438
|
+
import { defineCommand as defineCommand115 } from "citty";
|
|
34648
35439
|
var OPTIONAL_STRINGS = ["oauthProviderId"];
|
|
34649
35440
|
var ARRAY_FIELDS = [
|
|
34650
35441
|
"fieldMapping",
|
|
@@ -34658,18 +35449,18 @@ var ARRAY_FIELDS = [
|
|
|
34658
35449
|
"tagIds"
|
|
34659
35450
|
];
|
|
34660
35451
|
var ARRAY_OWNERS = ["", "body"];
|
|
34661
|
-
function dropUnsetOptionals(sideEffect,
|
|
35452
|
+
function dropUnsetOptionals(sideEffect, path43) {
|
|
34662
35453
|
return OPTIONAL_STRINGS.flatMap((key) => {
|
|
34663
35454
|
if (!(key in sideEffect) || sideEffect[key] !== null && sideEffect[key] !== "") return [];
|
|
34664
35455
|
delete sideEffect[key];
|
|
34665
|
-
return [{ path:
|
|
35456
|
+
return [{ path: path43, change: `dropped \`${key}\` (an optional string is absent, never null)` }];
|
|
34666
35457
|
});
|
|
34667
35458
|
}
|
|
34668
|
-
function fillNulledArrays(target, prefix,
|
|
35459
|
+
function fillNulledArrays(target, prefix, path43) {
|
|
34669
35460
|
return ARRAY_FIELDS.flatMap((key) => {
|
|
34670
35461
|
if (!(key in target) || target[key] !== null) return [];
|
|
34671
35462
|
target[key] = [];
|
|
34672
|
-
return [{ path:
|
|
35463
|
+
return [{ path: path43, change: `\`${prefix}${key}: null\` \u2192 \`[]\`` }];
|
|
34673
35464
|
});
|
|
34674
35465
|
}
|
|
34675
35466
|
function sideEffectsOf(node) {
|
|
@@ -34679,13 +35470,13 @@ function sideEffectsOf(node) {
|
|
|
34679
35470
|
);
|
|
34680
35471
|
}
|
|
34681
35472
|
function normalizeSideEffect(sideEffect, where) {
|
|
34682
|
-
const
|
|
35473
|
+
const path43 = `${where} \u2192 ${String(sideEffect.id ?? "side effect")}`;
|
|
34683
35474
|
const arrays = ARRAY_OWNERS.flatMap((owner) => {
|
|
34684
35475
|
const target = owner ? sideEffect[owner] : sideEffect;
|
|
34685
35476
|
if (!target || typeof target !== "object") return [];
|
|
34686
|
-
return fillNulledArrays(target, owner ? `${owner}.` : "",
|
|
35477
|
+
return fillNulledArrays(target, owner ? `${owner}.` : "", path43);
|
|
34687
35478
|
});
|
|
34688
|
-
return [...dropUnsetOptionals(sideEffect,
|
|
35479
|
+
return [...dropUnsetOptionals(sideEffect, path43), ...arrays];
|
|
34689
35480
|
}
|
|
34690
35481
|
function normalizeFlowTree(tree) {
|
|
34691
35482
|
const changes = [];
|
|
@@ -34713,7 +35504,7 @@ registerSchema({
|
|
|
34713
35504
|
}
|
|
34714
35505
|
}
|
|
34715
35506
|
});
|
|
34716
|
-
var normalizeCommand =
|
|
35507
|
+
var normalizeCommand = defineCommand115({
|
|
34717
35508
|
meta: {
|
|
34718
35509
|
name: "normalize",
|
|
34719
35510
|
description: "Repair the shapes a form file is allowed to get slightly wrong. Example: baker flows normalize contact --write"
|
|
@@ -34744,7 +35535,7 @@ var normalizeCommand = defineCommand114({
|
|
|
34744
35535
|
});
|
|
34745
35536
|
|
|
34746
35537
|
// src/commands/flows/schema.ts
|
|
34747
|
-
import { defineCommand as
|
|
35538
|
+
import { defineCommand as defineCommand116 } from "citty";
|
|
34748
35539
|
registerSchema({
|
|
34749
35540
|
command: "flows.schema",
|
|
34750
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.",
|
|
@@ -34769,7 +35560,7 @@ function triggerHints(triggers) {
|
|
|
34769
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(", ")}.` : "")
|
|
34770
35561
|
];
|
|
34771
35562
|
}
|
|
34772
|
-
var schemaCommand =
|
|
35563
|
+
var schemaCommand = defineCommand116({
|
|
34773
35564
|
meta: {
|
|
34774
35565
|
name: "schema",
|
|
34775
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)"
|
|
@@ -34903,7 +35694,7 @@ function displayName(slug) {
|
|
|
34903
35694
|
const name = tree.displayName;
|
|
34904
35695
|
return typeof name === "string" && name.trim() ? name.trim() : slug;
|
|
34905
35696
|
}
|
|
34906
|
-
var listCommand12 =
|
|
35697
|
+
var listCommand12 = defineCommand117({
|
|
34907
35698
|
meta: {
|
|
34908
35699
|
name: "list",
|
|
34909
35700
|
description: "List Forms in this workspace with the count of confidential fields still needing setup. Example: baker flows list"
|
|
@@ -34924,7 +35715,7 @@ var listCommand12 = defineCommand116({
|
|
|
34924
35715
|
});
|
|
34925
35716
|
}
|
|
34926
35717
|
});
|
|
34927
|
-
var showCommand3 =
|
|
35718
|
+
var showCommand3 = defineCommand117({
|
|
34928
35719
|
meta: {
|
|
34929
35720
|
name: "show",
|
|
34930
35721
|
description: "Show a Form's confidential fields and their configuration status (never secret values). Example: baker flows show contact"
|
|
@@ -34952,7 +35743,7 @@ var showCommand3 = defineCommand116({
|
|
|
34952
35743
|
});
|
|
34953
35744
|
}
|
|
34954
35745
|
});
|
|
34955
|
-
var flowsCommand =
|
|
35746
|
+
var flowsCommand = defineCommand117({
|
|
34956
35747
|
meta: {
|
|
34957
35748
|
name: "flows",
|
|
34958
35749
|
description: `Read and shape this workspace's Forms (flows).
|
|
@@ -34986,10 +35777,10 @@ Full guide: __tooling__/docs/tools/baker/flows.md`
|
|
|
34986
35777
|
});
|
|
34987
35778
|
|
|
34988
35779
|
// src/commands/ga4/index.ts
|
|
34989
|
-
import { defineCommand as
|
|
35780
|
+
import { defineCommand as defineCommand124 } from "citty";
|
|
34990
35781
|
|
|
34991
35782
|
// src/commands/ga4/audit.ts
|
|
34992
|
-
import { defineCommand as
|
|
35783
|
+
import { defineCommand as defineCommand118 } from "citty";
|
|
34993
35784
|
|
|
34994
35785
|
// src/commands/ga4/resolve.ts
|
|
34995
35786
|
async function fetchProperties(useCache = true) {
|
|
@@ -35055,7 +35846,7 @@ registerSchema({
|
|
|
35055
35846
|
"no-cache": { type: "boolean", description: "Skip cache, hit API directly", required: false }
|
|
35056
35847
|
}
|
|
35057
35848
|
});
|
|
35058
|
-
var auditCommand2 =
|
|
35849
|
+
var auditCommand2 = defineCommand118({
|
|
35059
35850
|
meta: {
|
|
35060
35851
|
name: "audit",
|
|
35061
35852
|
description: `Run all GA4 admin health checks. Returns property config with playbook warnings.
|
|
@@ -35110,7 +35901,7 @@ Examples:
|
|
|
35110
35901
|
});
|
|
35111
35902
|
|
|
35112
35903
|
// src/commands/ga4/config.ts
|
|
35113
|
-
import { defineCommand as
|
|
35904
|
+
import { defineCommand as defineCommand119 } from "citty";
|
|
35114
35905
|
|
|
35115
35906
|
// src/commands/ga4/shared.ts
|
|
35116
35907
|
import { readFileSync as readFileSync12 } from "fs";
|
|
@@ -35223,10 +36014,10 @@ async function stageOps(ops) {
|
|
|
35223
36014
|
handleError2(err);
|
|
35224
36015
|
}
|
|
35225
36016
|
}
|
|
35226
|
-
async function draftAction2(
|
|
36017
|
+
async function draftAction2(path43, body, chat) {
|
|
35227
36018
|
const chatId = resolveChatId(chat);
|
|
35228
36019
|
try {
|
|
35229
|
-
const data = await apiPost(
|
|
36020
|
+
const data = await apiPost(path43, { chatId, ...body });
|
|
35230
36021
|
writeJsonEnvelope({ ok: true, data });
|
|
35231
36022
|
return data;
|
|
35232
36023
|
} catch (err) {
|
|
@@ -35306,7 +36097,7 @@ function configHints(config) {
|
|
|
35306
36097
|
}
|
|
35307
36098
|
return hints;
|
|
35308
36099
|
}
|
|
35309
|
-
var configCommand =
|
|
36100
|
+
var configCommand = defineCommand119({
|
|
35310
36101
|
meta: {
|
|
35311
36102
|
name: "config",
|
|
35312
36103
|
description: `Read how the property is configured to measure \u2014 key events, custom definitions, custom events, retention
|
|
@@ -35338,7 +36129,7 @@ Examples:
|
|
|
35338
36129
|
});
|
|
35339
36130
|
|
|
35340
36131
|
// src/commands/ga4/draft.ts
|
|
35341
|
-
import { defineCommand as
|
|
36132
|
+
import { defineCommand as defineCommand120 } from "citty";
|
|
35342
36133
|
registerSchema({
|
|
35343
36134
|
command: "ga4.draft",
|
|
35344
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.",
|
|
@@ -35347,13 +36138,13 @@ registerSchema({
|
|
|
35347
36138
|
chat: { type: "string", description: CHAT_READ_ARG.description, required: false }
|
|
35348
36139
|
}
|
|
35349
36140
|
});
|
|
35350
|
-
var draftCommand3 =
|
|
36141
|
+
var draftCommand3 = defineCommand120({
|
|
35351
36142
|
meta: {
|
|
35352
36143
|
name: "draft",
|
|
35353
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."
|
|
35354
36145
|
},
|
|
35355
36146
|
subCommands: {
|
|
35356
|
-
list:
|
|
36147
|
+
list: defineCommand120({
|
|
35357
36148
|
meta: { name: "list", description: "Review everything staged on this chat (--json for the raw envelope)" },
|
|
35358
36149
|
args: {
|
|
35359
36150
|
json: { type: "boolean", description: "Print the raw JSON envelope", required: false },
|
|
@@ -35363,7 +36154,7 @@ var draftCommand3 = defineCommand119({
|
|
|
35363
36154
|
await draftList(args.json === true, args.chat);
|
|
35364
36155
|
}
|
|
35365
36156
|
}),
|
|
35366
|
-
show:
|
|
36157
|
+
show: defineCommand120({
|
|
35367
36158
|
meta: {
|
|
35368
36159
|
name: "show",
|
|
35369
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)."
|
|
@@ -35380,7 +36171,7 @@ var draftCommand3 = defineCommand119({
|
|
|
35380
36171
|
);
|
|
35381
36172
|
}
|
|
35382
36173
|
}),
|
|
35383
|
-
amend:
|
|
36174
|
+
amend: defineCommand120({
|
|
35384
36175
|
meta: {
|
|
35385
36176
|
name: "amend",
|
|
35386
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."
|
|
@@ -35397,7 +36188,7 @@ var draftCommand3 = defineCommand119({
|
|
|
35397
36188
|
});
|
|
35398
36189
|
}
|
|
35399
36190
|
}),
|
|
35400
|
-
remove:
|
|
36191
|
+
remove: defineCommand120({
|
|
35401
36192
|
meta: { name: "remove", description: "Remove one staged change" },
|
|
35402
36193
|
args: { ref: { type: "positional", description: "Staged ref or target id", required: false } },
|
|
35403
36194
|
run: async ({ args }) => {
|
|
@@ -35406,7 +36197,7 @@ var draftCommand3 = defineCommand119({
|
|
|
35406
36197
|
});
|
|
35407
36198
|
}
|
|
35408
36199
|
}),
|
|
35409
|
-
clear:
|
|
36200
|
+
clear: defineCommand120({
|
|
35410
36201
|
meta: { name: "clear", description: "Discard all Google Analytics changes staged on this chat" },
|
|
35411
36202
|
run: async () => {
|
|
35412
36203
|
await draftAction2("/api/ga4/draft/clear", {});
|
|
@@ -35416,7 +36207,7 @@ var draftCommand3 = defineCommand119({
|
|
|
35416
36207
|
});
|
|
35417
36208
|
|
|
35418
36209
|
// src/commands/ga4/properties.ts
|
|
35419
|
-
import { defineCommand as
|
|
36210
|
+
import { defineCommand as defineCommand121 } from "citty";
|
|
35420
36211
|
registerSchema({
|
|
35421
36212
|
command: "ga4.properties",
|
|
35422
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.",
|
|
@@ -35432,7 +36223,7 @@ function propertyHints(properties) {
|
|
|
35432
36223
|
resources: properties.map((property) => ({ id: property.externalId, label: property.name }))
|
|
35433
36224
|
});
|
|
35434
36225
|
}
|
|
35435
|
-
var propertiesCommand =
|
|
36226
|
+
var propertiesCommand = defineCommand121({
|
|
35436
36227
|
meta: {
|
|
35437
36228
|
name: "properties",
|
|
35438
36229
|
description: `List accessible GA4 properties.
|
|
@@ -35482,7 +36273,7 @@ Examples:
|
|
|
35482
36273
|
// src/commands/ga4/query.ts
|
|
35483
36274
|
import { appendFileSync as appendFileSync2, existsSync as existsSync7, readFileSync as readFileSync13, writeFileSync as writeFileSync4 } from "fs";
|
|
35484
36275
|
import { resolve as resolve2 } from "path";
|
|
35485
|
-
import { defineCommand as
|
|
36276
|
+
import { defineCommand as defineCommand122 } from "citty";
|
|
35486
36277
|
|
|
35487
36278
|
// src/commands/ga4/presets.ts
|
|
35488
36279
|
var GA4_PRESETS = [
|
|
@@ -35617,7 +36408,7 @@ function handleError3(err) {
|
|
|
35617
36408
|
});
|
|
35618
36409
|
process.exit(1);
|
|
35619
36410
|
}
|
|
35620
|
-
var queryCommand2 =
|
|
36411
|
+
var queryCommand2 = defineCommand122({
|
|
35621
36412
|
meta: {
|
|
35622
36413
|
name: "query",
|
|
35623
36414
|
description: `Run GA4 Data API reports. Preset-first with free-form escape hatch.
|
|
@@ -35688,7 +36479,7 @@ Free-form (escape hatch):
|
|
|
35688
36479
|
});
|
|
35689
36480
|
|
|
35690
36481
|
// src/commands/ga4/write-commands.ts
|
|
35691
|
-
import { defineCommand as
|
|
36482
|
+
import { defineCommand as defineCommand123 } from "citty";
|
|
35692
36483
|
var PROPERTY_ARG_DESCRIPTION = "GA4 property id (optional only when one property is connected \u2014 run `baker ga4 properties`)";
|
|
35693
36484
|
var propertyArg = { type: "string", description: PROPERTY_ARG_DESCRIPTION, required: false };
|
|
35694
36485
|
function createJsonDescription(noun) {
|
|
@@ -35706,7 +36497,7 @@ registerSchema({
|
|
|
35706
36497
|
"property-id": { type: "string", description: PROPERTY_ARG_DESCRIPTION, required: false }
|
|
35707
36498
|
}
|
|
35708
36499
|
});
|
|
35709
|
-
var keyEventCommand =
|
|
36500
|
+
var keyEventCommand = defineCommand123({
|
|
35710
36501
|
meta: {
|
|
35711
36502
|
name: "key-event",
|
|
35712
36503
|
description: `Stage key-event (conversion) changes
|
|
@@ -35719,7 +36510,7 @@ Examples:
|
|
|
35719
36510
|
baker ga4 key-event delete 4185`
|
|
35720
36511
|
},
|
|
35721
36512
|
subCommands: {
|
|
35722
|
-
create:
|
|
36513
|
+
create: defineCommand123({
|
|
35723
36514
|
meta: {
|
|
35724
36515
|
name: "create",
|
|
35725
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."
|
|
@@ -35740,7 +36531,7 @@ Examples:
|
|
|
35740
36531
|
);
|
|
35741
36532
|
}
|
|
35742
36533
|
}),
|
|
35743
|
-
update:
|
|
36534
|
+
update: defineCommand123({
|
|
35744
36535
|
meta: {
|
|
35745
36536
|
name: "update",
|
|
35746
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."
|
|
@@ -35760,7 +36551,7 @@ Examples:
|
|
|
35760
36551
|
});
|
|
35761
36552
|
}
|
|
35762
36553
|
}),
|
|
35763
|
-
delete:
|
|
36554
|
+
delete: defineCommand123({
|
|
35764
36555
|
meta: {
|
|
35765
36556
|
name: "delete",
|
|
35766
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."
|
|
@@ -35808,7 +36599,7 @@ for (const { kind, noun, createHint } of DEFINITIONS) {
|
|
|
35808
36599
|
}
|
|
35809
36600
|
function definitionCommand(definition) {
|
|
35810
36601
|
const { command, kind, noun, example } = definition;
|
|
35811
|
-
return
|
|
36602
|
+
return defineCommand123({
|
|
35812
36603
|
meta: {
|
|
35813
36604
|
name: command,
|
|
35814
36605
|
description: `Stage ${noun} changes
|
|
@@ -35820,7 +36611,7 @@ Examples:
|
|
|
35820
36611
|
baker ga4 ${command} archive 12`
|
|
35821
36612
|
},
|
|
35822
36613
|
subCommands: {
|
|
35823
|
-
create:
|
|
36614
|
+
create: defineCommand123({
|
|
35824
36615
|
meta: { name: "create", description: `Stage a new ${noun} (or a JSON array of them, staged together)` },
|
|
35825
36616
|
args: {
|
|
35826
36617
|
json: { type: "string", description: createJsonDescription(noun), required: false },
|
|
@@ -35838,7 +36629,7 @@ Examples:
|
|
|
35838
36629
|
);
|
|
35839
36630
|
}
|
|
35840
36631
|
}),
|
|
35841
|
-
update:
|
|
36632
|
+
update: defineCommand123({
|
|
35842
36633
|
meta: {
|
|
35843
36634
|
name: "update",
|
|
35844
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.`
|
|
@@ -35858,7 +36649,7 @@ Examples:
|
|
|
35858
36649
|
});
|
|
35859
36650
|
}
|
|
35860
36651
|
}),
|
|
35861
|
-
archive:
|
|
36652
|
+
archive: defineCommand123({
|
|
35862
36653
|
meta: {
|
|
35863
36654
|
name: "archive",
|
|
35864
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.`
|
|
@@ -35903,7 +36694,7 @@ var dataStreamArg = {
|
|
|
35903
36694
|
function withStream(args) {
|
|
35904
36695
|
return typeof args["data-stream"] === "string" ? { dataStream: args["data-stream"] } : {};
|
|
35905
36696
|
}
|
|
35906
|
-
var customEventCommand =
|
|
36697
|
+
var customEventCommand = defineCommand123({
|
|
35907
36698
|
meta: {
|
|
35908
36699
|
name: "custom-event",
|
|
35909
36700
|
description: `Stage custom events \u2014 new events built from events the site already sends
|
|
@@ -35914,7 +36705,7 @@ Examples:
|
|
|
35914
36705
|
baker ga4 custom-event delete 7`
|
|
35915
36706
|
},
|
|
35916
36707
|
subCommands: {
|
|
35917
|
-
create:
|
|
36708
|
+
create: defineCommand123({
|
|
35918
36709
|
meta: {
|
|
35919
36710
|
name: "create",
|
|
35920
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."
|
|
@@ -35937,7 +36728,7 @@ Examples:
|
|
|
35937
36728
|
);
|
|
35938
36729
|
}
|
|
35939
36730
|
}),
|
|
35940
|
-
update:
|
|
36731
|
+
update: defineCommand123({
|
|
35941
36732
|
meta: { name: "update", description: "Stage a change to an existing custom event (pass its id)" },
|
|
35942
36733
|
args: {
|
|
35943
36734
|
id: { type: "positional", description: "Custom event rule id", required: false },
|
|
@@ -35956,7 +36747,7 @@ Examples:
|
|
|
35956
36747
|
});
|
|
35957
36748
|
}
|
|
35958
36749
|
}),
|
|
35959
|
-
delete:
|
|
36750
|
+
delete: defineCommand123({
|
|
35960
36751
|
meta: {
|
|
35961
36752
|
name: "delete",
|
|
35962
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."
|
|
@@ -36011,7 +36802,7 @@ function booleanArg(input, flag) {
|
|
|
36011
36802
|
if (input === "false") return false;
|
|
36012
36803
|
return failValidation3(`--${flag} must be true or false`);
|
|
36013
36804
|
}
|
|
36014
|
-
var dataRetentionCommand =
|
|
36805
|
+
var dataRetentionCommand = defineCommand123({
|
|
36015
36806
|
meta: {
|
|
36016
36807
|
name: "data-retention",
|
|
36017
36808
|
description: `Stage a change to how long Google Analytics keeps data
|
|
@@ -36021,7 +36812,7 @@ Examples:
|
|
|
36021
36812
|
baker ga4 data-retention set --event-data 14 --user-data 14 --reset-on-activity true`
|
|
36022
36813
|
},
|
|
36023
36814
|
subCommands: {
|
|
36024
|
-
set:
|
|
36815
|
+
set: defineCommand123({
|
|
36025
36816
|
meta: { name: "set", description: "Stage the retention window (months)" },
|
|
36026
36817
|
args: {
|
|
36027
36818
|
"event-data": { type: "string", description: "2, 14, 26, 38 or 50", required: false },
|
|
@@ -36057,7 +36848,7 @@ registerSchema({
|
|
|
36057
36848
|
"property-id": { type: "string", description: PROPERTY_ARG_DESCRIPTION, required: false }
|
|
36058
36849
|
}
|
|
36059
36850
|
});
|
|
36060
|
-
var audienceCommand =
|
|
36851
|
+
var audienceCommand = defineCommand123({
|
|
36061
36852
|
meta: {
|
|
36062
36853
|
name: "audience",
|
|
36063
36854
|
description: `Stage audience changes (saved groups of users, for retargeting and analysis)
|
|
@@ -36089,7 +36880,7 @@ Examples:
|
|
|
36089
36880
|
baker ga4 audience archive 8813`
|
|
36090
36881
|
},
|
|
36091
36882
|
subCommands: {
|
|
36092
|
-
create:
|
|
36883
|
+
create: defineCommand123({
|
|
36093
36884
|
meta: { name: "create", description: createJsonDescription("audience") },
|
|
36094
36885
|
args: {
|
|
36095
36886
|
json: { type: "string", description: createJsonDescription("audience"), required: false },
|
|
@@ -36107,7 +36898,7 @@ Examples:
|
|
|
36107
36898
|
);
|
|
36108
36899
|
}
|
|
36109
36900
|
}),
|
|
36110
|
-
update:
|
|
36901
|
+
update: defineCommand123({
|
|
36111
36902
|
meta: {
|
|
36112
36903
|
name: "update",
|
|
36113
36904
|
description: "Stage a new name or description (pass the audience id). Who is in it cannot be changed."
|
|
@@ -36128,7 +36919,7 @@ Examples:
|
|
|
36128
36919
|
});
|
|
36129
36920
|
}
|
|
36130
36921
|
}),
|
|
36131
|
-
archive:
|
|
36922
|
+
archive: defineCommand123({
|
|
36132
36923
|
meta: {
|
|
36133
36924
|
name: "archive",
|
|
36134
36925
|
description: "Stage retiring an audience. Cannot be undone \u2014 the users it gathered do not come back."
|
|
@@ -36157,7 +36948,7 @@ registerSchema({
|
|
|
36157
36948
|
"property-id": { type: "string", description: PROPERTY_ARG_DESCRIPTION, required: false }
|
|
36158
36949
|
}
|
|
36159
36950
|
});
|
|
36160
|
-
var propertyCommand =
|
|
36951
|
+
var propertyCommand = defineCommand123({
|
|
36161
36952
|
meta: {
|
|
36162
36953
|
name: "property",
|
|
36163
36954
|
description: `Stage a change to the property's own settings
|
|
@@ -36167,7 +36958,7 @@ Examples:
|
|
|
36167
36958
|
baker ga4 property set --name "Acme \u2014 Web" --industry REAL_ESTATE`
|
|
36168
36959
|
},
|
|
36169
36960
|
subCommands: {
|
|
36170
|
-
set:
|
|
36961
|
+
set: defineCommand123({
|
|
36171
36962
|
meta: { name: "set", description: "Stage the property name and/or industry category" },
|
|
36172
36963
|
args: {
|
|
36173
36964
|
name: { type: "string", description: "New property display name", required: false },
|
|
@@ -36207,7 +36998,7 @@ registerSchema({
|
|
|
36207
36998
|
"property-id": { type: "string", description: PROPERTY_ARG_DESCRIPTION, required: false }
|
|
36208
36999
|
}
|
|
36209
37000
|
});
|
|
36210
|
-
var adsLinkCommand =
|
|
37001
|
+
var adsLinkCommand = defineCommand123({
|
|
36211
37002
|
meta: {
|
|
36212
37003
|
name: "ads-link",
|
|
36213
37004
|
description: `Stage linking this property to a Google Ads account
|
|
@@ -36222,7 +37013,7 @@ Examples:
|
|
|
36222
37013
|
baker ga4 ads-link delete L1234`
|
|
36223
37014
|
},
|
|
36224
37015
|
subCommands: {
|
|
36225
|
-
create:
|
|
37016
|
+
create: defineCommand123({
|
|
36226
37017
|
meta: { name: "create", description: "Stage linking the property to a Google Ads account" },
|
|
36227
37018
|
args: {
|
|
36228
37019
|
"customer-id": { type: "string", description: "Google Ads customer id (123-456-7890)", required: false },
|
|
@@ -36241,7 +37032,7 @@ Examples:
|
|
|
36241
37032
|
await stageOp3({ kind: "ga4.googleAdsLink.create", payload, ...withProperty(args) });
|
|
36242
37033
|
}
|
|
36243
37034
|
}),
|
|
36244
|
-
update:
|
|
37035
|
+
update: defineCommand123({
|
|
36245
37036
|
meta: {
|
|
36246
37037
|
name: "update",
|
|
36247
37038
|
description: "Stage the ads-personalisation setting on an existing link (pass its id)"
|
|
@@ -36263,7 +37054,7 @@ Examples:
|
|
|
36263
37054
|
});
|
|
36264
37055
|
}
|
|
36265
37056
|
}),
|
|
36266
|
-
delete:
|
|
37057
|
+
delete: defineCommand123({
|
|
36267
37058
|
meta: {
|
|
36268
37059
|
name: "delete",
|
|
36269
37060
|
description: "Stage unlinking. Stops conversion imports and strands every audience on this property."
|
|
@@ -36314,7 +37105,7 @@ var MEASUREMENT_FLAGS = {
|
|
|
36314
37105
|
"page-changes": "pageChangesEnabled",
|
|
36315
37106
|
"form-interactions": "formInteractionsEnabled"
|
|
36316
37107
|
};
|
|
36317
|
-
var enhancedMeasurementCommand =
|
|
37108
|
+
var enhancedMeasurementCommand = defineCommand123({
|
|
36318
37109
|
meta: {
|
|
36319
37110
|
name: "enhanced-measurement",
|
|
36320
37111
|
description: `Stage the automatic events GA4 collects on a website stream
|
|
@@ -36328,7 +37119,7 @@ Examples:
|
|
|
36328
37119
|
baker ga4 enhanced-measurement set --video false`
|
|
36329
37120
|
},
|
|
36330
37121
|
subCommands: {
|
|
36331
|
-
set:
|
|
37122
|
+
set: defineCommand123({
|
|
36332
37123
|
meta: { name: "set", description: "Stage which automatic events the website stream collects" },
|
|
36333
37124
|
args: {
|
|
36334
37125
|
scrolls: { type: "string", description: "true|false", required: false },
|
|
@@ -36371,7 +37162,7 @@ Examples:
|
|
|
36371
37162
|
});
|
|
36372
37163
|
|
|
36373
37164
|
// src/commands/ga4/index.ts
|
|
36374
|
-
var ga4Command =
|
|
37165
|
+
var ga4Command = defineCommand124({
|
|
36375
37166
|
meta: {
|
|
36376
37167
|
name: "ga4",
|
|
36377
37168
|
description: `Google Analytics 4. Report on a property, audit its config, and change what it measures.
|
|
@@ -36420,12 +37211,12 @@ Full guide: __tooling__/docs/tools/baker/ga4.md`
|
|
|
36420
37211
|
});
|
|
36421
37212
|
|
|
36422
37213
|
// src/commands/gsc/index.ts
|
|
36423
|
-
import { defineCommand as
|
|
37214
|
+
import { defineCommand as defineCommand128 } from "citty";
|
|
36424
37215
|
|
|
36425
37216
|
// src/commands/gsc/query.ts
|
|
36426
37217
|
import { appendFileSync as appendFileSync3, existsSync as existsSync8, readFileSync as readFileSync14, writeFileSync as writeFileSync5 } from "fs";
|
|
36427
37218
|
import { resolve as resolve3 } from "path";
|
|
36428
|
-
import { defineCommand as
|
|
37219
|
+
import { defineCommand as defineCommand125 } from "citty";
|
|
36429
37220
|
|
|
36430
37221
|
// src/commands/gsc/presets.ts
|
|
36431
37222
|
var GSC_PRESETS = [
|
|
@@ -36619,7 +37410,7 @@ function handleError4(err) {
|
|
|
36619
37410
|
});
|
|
36620
37411
|
process.exit(1);
|
|
36621
37412
|
}
|
|
36622
|
-
var queryCommand3 =
|
|
37413
|
+
var queryCommand3 = defineCommand125({
|
|
36623
37414
|
meta: {
|
|
36624
37415
|
name: "query",
|
|
36625
37416
|
description: `Run GSC Search Analytics queries. Preset-first with free-form escape hatch.
|
|
@@ -36697,7 +37488,7 @@ Free-form (escape hatch):
|
|
|
36697
37488
|
});
|
|
36698
37489
|
|
|
36699
37490
|
// src/commands/gsc/sitemaps.ts
|
|
36700
|
-
import { defineCommand as
|
|
37491
|
+
import { defineCommand as defineCommand126 } from "citty";
|
|
36701
37492
|
registerSchema({
|
|
36702
37493
|
command: "gsc.sitemaps",
|
|
36703
37494
|
description: "List sitemaps for a Search Console site. Check sitemap health and errors.",
|
|
@@ -36706,7 +37497,7 @@ registerSchema({
|
|
|
36706
37497
|
"no-cache": { type: "boolean", description: "Skip cache, hit API directly", required: false }
|
|
36707
37498
|
}
|
|
36708
37499
|
});
|
|
36709
|
-
var sitemapsCommand =
|
|
37500
|
+
var sitemapsCommand = defineCommand126({
|
|
36710
37501
|
meta: {
|
|
36711
37502
|
name: "sitemaps",
|
|
36712
37503
|
description: `List sitemaps for a site. Check health and errors.
|
|
@@ -36759,7 +37550,7 @@ Examples:
|
|
|
36759
37550
|
});
|
|
36760
37551
|
|
|
36761
37552
|
// src/commands/gsc/sites.ts
|
|
36762
|
-
import { defineCommand as
|
|
37553
|
+
import { defineCommand as defineCommand127 } from "citty";
|
|
36763
37554
|
registerSchema({
|
|
36764
37555
|
command: "gsc.sites",
|
|
36765
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.",
|
|
@@ -36775,7 +37566,7 @@ function siteHints(sites) {
|
|
|
36775
37566
|
resources: sites.map((site) => ({ id: site.siteUrl, label: site.permissionLevel }))
|
|
36776
37567
|
});
|
|
36777
37568
|
}
|
|
36778
|
-
var sitesCommand =
|
|
37569
|
+
var sitesCommand = defineCommand127({
|
|
36779
37570
|
meta: {
|
|
36780
37571
|
name: "sites",
|
|
36781
37572
|
description: `List verified Search Console sites.
|
|
@@ -36823,7 +37614,7 @@ Examples:
|
|
|
36823
37614
|
});
|
|
36824
37615
|
|
|
36825
37616
|
// src/commands/gsc/index.ts
|
|
36826
|
-
var gscCommand =
|
|
37617
|
+
var gscCommand = defineCommand128({
|
|
36827
37618
|
meta: {
|
|
36828
37619
|
name: "gsc",
|
|
36829
37620
|
description: `Google Search Console commands. PPC-SEO arbitrage, brand halo analysis, negative keyword discovery.
|
|
@@ -36847,7 +37638,7 @@ Full guide: __tooling__/docs/tools/baker/gsc.md`
|
|
|
36847
37638
|
});
|
|
36848
37639
|
|
|
36849
37640
|
// src/commands/history/index.ts
|
|
36850
|
-
import { defineCommand as
|
|
37641
|
+
import { defineCommand as defineCommand129 } from "citty";
|
|
36851
37642
|
registerSchema({
|
|
36852
37643
|
command: "history.list",
|
|
36853
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.",
|
|
@@ -36902,7 +37693,7 @@ function parseBoundedInt2(raw, name, min, max) {
|
|
|
36902
37693
|
}
|
|
36903
37694
|
return value;
|
|
36904
37695
|
}
|
|
36905
|
-
var listCommand13 =
|
|
37696
|
+
var listCommand13 = defineCommand129({
|
|
36906
37697
|
meta: {
|
|
36907
37698
|
name: "list",
|
|
36908
37699
|
description: "List recent account changes (unified audit log), newest first."
|
|
@@ -36948,7 +37739,7 @@ var listCommand13 = defineCommand128({
|
|
|
36948
37739
|
}
|
|
36949
37740
|
}
|
|
36950
37741
|
});
|
|
36951
|
-
var historyCommand =
|
|
37742
|
+
var historyCommand = defineCommand129({
|
|
36952
37743
|
meta: {
|
|
36953
37744
|
name: "history",
|
|
36954
37745
|
description: `Unified account history (audit log): what changed, who did it, and when.
|
|
@@ -36958,7 +37749,7 @@ Full guide: __tooling__/docs/tools/baker/history.md`
|
|
|
36958
37749
|
});
|
|
36959
37750
|
|
|
36960
37751
|
// src/commands/hubspot/index.ts
|
|
36961
|
-
import { defineCommand as
|
|
37752
|
+
import { defineCommand as defineCommand130 } from "citty";
|
|
36962
37753
|
var EMBED_TYPES = ["legacy", "v4", "unknown"];
|
|
36963
37754
|
function failNotConnected(err) {
|
|
36964
37755
|
if (err instanceof ApiError && err.code === "FORBIDDEN" && err.message.includes(HUBSPOT_MISSING_GRANT_MARKER)) {
|
|
@@ -37112,7 +37903,7 @@ registerSchema({
|
|
|
37112
37903
|
}
|
|
37113
37904
|
}
|
|
37114
37905
|
});
|
|
37115
|
-
var formsListCommand =
|
|
37906
|
+
var formsListCommand = defineCommand130({
|
|
37116
37907
|
meta: {
|
|
37117
37908
|
name: "list",
|
|
37118
37909
|
description: "List HubSpot forms with embed type and post-submit action. Example: baker hubspot forms list --redirecting-only"
|
|
@@ -37157,7 +37948,7 @@ var formsListCommand = defineCommand129({
|
|
|
37157
37948
|
}
|
|
37158
37949
|
}
|
|
37159
37950
|
});
|
|
37160
|
-
var formsViewCommand =
|
|
37951
|
+
var formsViewCommand = defineCommand130({
|
|
37161
37952
|
meta: {
|
|
37162
37953
|
name: "view",
|
|
37163
37954
|
description: "Show one HubSpot form's fields and configuration. Example: baker hubspot forms view 1a2b3c"
|
|
@@ -37209,7 +38000,7 @@ var formsViewCommand = defineCommand129({
|
|
|
37209
38000
|
}
|
|
37210
38001
|
}
|
|
37211
38002
|
});
|
|
37212
|
-
var meetingsListCommand =
|
|
38003
|
+
var meetingsListCommand = defineCommand130({
|
|
37213
38004
|
meta: {
|
|
37214
38005
|
name: "list",
|
|
37215
38006
|
description: "List HubSpot meeting links (calendars). Example: baker hubspot meetings list"
|
|
@@ -37251,7 +38042,7 @@ var meetingsListCommand = defineCommand129({
|
|
|
37251
38042
|
}
|
|
37252
38043
|
}
|
|
37253
38044
|
});
|
|
37254
|
-
var meetingsViewCommand =
|
|
38045
|
+
var meetingsViewCommand = defineCommand130({
|
|
37255
38046
|
meta: {
|
|
37256
38047
|
name: "view",
|
|
37257
38048
|
description: "Show one HubSpot meeting link's booking fields. Example: baker hubspot meetings view discovery-call"
|
|
@@ -37295,7 +38086,7 @@ var meetingsViewCommand = defineCommand129({
|
|
|
37295
38086
|
}
|
|
37296
38087
|
}
|
|
37297
38088
|
});
|
|
37298
|
-
var formsSubmissionsCommand =
|
|
38089
|
+
var formsSubmissionsCommand = defineCommand130({
|
|
37299
38090
|
meta: {
|
|
37300
38091
|
name: "submissions",
|
|
37301
38092
|
description: "How many leads a form received, and when. Example: baker hubspot forms submissions <formId> --days 30"
|
|
@@ -37344,7 +38135,7 @@ var formsSubmissionsCommand = defineCommand129({
|
|
|
37344
38135
|
}
|
|
37345
38136
|
}
|
|
37346
38137
|
});
|
|
37347
|
-
var workflowsListCommand =
|
|
38138
|
+
var workflowsListCommand = defineCommand130({
|
|
37348
38139
|
meta: {
|
|
37349
38140
|
name: "list",
|
|
37350
38141
|
description: "List HubSpot workflows. Example: baker hubspot workflows list --enabled-only"
|
|
@@ -37409,7 +38200,7 @@ function workflowViewHints(workflow) {
|
|
|
37409
38200
|
}
|
|
37410
38201
|
return hints;
|
|
37411
38202
|
}
|
|
37412
|
-
var workflowsViewCommand =
|
|
38203
|
+
var workflowsViewCommand = defineCommand130({
|
|
37413
38204
|
meta: {
|
|
37414
38205
|
name: "view",
|
|
37415
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"
|
|
@@ -37436,7 +38227,7 @@ var workflowsViewCommand = defineCommand129({
|
|
|
37436
38227
|
}
|
|
37437
38228
|
}
|
|
37438
38229
|
});
|
|
37439
|
-
var pipelinesListCommand =
|
|
38230
|
+
var pipelinesListCommand = defineCommand130({
|
|
37440
38231
|
meta: {
|
|
37441
38232
|
name: "list",
|
|
37442
38233
|
description: "List HubSpot deal pipelines and their stages. Example: baker hubspot pipelines list"
|
|
@@ -37453,7 +38244,7 @@ var pipelinesListCommand = defineCommand129({
|
|
|
37453
38244
|
}
|
|
37454
38245
|
}
|
|
37455
38246
|
});
|
|
37456
|
-
var contactsSummaryCommand =
|
|
38247
|
+
var contactsSummaryCommand = defineCommand130({
|
|
37457
38248
|
meta: {
|
|
37458
38249
|
name: "summary",
|
|
37459
38250
|
description: "Whether recent leads are being worked, as counts. Example: baker hubspot contacts summary --days 30"
|
|
@@ -37523,7 +38314,7 @@ function contactLookupHints(data) {
|
|
|
37523
38314
|
}
|
|
37524
38315
|
return hints;
|
|
37525
38316
|
}
|
|
37526
|
-
var contactsLookupCommand =
|
|
38317
|
+
var contactsLookupCommand = defineCommand130({
|
|
37527
38318
|
meta: {
|
|
37528
38319
|
name: "lookup",
|
|
37529
38320
|
description: "Find one contact by email and see whether it was worked. Example: baker hubspot contacts lookup a@b.com"
|
|
@@ -37547,27 +38338,27 @@ var contactsLookupCommand = defineCommand129({
|
|
|
37547
38338
|
}
|
|
37548
38339
|
}
|
|
37549
38340
|
});
|
|
37550
|
-
var contactsCommand =
|
|
38341
|
+
var contactsCommand = defineCommand130({
|
|
37551
38342
|
meta: { name: "contacts", description: "Contacts on the connected HubSpot account." },
|
|
37552
38343
|
subCommands: { summary: contactsSummaryCommand, lookup: contactsLookupCommand }
|
|
37553
38344
|
});
|
|
37554
|
-
var formsCommand =
|
|
38345
|
+
var formsCommand = defineCommand130({
|
|
37555
38346
|
meta: { name: "forms", description: "HubSpot forms on the connected account." },
|
|
37556
38347
|
subCommands: { list: formsListCommand, view: formsViewCommand, submissions: formsSubmissionsCommand }
|
|
37557
38348
|
});
|
|
37558
|
-
var workflowsCommand =
|
|
38349
|
+
var workflowsCommand = defineCommand130({
|
|
37559
38350
|
meta: { name: "workflows", description: "HubSpot workflows on the connected account." },
|
|
37560
38351
|
subCommands: { list: workflowsListCommand, view: workflowsViewCommand }
|
|
37561
38352
|
});
|
|
37562
|
-
var pipelinesCommand =
|
|
38353
|
+
var pipelinesCommand = defineCommand130({
|
|
37563
38354
|
meta: { name: "pipelines", description: "HubSpot deal pipelines on the connected account." },
|
|
37564
38355
|
subCommands: { list: pipelinesListCommand }
|
|
37565
38356
|
});
|
|
37566
|
-
var meetingsCommand =
|
|
38357
|
+
var meetingsCommand = defineCommand130({
|
|
37567
38358
|
meta: { name: "meetings", description: "HubSpot meeting links (calendars) on the connected account." },
|
|
37568
38359
|
subCommands: { list: meetingsListCommand, view: meetingsViewCommand }
|
|
37569
38360
|
});
|
|
37570
|
-
var hubspotCommand =
|
|
38361
|
+
var hubspotCommand = defineCommand130({
|
|
37571
38362
|
meta: {
|
|
37572
38363
|
name: "hubspot",
|
|
37573
38364
|
description: `Read the connected HubSpot account \u2014 forms, the leads they received, workflows, deal pipelines and
|
|
@@ -37605,10 +38396,10 @@ Full guide: __tooling__/docs/tools/baker/hubspot.md`
|
|
|
37605
38396
|
});
|
|
37606
38397
|
|
|
37607
38398
|
// src/commands/images/index.ts
|
|
37608
|
-
import { defineCommand as
|
|
38399
|
+
import { defineCommand as defineCommand156 } from "citty";
|
|
37609
38400
|
|
|
37610
38401
|
// src/commands/images/crop.ts
|
|
37611
|
-
import { defineCommand as
|
|
38402
|
+
import { defineCommand as defineCommand131 } from "citty";
|
|
37612
38403
|
|
|
37613
38404
|
// src/lib/image/crop-sprite.ts
|
|
37614
38405
|
import sharp from "sharp";
|
|
@@ -37623,7 +38414,7 @@ function cropSprite(input, region) {
|
|
|
37623
38414
|
|
|
37624
38415
|
// src/lib/image/io.ts
|
|
37625
38416
|
import { randomBytes } from "crypto";
|
|
37626
|
-
import { glob as fsGlob, readFile as
|
|
38417
|
+
import { glob as fsGlob, readFile as readFile21, rename, stat as stat5, writeFile as writeFile11 } from "fs/promises";
|
|
37627
38418
|
import { dirname as dirname2, extname as extname2, join as join5, resolve as resolve4 } from "path";
|
|
37628
38419
|
var REMOTE_RE = /^https?:\/\//i;
|
|
37629
38420
|
var GLOB_RE = /[*?[\]{}]/;
|
|
@@ -37656,11 +38447,11 @@ async function readImageBuffer(pathOrUrl) {
|
|
|
37656
38447
|
const { buffer } = await fetchExternalBytes(pathOrUrl, { maxBytes: MAX_REMOTE_IMAGE_BYTES });
|
|
37657
38448
|
return buffer;
|
|
37658
38449
|
}
|
|
37659
|
-
return
|
|
38450
|
+
return readFile21(pathOrUrl);
|
|
37660
38451
|
}
|
|
37661
|
-
async function isDirectory(
|
|
38452
|
+
async function isDirectory(path43) {
|
|
37662
38453
|
try {
|
|
37663
|
-
const s = await
|
|
38454
|
+
const s = await stat5(path43);
|
|
37664
38455
|
return s.isDirectory();
|
|
37665
38456
|
} catch {
|
|
37666
38457
|
return false;
|
|
@@ -37730,7 +38521,7 @@ function emitError2(err) {
|
|
|
37730
38521
|
}
|
|
37731
38522
|
process.exit(1);
|
|
37732
38523
|
}
|
|
37733
|
-
var cropCommand =
|
|
38524
|
+
var cropCommand = defineCommand131({
|
|
37734
38525
|
meta: {
|
|
37735
38526
|
name: "crop",
|
|
37736
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"
|
|
@@ -37766,7 +38557,7 @@ var cropCommand = defineCommand130({
|
|
|
37766
38557
|
});
|
|
37767
38558
|
|
|
37768
38559
|
// src/commands/images/delete.ts
|
|
37769
|
-
import { defineCommand as
|
|
38560
|
+
import { defineCommand as defineCommand132 } from "citty";
|
|
37770
38561
|
registerSchema({
|
|
37771
38562
|
command: "images.delete",
|
|
37772
38563
|
description: "Delete an image by ID",
|
|
@@ -37780,7 +38571,7 @@ registerSchema({
|
|
|
37780
38571
|
}
|
|
37781
38572
|
}
|
|
37782
38573
|
});
|
|
37783
|
-
var deleteCommand2 =
|
|
38574
|
+
var deleteCommand2 = defineCommand132({
|
|
37784
38575
|
meta: {
|
|
37785
38576
|
name: "delete",
|
|
37786
38577
|
description: "Delete an image by ID. Use --dry-run to preview. Example: baker images delete j571abc123 --dry-run"
|
|
@@ -37821,7 +38612,7 @@ var deleteCommand2 = defineCommand131({
|
|
|
37821
38612
|
});
|
|
37822
38613
|
|
|
37823
38614
|
// src/commands/images/dimensions.ts
|
|
37824
|
-
import { defineCommand as
|
|
38615
|
+
import { defineCommand as defineCommand133 } from "citty";
|
|
37825
38616
|
|
|
37826
38617
|
// src/lib/image/dimensions.ts
|
|
37827
38618
|
import { imageSize } from "image-size";
|
|
@@ -37850,7 +38641,7 @@ registerSchema({
|
|
|
37850
38641
|
}
|
|
37851
38642
|
}
|
|
37852
38643
|
});
|
|
37853
|
-
var dimensionsCommand =
|
|
38644
|
+
var dimensionsCommand = defineCommand133({
|
|
37854
38645
|
meta: {
|
|
37855
38646
|
name: "dimensions",
|
|
37856
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"
|
|
@@ -37906,7 +38697,7 @@ var dimensionsCommand = defineCommand132({
|
|
|
37906
38697
|
});
|
|
37907
38698
|
|
|
37908
38699
|
// src/commands/images/download.ts
|
|
37909
|
-
import { defineCommand as
|
|
38700
|
+
import { defineCommand as defineCommand134 } from "citty";
|
|
37910
38701
|
|
|
37911
38702
|
// src/commands/images/downloadPaths.ts
|
|
37912
38703
|
import { basename as basename2, extname as extname3, join as join6 } from "path";
|
|
@@ -37962,13 +38753,13 @@ function resolveDownloadPath({ baseName, extension, out, outIsDirectory: outIsDi
|
|
|
37962
38753
|
}
|
|
37963
38754
|
function disambiguate(paths) {
|
|
37964
38755
|
const taken = /* @__PURE__ */ new Set();
|
|
37965
|
-
return paths.map((
|
|
37966
|
-
if (!taken.has(
|
|
37967
|
-
taken.add(
|
|
37968
|
-
return
|
|
38756
|
+
return paths.map((path43) => {
|
|
38757
|
+
if (!taken.has(path43)) {
|
|
38758
|
+
taken.add(path43);
|
|
38759
|
+
return path43;
|
|
37969
38760
|
}
|
|
37970
|
-
const ext = extname3(
|
|
37971
|
-
const stem =
|
|
38761
|
+
const ext = extname3(path43);
|
|
38762
|
+
const stem = path43.slice(0, path43.length - ext.length);
|
|
37972
38763
|
let n = 2;
|
|
37973
38764
|
while (taken.has(`${stem}-${n}${ext}`)) n += 1;
|
|
37974
38765
|
const unique = `${stem}-${n}${ext}`;
|
|
@@ -38095,10 +38886,10 @@ async function runDownloads(plan) {
|
|
|
38095
38886
|
const paths = disambiguate(fetched.map((item) => item.path));
|
|
38096
38887
|
const downloaded = [];
|
|
38097
38888
|
for (const [index, item] of fetched.entries()) {
|
|
38098
|
-
const
|
|
38889
|
+
const path43 = paths[index] ?? item.path;
|
|
38099
38890
|
try {
|
|
38100
|
-
await atomicWrite(
|
|
38101
|
-
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 });
|
|
38102
38893
|
} catch (err) {
|
|
38103
38894
|
failed.push({ input: item.input, error: failureMessage(err, "Write failed") });
|
|
38104
38895
|
}
|
|
@@ -38131,7 +38922,7 @@ function emitError3(err) {
|
|
|
38131
38922
|
writeJson({ ok: false, error: { code: "INTERNAL_ERROR", message: "Unexpected error" } });
|
|
38132
38923
|
process.exit(1);
|
|
38133
38924
|
}
|
|
38134
|
-
var downloadCommand =
|
|
38925
|
+
var downloadCommand = defineCommand134({
|
|
38135
38926
|
meta: {
|
|
38136
38927
|
name: "download",
|
|
38137
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"
|
|
@@ -38164,7 +38955,7 @@ var downloadCommand = defineCommand133({
|
|
|
38164
38955
|
});
|
|
38165
38956
|
|
|
38166
38957
|
// src/commands/images/extract.ts
|
|
38167
|
-
import { defineCommand as
|
|
38958
|
+
import { defineCommand as defineCommand135 } from "citty";
|
|
38168
38959
|
|
|
38169
38960
|
// src/commands/images/autoIngest.ts
|
|
38170
38961
|
var AUTO_INGEST_MAX = {
|
|
@@ -38211,7 +39002,7 @@ registerSchema({
|
|
|
38211
39002
|
}
|
|
38212
39003
|
}
|
|
38213
39004
|
});
|
|
38214
|
-
var extractCommand =
|
|
39005
|
+
var extractCommand = defineCommand135({
|
|
38215
39006
|
meta: {
|
|
38216
39007
|
name: "extract",
|
|
38217
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"
|
|
@@ -38266,7 +39057,7 @@ var extractCommand = defineCommand134({
|
|
|
38266
39057
|
});
|
|
38267
39058
|
|
|
38268
39059
|
// src/commands/images/find.ts
|
|
38269
|
-
import { defineCommand as
|
|
39060
|
+
import { defineCommand as defineCommand136 } from "citty";
|
|
38270
39061
|
|
|
38271
39062
|
// src/commands/images/providerHits.ts
|
|
38272
39063
|
function asRecord3(value) {
|
|
@@ -38404,7 +39195,7 @@ registerSchema({
|
|
|
38404
39195
|
full: { type: "boolean", description: "Include full metadata", required: false, default: false }
|
|
38405
39196
|
}
|
|
38406
39197
|
});
|
|
38407
|
-
var findCommand =
|
|
39198
|
+
var findCommand = defineCommand136({
|
|
38408
39199
|
meta: {
|
|
38409
39200
|
name: "find",
|
|
38410
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"
|
|
@@ -38477,7 +39268,7 @@ var findCommand = defineCommand135({
|
|
|
38477
39268
|
});
|
|
38478
39269
|
|
|
38479
39270
|
// src/commands/images/get.ts
|
|
38480
|
-
import { defineCommand as
|
|
39271
|
+
import { defineCommand as defineCommand137 } from "citty";
|
|
38481
39272
|
registerSchema({
|
|
38482
39273
|
command: "images.get",
|
|
38483
39274
|
description: "Get a single image by ID",
|
|
@@ -38485,7 +39276,7 @@ registerSchema({
|
|
|
38485
39276
|
id: { type: "string", description: "Image ID", required: true }
|
|
38486
39277
|
}
|
|
38487
39278
|
});
|
|
38488
|
-
var getCommand3 =
|
|
39279
|
+
var getCommand3 = defineCommand137({
|
|
38489
39280
|
meta: { name: "get", description: "Get a single image by ID. Example: baker images get j571abc123" },
|
|
38490
39281
|
args: {
|
|
38491
39282
|
id: { type: "positional", description: "Image ID", required: false },
|
|
@@ -38521,7 +39312,7 @@ var getCommand3 = defineCommand136({
|
|
|
38521
39312
|
});
|
|
38522
39313
|
|
|
38523
39314
|
// src/commands/images/gif.ts
|
|
38524
|
-
import { defineCommand as
|
|
39315
|
+
import { defineCommand as defineCommand138 } from "citty";
|
|
38525
39316
|
registerSchema({
|
|
38526
39317
|
command: "images.gif",
|
|
38527
39318
|
description: "Search Giphy for GIFs / reaction memes (paid social creative).",
|
|
@@ -38553,7 +39344,7 @@ registerSchema({
|
|
|
38553
39344
|
}
|
|
38554
39345
|
}
|
|
38555
39346
|
});
|
|
38556
|
-
var gifCommand =
|
|
39347
|
+
var gifCommand = defineCommand138({
|
|
38557
39348
|
meta: {
|
|
38558
39349
|
name: "gif",
|
|
38559
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"
|
|
@@ -38600,7 +39391,7 @@ var gifCommand = defineCommand137({
|
|
|
38600
39391
|
});
|
|
38601
39392
|
|
|
38602
39393
|
// src/commands/images/google.ts
|
|
38603
|
-
import { defineCommand as
|
|
39394
|
+
import { defineCommand as defineCommand139 } from "citty";
|
|
38604
39395
|
var GOOGLE_ERROR_FIX = {
|
|
38605
39396
|
action: "use_different_resource",
|
|
38606
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."
|
|
@@ -38640,7 +39431,7 @@ registerSchema({
|
|
|
38640
39431
|
}
|
|
38641
39432
|
}
|
|
38642
39433
|
});
|
|
38643
|
-
var googleCommand2 =
|
|
39434
|
+
var googleCommand2 = defineCommand139({
|
|
38644
39435
|
meta: {
|
|
38645
39436
|
name: "google",
|
|
38646
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"
|
|
@@ -38713,7 +39504,7 @@ var googleCommand2 = defineCommand138({
|
|
|
38713
39504
|
});
|
|
38714
39505
|
|
|
38715
39506
|
// src/commands/images/group.ts
|
|
38716
|
-
import { defineCommand as
|
|
39507
|
+
import { defineCommand as defineCommand140 } from "citty";
|
|
38717
39508
|
|
|
38718
39509
|
// src/commands/mediaGroup.ts
|
|
38719
39510
|
async function runMediaGroupLookup(input) {
|
|
@@ -38766,7 +39557,7 @@ registerSchema({
|
|
|
38766
39557
|
"group-key": { type: "string", description: "The set key directly, when you already have it", required: false }
|
|
38767
39558
|
}
|
|
38768
39559
|
});
|
|
38769
|
-
var groupCommand =
|
|
39560
|
+
var groupCommand = defineCommand140({
|
|
38770
39561
|
meta: {
|
|
38771
39562
|
name: "group",
|
|
38772
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>"
|
|
@@ -38786,7 +39577,7 @@ var groupCommand = defineCommand139({
|
|
|
38786
39577
|
});
|
|
38787
39578
|
|
|
38788
39579
|
// src/commands/images/icon.ts
|
|
38789
|
-
import { defineCommand as
|
|
39580
|
+
import { defineCommand as defineCommand141 } from "citty";
|
|
38790
39581
|
|
|
38791
39582
|
// src/commands/images/brandVerification.ts
|
|
38792
39583
|
function brandToken(domain) {
|
|
@@ -38883,7 +39674,7 @@ registerSchema({
|
|
|
38883
39674
|
full: { type: "boolean", description: "Include full metadata", required: false, default: false }
|
|
38884
39675
|
}
|
|
38885
39676
|
});
|
|
38886
|
-
var iconCommand =
|
|
39677
|
+
var iconCommand = defineCommand141({
|
|
38887
39678
|
meta: {
|
|
38888
39679
|
name: "icon",
|
|
38889
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'"
|
|
@@ -38953,7 +39744,7 @@ var iconCommand = defineCommand140({
|
|
|
38953
39744
|
});
|
|
38954
39745
|
|
|
38955
39746
|
// src/commands/images/ingest.ts
|
|
38956
|
-
import { defineCommand as
|
|
39747
|
+
import { defineCommand as defineCommand142 } from "citty";
|
|
38957
39748
|
var SOURCE_VALUES = imageSourceSchema.options.join(" | ");
|
|
38958
39749
|
registerSchema({
|
|
38959
39750
|
command: "images.ingest",
|
|
@@ -38968,7 +39759,7 @@ registerSchema({
|
|
|
38968
39759
|
fields: { type: "string", description: "Comma-separated field names to include", required: false }
|
|
38969
39760
|
}
|
|
38970
39761
|
});
|
|
38971
|
-
var ingestCommand =
|
|
39762
|
+
var ingestCommand = defineCommand142({
|
|
38972
39763
|
meta: {
|
|
38973
39764
|
name: "ingest",
|
|
38974
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"
|
|
@@ -39037,7 +39828,7 @@ var ingestCommand = defineCommand141({
|
|
|
39037
39828
|
});
|
|
39038
39829
|
|
|
39039
39830
|
// src/commands/images/layerize.ts
|
|
39040
|
-
import { defineCommand as
|
|
39831
|
+
import { defineCommand as defineCommand143 } from "citty";
|
|
39041
39832
|
registerSchema({
|
|
39042
39833
|
command: "images.layerize",
|
|
39043
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.",
|
|
@@ -39101,7 +39892,7 @@ async function pollUntilSettled(imageId, maxWait) {
|
|
|
39101
39892
|
}
|
|
39102
39893
|
return null;
|
|
39103
39894
|
}
|
|
39104
|
-
var layerizeCommand =
|
|
39895
|
+
var layerizeCommand = defineCommand143({
|
|
39105
39896
|
meta: {
|
|
39106
39897
|
name: "layerize",
|
|
39107
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'"
|
|
@@ -39170,7 +39961,7 @@ registerSchema({
|
|
|
39170
39961
|
full: { type: "boolean", description: "Include geometry and typography for every layer", required: false }
|
|
39171
39962
|
}
|
|
39172
39963
|
});
|
|
39173
|
-
var layersCommand =
|
|
39964
|
+
var layersCommand = defineCommand143({
|
|
39174
39965
|
meta: {
|
|
39175
39966
|
name: "layers",
|
|
39176
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"
|
|
@@ -39210,7 +40001,7 @@ var layersCommand = defineCommand142({
|
|
|
39210
40001
|
});
|
|
39211
40002
|
|
|
39212
40003
|
// src/commands/images/library.ts
|
|
39213
|
-
import { defineCommand as
|
|
40004
|
+
import { defineCommand as defineCommand144 } from "citty";
|
|
39214
40005
|
registerSchema({
|
|
39215
40006
|
command: "images.library",
|
|
39216
40007
|
description: "Search the company image library. Returns only ready images.",
|
|
@@ -39236,7 +40027,7 @@ registerSchema({
|
|
|
39236
40027
|
}
|
|
39237
40028
|
}
|
|
39238
40029
|
});
|
|
39239
|
-
var libraryCommand =
|
|
40030
|
+
var libraryCommand = defineCommand144({
|
|
39240
40031
|
meta: {
|
|
39241
40032
|
name: "library",
|
|
39242
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"
|
|
@@ -39299,7 +40090,7 @@ var libraryCommand = defineCommand143({
|
|
|
39299
40090
|
});
|
|
39300
40091
|
|
|
39301
40092
|
// src/commands/images/logo.ts
|
|
39302
|
-
import { defineCommand as
|
|
40093
|
+
import { defineCommand as defineCommand145 } from "citty";
|
|
39303
40094
|
registerSchema({
|
|
39304
40095
|
command: "images.logo",
|
|
39305
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.",
|
|
@@ -39327,7 +40118,7 @@ registerSchema({
|
|
|
39327
40118
|
full: { type: "boolean", description: "Include full metadata", required: false, default: false }
|
|
39328
40119
|
}
|
|
39329
40120
|
});
|
|
39330
|
-
var logoCommand =
|
|
40121
|
+
var logoCommand = defineCommand145({
|
|
39331
40122
|
meta: {
|
|
39332
40123
|
name: "logo",
|
|
39333
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"
|
|
@@ -39400,7 +40191,7 @@ var logoCommand = defineCommand144({
|
|
|
39400
40191
|
});
|
|
39401
40192
|
|
|
39402
40193
|
// src/commands/images/normalize.ts
|
|
39403
|
-
import { defineCommand as
|
|
40194
|
+
import { defineCommand as defineCommand146 } from "citty";
|
|
39404
40195
|
|
|
39405
40196
|
// src/lib/image/color-changer.ts
|
|
39406
40197
|
import quantize from "quantize";
|
|
@@ -40132,7 +40923,7 @@ function coerceRawArgs(args) {
|
|
|
40132
40923
|
"dry-run": bool(args["dry-run"])
|
|
40133
40924
|
};
|
|
40134
40925
|
}
|
|
40135
|
-
var normalizeCommand2 =
|
|
40926
|
+
var normalizeCommand2 = defineCommand146({
|
|
40136
40927
|
meta: {
|
|
40137
40928
|
name: "normalize",
|
|
40138
40929
|
description: `Normalize logos / images: declarative recolor + bg removal + trim + resize. Operates on local files; writes in-place by default.
|
|
@@ -40187,7 +40978,7 @@ Examples:
|
|
|
40187
40978
|
});
|
|
40188
40979
|
|
|
40189
40980
|
// src/commands/images/pinterest.ts
|
|
40190
|
-
import { defineCommand as
|
|
40981
|
+
import { defineCommand as defineCommand147 } from "citty";
|
|
40191
40982
|
registerSchema({
|
|
40192
40983
|
command: "images.pinterest",
|
|
40193
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.",
|
|
@@ -40207,7 +40998,7 @@ registerSchema({
|
|
|
40207
40998
|
}
|
|
40208
40999
|
}
|
|
40209
41000
|
});
|
|
40210
|
-
var pinterestCommand =
|
|
41001
|
+
var pinterestCommand = defineCommand147({
|
|
40211
41002
|
meta: {
|
|
40212
41003
|
name: "pinterest",
|
|
40213
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'"
|
|
@@ -40264,7 +41055,7 @@ var pinterestCommand = defineCommand146({
|
|
|
40264
41055
|
});
|
|
40265
41056
|
|
|
40266
41057
|
// src/commands/images/screenshot.ts
|
|
40267
|
-
import { defineCommand as
|
|
41058
|
+
import { defineCommand as defineCommand148 } from "citty";
|
|
40268
41059
|
registerSchema({
|
|
40269
41060
|
command: "images.screenshot",
|
|
40270
41061
|
description: "Capture a website screenshot via ScreenshotOne. Auto-ingests on success.",
|
|
@@ -40283,7 +41074,7 @@ registerSchema({
|
|
|
40283
41074
|
full: { type: "boolean", description: "Include full metadata", required: false, default: false }
|
|
40284
41075
|
}
|
|
40285
41076
|
});
|
|
40286
|
-
var screenshotCommand =
|
|
41077
|
+
var screenshotCommand = defineCommand148({
|
|
40287
41078
|
meta: {
|
|
40288
41079
|
name: "screenshot",
|
|
40289
41080
|
description: "Screenshot a URL via ScreenshotOne. $0.009/capture. Auto-ingests to library.\n\nExample: baker images screenshot https://stripe.com --full-page"
|
|
@@ -40347,7 +41138,7 @@ var screenshotCommand = defineCommand147({
|
|
|
40347
41138
|
});
|
|
40348
41139
|
|
|
40349
41140
|
// src/commands/images/search.ts
|
|
40350
|
-
import { defineCommand as
|
|
41141
|
+
import { defineCommand as defineCommand149 } from "citty";
|
|
40351
41142
|
registerSchema({
|
|
40352
41143
|
command: "images.search",
|
|
40353
41144
|
description: "Search images by text query. Only returns ready images.",
|
|
@@ -40363,7 +41154,7 @@ registerSchema({
|
|
|
40363
41154
|
tags: { type: "string", description: "Comma-separated tags to filter by", required: false }
|
|
40364
41155
|
}
|
|
40365
41156
|
});
|
|
40366
|
-
var searchCommand =
|
|
41157
|
+
var searchCommand = defineCommand149({
|
|
40367
41158
|
meta: {
|
|
40368
41159
|
name: "search",
|
|
40369
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"
|
|
@@ -40423,7 +41214,7 @@ var searchCommand = defineCommand148({
|
|
|
40423
41214
|
});
|
|
40424
41215
|
|
|
40425
41216
|
// src/commands/images/sticker.ts
|
|
40426
|
-
import { defineCommand as
|
|
41217
|
+
import { defineCommand as defineCommand150 } from "citty";
|
|
40427
41218
|
registerSchema({
|
|
40428
41219
|
command: "images.sticker",
|
|
40429
41220
|
description: "Search Giphy stickers \u2014 transparent-background overlays for ad creative.",
|
|
@@ -40455,7 +41246,7 @@ registerSchema({
|
|
|
40455
41246
|
}
|
|
40456
41247
|
}
|
|
40457
41248
|
});
|
|
40458
|
-
var stickerCommand =
|
|
41249
|
+
var stickerCommand = defineCommand150({
|
|
40459
41250
|
meta: {
|
|
40460
41251
|
name: "sticker",
|
|
40461
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"
|
|
@@ -40502,7 +41293,7 @@ var stickerCommand = defineCommand149({
|
|
|
40502
41293
|
});
|
|
40503
41294
|
|
|
40504
41295
|
// src/commands/images/stock.ts
|
|
40505
|
-
import { defineCommand as
|
|
41296
|
+
import { defineCommand as defineCommand151 } from "citty";
|
|
40506
41297
|
var STOCK_ERROR_FIX = {
|
|
40507
41298
|
action: "use_different_resource",
|
|
40508
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."
|
|
@@ -40579,7 +41370,7 @@ function buildStockRequest(query, args) {
|
|
|
40579
41370
|
if (args.context) body.descriptionContext = args.context;
|
|
40580
41371
|
return body;
|
|
40581
41372
|
}
|
|
40582
|
-
var stockCommand =
|
|
41373
|
+
var stockCommand = defineCommand151({
|
|
40583
41374
|
meta: {
|
|
40584
41375
|
name: "stock",
|
|
40585
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"
|
|
@@ -40652,7 +41443,7 @@ var stockCommand = defineCommand150({
|
|
|
40652
41443
|
});
|
|
40653
41444
|
|
|
40654
41445
|
// src/lib/tags-command.ts
|
|
40655
|
-
import { defineCommand as
|
|
41446
|
+
import { defineCommand as defineCommand152 } from "citty";
|
|
40656
41447
|
function makeTagsCommand(command, label, endpoint) {
|
|
40657
41448
|
registerSchema({
|
|
40658
41449
|
command: `${command}.tags`,
|
|
@@ -40661,7 +41452,7 @@ function makeTagsCommand(command, label, endpoint) {
|
|
|
40661
41452
|
output: { type: "string", description: "Output format: md|json", required: false, default: "md" }
|
|
40662
41453
|
}
|
|
40663
41454
|
});
|
|
40664
|
-
return
|
|
41455
|
+
return defineCommand152({
|
|
40665
41456
|
meta: {
|
|
40666
41457
|
name: "tags",
|
|
40667
41458
|
description: `List the available ${label} tag names (defaults + company custom tags). Use before filtering with --tags. Example: baker ${command} tags`
|
|
@@ -40697,7 +41488,7 @@ function makeTagsCommand(command, label, endpoint) {
|
|
|
40697
41488
|
var tagsCommand3 = makeTagsCommand("images", "image", "/api/images/tags");
|
|
40698
41489
|
|
|
40699
41490
|
// src/commands/images/upload.ts
|
|
40700
|
-
import { defineCommand as
|
|
41491
|
+
import { defineCommand as defineCommand153 } from "citty";
|
|
40701
41492
|
registerSchema({
|
|
40702
41493
|
command: "images.upload",
|
|
40703
41494
|
description: "Upload an image to the library \u2014 local file path or remote http(s) URL.",
|
|
@@ -40735,7 +41526,7 @@ registerSchema({
|
|
|
40735
41526
|
function isRemoteUrl2(value) {
|
|
40736
41527
|
return /^https?:\/\//i.test(value);
|
|
40737
41528
|
}
|
|
40738
|
-
var uploadCommand =
|
|
41529
|
+
var uploadCommand = defineCommand153({
|
|
40739
41530
|
meta: {
|
|
40740
41531
|
name: "upload",
|
|
40741
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'"
|
|
@@ -40828,7 +41619,7 @@ async function uploadLocal(target, args) {
|
|
|
40828
41619
|
}
|
|
40829
41620
|
|
|
40830
41621
|
// src/commands/images/upscale.ts
|
|
40831
|
-
import { defineCommand as
|
|
41622
|
+
import { defineCommand as defineCommand154 } from "citty";
|
|
40832
41623
|
registerSchema({
|
|
40833
41624
|
command: "images.upscale",
|
|
40834
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).",
|
|
@@ -40843,7 +41634,7 @@ registerSchema({
|
|
|
40843
41634
|
}
|
|
40844
41635
|
});
|
|
40845
41636
|
var POLL_INTERVAL_MS4 = 1500;
|
|
40846
|
-
var upscaleCommand =
|
|
41637
|
+
var upscaleCommand = defineCommand154({
|
|
40847
41638
|
meta: {
|
|
40848
41639
|
name: "upscale",
|
|
40849
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"
|
|
@@ -40898,7 +41689,7 @@ var upscaleCommand = defineCommand153({
|
|
|
40898
41689
|
});
|
|
40899
41690
|
|
|
40900
41691
|
// src/commands/images/use.ts
|
|
40901
|
-
import { defineCommand as
|
|
41692
|
+
import { defineCommand as defineCommand155 } from "citty";
|
|
40902
41693
|
registerSchema({
|
|
40903
41694
|
command: "images.use",
|
|
40904
41695
|
description: "Ingest a URL and wait for the library record to be ready.",
|
|
@@ -40927,7 +41718,7 @@ function emitReady(ingestResult, doc, args) {
|
|
|
40927
41718
|
args.full === true
|
|
40928
41719
|
);
|
|
40929
41720
|
}
|
|
40930
|
-
var useCommand =
|
|
41721
|
+
var useCommand = defineCommand155({
|
|
40931
41722
|
meta: {
|
|
40932
41723
|
name: "use",
|
|
40933
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"
|
|
@@ -40976,7 +41767,7 @@ var useCommand = defineCommand154({
|
|
|
40976
41767
|
});
|
|
40977
41768
|
|
|
40978
41769
|
// src/commands/images/index.ts
|
|
40979
|
-
var imagesCommand =
|
|
41770
|
+
var imagesCommand = defineCommand156({
|
|
40980
41771
|
meta: {
|
|
40981
41772
|
name: "images",
|
|
40982
41773
|
description: `Find, source, and normalize images. Subcommands route by provider so cost + license are explicit.
|
|
@@ -41054,12 +41845,12 @@ Full guide: __tooling__/docs/tools/baker/images.md`
|
|
|
41054
41845
|
});
|
|
41055
41846
|
|
|
41056
41847
|
// src/commands/landing/index.ts
|
|
41057
|
-
import { defineCommand as
|
|
41848
|
+
import { defineCommand as defineCommand168 } from "citty";
|
|
41058
41849
|
|
|
41059
41850
|
// src/commands/landing/critique.ts
|
|
41060
41851
|
import { readdir as readdir9, stat as stat6 } from "fs/promises";
|
|
41061
|
-
import
|
|
41062
|
-
import { defineCommand as
|
|
41852
|
+
import path30 from "path";
|
|
41853
|
+
import { defineCommand as defineCommand157 } from "citty";
|
|
41063
41854
|
|
|
41064
41855
|
// src/engine/landing/lib/constants.ts
|
|
41065
41856
|
var OVERUSED_FONTS = /* @__PURE__ */ new Set([
|
|
@@ -42098,13 +42889,13 @@ function describeCounts(findings) {
|
|
|
42098
42889
|
}
|
|
42099
42890
|
|
|
42100
42891
|
// src/engine/landing/lib/referenceStore.ts
|
|
42101
|
-
import { mkdir as mkdir8, readFile as
|
|
42102
|
-
import
|
|
42892
|
+
import { mkdir as mkdir8, readFile as readFile22, writeFile as writeFile12 } from "fs/promises";
|
|
42893
|
+
import path27 from "path";
|
|
42103
42894
|
var REFERENCES_FILE = ".cache/inspiration-refs.json";
|
|
42104
42895
|
var REFERENCE_TTL_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
42105
42896
|
async function readReferences(projectRoot) {
|
|
42106
42897
|
try {
|
|
42107
|
-
const raw = await
|
|
42898
|
+
const raw = await readFile22(path27.join(projectRoot, REFERENCES_FILE), "utf8");
|
|
42108
42899
|
const parsed = JSON.parse(raw);
|
|
42109
42900
|
if (!Array.isArray(parsed)) return [];
|
|
42110
42901
|
const cutoff = Date.now() - REFERENCE_TTL_MS;
|
|
@@ -42128,8 +42919,8 @@ async function recordReferences(projectRoot, references) {
|
|
|
42128
42919
|
const existing = await readReferences(projectRoot);
|
|
42129
42920
|
const replaced = new Set(references.map((reference) => reference.sectionId));
|
|
42130
42921
|
const merged = [...existing.filter((entry) => !replaced.has(entry.sectionId)), ...references];
|
|
42131
|
-
const file =
|
|
42132
|
-
await mkdir8(
|
|
42922
|
+
const file = path27.join(projectRoot, REFERENCES_FILE);
|
|
42923
|
+
await mkdir8(path27.dirname(file), { recursive: true });
|
|
42133
42924
|
await writeFile12(file, `${JSON.stringify(merged, null, 2)}
|
|
42134
42925
|
`);
|
|
42135
42926
|
return true;
|
|
@@ -42140,13 +42931,13 @@ async function recordReferences(projectRoot, references) {
|
|
|
42140
42931
|
|
|
42141
42932
|
// src/commands/landing/snapshot.ts
|
|
42142
42933
|
import { mkdir as mkdir9, rename as rename2, writeFile as writeFile13 } from "fs/promises";
|
|
42143
|
-
import
|
|
42934
|
+
import path28 from "path";
|
|
42144
42935
|
var CRITIC_VERSION = "2";
|
|
42145
42936
|
function critiqueCacheDir(projectRoot) {
|
|
42146
|
-
return
|
|
42937
|
+
return path28.join(projectRoot, ".cache", "landing-critique");
|
|
42147
42938
|
}
|
|
42148
42939
|
function snapshotPath(projectRoot, slug) {
|
|
42149
|
-
return
|
|
42940
|
+
return path28.join(critiqueCacheDir(projectRoot), `${slug}.json`);
|
|
42150
42941
|
}
|
|
42151
42942
|
async function writeCritiqueSnapshot(projectRoot, snapshot) {
|
|
42152
42943
|
await mkdir9(critiqueCacheDir(projectRoot), { recursive: true });
|
|
@@ -42158,30 +42949,26 @@ async function writeCritiqueSnapshot(projectRoot, snapshot) {
|
|
|
42158
42949
|
}
|
|
42159
42950
|
|
|
42160
42951
|
// src/commands/landing/source-version.ts
|
|
42161
|
-
import {
|
|
42162
|
-
import
|
|
42163
|
-
|
|
42164
|
-
|
|
42165
|
-
|
|
42166
|
-
|
|
42167
|
-
for (const abs of await walkAstro(componentsDir)) {
|
|
42168
|
-
rel.push(path28.relative(landingDir, abs).split(path28.sep).join("/"));
|
|
42169
|
-
}
|
|
42170
|
-
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);
|
|
42171
42958
|
}
|
|
42172
|
-
async function readLandingSources(
|
|
42173
|
-
const rel = await landingSourceRelPaths(
|
|
42959
|
+
async function readLandingSources(root, slug) {
|
|
42960
|
+
const rel = await landingSourceRelPaths(root, slug);
|
|
42174
42961
|
const out = [];
|
|
42175
|
-
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") });
|
|
42176
42963
|
return out;
|
|
42177
42964
|
}
|
|
42178
|
-
async function computeLandingSourceSha(
|
|
42179
|
-
const rel = await landingSourceRelPaths(
|
|
42965
|
+
async function computeLandingSourceSha(root, slug) {
|
|
42966
|
+
const rel = await landingSourceRelPaths(root, slug);
|
|
42180
42967
|
const parts = [];
|
|
42181
42968
|
for (const r of rel) {
|
|
42182
42969
|
let bytes;
|
|
42183
42970
|
try {
|
|
42184
|
-
bytes = await
|
|
42971
|
+
bytes = await readFile23(path29.join(root, r));
|
|
42185
42972
|
} catch {
|
|
42186
42973
|
bytes = Buffer.alloc(0);
|
|
42187
42974
|
}
|
|
@@ -42189,28 +42976,6 @@ async function computeLandingSourceSha(landingDir) {
|
|
|
42189
42976
|
}
|
|
42190
42977
|
return sha256Hex(Buffer.concat(parts));
|
|
42191
42978
|
}
|
|
42192
|
-
async function isFile(p) {
|
|
42193
|
-
try {
|
|
42194
|
-
return (await stat5(p)).isFile();
|
|
42195
|
-
} catch {
|
|
42196
|
-
return false;
|
|
42197
|
-
}
|
|
42198
|
-
}
|
|
42199
|
-
async function walkAstro(dir) {
|
|
42200
|
-
let entries;
|
|
42201
|
-
try {
|
|
42202
|
-
entries = await readdir8(dir, { withFileTypes: true });
|
|
42203
|
-
} catch {
|
|
42204
|
-
return [];
|
|
42205
|
-
}
|
|
42206
|
-
const out = [];
|
|
42207
|
-
for (const entry of entries) {
|
|
42208
|
-
const abs = path28.join(dir, entry.name);
|
|
42209
|
-
if (entry.isDirectory()) out.push(...await walkAstro(abs));
|
|
42210
|
-
else if (entry.isFile() && entry.name.endsWith(".astro")) out.push(abs);
|
|
42211
|
-
}
|
|
42212
|
-
return out;
|
|
42213
|
-
}
|
|
42214
42979
|
|
|
42215
42980
|
// src/commands/landing/critique.ts
|
|
42216
42981
|
registerSchema({
|
|
@@ -42235,14 +43000,14 @@ function parseCritiqueSlugs(args) {
|
|
|
42235
43000
|
const all = [args.slug, ...rest].filter((s) => typeof s === "string" && s.length > 0);
|
|
42236
43001
|
return [...new Set(all.map(String))];
|
|
42237
43002
|
}
|
|
42238
|
-
function
|
|
43003
|
+
function fail6(code, message, fix) {
|
|
42239
43004
|
process.stderr.write(
|
|
42240
43005
|
`${JSON.stringify({ ok: false, error: { code, message, ...fix ? { fix } : {} } }, null, 2)}
|
|
42241
43006
|
`
|
|
42242
43007
|
);
|
|
42243
43008
|
process.exit(2);
|
|
42244
43009
|
}
|
|
42245
|
-
var critiqueCommand2 =
|
|
43010
|
+
var critiqueCommand2 = defineCommand157({
|
|
42246
43011
|
meta: {
|
|
42247
43012
|
name: "critique",
|
|
42248
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."
|
|
@@ -42260,14 +43025,14 @@ var critiqueCommand2 = defineCommand156({
|
|
|
42260
43025
|
const projectRoot = process.cwd();
|
|
42261
43026
|
for (const slug of slugs) {
|
|
42262
43027
|
if (!SLUG_RE.test(slug) || slug.includes("..")) {
|
|
42263
|
-
|
|
43028
|
+
fail6(
|
|
42264
43029
|
"INVALID_SLUG",
|
|
42265
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).`,
|
|
42266
43031
|
{ availableSlugs: await listLandingSlugs(projectRoot) }
|
|
42267
43032
|
);
|
|
42268
43033
|
}
|
|
42269
|
-
if (!await isDir(
|
|
42270
|
-
|
|
43034
|
+
if (!await isDir(path30.resolve(projectRoot, "src", "pages", slug))) {
|
|
43035
|
+
fail6("NOT_FOUND", `No landing at src/pages/${slug}/`, {
|
|
42271
43036
|
availableSlugs: await listLandingSlugs(projectRoot)
|
|
42272
43037
|
});
|
|
42273
43038
|
}
|
|
@@ -42305,8 +43070,10 @@ var critiqueCommand2 = defineCommand156({
|
|
|
42305
43070
|
}
|
|
42306
43071
|
});
|
|
42307
43072
|
async function critiqueOne(projectRoot, slug, brand, references) {
|
|
42308
|
-
const
|
|
42309
|
-
|
|
43073
|
+
const [sources, sourceSha] = await Promise.all([
|
|
43074
|
+
readLandingSources(projectRoot, slug),
|
|
43075
|
+
computeLandingSourceSha(projectRoot, slug)
|
|
43076
|
+
]);
|
|
42310
43077
|
const report = critiqueLanding({ slug, sources, brand, references });
|
|
42311
43078
|
let snapshotFailed = false;
|
|
42312
43079
|
try {
|
|
@@ -42325,7 +43092,7 @@ async function critiqueOne(projectRoot, slug, brand, references) {
|
|
|
42325
43092
|
}
|
|
42326
43093
|
async function listLandingSlugs(projectRoot) {
|
|
42327
43094
|
try {
|
|
42328
|
-
const entries = await readdir9(
|
|
43095
|
+
const entries = await readdir9(path30.join(projectRoot, "src", "pages"), { withFileTypes: true });
|
|
42329
43096
|
return entries.filter((e) => e.isDirectory() && !e.name.startsWith("_") && !e.name.startsWith(".")).map((e) => e.name).sort();
|
|
42330
43097
|
} catch {
|
|
42331
43098
|
return [];
|
|
@@ -42351,10 +43118,10 @@ async function isDir(p) {
|
|
|
42351
43118
|
}
|
|
42352
43119
|
|
|
42353
43120
|
// src/commands/landing/inspiration/index.ts
|
|
42354
|
-
import { defineCommand as
|
|
43121
|
+
import { defineCommand as defineCommand166 } from "citty";
|
|
42355
43122
|
|
|
42356
43123
|
// src/commands/landing/inspiration/add.ts
|
|
42357
|
-
import { defineCommand as
|
|
43124
|
+
import { defineCommand as defineCommand158 } from "citty";
|
|
42358
43125
|
|
|
42359
43126
|
// src/commands/landing/inspiration/shared.ts
|
|
42360
43127
|
var INSPIRATION_HINTS = {
|
|
@@ -42436,7 +43203,7 @@ registerSchema({
|
|
|
42436
43203
|
note: { type: "string", description: "Why this page is worth keeping", required: false }
|
|
42437
43204
|
}
|
|
42438
43205
|
});
|
|
42439
|
-
var addCommand =
|
|
43206
|
+
var addCommand = defineCommand158({
|
|
42440
43207
|
meta: {
|
|
42441
43208
|
name: "add",
|
|
42442
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'"
|
|
@@ -42477,8 +43244,8 @@ var addCommand = defineCommand157({
|
|
|
42477
43244
|
|
|
42478
43245
|
// src/commands/landing/inspiration/code.ts
|
|
42479
43246
|
import { mkdir as mkdir10, writeFile as writeFile14 } from "fs/promises";
|
|
42480
|
-
import
|
|
42481
|
-
import { defineCommand as
|
|
43247
|
+
import path31 from "path";
|
|
43248
|
+
import { defineCommand as defineCommand159 } from "citty";
|
|
42482
43249
|
registerSchema({
|
|
42483
43250
|
command: "landing.inspiration.code",
|
|
42484
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.",
|
|
@@ -42487,7 +43254,7 @@ registerSchema({
|
|
|
42487
43254
|
full: { type: "boolean", description: "Print the markup inline as well as writing it", required: false }
|
|
42488
43255
|
}
|
|
42489
43256
|
});
|
|
42490
|
-
var codeCommand =
|
|
43257
|
+
var codeCommand = defineCommand159({
|
|
42491
43258
|
meta: {
|
|
42492
43259
|
name: "code",
|
|
42493
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."
|
|
@@ -42500,9 +43267,9 @@ var codeCommand = defineCommand158({
|
|
|
42500
43267
|
try {
|
|
42501
43268
|
const id = args.id;
|
|
42502
43269
|
const data = await apiGet("/api/landing-inspiration/section-code", { id });
|
|
42503
|
-
const dir =
|
|
43270
|
+
const dir = path31.join(process.cwd(), ".baker", "inspiration", id);
|
|
42504
43271
|
await mkdir10(dir, { recursive: true });
|
|
42505
|
-
const file =
|
|
43272
|
+
const file = path31.join(dir, "section.html");
|
|
42506
43273
|
await writeFile14(file, data.html);
|
|
42507
43274
|
const recorded = await recordReference(process.cwd(), {
|
|
42508
43275
|
sectionId: id,
|
|
@@ -42523,7 +43290,7 @@ var codeCommand = defineCommand158({
|
|
|
42523
43290
|
ok: true,
|
|
42524
43291
|
data: {
|
|
42525
43292
|
id,
|
|
42526
|
-
file:
|
|
43293
|
+
file: path31.relative(process.cwd(), file),
|
|
42527
43294
|
bytes: data.html.length,
|
|
42528
43295
|
fidelity: data.fidelity,
|
|
42529
43296
|
reproduction_notes: data.reproductionNotes,
|
|
@@ -42544,7 +43311,7 @@ var codeCommand = defineCommand158({
|
|
|
42544
43311
|
});
|
|
42545
43312
|
|
|
42546
43313
|
// src/commands/landing/inspiration/favorites.ts
|
|
42547
|
-
import { defineCommand as
|
|
43314
|
+
import { defineCommand as defineCommand160 } from "citty";
|
|
42548
43315
|
registerSchema({
|
|
42549
43316
|
command: "landing.inspiration.favorites",
|
|
42550
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.",
|
|
@@ -42558,7 +43325,7 @@ registerSchema({
|
|
|
42558
43325
|
}
|
|
42559
43326
|
}
|
|
42560
43327
|
});
|
|
42561
|
-
var favoritesCommand =
|
|
43328
|
+
var favoritesCommand = defineCommand160({
|
|
42562
43329
|
meta: {
|
|
42563
43330
|
name: "favorites",
|
|
42564
43331
|
description: "List this company's saved reference sections. Example: baker landing inspiration favorites --type hero,pricing"
|
|
@@ -42638,7 +43405,7 @@ registerSchema({
|
|
|
42638
43405
|
note: { type: "string", description: "Why this is worth keeping", required: false }
|
|
42639
43406
|
}
|
|
42640
43407
|
});
|
|
42641
|
-
var favoriteCommand =
|
|
43408
|
+
var favoriteCommand = defineCommand160({
|
|
42642
43409
|
meta: {
|
|
42643
43410
|
name: "favorite",
|
|
42644
43411
|
description: "Save a reference section to this company. Example: baker landing inspiration favorite k57abc\u2026"
|
|
@@ -42676,7 +43443,7 @@ registerSchema({
|
|
|
42676
43443
|
page: { type: "boolean", description: "Treat the id as a page rather than a section", required: false }
|
|
42677
43444
|
}
|
|
42678
43445
|
});
|
|
42679
|
-
var unfavoriteCommand =
|
|
43446
|
+
var unfavoriteCommand = defineCommand160({
|
|
42680
43447
|
meta: {
|
|
42681
43448
|
name: "unfavorite",
|
|
42682
43449
|
description: "Remove a reference section from this company's saved set. Example: baker landing inspiration unfavorite k57abc\u2026"
|
|
@@ -42698,7 +43465,7 @@ var unfavoriteCommand = defineCommand159({
|
|
|
42698
43465
|
});
|
|
42699
43466
|
|
|
42700
43467
|
// src/commands/landing/inspiration/page.ts
|
|
42701
|
-
import { defineCommand as
|
|
43468
|
+
import { defineCommand as defineCommand161 } from "citty";
|
|
42702
43469
|
registerSchema({
|
|
42703
43470
|
command: "landing.inspiration.page",
|
|
42704
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.",
|
|
@@ -42715,7 +43482,7 @@ registerSchema({
|
|
|
42715
43482
|
}
|
|
42716
43483
|
}
|
|
42717
43484
|
});
|
|
42718
|
-
var pageCommand2 =
|
|
43485
|
+
var pageCommand2 = defineCommand161({
|
|
42719
43486
|
meta: {
|
|
42720
43487
|
name: "page",
|
|
42721
43488
|
description: "Show how a reference page sequences its sections. Example: baker landing inspiration page j91xyz\u2026 \u2014 the blueprint, not the pixels."
|
|
@@ -42778,9 +43545,9 @@ var pageCommand2 = defineCommand160({
|
|
|
42778
43545
|
});
|
|
42779
43546
|
|
|
42780
43547
|
// src/commands/landing/inspiration/scrape.ts
|
|
42781
|
-
import { readFile as
|
|
42782
|
-
import
|
|
42783
|
-
import { defineCommand as
|
|
43548
|
+
import { readFile as readFile24 } from "fs/promises";
|
|
43549
|
+
import path35 from "path";
|
|
43550
|
+
import { defineCommand as defineCommand162 } from "citty";
|
|
42784
43551
|
|
|
42785
43552
|
// src/engine/landing/lib/capturedReferences.ts
|
|
42786
43553
|
var MAX_STRINGS_PER_SECTION = 40;
|
|
@@ -42972,7 +43739,7 @@ function classifyCaptureFailure(error) {
|
|
|
42972
43739
|
|
|
42973
43740
|
// src/engine/landing-library/run.ts
|
|
42974
43741
|
import { mkdir as mkdir11, writeFile as writeFile16 } from "fs/promises";
|
|
42975
|
-
import
|
|
43742
|
+
import path33 from "path";
|
|
42976
43743
|
|
|
42977
43744
|
// ../proxy/src/preflight.ts
|
|
42978
43745
|
import http from "http";
|
|
@@ -44388,9 +45155,9 @@ async function renderBundleToPng(browser, html, viewportWidth, options = {}) {
|
|
|
44388
45155
|
|
|
44389
45156
|
// src/engine/landing-library/report.ts
|
|
44390
45157
|
import { writeFile as writeFile15 } from "fs/promises";
|
|
44391
|
-
import
|
|
45158
|
+
import path32 from "path";
|
|
44392
45159
|
async function writeCaptureReport(manifest, outDir) {
|
|
44393
|
-
const file =
|
|
45160
|
+
const file = path32.join(outDir, "report.html");
|
|
44394
45161
|
await writeFile15(file, renderReport(manifest));
|
|
44395
45162
|
return file;
|
|
44396
45163
|
}
|
|
@@ -44569,32 +45336,32 @@ async function reproducePage(args) {
|
|
|
44569
45336
|
const { browser, page, outDir, pageUrl, livePageShot } = args;
|
|
44570
45337
|
const built = await buildSectionBundle(page, "body", pageUrl).catch(() => null);
|
|
44571
45338
|
if (!built) return { bundle: null, fidelity: null };
|
|
44572
|
-
await writeFile16(
|
|
45339
|
+
await writeFile16(path33.join(outDir, "page.html"), built.html);
|
|
44573
45340
|
const rendered = await renderBundleToPng(browser, built.html, DESKTOP_VIEWPORT.width, {
|
|
44574
45341
|
wholePage: true,
|
|
44575
45342
|
timeoutMs: 6e4
|
|
44576
45343
|
});
|
|
44577
45344
|
if (!rendered || !livePageShot) return { bundle: "page.html", fidelity: null };
|
|
44578
|
-
await writeFile16(
|
|
45345
|
+
await writeFile16(path33.join(outDir, "page-rendered.png"), rendered);
|
|
44579
45346
|
const { score, note } = await scoreFidelity(livePageShot, rendered);
|
|
44580
45347
|
return { bundle: "page.html", fidelity: score, ...note ? { fidelityNote: note } : {} };
|
|
44581
45348
|
}
|
|
44582
45349
|
async function captureOneSection(args) {
|
|
44583
45350
|
const { browser, page, candidate, sectionsDir, outDir, pageUrl, withCode } = args;
|
|
44584
|
-
const dir =
|
|
45351
|
+
const dir = path33.join(sectionsDir, String(candidate.index).padStart(2, "0"));
|
|
44585
45352
|
await mkdir11(dir, { recursive: true });
|
|
44586
45353
|
const desktop = await captureSection(page, candidate);
|
|
44587
|
-
if (desktop) await writeFile16(
|
|
45354
|
+
if (desktop) await writeFile16(path33.join(dir, "desktop.png"), desktop);
|
|
44588
45355
|
const visualHash = desktop ? await perceptualHash(desktop) : null;
|
|
44589
45356
|
const motion = await collectMotion(page, candidate.selector);
|
|
44590
45357
|
const built = withCode ? await buildSectionBundle(page, candidate.selector, pageUrl) : null;
|
|
44591
45358
|
let fidelity = null;
|
|
44592
45359
|
let fidelityNote;
|
|
44593
45360
|
if (built) {
|
|
44594
|
-
await writeFile16(
|
|
45361
|
+
await writeFile16(path33.join(dir, "section.html"), built.html);
|
|
44595
45362
|
const rendered = await renderBundleToPng(browser, built.html, DESKTOP_VIEWPORT.width);
|
|
44596
45363
|
if (rendered && desktop) {
|
|
44597
|
-
await writeFile16(
|
|
45364
|
+
await writeFile16(path33.join(dir, "section-rendered.png"), rendered);
|
|
44598
45365
|
const result = await scoreFidelity(desktop, rendered);
|
|
44599
45366
|
fidelity = result.score;
|
|
44600
45367
|
fidelityNote = result.note;
|
|
@@ -44602,9 +45369,9 @@ async function captureOneSection(args) {
|
|
|
44602
45369
|
}
|
|
44603
45370
|
return {
|
|
44604
45371
|
...candidate,
|
|
44605
|
-
desktopShot: desktop ?
|
|
45372
|
+
desktopShot: desktop ? path33.relative(outDir, path33.join(dir, "desktop.png")) : null,
|
|
44606
45373
|
mobileShot: null,
|
|
44607
|
-
bundle: built ?
|
|
45374
|
+
bundle: built ? path33.relative(outDir, path33.join(dir, "section.html")) : null,
|
|
44608
45375
|
fidelity,
|
|
44609
45376
|
...fidelityNote ? { fidelityNote } : {},
|
|
44610
45377
|
...built ? { cssStats: built.stats } : {},
|
|
@@ -44623,9 +45390,9 @@ async function captureMobileShots(args) {
|
|
|
44623
45390
|
for (const section of sections) {
|
|
44624
45391
|
const shot = await captureSectionOnMobile(mobile.page, section);
|
|
44625
45392
|
if (!shot) continue;
|
|
44626
|
-
const file =
|
|
45393
|
+
const file = path33.join(sectionsDir, String(section.index).padStart(2, "0"), "mobile.png");
|
|
44627
45394
|
await writeFile16(file, shot);
|
|
44628
|
-
section.mobileShot =
|
|
45395
|
+
section.mobileShot = path33.relative(outDir, file);
|
|
44629
45396
|
}
|
|
44630
45397
|
} finally {
|
|
44631
45398
|
await mobile.context.close();
|
|
@@ -44640,10 +45407,10 @@ async function captureMotionTakes(args) {
|
|
|
44640
45407
|
const filmOne = async (section) => {
|
|
44641
45408
|
const take = await captureMotionTake(browser, pageUrl, section.selector).catch(() => null);
|
|
44642
45409
|
if (!take) return;
|
|
44643
|
-
const dir =
|
|
44644
|
-
const file =
|
|
45410
|
+
const dir = path33.join(sectionsDir, String(section.index).padStart(2, "0"));
|
|
45411
|
+
const file = path33.join(dir, "motion-filmstrip.png");
|
|
44645
45412
|
await writeFile16(file, take.filmstrip);
|
|
44646
|
-
section.motionFilmstrip =
|
|
45413
|
+
section.motionFilmstrip = path33.relative(outDir, file);
|
|
44647
45414
|
log(` [${section.index}] ${section.motion.summary}`);
|
|
44648
45415
|
};
|
|
44649
45416
|
const queue = [...moving];
|
|
@@ -44700,7 +45467,7 @@ async function captureAlternateViews(args) {
|
|
|
44700
45467
|
async function reproduceWholePage(args) {
|
|
44701
45468
|
const { browser, page, outDir, pageUrl, withCode, log } = args;
|
|
44702
45469
|
const fullPage = await page.screenshot({ type: "png", fullPage: true }).catch(() => null);
|
|
44703
|
-
if (fullPage) await writeFile16(
|
|
45470
|
+
if (fullPage) await writeFile16(path33.join(outDir, "full-page.png"), fullPage);
|
|
44704
45471
|
if (!withCode) return { bundle: null, fidelity: null };
|
|
44705
45472
|
const reproduction = await reproducePage({ browser, page, outDir, pageUrl, livePageShot: fullPage });
|
|
44706
45473
|
log(`page reproduction: ${reproduction.fidelity === null ? "unavailable" : reproduction.fidelity.toFixed(2)}`);
|
|
@@ -44771,7 +45538,7 @@ async function openViaLadder(args) {
|
|
|
44771
45538
|
async function scrapeLanding(options) {
|
|
44772
45539
|
const timeoutMs = options.timeoutMs ?? 45e3;
|
|
44773
45540
|
const log = options.onProgress ?? (() => void 0);
|
|
44774
|
-
const sectionsDir =
|
|
45541
|
+
const sectionsDir = path33.join(options.outDir, "sections");
|
|
44775
45542
|
const nonPublic = refuseNonPublicUrl(options.url);
|
|
44776
45543
|
if (nonPublic) {
|
|
44777
45544
|
throw new BlockedPageError({
|
|
@@ -44833,7 +45600,7 @@ async function scrapeLanding(options) {
|
|
|
44833
45600
|
security: prepared.security,
|
|
44834
45601
|
captureTier: tier
|
|
44835
45602
|
};
|
|
44836
|
-
await writeFile16(
|
|
45603
|
+
await writeFile16(path33.join(options.outDir, "manifest.json"), `${JSON.stringify(manifest, null, 2)}
|
|
44837
45604
|
`);
|
|
44838
45605
|
if (options.report !== false) {
|
|
44839
45606
|
const reportPath = await writeCaptureReport(manifest, options.outDir);
|
|
@@ -44848,28 +45615,28 @@ async function scrapeLanding(options) {
|
|
|
44848
45615
|
|
|
44849
45616
|
// src/commands/landing/inspiration/captureOut.ts
|
|
44850
45617
|
import { existsSync as existsSync9 } from "fs";
|
|
44851
|
-
import
|
|
45618
|
+
import path34 from "path";
|
|
44852
45619
|
var SCRATCH_DIR = ".baker";
|
|
44853
45620
|
function isWithin(parent, target) {
|
|
44854
|
-
const relative =
|
|
44855
|
-
return relative === "" || !relative.startsWith("..") && !
|
|
45621
|
+
const relative = path34.relative(parent, target);
|
|
45622
|
+
return relative === "" || !relative.startsWith("..") && !path34.isAbsolute(relative);
|
|
44856
45623
|
}
|
|
44857
45624
|
function findRepoRoot(from) {
|
|
44858
|
-
let dir =
|
|
45625
|
+
let dir = path34.resolve(from);
|
|
44859
45626
|
for (; ; ) {
|
|
44860
|
-
if (existsSync9(
|
|
44861
|
-
const parent =
|
|
45627
|
+
if (existsSync9(path34.join(dir, ".git"))) return dir;
|
|
45628
|
+
const parent = path34.dirname(dir);
|
|
44862
45629
|
if (parent === dir) return null;
|
|
44863
45630
|
dir = parent;
|
|
44864
45631
|
}
|
|
44865
45632
|
}
|
|
44866
45633
|
function checkCaptureOut(out, options) {
|
|
44867
45634
|
const { cwd, repoRoot } = options;
|
|
44868
|
-
const resolved =
|
|
45635
|
+
const resolved = path34.resolve(cwd, out);
|
|
44869
45636
|
if (repoRoot === null || !isWithin(repoRoot, resolved)) return { ok: true };
|
|
44870
|
-
const scratch =
|
|
45637
|
+
const scratch = path34.join(repoRoot, SCRATCH_DIR);
|
|
44871
45638
|
if (isWithin(scratch, resolved)) return { ok: true };
|
|
44872
|
-
const suggestion =
|
|
45639
|
+
const suggestion = path34.posix.join(SCRATCH_DIR, "teardowns", path34.basename(resolved) || "capture");
|
|
44873
45640
|
return {
|
|
44874
45641
|
ok: false,
|
|
44875
45642
|
error: {
|
|
@@ -44898,7 +45665,7 @@ async function recordCapture(manifest, outDir) {
|
|
|
44898
45665
|
for (const section of manifest.sections) {
|
|
44899
45666
|
if (!section.bundle) continue;
|
|
44900
45667
|
try {
|
|
44901
|
-
const markup = await
|
|
45668
|
+
const markup = await readFile24(path35.join(outDir, section.bundle), "utf8");
|
|
44902
45669
|
const copyStrings = capturedCopyStrings(markup);
|
|
44903
45670
|
if (copyStrings.length === 0) continue;
|
|
44904
45671
|
references.push({
|
|
@@ -44959,7 +45726,7 @@ registerSchema({
|
|
|
44959
45726
|
report: { type: "boolean", description: "Write report.html. `--no-report` to skip", required: false }
|
|
44960
45727
|
}
|
|
44961
45728
|
});
|
|
44962
|
-
var scrapeCommand =
|
|
45729
|
+
var scrapeCommand = defineCommand162({
|
|
44963
45730
|
meta: {
|
|
44964
45731
|
name: "scrape",
|
|
44965
45732
|
description: "Capture a landing page to a directory, now. Example: baker landing inspiration scrape https://linear.app --out .baker/inspiration/linear.app"
|
|
@@ -45068,12 +45835,12 @@ var scrapeCommand = defineCommand161({
|
|
|
45068
45835
|
});
|
|
45069
45836
|
|
|
45070
45837
|
// src/commands/landing/inspiration/search.ts
|
|
45071
|
-
import
|
|
45072
|
-
import { defineCommand as
|
|
45838
|
+
import path37 from "path";
|
|
45839
|
+
import { defineCommand as defineCommand163 } from "citty";
|
|
45073
45840
|
|
|
45074
45841
|
// src/commands/landing/inspiration/shot.ts
|
|
45075
45842
|
import { mkdir as mkdir12, writeFile as writeFile17 } from "fs/promises";
|
|
45076
|
-
import
|
|
45843
|
+
import path36 from "path";
|
|
45077
45844
|
import sharp6 from "sharp";
|
|
45078
45845
|
var READABLE_SHOT = {
|
|
45079
45846
|
maxWidth: 1440,
|
|
@@ -45099,9 +45866,9 @@ async function downloadReadableShot(url, file) {
|
|
|
45099
45866
|
const response = await fetch(url);
|
|
45100
45867
|
if (!response.ok) return null;
|
|
45101
45868
|
const shot = await toReadableShot(Buffer.from(await response.arrayBuffer()));
|
|
45102
|
-
await mkdir12(
|
|
45869
|
+
await mkdir12(path36.dirname(file), { recursive: true });
|
|
45103
45870
|
await writeFile17(file, shot);
|
|
45104
|
-
return
|
|
45871
|
+
return path36.relative(process.cwd(), file);
|
|
45105
45872
|
} catch {
|
|
45106
45873
|
return null;
|
|
45107
45874
|
}
|
|
@@ -45193,20 +45960,20 @@ function buildSearchBody(args) {
|
|
|
45193
45960
|
return body;
|
|
45194
45961
|
}
|
|
45195
45962
|
async function downloadShots(results) {
|
|
45196
|
-
const dir =
|
|
45963
|
+
const dir = path37.join(process.cwd(), ".baker", "inspiration");
|
|
45197
45964
|
const saved = /* @__PURE__ */ new Map();
|
|
45198
45965
|
await Promise.all(
|
|
45199
45966
|
results.map(async (result) => {
|
|
45200
45967
|
const file = await downloadReadableShot(
|
|
45201
45968
|
result.desktopShotUrl,
|
|
45202
|
-
|
|
45969
|
+
path37.join(dir, `${result.id}.${READABLE_SHOT.extension}`)
|
|
45203
45970
|
);
|
|
45204
45971
|
if (file) saved.set(result.id, file);
|
|
45205
45972
|
})
|
|
45206
45973
|
);
|
|
45207
45974
|
return saved;
|
|
45208
45975
|
}
|
|
45209
|
-
var searchCommand2 =
|
|
45976
|
+
var searchCommand2 = defineCommand163({
|
|
45210
45977
|
meta: {
|
|
45211
45978
|
name: "search",
|
|
45212
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"
|
|
@@ -45304,7 +46071,7 @@ var searchCommand2 = defineCommand162({
|
|
|
45304
46071
|
});
|
|
45305
46072
|
|
|
45306
46073
|
// src/commands/landing/inspiration/sequences.ts
|
|
45307
|
-
import { defineCommand as
|
|
46074
|
+
import { defineCommand as defineCommand164 } from "citty";
|
|
45308
46075
|
var COMPACT_TRANSITIONS = 12;
|
|
45309
46076
|
var COMPACT_ENDS = 5;
|
|
45310
46077
|
var MAX_PAGES = 50;
|
|
@@ -45378,7 +46145,7 @@ function sequencesHints(data, { scope, full }) {
|
|
|
45378
46145
|
if (scope === "favorites") hints.push(...favoritesScopeHints(data.favoritesHealth, data.pagesReturned));
|
|
45379
46146
|
return hints;
|
|
45380
46147
|
}
|
|
45381
|
-
var sequencesCommand =
|
|
46148
|
+
var sequencesCommand = defineCommand164({
|
|
45382
46149
|
meta: {
|
|
45383
46150
|
name: "sequences",
|
|
45384
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."
|
|
@@ -45427,8 +46194,8 @@ var sequencesCommand = defineCommand163({
|
|
|
45427
46194
|
});
|
|
45428
46195
|
|
|
45429
46196
|
// src/commands/landing/inspiration/view.ts
|
|
45430
|
-
import
|
|
45431
|
-
import { defineCommand as
|
|
46197
|
+
import path38 from "path";
|
|
46198
|
+
import { defineCommand as defineCommand165 } from "citty";
|
|
45432
46199
|
registerSchema({
|
|
45433
46200
|
command: "landing.inspiration.view",
|
|
45434
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.",
|
|
@@ -45441,7 +46208,7 @@ registerSchema({
|
|
|
45441
46208
|
}
|
|
45442
46209
|
}
|
|
45443
46210
|
});
|
|
45444
|
-
var viewCommand2 =
|
|
46211
|
+
var viewCommand2 = defineCommand165({
|
|
45445
46212
|
meta: {
|
|
45446
46213
|
name: "view",
|
|
45447
46214
|
description: "Full detail for one reference section. Example: baker landing inspiration view k57abc\u2026 \u2014 read the screenshots it saves before you build."
|
|
@@ -45460,12 +46227,12 @@ var viewCommand2 = defineCommand164({
|
|
|
45460
46227
|
const id = args.id;
|
|
45461
46228
|
const data = await apiGet("/api/landing-inspiration/section", { id });
|
|
45462
46229
|
const section = data.section;
|
|
45463
|
-
const dir =
|
|
46230
|
+
const dir = path38.join(process.cwd(), ".baker", "inspiration", id);
|
|
45464
46231
|
const ext = READABLE_SHOT.extension;
|
|
45465
46232
|
const [desktop, mobile, filmstrip] = await Promise.all([
|
|
45466
|
-
downloadReadableShot(section.desktopShotUrl,
|
|
45467
|
-
downloadReadableShot(section.mobileShotUrl,
|
|
45468
|
-
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}`))
|
|
45469
46236
|
]);
|
|
45470
46237
|
const full = args.full;
|
|
45471
46238
|
const hints = [INSPIRATION_HINTS.structureNotCopy, INSPIRATION_HINTS.adapt];
|
|
@@ -45525,7 +46292,7 @@ var viewCommand2 = defineCommand164({
|
|
|
45525
46292
|
});
|
|
45526
46293
|
|
|
45527
46294
|
// src/commands/landing/inspiration/index.ts
|
|
45528
|
-
var inspirationCommand =
|
|
46295
|
+
var inspirationCommand = defineCommand166({
|
|
45529
46296
|
meta: {
|
|
45530
46297
|
name: "inspiration",
|
|
45531
46298
|
description: `Reference library of real landing-page sections \u2014 look at how good pages actually solve a problem before you design one.
|
|
@@ -45569,8 +46336,310 @@ Full guide: __tooling__/docs/tools/baker/landing.md`
|
|
|
45569
46336
|
}
|
|
45570
46337
|
});
|
|
45571
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
|
+
|
|
45572
46641
|
// src/commands/landing/index.ts
|
|
45573
|
-
var landingCommand =
|
|
46642
|
+
var landingCommand = defineCommand168({
|
|
45574
46643
|
meta: {
|
|
45575
46644
|
name: "landing",
|
|
45576
46645
|
description: `Design-quality tools for landing pages (src/pages/<slug>/).
|
|
@@ -45579,16 +46648,18 @@ Start here: \`baker landing critique <slug>\` after building or editing a landin
|
|
|
45579
46648
|
|
|
45580
46649
|
Subcommands:
|
|
45581
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.
|
|
45582
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.`
|
|
45583
46653
|
},
|
|
45584
46654
|
subCommands: {
|
|
45585
46655
|
inspiration: inspirationCommand,
|
|
45586
|
-
critique: critiqueCommand2
|
|
46656
|
+
critique: critiqueCommand2,
|
|
46657
|
+
variant: variantCommand
|
|
45587
46658
|
}
|
|
45588
46659
|
});
|
|
45589
46660
|
|
|
45590
46661
|
// src/commands/mcp/index.ts
|
|
45591
|
-
import { defineCommand as
|
|
46662
|
+
import { defineCommand as defineCommand169 } from "citty";
|
|
45592
46663
|
|
|
45593
46664
|
// src/commands/mcp/platforms.ts
|
|
45594
46665
|
function readsKey(label) {
|
|
@@ -45643,7 +46714,7 @@ function parseHeaders(raw) {
|
|
|
45643
46714
|
}
|
|
45644
46715
|
return Object.keys(headers).length > 0 ? headers : void 0;
|
|
45645
46716
|
}
|
|
45646
|
-
function
|
|
46717
|
+
function fail8(err) {
|
|
45647
46718
|
if (err instanceof ApiError) {
|
|
45648
46719
|
writeJson({ ok: false, error: { code: err.code, message: err.message } });
|
|
45649
46720
|
process.exit(1);
|
|
@@ -45657,7 +46728,7 @@ registerSchema({
|
|
|
45657
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.",
|
|
45658
46729
|
args: {}
|
|
45659
46730
|
});
|
|
45660
|
-
var connectedCommand =
|
|
46731
|
+
var connectedCommand = defineCommand169({
|
|
45661
46732
|
meta: {
|
|
45662
46733
|
name: "connected",
|
|
45663
46734
|
description: `Everything this chat can reach \u2014 managed integrations, custom MCP servers, and connected platforms.
|
|
@@ -45707,7 +46778,7 @@ A tool or platform the user names that is NOT listed anywhere here is simply not
|
|
|
45707
46778
|
hints
|
|
45708
46779
|
});
|
|
45709
46780
|
} catch (err) {
|
|
45710
|
-
|
|
46781
|
+
fail8(err);
|
|
45711
46782
|
}
|
|
45712
46783
|
}
|
|
45713
46784
|
});
|
|
@@ -45716,7 +46787,7 @@ registerSchema({
|
|
|
45716
46787
|
description: "List the custom MCP servers this company's chats see (org + company + your own user scope).",
|
|
45717
46788
|
args: {}
|
|
45718
46789
|
});
|
|
45719
|
-
var listCommand14 =
|
|
46790
|
+
var listCommand14 = defineCommand169({
|
|
45720
46791
|
meta: { name: "list", description: "List custom MCP servers visible to this company's chats." },
|
|
45721
46792
|
run: async () => {
|
|
45722
46793
|
try {
|
|
@@ -45735,7 +46806,7 @@ var listCommand14 = defineCommand167({
|
|
|
45735
46806
|
} : {}
|
|
45736
46807
|
});
|
|
45737
46808
|
} catch (err) {
|
|
45738
|
-
|
|
46809
|
+
fail8(err);
|
|
45739
46810
|
}
|
|
45740
46811
|
}
|
|
45741
46812
|
});
|
|
@@ -45753,7 +46824,7 @@ registerSchema({
|
|
|
45753
46824
|
header: { type: "string", description: 'Auth header "Key: Value" (repeatable)', required: false }
|
|
45754
46825
|
}
|
|
45755
46826
|
});
|
|
45756
|
-
var addCommand2 =
|
|
46827
|
+
var addCommand2 = defineCommand169({
|
|
45757
46828
|
meta: {
|
|
45758
46829
|
name: "add",
|
|
45759
46830
|
description: `Register a custom MCP server. Tools appear as mcp__<name>__* on the NEXT message.
|
|
@@ -45796,7 +46867,7 @@ Examples:
|
|
|
45796
46867
|
]
|
|
45797
46868
|
});
|
|
45798
46869
|
} catch (err) {
|
|
45799
|
-
|
|
46870
|
+
fail8(err);
|
|
45800
46871
|
}
|
|
45801
46872
|
}
|
|
45802
46873
|
});
|
|
@@ -45805,7 +46876,7 @@ registerSchema({
|
|
|
45805
46876
|
description: "Remove a company custom MCP server by name.",
|
|
45806
46877
|
args: { name: { type: "string", description: "Server name to remove", required: true } }
|
|
45807
46878
|
});
|
|
45808
|
-
var removeCommand4 =
|
|
46879
|
+
var removeCommand4 = defineCommand169({
|
|
45809
46880
|
meta: {
|
|
45810
46881
|
name: "remove",
|
|
45811
46882
|
description: `Remove a company custom MCP server by name.
|
|
@@ -45823,11 +46894,11 @@ Example:
|
|
|
45823
46894
|
});
|
|
45824
46895
|
writeJson({ ok: true, data });
|
|
45825
46896
|
} catch (err) {
|
|
45826
|
-
|
|
46897
|
+
fail8(err);
|
|
45827
46898
|
}
|
|
45828
46899
|
}
|
|
45829
46900
|
});
|
|
45830
|
-
var mcpCommand =
|
|
46901
|
+
var mcpCommand = defineCommand169({
|
|
45831
46902
|
meta: {
|
|
45832
46903
|
name: "mcp",
|
|
45833
46904
|
description: `Third-party tools for this company \u2014 see what's connected, register custom HTTPS MCP endpoints.
|
|
@@ -45853,10 +46924,10 @@ Full guide: __tooling__/docs/tools/baker/mcp.md`
|
|
|
45853
46924
|
});
|
|
45854
46925
|
|
|
45855
46926
|
// src/commands/research/index.ts
|
|
45856
|
-
import { defineCommand as
|
|
46927
|
+
import { defineCommand as defineCommand181 } from "citty";
|
|
45857
46928
|
|
|
45858
46929
|
// src/commands/research/advertisers.ts
|
|
45859
|
-
import { defineCommand as
|
|
46930
|
+
import { defineCommand as defineCommand170 } from "citty";
|
|
45860
46931
|
|
|
45861
46932
|
// src/commands/research/hints.ts
|
|
45862
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.';
|
|
@@ -46039,7 +47110,7 @@ var FIELDS3 = {
|
|
|
46039
47110
|
etv: "Estimated traffic value (USD)",
|
|
46040
47111
|
visibility: "SERP visibility score (0-1)"
|
|
46041
47112
|
};
|
|
46042
|
-
var advertisersCommand =
|
|
47113
|
+
var advertisersCommand = defineCommand170({
|
|
46043
47114
|
meta: {
|
|
46044
47115
|
name: "advertisers",
|
|
46045
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).
|
|
@@ -46095,7 +47166,7 @@ Examples:
|
|
|
46095
47166
|
});
|
|
46096
47167
|
|
|
46097
47168
|
// src/commands/research/autocomplete.ts
|
|
46098
|
-
import { defineCommand as
|
|
47169
|
+
import { defineCommand as defineCommand171 } from "citty";
|
|
46099
47170
|
registerSchema({
|
|
46100
47171
|
command: "research.autocomplete",
|
|
46101
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).",
|
|
@@ -46118,7 +47189,7 @@ registerSchema({
|
|
|
46118
47189
|
var FIELDS4 = {
|
|
46119
47190
|
suggestion: "Autocomplete suggestion from Google"
|
|
46120
47191
|
};
|
|
46121
|
-
var autocompleteCommand =
|
|
47192
|
+
var autocompleteCommand = defineCommand171({
|
|
46122
47193
|
meta: {
|
|
46123
47194
|
name: "autocomplete",
|
|
46124
47195
|
description: `Get Google Autocomplete suggestions for keyword expansion.
|
|
@@ -46173,7 +47244,7 @@ Examples:
|
|
|
46173
47244
|
});
|
|
46174
47245
|
|
|
46175
47246
|
// src/commands/research/countries.ts
|
|
46176
|
-
import { defineCommand as
|
|
47247
|
+
import { defineCommand as defineCommand172 } from "citty";
|
|
46177
47248
|
registerSchema({
|
|
46178
47249
|
command: "research.countries",
|
|
46179
47250
|
description: "List all supported country codes for --location flag in research commands.",
|
|
@@ -46230,7 +47301,7 @@ var FIELDS5 = {
|
|
|
46230
47301
|
code: "Country code to pass as --location",
|
|
46231
47302
|
name: "Country name"
|
|
46232
47303
|
};
|
|
46233
|
-
var countriesCommand =
|
|
47304
|
+
var countriesCommand = defineCommand172({
|
|
46234
47305
|
meta: {
|
|
46235
47306
|
name: "countries",
|
|
46236
47307
|
description: "List all supported country codes for --location flag."
|
|
@@ -46241,7 +47312,7 @@ var countriesCommand = defineCommand170({
|
|
|
46241
47312
|
});
|
|
46242
47313
|
|
|
46243
47314
|
// src/commands/research/fetch.ts
|
|
46244
|
-
import { defineCommand as
|
|
47315
|
+
import { defineCommand as defineCommand173 } from "citty";
|
|
46245
47316
|
var CONTENT_PREVIEW_CHARS = 2e4;
|
|
46246
47317
|
var TIMEOUT_MS = 18e4;
|
|
46247
47318
|
registerSchema({
|
|
@@ -46314,7 +47385,7 @@ function fetchFix(code) {
|
|
|
46314
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."
|
|
46315
47386
|
};
|
|
46316
47387
|
}
|
|
46317
|
-
var fetchCommand =
|
|
47388
|
+
var fetchCommand = defineCommand173({
|
|
46318
47389
|
meta: {
|
|
46319
47390
|
name: "fetch",
|
|
46320
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.
|
|
@@ -46391,7 +47462,7 @@ Examples:
|
|
|
46391
47462
|
});
|
|
46392
47463
|
|
|
46393
47464
|
// src/commands/research/intent.ts
|
|
46394
|
-
import { defineCommand as
|
|
47465
|
+
import { defineCommand as defineCommand174 } from "citty";
|
|
46395
47466
|
registerSchema({
|
|
46396
47467
|
command: "research.intent",
|
|
46397
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.",
|
|
@@ -46414,7 +47485,7 @@ var FIELDS7 = {
|
|
|
46414
47485
|
intent: "Primary Google Search intent: informational, navigational, commercial, transactional",
|
|
46415
47486
|
probability: "Confidence score 0.0-1.0"
|
|
46416
47487
|
};
|
|
46417
|
-
var intentCommand =
|
|
47488
|
+
var intentCommand = defineCommand174({
|
|
46418
47489
|
meta: {
|
|
46419
47490
|
name: "intent",
|
|
46420
47491
|
description: `Classify Google Search intent for keywords. Returns intent type and confidence.
|
|
@@ -46462,7 +47533,7 @@ Examples:
|
|
|
46462
47533
|
});
|
|
46463
47534
|
|
|
46464
47535
|
// src/commands/research/keyword-gap.ts
|
|
46465
|
-
import { defineCommand as
|
|
47536
|
+
import { defineCommand as defineCommand175 } from "citty";
|
|
46466
47537
|
registerSchema({
|
|
46467
47538
|
command: "research.keyword-gap",
|
|
46468
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.",
|
|
@@ -46491,7 +47562,7 @@ var FIELDS8 = {
|
|
|
46491
47562
|
cpc: "Cost per click USD",
|
|
46492
47563
|
their_position: "Competitor's ranking position"
|
|
46493
47564
|
};
|
|
46494
|
-
var keywordGapCommand =
|
|
47565
|
+
var keywordGapCommand = defineCommand175({
|
|
46495
47566
|
meta: {
|
|
46496
47567
|
name: "keyword-gap",
|
|
46497
47568
|
description: `Find keywords a competitor has that you don't. Supports pagination via --offset.
|
|
@@ -46566,7 +47637,7 @@ Examples:
|
|
|
46566
47637
|
});
|
|
46567
47638
|
|
|
46568
47639
|
// src/commands/research/keywords-for-site.ts
|
|
46569
|
-
import { defineCommand as
|
|
47640
|
+
import { defineCommand as defineCommand176 } from "citty";
|
|
46570
47641
|
registerSchema({
|
|
46571
47642
|
command: "research.keywords-for-site",
|
|
46572
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.",
|
|
@@ -46599,7 +47670,7 @@ var FIELDS9 = {
|
|
|
46599
47670
|
competition: "LOW, MEDIUM, or HIGH",
|
|
46600
47671
|
competition_index: "Competition score 0-100"
|
|
46601
47672
|
};
|
|
46602
|
-
var keywordsForSiteCommand =
|
|
47673
|
+
var keywordsForSiteCommand = defineCommand176({
|
|
46603
47674
|
meta: {
|
|
46604
47675
|
name: "keywords-for-site",
|
|
46605
47676
|
description: `Get keywords a competitor targets in Google. Use --type to filter paid/organic.
|
|
@@ -46661,7 +47732,7 @@ Examples:
|
|
|
46661
47732
|
});
|
|
46662
47733
|
|
|
46663
47734
|
// src/commands/research/languages.ts
|
|
46664
|
-
import { defineCommand as
|
|
47735
|
+
import { defineCommand as defineCommand177 } from "citty";
|
|
46665
47736
|
registerSchema({
|
|
46666
47737
|
command: "research.languages",
|
|
46667
47738
|
description: "List all supported language codes for --language flag in research commands.",
|
|
@@ -46691,7 +47762,7 @@ var FIELDS10 = {
|
|
|
46691
47762
|
code: "Language code to pass as --language",
|
|
46692
47763
|
name: "Language name (also accepted by --language)"
|
|
46693
47764
|
};
|
|
46694
|
-
var languagesCommand2 =
|
|
47765
|
+
var languagesCommand2 = defineCommand177({
|
|
46695
47766
|
meta: {
|
|
46696
47767
|
name: "languages",
|
|
46697
47768
|
description: "List all supported language codes for --language flag."
|
|
@@ -46702,7 +47773,7 @@ var languagesCommand2 = defineCommand175({
|
|
|
46702
47773
|
});
|
|
46703
47774
|
|
|
46704
47775
|
// src/commands/research/lighthouse.ts
|
|
46705
|
-
import { defineCommand as
|
|
47776
|
+
import { defineCommand as defineCommand178 } from "citty";
|
|
46706
47777
|
registerSchema({
|
|
46707
47778
|
command: "research.lighthouse",
|
|
46708
47779
|
description: "Landing page performance audit. Returns metrics that affect Google Ads Quality Score and CPC.",
|
|
@@ -46721,7 +47792,7 @@ var FIELDS11 = {
|
|
|
46721
47792
|
speed_index_ms: "Speed Index in ms (good: < 3400)",
|
|
46722
47793
|
interactive_ms: "Time to Interactive in ms (good: < 3800)"
|
|
46723
47794
|
};
|
|
46724
|
-
var lighthouseCommand =
|
|
47795
|
+
var lighthouseCommand = defineCommand178({
|
|
46725
47796
|
meta: {
|
|
46726
47797
|
name: "lighthouse",
|
|
46727
47798
|
description: `Landing page performance audit. Metrics affecting Google Ads Quality Score.
|
|
@@ -46759,7 +47830,7 @@ Examples:
|
|
|
46759
47830
|
});
|
|
46760
47831
|
|
|
46761
47832
|
// src/commands/research/relevant-pages.ts
|
|
46762
|
-
import { defineCommand as
|
|
47833
|
+
import { defineCommand as defineCommand179 } from "citty";
|
|
46763
47834
|
registerSchema({
|
|
46764
47835
|
command: "research.relevant-pages",
|
|
46765
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).",
|
|
@@ -46785,7 +47856,7 @@ var FIELDS12 = {
|
|
|
46785
47856
|
keywords: "Total organic keywords the page ranks for",
|
|
46786
47857
|
top_10: "Keywords in positions 1-10"
|
|
46787
47858
|
};
|
|
46788
|
-
var relevantPagesCommand =
|
|
47859
|
+
var relevantPagesCommand = defineCommand179({
|
|
46789
47860
|
meta: {
|
|
46790
47861
|
name: "relevant-pages",
|
|
46791
47862
|
description: `Get the top pages of a competitor domain with traffic data.
|
|
@@ -46832,7 +47903,7 @@ Examples:
|
|
|
46832
47903
|
});
|
|
46833
47904
|
|
|
46834
47905
|
// src/commands/research/web.ts
|
|
46835
|
-
import { defineCommand as
|
|
47906
|
+
import { defineCommand as defineCommand180 } from "citty";
|
|
46836
47907
|
registerSchema({
|
|
46837
47908
|
command: "research.web",
|
|
46838
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).",
|
|
@@ -46883,7 +47954,7 @@ async function runDeepResearch(question) {
|
|
|
46883
47954
|
}
|
|
46884
47955
|
throw new Error("Deep research timed out");
|
|
46885
47956
|
}
|
|
46886
|
-
var webCommand =
|
|
47957
|
+
var webCommand = defineCommand180({
|
|
46887
47958
|
meta: {
|
|
46888
47959
|
name: "web",
|
|
46889
47960
|
description: `Search the web with AI to answer any open-ended marketing question. Uses live internet data via Google Search.
|
|
@@ -46945,7 +48016,7 @@ Examples:
|
|
|
46945
48016
|
});
|
|
46946
48017
|
|
|
46947
48018
|
// src/commands/research/index.ts
|
|
46948
|
-
var researchCommand =
|
|
48019
|
+
var researchCommand = defineCommand181({
|
|
46949
48020
|
meta: {
|
|
46950
48021
|
name: "research",
|
|
46951
48022
|
description: `Competitive intelligence and AI-powered research commands.
|
|
@@ -46989,10 +48060,10 @@ Full guide: __tooling__/docs/tools/baker/research.md`
|
|
|
46989
48060
|
});
|
|
46990
48061
|
|
|
46991
48062
|
// src/commands/scheduled-actions/index.ts
|
|
46992
|
-
import { defineCommand as
|
|
48063
|
+
import { defineCommand as defineCommand189 } from "citty";
|
|
46993
48064
|
|
|
46994
48065
|
// src/commands/scheduled-actions/create.ts
|
|
46995
|
-
import { defineCommand as
|
|
48066
|
+
import { defineCommand as defineCommand182 } from "citty";
|
|
46996
48067
|
|
|
46997
48068
|
// src/commands/scheduled-actions/shared.ts
|
|
46998
48069
|
var TEMP_SCHEDULED_ACTION_PREFIX = "temp_sched_";
|
|
@@ -47139,7 +48210,7 @@ registerSchema({
|
|
|
47139
48210
|
}
|
|
47140
48211
|
}
|
|
47141
48212
|
});
|
|
47142
|
-
var createCommand3 =
|
|
48213
|
+
var createCommand3 = defineCommand182({
|
|
47143
48214
|
meta: {
|
|
47144
48215
|
name: "create",
|
|
47145
48216
|
description: 'Stage a scheduled action. Example: baker scheduled-actions create --name "Weekly report" --description "..." --cron "0 9 * * MON"'
|
|
@@ -47198,7 +48269,7 @@ var createCommand3 = defineCommand180({
|
|
|
47198
48269
|
});
|
|
47199
48270
|
|
|
47200
48271
|
// src/commands/scheduled-actions/delete.ts
|
|
47201
|
-
import { defineCommand as
|
|
48272
|
+
import { defineCommand as defineCommand183 } from "citty";
|
|
47202
48273
|
registerSchema({
|
|
47203
48274
|
command: "scheduled-actions.delete",
|
|
47204
48275
|
description: "Stage deletion of a published scheduled action or cancellation of a temp_sched_* draft creation.",
|
|
@@ -47206,7 +48277,7 @@ registerSchema({
|
|
|
47206
48277
|
id: { type: "string", description: "Published scheduled action ID or temp_sched_* draft ID", required: true }
|
|
47207
48278
|
}
|
|
47208
48279
|
});
|
|
47209
|
-
var deleteCommand3 =
|
|
48280
|
+
var deleteCommand3 = defineCommand183({
|
|
47210
48281
|
meta: {
|
|
47211
48282
|
name: "delete",
|
|
47212
48283
|
description: "Stage scheduled action deletion. Example: baker scheduled-actions delete <id-or-temp_sched_id>"
|
|
@@ -47235,7 +48306,7 @@ var deleteCommand3 = defineCommand181({
|
|
|
47235
48306
|
});
|
|
47236
48307
|
|
|
47237
48308
|
// src/commands/scheduled-actions/get.ts
|
|
47238
|
-
import { defineCommand as
|
|
48309
|
+
import { defineCommand as defineCommand184 } from "citty";
|
|
47239
48310
|
registerSchema({
|
|
47240
48311
|
command: "scheduled-actions.get",
|
|
47241
48312
|
description: "Get a published scheduled action or a temp_sched_* draft-created scheduled action.",
|
|
@@ -47244,7 +48315,7 @@ registerSchema({
|
|
|
47244
48315
|
chat: { type: "string", description: CHAT_READ_ARG.description, required: false }
|
|
47245
48316
|
}
|
|
47246
48317
|
});
|
|
47247
|
-
var getCommand4 =
|
|
48318
|
+
var getCommand4 = defineCommand184({
|
|
47248
48319
|
meta: {
|
|
47249
48320
|
name: "get",
|
|
47250
48321
|
description: "Get a scheduled action. Example: baker scheduled-actions get <id-or-temp_sched_id>"
|
|
@@ -47283,7 +48354,7 @@ var getCommand4 = defineCommand182({
|
|
|
47283
48354
|
});
|
|
47284
48355
|
|
|
47285
48356
|
// src/commands/scheduled-actions/list.ts
|
|
47286
|
-
import { defineCommand as
|
|
48357
|
+
import { defineCommand as defineCommand185 } from "citty";
|
|
47287
48358
|
registerSchema({
|
|
47288
48359
|
command: "scheduled-actions.list",
|
|
47289
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.",
|
|
@@ -47291,7 +48362,7 @@ registerSchema({
|
|
|
47291
48362
|
chat: { type: "string", description: CHAT_READ_ARG.description, required: false }
|
|
47292
48363
|
}
|
|
47293
48364
|
});
|
|
47294
|
-
var listCommand15 =
|
|
48365
|
+
var listCommand15 = defineCommand185({
|
|
47295
48366
|
meta: {
|
|
47296
48367
|
name: "list",
|
|
47297
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."
|
|
@@ -47314,9 +48385,9 @@ var listCommand15 = defineCommand183({
|
|
|
47314
48385
|
});
|
|
47315
48386
|
|
|
47316
48387
|
// src/commands/scheduled-actions/templates.ts
|
|
47317
|
-
import { readFile as
|
|
47318
|
-
import
|
|
47319
|
-
import { defineCommand as
|
|
48388
|
+
import { readFile as readFile26 } from "fs/promises";
|
|
48389
|
+
import path41 from "path";
|
|
48390
|
+
import { defineCommand as defineCommand186 } from "citty";
|
|
47320
48391
|
registerSchema({
|
|
47321
48392
|
command: "scheduled-actions.templates",
|
|
47322
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.",
|
|
@@ -47371,7 +48442,7 @@ registerSchema({
|
|
|
47371
48442
|
}
|
|
47372
48443
|
}
|
|
47373
48444
|
});
|
|
47374
|
-
var templatesCommand =
|
|
48445
|
+
var templatesCommand = defineCommand186({
|
|
47375
48446
|
meta: {
|
|
47376
48447
|
name: "templates",
|
|
47377
48448
|
description: `The recipes this company can run \u2014 Baker's own plus theirs, with what each one produces.
|
|
@@ -47418,7 +48489,7 @@ Full guide: __tooling__/docs/tools/baker/scheduled-actions.md`
|
|
|
47418
48489
|
}
|
|
47419
48490
|
if (save.length > 0) {
|
|
47420
48491
|
const briefFile = flag("brief-file");
|
|
47421
|
-
const brief = briefFile.length > 0 ? await
|
|
48492
|
+
const brief = briefFile.length > 0 ? await readFile26(path41.resolve(briefFile), "utf8") : flag("brief");
|
|
47422
48493
|
if (brief.trim().length === 0) {
|
|
47423
48494
|
failValidation4("--brief-file (preferred) or --brief is required: the brief is the recipe.");
|
|
47424
48495
|
}
|
|
@@ -47463,7 +48534,7 @@ Full guide: __tooling__/docs/tools/baker/scheduled-actions.md`
|
|
|
47463
48534
|
});
|
|
47464
48535
|
|
|
47465
48536
|
// src/commands/scheduled-actions/trigger.ts
|
|
47466
|
-
import { defineCommand as
|
|
48537
|
+
import { defineCommand as defineCommand187 } from "citty";
|
|
47467
48538
|
registerSchema({
|
|
47468
48539
|
command: "scheduled-actions.trigger",
|
|
47469
48540
|
description: "Immediately trigger a published scheduled action. Does not require BAKER_CHAT_ID and rejects temp_sched_* IDs.",
|
|
@@ -47471,7 +48542,7 @@ registerSchema({
|
|
|
47471
48542
|
id: { type: "string", description: "Published scheduled action ID", required: true }
|
|
47472
48543
|
}
|
|
47473
48544
|
});
|
|
47474
|
-
var triggerCommand =
|
|
48545
|
+
var triggerCommand = defineCommand187({
|
|
47475
48546
|
meta: {
|
|
47476
48547
|
name: "trigger",
|
|
47477
48548
|
description: "Immediately trigger a published scheduled action. Example: baker scheduled-actions trigger <id>"
|
|
@@ -47508,7 +48579,7 @@ var triggerCommand = defineCommand185({
|
|
|
47508
48579
|
});
|
|
47509
48580
|
|
|
47510
48581
|
// src/commands/scheduled-actions/update.ts
|
|
47511
|
-
import { defineCommand as
|
|
48582
|
+
import { defineCommand as defineCommand188 } from "citty";
|
|
47512
48583
|
registerSchema({
|
|
47513
48584
|
command: "scheduled-actions.update",
|
|
47514
48585
|
description: "Stage an update to a published scheduled action or temp_sched_* draft-created scheduled action.",
|
|
@@ -47539,7 +48610,7 @@ registerSchema({
|
|
|
47539
48610
|
prompt: { type: "string", description: "Replacement additional spawned-agent instructions", required: false }
|
|
47540
48611
|
}
|
|
47541
48612
|
});
|
|
47542
|
-
var updateCommand3 =
|
|
48613
|
+
var updateCommand3 = defineCommand188({
|
|
47543
48614
|
meta: {
|
|
47544
48615
|
name: "update",
|
|
47545
48616
|
description: "Stage a scheduled action update. Examples: baker scheduled-actions update <id> --enabled false | baker scheduled-actions update <id> --mode publish"
|
|
@@ -47617,7 +48688,7 @@ var updateCommand3 = defineCommand186({
|
|
|
47617
48688
|
});
|
|
47618
48689
|
|
|
47619
48690
|
// src/commands/scheduled-actions/index.ts
|
|
47620
|
-
var scheduledActionsCommand =
|
|
48691
|
+
var scheduledActionsCommand = defineCommand189({
|
|
47621
48692
|
meta: {
|
|
47622
48693
|
name: "scheduled-actions",
|
|
47623
48694
|
description: `Manage Scheduled Actions. Subcommands: list, get, create, update, delete, trigger, templates.
|
|
@@ -47646,14 +48717,14 @@ Full guide: __tooling__/docs/tools/baker/scheduled-actions.md`
|
|
|
47646
48717
|
});
|
|
47647
48718
|
|
|
47648
48719
|
// src/commands/schema.ts
|
|
47649
|
-
import { defineCommand as
|
|
48720
|
+
import { defineCommand as defineCommand190 } from "citty";
|
|
47650
48721
|
function narrowToFamily(commandName, available) {
|
|
47651
48722
|
const segments = commandName.split(".");
|
|
47652
48723
|
const prefix = segments[0] === "ads" && segments[1] ? `ads.${segments[1]}.` : `${segments[0]}.`;
|
|
47653
48724
|
const siblings = available.filter((name) => name.startsWith(prefix));
|
|
47654
48725
|
return siblings.length > 0 ? siblings : available;
|
|
47655
48726
|
}
|
|
47656
|
-
var schemaCommand2 =
|
|
48727
|
+
var schemaCommand2 = defineCommand190({
|
|
47657
48728
|
meta: {
|
|
47658
48729
|
name: "schema",
|
|
47659
48730
|
description: "Inspect command argument schemas (for AI agent introspection). Lists all commands if no argument given. Example: baker schema images.search"
|
|
@@ -47697,10 +48768,10 @@ var schemaCommand2 = defineCommand188({
|
|
|
47697
48768
|
});
|
|
47698
48769
|
|
|
47699
48770
|
// src/commands/studio/index.ts
|
|
47700
|
-
import { defineCommand as
|
|
48771
|
+
import { defineCommand as defineCommand199 } from "citty";
|
|
47701
48772
|
|
|
47702
48773
|
// src/commands/studio/animate.ts
|
|
47703
|
-
import { defineCommand as
|
|
48774
|
+
import { defineCommand as defineCommand191 } from "citty";
|
|
47704
48775
|
|
|
47705
48776
|
// src/commands/studio/batch.ts
|
|
47706
48777
|
function projectBatch(generation, full) {
|
|
@@ -47889,7 +48960,7 @@ function parseImageRefs(spec) {
|
|
|
47889
48960
|
}
|
|
47890
48961
|
var defaultDeps = {
|
|
47891
48962
|
ingest: (url) => apiPost("/api/images/ingest", { url, source: "uploaded" }),
|
|
47892
|
-
upload: (
|
|
48963
|
+
upload: (path43) => uploadLocalImage({ file: path43, contentType: detectImageContentType(path43), source: "uploaded" })
|
|
47893
48964
|
};
|
|
47894
48965
|
async function resolveLibraryImageIds(spec, limit, deps = defaultDeps) {
|
|
47895
48966
|
const refs = parseImageRefs(spec);
|
|
@@ -47909,10 +48980,10 @@ async function resolveLibraryImageIds(spec, limit, deps = defaultDeps) {
|
|
|
47909
48980
|
}
|
|
47910
48981
|
return { imageIds, added };
|
|
47911
48982
|
}
|
|
47912
|
-
function uploadFailure(
|
|
48983
|
+
function uploadFailure(path43) {
|
|
47913
48984
|
return (error) => {
|
|
47914
48985
|
if (error instanceof ApiError) throw error;
|
|
47915
|
-
throw new ApiError("VALIDATION_ERROR", `Could not read "${
|
|
48986
|
+
throw new ApiError("VALIDATION_ERROR", `Could not read "${path43}" as an image.`);
|
|
47916
48987
|
};
|
|
47917
48988
|
}
|
|
47918
48989
|
|
|
@@ -48151,7 +49222,7 @@ function costHintsFor(body) {
|
|
|
48151
49222
|
}
|
|
48152
49223
|
return hints;
|
|
48153
49224
|
}
|
|
48154
|
-
var animateCommand =
|
|
49225
|
+
var animateCommand = defineCommand191({
|
|
48155
49226
|
meta: {
|
|
48156
49227
|
name: "animate",
|
|
48157
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"
|
|
@@ -48270,7 +49341,7 @@ var animateCommand = defineCommand189({
|
|
|
48270
49341
|
});
|
|
48271
49342
|
|
|
48272
49343
|
// src/commands/studio/generate.ts
|
|
48273
|
-
import { defineCommand as
|
|
49344
|
+
import { defineCommand as defineCommand192 } from "citty";
|
|
48274
49345
|
var MODEL_LIST2 = IMAGE_MODEL_IDS;
|
|
48275
49346
|
var DEFAULT_MAX_WAIT_MS2 = 24e4;
|
|
48276
49347
|
registerSchema({
|
|
@@ -48406,7 +49477,7 @@ function buildGenerateBody(args, prompt) {
|
|
|
48406
49477
|
}
|
|
48407
49478
|
return body;
|
|
48408
49479
|
}
|
|
48409
|
-
var generateCommand =
|
|
49480
|
+
var generateCommand = defineCommand192({
|
|
48410
49481
|
meta: {
|
|
48411
49482
|
name: "generate",
|
|
48412
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]]'"
|
|
@@ -48484,7 +49555,7 @@ var generateCommand = defineCommand190({
|
|
|
48484
49555
|
});
|
|
48485
49556
|
|
|
48486
49557
|
// src/commands/studio/get.ts
|
|
48487
|
-
import { defineCommand as
|
|
49558
|
+
import { defineCommand as defineCommand193 } from "citty";
|
|
48488
49559
|
registerSchema({
|
|
48489
49560
|
command: "studio.get",
|
|
48490
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.",
|
|
@@ -48493,7 +49564,7 @@ registerSchema({
|
|
|
48493
49564
|
full: { type: "boolean", description: "Include settings, references and attribution", required: false }
|
|
48494
49565
|
}
|
|
48495
49566
|
});
|
|
48496
|
-
var getCommand5 =
|
|
49567
|
+
var getCommand5 = defineCommand193({
|
|
48497
49568
|
meta: {
|
|
48498
49569
|
name: "get",
|
|
48499
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"
|
|
@@ -48522,7 +49593,7 @@ var getCommand5 = defineCommand191({
|
|
|
48522
49593
|
});
|
|
48523
49594
|
|
|
48524
49595
|
// src/commands/studio/improve.ts
|
|
48525
|
-
import { defineCommand as
|
|
49596
|
+
import { defineCommand as defineCommand194 } from "citty";
|
|
48526
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.";
|
|
48527
49598
|
registerSchema({
|
|
48528
49599
|
command: "studio.improve",
|
|
@@ -48547,7 +49618,7 @@ registerSchema({
|
|
|
48547
49618
|
}
|
|
48548
49619
|
}
|
|
48549
49620
|
});
|
|
48550
|
-
var improveCommand =
|
|
49621
|
+
var improveCommand = defineCommand194({
|
|
48551
49622
|
meta: {
|
|
48552
49623
|
name: "improve",
|
|
48553
49624
|
description: `${DESCRIPTION}
|
|
@@ -48591,7 +49662,7 @@ Examples:
|
|
|
48591
49662
|
});
|
|
48592
49663
|
|
|
48593
49664
|
// src/commands/studio/keep.ts
|
|
48594
|
-
import { defineCommand as
|
|
49665
|
+
import { defineCommand as defineCommand195 } from "citty";
|
|
48595
49666
|
registerSchema({
|
|
48596
49667
|
command: "studio.keep",
|
|
48597
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.",
|
|
@@ -48601,7 +49672,7 @@ registerSchema({
|
|
|
48601
49672
|
undo: { type: "boolean", description: "Un-star an image, or take a kept clip back out", required: false }
|
|
48602
49673
|
}
|
|
48603
49674
|
});
|
|
48604
|
-
var keepCommand =
|
|
49675
|
+
var keepCommand = defineCommand195({
|
|
48605
49676
|
meta: {
|
|
48606
49677
|
name: "keep",
|
|
48607
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"
|
|
@@ -48652,7 +49723,7 @@ var keepCommand = defineCommand193({
|
|
|
48652
49723
|
});
|
|
48653
49724
|
|
|
48654
49725
|
// src/commands/studio/list.ts
|
|
48655
|
-
import { defineCommand as
|
|
49726
|
+
import { defineCommand as defineCommand196 } from "citty";
|
|
48656
49727
|
registerSchema({
|
|
48657
49728
|
command: "studio.list",
|
|
48658
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.",
|
|
@@ -48663,7 +49734,7 @@ registerSchema({
|
|
|
48663
49734
|
full: { type: "boolean", description: "Include settings, references and attribution", required: false }
|
|
48664
49735
|
}
|
|
48665
49736
|
});
|
|
48666
|
-
var listCommand16 =
|
|
49737
|
+
var listCommand16 = defineCommand196({
|
|
48667
49738
|
meta: {
|
|
48668
49739
|
name: "list",
|
|
48669
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"
|
|
@@ -48697,7 +49768,7 @@ var listCommand16 = defineCommand194({
|
|
|
48697
49768
|
});
|
|
48698
49769
|
|
|
48699
49770
|
// src/commands/studio/models.ts
|
|
48700
|
-
import { defineCommand as
|
|
49771
|
+
import { defineCommand as defineCommand197 } from "citty";
|
|
48701
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.";
|
|
48702
49773
|
registerSchema({
|
|
48703
49774
|
command: "studio.models",
|
|
@@ -48784,7 +49855,7 @@ function buildModelCards(kind, model) {
|
|
|
48784
49855
|
const selected = model ? ids.filter((id) => id === model) : ids;
|
|
48785
49856
|
return selected.map((id) => build(id));
|
|
48786
49857
|
}
|
|
48787
|
-
var modelsCommand =
|
|
49858
|
+
var modelsCommand = defineCommand197({
|
|
48788
49859
|
meta: {
|
|
48789
49860
|
name: "models",
|
|
48790
49861
|
description: `${DESCRIPTION2}
|
|
@@ -48831,13 +49902,13 @@ Examples:
|
|
|
48831
49902
|
});
|
|
48832
49903
|
|
|
48833
49904
|
// src/commands/studio/skills.ts
|
|
48834
|
-
import { defineCommand as
|
|
49905
|
+
import { defineCommand as defineCommand198 } from "citty";
|
|
48835
49906
|
registerSchema({
|
|
48836
49907
|
command: "studio.skills",
|
|
48837
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.",
|
|
48838
49909
|
args: {}
|
|
48839
49910
|
});
|
|
48840
|
-
var skillsCommand =
|
|
49911
|
+
var skillsCommand = defineCommand198({
|
|
48841
49912
|
meta: {
|
|
48842
49913
|
name: "skills",
|
|
48843
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"
|
|
@@ -48861,7 +49932,7 @@ var skillsCommand = defineCommand196({
|
|
|
48861
49932
|
});
|
|
48862
49933
|
|
|
48863
49934
|
// src/commands/studio/index.ts
|
|
48864
|
-
var studioCommand =
|
|
49935
|
+
var studioCommand = defineCommand199({
|
|
48865
49936
|
meta: {
|
|
48866
49937
|
name: "studio",
|
|
48867
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.
|
|
@@ -48904,10 +49975,10 @@ Full guide: __tooling__/docs/tools/baker/studio.md`
|
|
|
48904
49975
|
});
|
|
48905
49976
|
|
|
48906
49977
|
// src/commands/tag-manager/index.ts
|
|
48907
|
-
import { defineCommand as
|
|
49978
|
+
import { defineCommand as defineCommand203 } from "citty";
|
|
48908
49979
|
|
|
48909
49980
|
// src/commands/tag-manager/draft.ts
|
|
48910
|
-
import { defineCommand as
|
|
49981
|
+
import { defineCommand as defineCommand200 } from "citty";
|
|
48911
49982
|
|
|
48912
49983
|
// src/commands/tag-manager/shared.ts
|
|
48913
49984
|
import { readFileSync as readFileSync15 } from "fs";
|
|
@@ -48974,10 +50045,10 @@ async function stageOp4(op) {
|
|
|
48974
50045
|
handleError5(err);
|
|
48975
50046
|
}
|
|
48976
50047
|
}
|
|
48977
|
-
async function draftAction3(
|
|
50048
|
+
async function draftAction3(path43, body, chat) {
|
|
48978
50049
|
const chatId = resolveChatId(chat);
|
|
48979
50050
|
try {
|
|
48980
|
-
const data = await apiPost(
|
|
50051
|
+
const data = await apiPost(path43, { chatId, ...body });
|
|
48981
50052
|
writeJsonEnvelope({ ok: true, data });
|
|
48982
50053
|
return data;
|
|
48983
50054
|
} catch (err) {
|
|
@@ -49030,13 +50101,13 @@ registerSchema({
|
|
|
49030
50101
|
chat: { type: "string", description: CHAT_READ_ARG.description, required: false }
|
|
49031
50102
|
}
|
|
49032
50103
|
});
|
|
49033
|
-
var draftCommand4 =
|
|
50104
|
+
var draftCommand4 = defineCommand200({
|
|
49034
50105
|
meta: {
|
|
49035
50106
|
name: "draft",
|
|
49036
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."
|
|
49037
50108
|
},
|
|
49038
50109
|
subCommands: {
|
|
49039
|
-
list:
|
|
50110
|
+
list: defineCommand200({
|
|
49040
50111
|
meta: {
|
|
49041
50112
|
name: "list",
|
|
49042
50113
|
description: "Review everything staged on this chat (--json for the raw envelope)"
|
|
@@ -49049,7 +50120,7 @@ var draftCommand4 = defineCommand198({
|
|
|
49049
50120
|
await draftList2(args.json === true, args.chat);
|
|
49050
50121
|
}
|
|
49051
50122
|
}),
|
|
49052
|
-
show:
|
|
50123
|
+
show: defineCommand200({
|
|
49053
50124
|
meta: {
|
|
49054
50125
|
name: "show",
|
|
49055
50126
|
description: "Print the full staged payload for one change \u2014 the receipt to verify it looks right before publish (never truncated)."
|
|
@@ -49066,7 +50137,7 @@ var draftCommand4 = defineCommand198({
|
|
|
49066
50137
|
);
|
|
49067
50138
|
}
|
|
49068
50139
|
}),
|
|
49069
|
-
amend:
|
|
50140
|
+
amend: defineCommand200({
|
|
49070
50141
|
meta: {
|
|
49071
50142
|
name: "amend",
|
|
49072
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."
|
|
@@ -49083,7 +50154,7 @@ var draftCommand4 = defineCommand198({
|
|
|
49083
50154
|
});
|
|
49084
50155
|
}
|
|
49085
50156
|
}),
|
|
49086
|
-
remove:
|
|
50157
|
+
remove: defineCommand200({
|
|
49087
50158
|
meta: { name: "remove", description: "Remove one staged change (cascades to anything depending on it)" },
|
|
49088
50159
|
args: { ref: { type: "positional", description: "Staged ref (gtm_temp_*) or target", required: false } },
|
|
49089
50160
|
run: async ({ args }) => {
|
|
@@ -49092,7 +50163,7 @@ var draftCommand4 = defineCommand198({
|
|
|
49092
50163
|
});
|
|
49093
50164
|
}
|
|
49094
50165
|
}),
|
|
49095
|
-
clear:
|
|
50166
|
+
clear: defineCommand200({
|
|
49096
50167
|
meta: { name: "clear", description: "Discard all Tag Manager changes staged on this chat" },
|
|
49097
50168
|
run: async () => {
|
|
49098
50169
|
await draftAction3("/api/tag-manager/draft/clear", {});
|
|
@@ -49102,7 +50173,7 @@ var draftCommand4 = defineCommand198({
|
|
|
49102
50173
|
});
|
|
49103
50174
|
|
|
49104
50175
|
// src/commands/tag-manager/read.ts
|
|
49105
|
-
import { defineCommand as
|
|
50176
|
+
import { defineCommand as defineCommand201 } from "citty";
|
|
49106
50177
|
registerSchema({
|
|
49107
50178
|
command: "tagManager.containers",
|
|
49108
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.",
|
|
@@ -49143,7 +50214,7 @@ function containersHints(containers) {
|
|
|
49143
50214
|
}))
|
|
49144
50215
|
});
|
|
49145
50216
|
}
|
|
49146
|
-
var containersCommand =
|
|
50217
|
+
var containersCommand = defineCommand201({
|
|
49147
50218
|
meta: {
|
|
49148
50219
|
name: "containers",
|
|
49149
50220
|
description: `List Tag Manager containers reachable by this company's connection.
|
|
@@ -49160,7 +50231,7 @@ Start here:
|
|
|
49160
50231
|
}
|
|
49161
50232
|
}
|
|
49162
50233
|
});
|
|
49163
|
-
var readCommand =
|
|
50234
|
+
var readCommand = defineCommand201({
|
|
49164
50235
|
meta: {
|
|
49165
50236
|
name: "read",
|
|
49166
50237
|
description: `Read the current contents of the Tag Manager container \u2014 always do this before staging changes.
|
|
@@ -49202,7 +50273,7 @@ Examples:
|
|
|
49202
50273
|
});
|
|
49203
50274
|
|
|
49204
50275
|
// src/commands/tag-manager/write-commands.ts
|
|
49205
|
-
import { defineCommand as
|
|
50276
|
+
import { defineCommand as defineCommand202 } from "citty";
|
|
49206
50277
|
var CONTAINER_ARG_DESCRIPTION = "Numeric container id (optional only when one container is connected \u2014 run `baker tag-manager containers`)";
|
|
49207
50278
|
var ENTITIES = [
|
|
49208
50279
|
{
|
|
@@ -49258,10 +50329,10 @@ for (const { entity, noun, createHint } of ENTITIES) {
|
|
|
49258
50329
|
});
|
|
49259
50330
|
}
|
|
49260
50331
|
function entityCommand(entity, noun, example) {
|
|
49261
|
-
return
|
|
50332
|
+
return defineCommand202({
|
|
49262
50333
|
meta: { name: entity, description: `Stage ${noun} changes on this chat's Tag Manager draft` },
|
|
49263
50334
|
subCommands: {
|
|
49264
|
-
create:
|
|
50335
|
+
create: defineCommand202({
|
|
49265
50336
|
meta: {
|
|
49266
50337
|
name: "create",
|
|
49267
50338
|
description: `Stage a new ${noun}
|
|
@@ -49283,7 +50354,7 @@ Examples:
|
|
|
49283
50354
|
});
|
|
49284
50355
|
}
|
|
49285
50356
|
}),
|
|
49286
|
-
update:
|
|
50357
|
+
update: defineCommand202({
|
|
49287
50358
|
meta: {
|
|
49288
50359
|
name: "update",
|
|
49289
50360
|
description: `Stage an update to an existing ${noun} (pass its id or path)`
|
|
@@ -49303,7 +50374,7 @@ Examples:
|
|
|
49303
50374
|
});
|
|
49304
50375
|
}
|
|
49305
50376
|
}),
|
|
49306
|
-
delete:
|
|
50377
|
+
delete: defineCommand202({
|
|
49307
50378
|
meta: { name: "delete", description: `Stage the deletion of a ${noun} (pass its id or path)` },
|
|
49308
50379
|
args: {
|
|
49309
50380
|
id: { type: "positional", description: `${noun} id or path`, required: false },
|
|
@@ -49344,7 +50415,7 @@ function builtinTypes(args) {
|
|
|
49344
50415
|
}
|
|
49345
50416
|
return raw.split(",").map((entry) => entry.trim());
|
|
49346
50417
|
}
|
|
49347
|
-
var builtinCommand =
|
|
50418
|
+
var builtinCommand = defineCommand202({
|
|
49348
50419
|
meta: {
|
|
49349
50420
|
name: "builtin",
|
|
49350
50421
|
description: `Enable or disable built-in variables
|
|
@@ -49354,7 +50425,7 @@ Examples:
|
|
|
49354
50425
|
baker tag-manager builtin disable --types formId`
|
|
49355
50426
|
},
|
|
49356
50427
|
subCommands: {
|
|
49357
|
-
enable:
|
|
50428
|
+
enable: defineCommand202({
|
|
49358
50429
|
meta: { name: "enable", description: "Stage enabling built-in variables" },
|
|
49359
50430
|
args: {
|
|
49360
50431
|
types: { type: "string", description: "Comma-separated types", required: false },
|
|
@@ -49368,7 +50439,7 @@ Examples:
|
|
|
49368
50439
|
});
|
|
49369
50440
|
}
|
|
49370
50441
|
}),
|
|
49371
|
-
disable:
|
|
50442
|
+
disable: defineCommand202({
|
|
49372
50443
|
meta: { name: "disable", description: "Stage disabling built-in variables" },
|
|
49373
50444
|
args: {
|
|
49374
50445
|
types: { type: "string", description: "Comma-separated types", required: false },
|
|
@@ -49386,7 +50457,7 @@ Examples:
|
|
|
49386
50457
|
});
|
|
49387
50458
|
|
|
49388
50459
|
// src/commands/tag-manager/index.ts
|
|
49389
|
-
var tagManagerCommand =
|
|
50460
|
+
var tagManagerCommand = defineCommand203({
|
|
49390
50461
|
meta: {
|
|
49391
50462
|
name: "tag-manager",
|
|
49392
50463
|
description: `Read and change what lives inside the client's Google Tag Manager container \u2014 tags, triggers, variables, folders and built-in variables.
|
|
@@ -49423,7 +50494,7 @@ Full guide: __tooling__/docs/tools/baker/tag-manager.md`
|
|
|
49423
50494
|
});
|
|
49424
50495
|
|
|
49425
50496
|
// src/commands/tags/index.ts
|
|
49426
|
-
import { defineCommand as
|
|
50497
|
+
import { defineCommand as defineCommand204 } from "citty";
|
|
49427
50498
|
|
|
49428
50499
|
// src/commands/tags/shared.ts
|
|
49429
50500
|
function failApi3(err) {
|
|
@@ -49492,7 +50563,7 @@ async function listTags(json) {
|
|
|
49492
50563
|
var listArgs9 = {
|
|
49493
50564
|
json: { type: "boolean", description: "Print the raw JSON envelope instead of the readable list" }
|
|
49494
50565
|
};
|
|
49495
|
-
var listCommand17 =
|
|
50566
|
+
var listCommand17 = defineCommand204({
|
|
49496
50567
|
meta: {
|
|
49497
50568
|
name: "list",
|
|
49498
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"
|
|
@@ -49511,7 +50582,7 @@ async function listDraft3(chat) {
|
|
|
49511
50582
|
failApi3(err);
|
|
49512
50583
|
}
|
|
49513
50584
|
}
|
|
49514
|
-
var draftCommand5 =
|
|
50585
|
+
var draftCommand5 = defineCommand204({
|
|
49515
50586
|
meta: {
|
|
49516
50587
|
name: "draft",
|
|
49517
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."
|
|
@@ -49521,7 +50592,7 @@ var draftCommand5 = defineCommand202({
|
|
|
49521
50592
|
await listDraft3(args.chat);
|
|
49522
50593
|
}
|
|
49523
50594
|
});
|
|
49524
|
-
var tagsCommand4 =
|
|
50595
|
+
var tagsCommand4 = defineCommand204({
|
|
49525
50596
|
meta: {
|
|
49526
50597
|
name: "tags",
|
|
49527
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.
|
|
@@ -49550,10 +50621,10 @@ Full guide: __tooling__/docs/tools/baker/tags.md`
|
|
|
49550
50621
|
});
|
|
49551
50622
|
|
|
49552
50623
|
// src/commands/testimonials/index.ts
|
|
49553
|
-
import { defineCommand as
|
|
50624
|
+
import { defineCommand as defineCommand208 } from "citty";
|
|
49554
50625
|
|
|
49555
50626
|
// src/commands/testimonials/get.ts
|
|
49556
|
-
import { defineCommand as
|
|
50627
|
+
import { defineCommand as defineCommand205 } from "citty";
|
|
49557
50628
|
registerSchema({
|
|
49558
50629
|
command: "testimonials.get",
|
|
49559
50630
|
description: "Get a single testimonial by ID",
|
|
@@ -49561,7 +50632,7 @@ registerSchema({
|
|
|
49561
50632
|
id: { type: "string", description: "Testimonial ID", required: true }
|
|
49562
50633
|
}
|
|
49563
50634
|
});
|
|
49564
|
-
var getCommand6 =
|
|
50635
|
+
var getCommand6 = defineCommand205({
|
|
49565
50636
|
meta: { name: "get", description: "Get a single testimonial by ID. Example: baker testimonials get j571abc123" },
|
|
49566
50637
|
args: {
|
|
49567
50638
|
id: { type: "positional", description: "Testimonial ID", required: false },
|
|
@@ -49598,7 +50669,7 @@ var getCommand6 = defineCommand203({
|
|
|
49598
50669
|
});
|
|
49599
50670
|
|
|
49600
50671
|
// src/commands/testimonials/list.ts
|
|
49601
|
-
import { defineCommand as
|
|
50672
|
+
import { defineCommand as defineCommand206 } from "citty";
|
|
49602
50673
|
|
|
49603
50674
|
// src/commands/testimonials/emptyCorpusHints.ts
|
|
49604
50675
|
function resolveEmptyReason({
|
|
@@ -49733,7 +50804,7 @@ function buildListParams(args) {
|
|
|
49733
50804
|
}
|
|
49734
50805
|
return params;
|
|
49735
50806
|
}
|
|
49736
|
-
var listCommand18 =
|
|
50807
|
+
var listCommand18 = defineCommand206({
|
|
49737
50808
|
meta: {
|
|
49738
50809
|
name: "list",
|
|
49739
50810
|
description: "List testimonials with optional filters. Example: baker testimonials list --source google --sentiment positive"
|
|
@@ -49785,7 +50856,7 @@ var listCommand18 = defineCommand204({
|
|
|
49785
50856
|
});
|
|
49786
50857
|
|
|
49787
50858
|
// src/commands/testimonials/search.ts
|
|
49788
|
-
import { defineCommand as
|
|
50859
|
+
import { defineCommand as defineCommand207 } from "citty";
|
|
49789
50860
|
var FILTER_FLAGS2 = ["source", "rating-min", "rating-max", "status", "sentiment", "language", "tags"];
|
|
49790
50861
|
function languageBiasHint(results, requestedLanguage) {
|
|
49791
50862
|
if (requestedLanguage) {
|
|
@@ -49864,7 +50935,7 @@ function buildSearchRequest(query, args) {
|
|
|
49864
50935
|
}
|
|
49865
50936
|
return body;
|
|
49866
50937
|
}
|
|
49867
|
-
var searchCommand3 =
|
|
50938
|
+
var searchCommand3 = defineCommand207({
|
|
49868
50939
|
meta: {
|
|
49869
50940
|
name: "search",
|
|
49870
50941
|
description: "Semantic search testimonials by text query. Uses hybrid BM25 + vector + reranking. Example: baker testimonials search 'great service' --rating-min 4"
|
|
@@ -49928,7 +50999,7 @@ var searchCommand3 = defineCommand205({
|
|
|
49928
50999
|
var tagsCommand5 = makeTagsCommand("testimonials", "testimonial", "/api/testimonials/tags");
|
|
49929
51000
|
|
|
49930
51001
|
// src/commands/testimonials/index.ts
|
|
49931
|
-
var testimonialsCommand =
|
|
51002
|
+
var testimonialsCommand = defineCommand208({
|
|
49932
51003
|
meta: {
|
|
49933
51004
|
name: "testimonials",
|
|
49934
51005
|
description: `Find and browse testimonials in Baker. Subcommands: search, get, list, tags.
|
|
@@ -49950,10 +51021,10 @@ Full guide: __tooling__/docs/tools/baker/testimonials.md`
|
|
|
49950
51021
|
});
|
|
49951
51022
|
|
|
49952
51023
|
// src/commands/videos/index.ts
|
|
49953
|
-
import { defineCommand as
|
|
51024
|
+
import { defineCommand as defineCommand215 } from "citty";
|
|
49954
51025
|
|
|
49955
51026
|
// src/commands/videos/delete.ts
|
|
49956
|
-
import { defineCommand as
|
|
51027
|
+
import { defineCommand as defineCommand209 } from "citty";
|
|
49957
51028
|
registerSchema({
|
|
49958
51029
|
command: "videos.delete",
|
|
49959
51030
|
description: "Delete a video by ID",
|
|
@@ -49967,7 +51038,7 @@ registerSchema({
|
|
|
49967
51038
|
}
|
|
49968
51039
|
}
|
|
49969
51040
|
});
|
|
49970
|
-
var deleteCommand4 =
|
|
51041
|
+
var deleteCommand4 = defineCommand209({
|
|
49971
51042
|
meta: {
|
|
49972
51043
|
name: "delete",
|
|
49973
51044
|
description: "Delete a video by ID. Use --dry-run to preview. Example: baker videos delete j571abc123 --dry-run"
|
|
@@ -50008,7 +51079,7 @@ var deleteCommand4 = defineCommand207({
|
|
|
50008
51079
|
});
|
|
50009
51080
|
|
|
50010
51081
|
// src/commands/videos/get.ts
|
|
50011
|
-
import { defineCommand as
|
|
51082
|
+
import { defineCommand as defineCommand210 } from "citty";
|
|
50012
51083
|
registerSchema({
|
|
50013
51084
|
command: "videos.get",
|
|
50014
51085
|
description: "Get a single video by ID",
|
|
@@ -50016,7 +51087,7 @@ registerSchema({
|
|
|
50016
51087
|
id: { type: "string", description: "Video ID", required: true }
|
|
50017
51088
|
}
|
|
50018
51089
|
});
|
|
50019
|
-
var getCommand7 =
|
|
51090
|
+
var getCommand7 = defineCommand210({
|
|
50020
51091
|
meta: { name: "get", description: "Get a single video by ID. Example: baker videos get j571abc123" },
|
|
50021
51092
|
args: {
|
|
50022
51093
|
id: { type: "positional", description: "Video ID", required: false },
|
|
@@ -50053,7 +51124,7 @@ var getCommand7 = defineCommand208({
|
|
|
50053
51124
|
});
|
|
50054
51125
|
|
|
50055
51126
|
// src/commands/videos/group.ts
|
|
50056
|
-
import { defineCommand as
|
|
51127
|
+
import { defineCommand as defineCommand211 } from "citty";
|
|
50057
51128
|
registerSchema({
|
|
50058
51129
|
command: "videos.group",
|
|
50059
51130
|
description: "List every clip and image that arrived in the same set as this video (carousel slides, one page)",
|
|
@@ -50062,7 +51133,7 @@ registerSchema({
|
|
|
50062
51133
|
"group-key": { type: "string", description: "The set key directly, when you already have it", required: false }
|
|
50063
51134
|
}
|
|
50064
51135
|
});
|
|
50065
|
-
var groupCommand2 =
|
|
51136
|
+
var groupCommand2 = defineCommand211({
|
|
50066
51137
|
meta: {
|
|
50067
51138
|
name: "group",
|
|
50068
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>"
|
|
@@ -50082,10 +51153,10 @@ var groupCommand2 = defineCommand209({
|
|
|
50082
51153
|
});
|
|
50083
51154
|
|
|
50084
51155
|
// src/commands/videos/ingest.ts
|
|
50085
|
-
import { mkdtemp as mkdtemp2, rm as rm7, stat as
|
|
51156
|
+
import { mkdtemp as mkdtemp2, rm as rm7, stat as stat8 } from "fs/promises";
|
|
50086
51157
|
import { tmpdir as tmpdir3 } from "os";
|
|
50087
|
-
import
|
|
50088
|
-
import { defineCommand as
|
|
51158
|
+
import path42 from "path";
|
|
51159
|
+
import { defineCommand as defineCommand212 } from "citty";
|
|
50089
51160
|
|
|
50090
51161
|
// src/lib/streamUpload.ts
|
|
50091
51162
|
import { createHash as createHash2 } from "crypto";
|
|
@@ -50249,7 +51320,7 @@ registerSchema({
|
|
|
50249
51320
|
"dry-run": { type: "boolean", description: "Preview the operation without executing", required: false }
|
|
50250
51321
|
}
|
|
50251
51322
|
});
|
|
50252
|
-
var ingestCommand2 =
|
|
51323
|
+
var ingestCommand2 = defineCommand212({
|
|
50253
51324
|
meta: {
|
|
50254
51325
|
name: "ingest",
|
|
50255
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"
|
|
@@ -50365,35 +51436,35 @@ async function ingestByRoute(args, direct, country) {
|
|
|
50365
51436
|
function reportYtDlpFailure(err) {
|
|
50366
51437
|
const detail = `${err.stderrTail} ${err.message}`;
|
|
50367
51438
|
if (isProxyFailure(ytDlpBlockSignal(detail))) {
|
|
50368
|
-
|
|
51439
|
+
fail9(
|
|
50369
51440
|
"Couldn't download that video: our connection to the internet was refused.",
|
|
50370
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."
|
|
50371
51442
|
);
|
|
50372
51443
|
}
|
|
50373
51444
|
if (isDrmProtected(detail)) {
|
|
50374
|
-
|
|
51445
|
+
fail9(
|
|
50375
51446
|
"That video is copy-protected, so it can't be added to the library.",
|
|
50376
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>`."
|
|
50377
51448
|
);
|
|
50378
51449
|
}
|
|
50379
51450
|
if (isVimeoAuthFailure(detail)) {
|
|
50380
|
-
|
|
51451
|
+
fail9(
|
|
50381
51452
|
"Vimeo wouldn't hand over that video \u2014 Baker isn't signed in to Vimeo.",
|
|
50382
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>`."
|
|
50383
51454
|
);
|
|
50384
51455
|
}
|
|
50385
51456
|
if (isRegionBlocked(detail)) {
|
|
50386
|
-
|
|
51457
|
+
fail9(
|
|
50387
51458
|
`That video isn't available in the region we're fetching from. ${err.stderrTail || err.message}`,
|
|
50388
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."
|
|
50389
51460
|
);
|
|
50390
51461
|
}
|
|
50391
|
-
|
|
51462
|
+
fail9(
|
|
50392
51463
|
`Couldn't download that video. ${err.stderrTail || err.message}`,
|
|
50393
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>`."
|
|
50394
51465
|
);
|
|
50395
51466
|
}
|
|
50396
|
-
function
|
|
51467
|
+
function fail9(message, fix, code = "INGEST_FAILED") {
|
|
50397
51468
|
writeJson({ ok: false, error: { code, message, fix } });
|
|
50398
51469
|
process.exit(1);
|
|
50399
51470
|
}
|
|
@@ -50405,13 +51476,13 @@ function reportIngestFailure(err) {
|
|
|
50405
51476
|
if (err instanceof YtDlpError) reportYtDlpFailure(err);
|
|
50406
51477
|
const detail = err instanceof Error ? err.message : String(err);
|
|
50407
51478
|
if (detail.includes("timed out after")) {
|
|
50408
|
-
|
|
51479
|
+
fail9(
|
|
50409
51480
|
"That video took too long to download and was stopped part-way.",
|
|
50410
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>`.",
|
|
50411
51482
|
"INTERNAL_ERROR"
|
|
50412
51483
|
);
|
|
50413
51484
|
}
|
|
50414
|
-
|
|
51485
|
+
fail9(
|
|
50415
51486
|
`Couldn't add that video. ${detail}`,
|
|
50416
51487
|
"This is a fault on Baker's side, not a problem with the link. Report it.",
|
|
50417
51488
|
"INTERNAL_ERROR"
|
|
@@ -50435,7 +51506,7 @@ function ingestUrl(args) {
|
|
|
50435
51506
|
}
|
|
50436
51507
|
async function downloadThenIngest(args, country) {
|
|
50437
51508
|
const vimeoCookie = captureVimeoCookie();
|
|
50438
|
-
const workDir = await mkdtemp2(
|
|
51509
|
+
const workDir = await mkdtemp2(path42.join(tmpdir3(), "videos-ingest-"));
|
|
50439
51510
|
try {
|
|
50440
51511
|
const probe = await probeYtDlp({ url: args.url, country, vimeoCookie, cookieDir: workDir });
|
|
50441
51512
|
if (isAudioOnly(probe.info)) {
|
|
@@ -50466,7 +51537,7 @@ async function downloadThenIngest(args, country) {
|
|
|
50466
51537
|
// we allow to fetch it cannot drift apart.
|
|
50467
51538
|
timeoutMs: downloadTimeoutMs(durationSeconds(probe.info))
|
|
50468
51539
|
});
|
|
50469
|
-
const stats = await
|
|
51540
|
+
const stats = await stat8(filePath);
|
|
50470
51541
|
if (stats.size > MAX_VIDEO_INGEST_BYTES) {
|
|
50471
51542
|
throw new ApiError(
|
|
50472
51543
|
"VALIDATION_ERROR",
|
|
@@ -50511,7 +51582,7 @@ async function uploadToAssetStore(filePath, sizeBytes) {
|
|
|
50511
51582
|
}
|
|
50512
51583
|
|
|
50513
51584
|
// src/commands/videos/search.ts
|
|
50514
|
-
import { defineCommand as
|
|
51585
|
+
import { defineCommand as defineCommand213 } from "citty";
|
|
50515
51586
|
registerSchema({
|
|
50516
51587
|
command: "videos.search",
|
|
50517
51588
|
description: "Search videos by text query. Only returns ready videos.",
|
|
@@ -50521,7 +51592,7 @@ registerSchema({
|
|
|
50521
51592
|
tags: { type: "string", description: "Comma-separated tags to filter by", required: false }
|
|
50522
51593
|
}
|
|
50523
51594
|
});
|
|
50524
|
-
var searchCommand4 =
|
|
51595
|
+
var searchCommand4 = defineCommand213({
|
|
50525
51596
|
meta: {
|
|
50526
51597
|
name: "search",
|
|
50527
51598
|
description: "Semantic search videos by text query. Uses hybrid BM25 + vector + reranking. Example: baker videos search 'product demo' --tags tutorial"
|
|
@@ -50571,9 +51642,9 @@ var searchCommand4 = defineCommand211({
|
|
|
50571
51642
|
var tagsCommand6 = makeTagsCommand("videos", "video", "/api/videos/tags");
|
|
50572
51643
|
|
|
50573
51644
|
// src/commands/videos/upload.ts
|
|
50574
|
-
import { readFile as
|
|
51645
|
+
import { readFile as readFile27, stat as stat9 } from "fs/promises";
|
|
50575
51646
|
import { basename as basename3, extname as extname4 } from "path";
|
|
50576
|
-
import { defineCommand as
|
|
51647
|
+
import { defineCommand as defineCommand214 } from "citty";
|
|
50577
51648
|
var MIME_MAP = {
|
|
50578
51649
|
".mp4": "video/mp4",
|
|
50579
51650
|
".mov": "video/quicktime",
|
|
@@ -50615,7 +51686,7 @@ function detectContentType(filePath) {
|
|
|
50615
51686
|
function isRemoteUrl3(value) {
|
|
50616
51687
|
return /^https?:\/\//i.test(value);
|
|
50617
51688
|
}
|
|
50618
|
-
var uploadCommand2 =
|
|
51689
|
+
var uploadCommand2 = defineCommand214({
|
|
50619
51690
|
meta: {
|
|
50620
51691
|
name: "upload",
|
|
50621
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"
|
|
@@ -50655,7 +51726,7 @@ var uploadCommand2 = defineCommand212({
|
|
|
50655
51726
|
const originalFilename = basename3(filePath);
|
|
50656
51727
|
const descriptionContext = args.context;
|
|
50657
51728
|
if (args["dry-run"]) {
|
|
50658
|
-
const fileStats = await
|
|
51729
|
+
const fileStats = await stat9(filePath);
|
|
50659
51730
|
writeJson({
|
|
50660
51731
|
ok: true,
|
|
50661
51732
|
dryRun: true,
|
|
@@ -50668,7 +51739,7 @@ var uploadCommand2 = defineCommand212({
|
|
|
50668
51739
|
originalFilename,
|
|
50669
51740
|
descriptionContext
|
|
50670
51741
|
});
|
|
50671
|
-
const fileBuffer = await
|
|
51742
|
+
const fileBuffer = await readFile27(filePath);
|
|
50672
51743
|
const uploadResponse = await fetch(uploadUrl, {
|
|
50673
51744
|
method: "PUT",
|
|
50674
51745
|
headers: { "Content-Type": contentType },
|
|
@@ -50699,7 +51770,7 @@ var uploadCommand2 = defineCommand212({
|
|
|
50699
51770
|
});
|
|
50700
51771
|
|
|
50701
51772
|
// src/commands/videos/index.ts
|
|
50702
|
-
var videosCommand =
|
|
51773
|
+
var videosCommand = defineCommand215({
|
|
50703
51774
|
meta: {
|
|
50704
51775
|
name: "videos",
|
|
50705
51776
|
description: `Find and manage videos in Baker. Subcommands: search, get, upload, ingest, delete, tags.
|
|
@@ -50726,10 +51797,10 @@ Full guide: __tooling__/docs/tools/baker/videos.md`
|
|
|
50726
51797
|
});
|
|
50727
51798
|
|
|
50728
51799
|
// src/commands/winning-ads/index.ts
|
|
50729
|
-
import { defineCommand as
|
|
51800
|
+
import { defineCommand as defineCommand228 } from "citty";
|
|
50730
51801
|
|
|
50731
51802
|
// src/commands/winning-ads/advertisers.ts
|
|
50732
|
-
import { defineCommand as
|
|
51803
|
+
import { defineCommand as defineCommand216 } from "citty";
|
|
50733
51804
|
|
|
50734
51805
|
// src/commands/winning-ads/shared.ts
|
|
50735
51806
|
function splitList2(value) {
|
|
@@ -50782,7 +51853,7 @@ function advertiserNormalizer(record, full) {
|
|
|
50782
51853
|
last_synced_at: record.last_synced_at ?? null
|
|
50783
51854
|
};
|
|
50784
51855
|
}
|
|
50785
|
-
var advertisersCommand2 =
|
|
51856
|
+
var advertisersCommand2 = defineCommand216({
|
|
50786
51857
|
meta: {
|
|
50787
51858
|
name: "advertisers",
|
|
50788
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'
|
|
@@ -50840,7 +51911,7 @@ var advertisersCommand2 = defineCommand214({
|
|
|
50840
51911
|
});
|
|
50841
51912
|
|
|
50842
51913
|
// src/commands/winning-ads/brief.ts
|
|
50843
|
-
import { defineCommand as
|
|
51914
|
+
import { defineCommand as defineCommand217 } from "citty";
|
|
50844
51915
|
registerSchema({
|
|
50845
51916
|
command: "winning-ads.brief",
|
|
50846
51917
|
description: "Generate a creative brief grounded in strategically-similar winning ads. Optionally describe the target creative with --dna (JSON) and steer with --notes.",
|
|
@@ -50886,7 +51957,7 @@ function parseDna(raw) {
|
|
|
50886
51957
|
}
|
|
50887
51958
|
return parsed;
|
|
50888
51959
|
}
|
|
50889
|
-
var briefCommand =
|
|
51960
|
+
var briefCommand = defineCommand217({
|
|
50890
51961
|
meta: {
|
|
50891
51962
|
name: "brief",
|
|
50892
51963
|
description: `Generate a creative brief from winning references. Example: baker winning-ads brief --dna '{"angle":"cost savings"}' --notes "B2B, LinkedIn video" --k 8`
|
|
@@ -50922,7 +51993,7 @@ var briefCommand = defineCommand215({
|
|
|
50922
51993
|
});
|
|
50923
51994
|
|
|
50924
51995
|
// src/commands/winning-ads/content.ts
|
|
50925
|
-
import { defineCommand as
|
|
51996
|
+
import { defineCommand as defineCommand218 } from "citty";
|
|
50926
51997
|
registerSchema({
|
|
50927
51998
|
command: "winning-ads.content",
|
|
50928
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.",
|
|
@@ -50935,7 +52006,7 @@ registerSchema({
|
|
|
50935
52006
|
}
|
|
50936
52007
|
}
|
|
50937
52008
|
});
|
|
50938
|
-
var contentCommand =
|
|
52009
|
+
var contentCommand = defineCommand218({
|
|
50939
52010
|
meta: {
|
|
50940
52011
|
name: "content",
|
|
50941
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"
|
|
@@ -50984,7 +52055,7 @@ var contentCommand = defineCommand216({
|
|
|
50984
52055
|
});
|
|
50985
52056
|
|
|
50986
52057
|
// src/commands/winning-ads/feed.ts
|
|
50987
|
-
import { defineCommand as
|
|
52058
|
+
import { defineCommand as defineCommand219 } from "citty";
|
|
50988
52059
|
function buildFeedParams(input) {
|
|
50989
52060
|
const params = {};
|
|
50990
52061
|
const advertiser = splitList2(input.advertiser);
|
|
@@ -51036,7 +52107,7 @@ registerSchema({
|
|
|
51036
52107
|
format: { type: "string", description: "Comma-separated formats to include (e.g. static,video)", required: false }
|
|
51037
52108
|
}
|
|
51038
52109
|
});
|
|
51039
|
-
var feedCommand =
|
|
52110
|
+
var feedCommand = defineCommand219({
|
|
51040
52111
|
meta: {
|
|
51041
52112
|
name: "feed",
|
|
51042
52113
|
description: "Winners across every brand you follow (browse, then trim per advertiser). Example: baker winning-ads feed --per-advertiser 5 --output md"
|
|
@@ -51121,7 +52192,7 @@ var feedCommand = defineCommand217({
|
|
|
51121
52192
|
});
|
|
51122
52193
|
|
|
51123
52194
|
// src/commands/winning-ads/follow.ts
|
|
51124
|
-
import { defineCommand as
|
|
52195
|
+
import { defineCommand as defineCommand220 } from "citty";
|
|
51125
52196
|
var PLATFORMS = ["meta", "linkedin"];
|
|
51126
52197
|
registerSchema({
|
|
51127
52198
|
command: "winning-ads.follow",
|
|
@@ -51136,7 +52207,7 @@ registerSchema({
|
|
|
51136
52207
|
label: { type: "string", description: "Optional display label (defaults to the resolved name)", required: false }
|
|
51137
52208
|
}
|
|
51138
52209
|
});
|
|
51139
|
-
var followCommand =
|
|
52210
|
+
var followCommand = defineCommand220({
|
|
51140
52211
|
meta: {
|
|
51141
52212
|
name: "follow",
|
|
51142
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'
|
|
@@ -51183,7 +52254,7 @@ var followCommand = defineCommand218({
|
|
|
51183
52254
|
});
|
|
51184
52255
|
|
|
51185
52256
|
// src/commands/winning-ads/follow-competitors.ts
|
|
51186
|
-
import { defineCommand as
|
|
52257
|
+
import { defineCommand as defineCommand221 } from "citty";
|
|
51187
52258
|
var PLATFORMS2 = ["meta", "linkedin"];
|
|
51188
52259
|
var BATCH_TIMEOUT_MS = 3e5;
|
|
51189
52260
|
function buildFollowBatchBody(input) {
|
|
@@ -51216,7 +52287,7 @@ registerSchema({
|
|
|
51216
52287
|
}
|
|
51217
52288
|
}
|
|
51218
52289
|
});
|
|
51219
|
-
var followCompetitorsCommand =
|
|
52290
|
+
var followCompetitorsCommand = defineCommand221({
|
|
51220
52291
|
meta: {
|
|
51221
52292
|
name: "follow-competitors",
|
|
51222
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"'
|
|
@@ -51291,7 +52362,7 @@ var followCompetitorsCommand = defineCommand219({
|
|
|
51291
52362
|
});
|
|
51292
52363
|
|
|
51293
52364
|
// src/commands/winning-ads/following.ts
|
|
51294
|
-
import { defineCommand as
|
|
52365
|
+
import { defineCommand as defineCommand222 } from "citty";
|
|
51295
52366
|
registerSchema({
|
|
51296
52367
|
command: "winning-ads.following",
|
|
51297
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.",
|
|
@@ -51345,7 +52416,7 @@ function followingNormalizer(record, full) {
|
|
|
51345
52416
|
platforms: Array.isArray(record.platforms) ? record.platforms : []
|
|
51346
52417
|
};
|
|
51347
52418
|
}
|
|
51348
|
-
var followingCommand =
|
|
52419
|
+
var followingCommand = defineCommand222({
|
|
51349
52420
|
meta: {
|
|
51350
52421
|
name: "following",
|
|
51351
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"
|
|
@@ -51381,7 +52452,7 @@ var followingCommand = defineCommand220({
|
|
|
51381
52452
|
});
|
|
51382
52453
|
|
|
51383
52454
|
// src/commands/winning-ads/patterns.ts
|
|
51384
|
-
import { defineCommand as
|
|
52455
|
+
import { defineCommand as defineCommand223 } from "citty";
|
|
51385
52456
|
registerSchema({
|
|
51386
52457
|
command: "winning-ads.patterns",
|
|
51387
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.",
|
|
@@ -51420,7 +52491,7 @@ function discriminatorRow(record) {
|
|
|
51420
52491
|
top_values_duds: Array.isArray(record.top_values_b) ? record.top_values_b.join(", ") : ""
|
|
51421
52492
|
};
|
|
51422
52493
|
}
|
|
51423
|
-
var patternsCommand =
|
|
52494
|
+
var patternsCommand = defineCommand223({
|
|
51424
52495
|
meta: {
|
|
51425
52496
|
name: "patterns",
|
|
51426
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"
|
|
@@ -51476,7 +52547,7 @@ var patternsCommand = defineCommand221({
|
|
|
51476
52547
|
});
|
|
51477
52548
|
|
|
51478
52549
|
// src/commands/winning-ads/search.ts
|
|
51479
|
-
import { defineCommand as
|
|
52550
|
+
import { defineCommand as defineCommand224 } from "citty";
|
|
51480
52551
|
registerSchema({
|
|
51481
52552
|
command: "winning-ads.search",
|
|
51482
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.",
|
|
@@ -51584,7 +52655,7 @@ function buildSearchBody2(args) {
|
|
|
51584
52655
|
}
|
|
51585
52656
|
return body;
|
|
51586
52657
|
}
|
|
51587
|
-
var searchCommand5 =
|
|
52658
|
+
var searchCommand5 = defineCommand224({
|
|
51588
52659
|
meta: {
|
|
51589
52660
|
name: "search",
|
|
51590
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"
|
|
@@ -51699,7 +52770,7 @@ var searchCommand5 = defineCommand222({
|
|
|
51699
52770
|
});
|
|
51700
52771
|
|
|
51701
52772
|
// src/commands/winning-ads/seeds.ts
|
|
51702
|
-
import { defineCommand as
|
|
52773
|
+
import { defineCommand as defineCommand225 } from "citty";
|
|
51703
52774
|
function leanRow(r) {
|
|
51704
52775
|
return {
|
|
51705
52776
|
key: r.key,
|
|
@@ -51727,7 +52798,7 @@ function makeSeedCommand(opts) {
|
|
|
51727
52798
|
limit: { type: "number", description: "Max keys 1-100 (default 20)", required: false, default: 20 }
|
|
51728
52799
|
}
|
|
51729
52800
|
});
|
|
51730
|
-
return
|
|
52801
|
+
return defineCommand225({
|
|
51731
52802
|
meta: { name: opts.name, description: opts.description },
|
|
51732
52803
|
args: {
|
|
51733
52804
|
platform: { type: "string", description: "Single platform to segment on", required: false },
|
|
@@ -51776,7 +52847,7 @@ var formatsCommand = makeSeedCommand({
|
|
|
51776
52847
|
});
|
|
51777
52848
|
|
|
51778
52849
|
// src/commands/winning-ads/unfollow.ts
|
|
51779
|
-
import { defineCommand as
|
|
52850
|
+
import { defineCommand as defineCommand226 } from "citty";
|
|
51780
52851
|
registerSchema({
|
|
51781
52852
|
command: "winning-ads.unfollow",
|
|
51782
52853
|
description: "Stop following a brand \u2014 removes it from your ad-dna library by advertiser id.",
|
|
@@ -51784,7 +52855,7 @@ registerSchema({
|
|
|
51784
52855
|
advertiser: { type: "string", description: "Advertiser id to unfollow", required: true }
|
|
51785
52856
|
}
|
|
51786
52857
|
});
|
|
51787
|
-
var unfollowCommand =
|
|
52858
|
+
var unfollowCommand = defineCommand226({
|
|
51788
52859
|
meta: {
|
|
51789
52860
|
name: "unfollow",
|
|
51790
52861
|
description: "Stop following a brand by advertiser id. Example: baker winning-ads unfollow adv_123"
|
|
@@ -51805,7 +52876,7 @@ var unfollowCommand = defineCommand224({
|
|
|
51805
52876
|
});
|
|
51806
52877
|
|
|
51807
52878
|
// src/commands/winning-ads/winners.ts
|
|
51808
|
-
import { defineCommand as
|
|
52879
|
+
import { defineCommand as defineCommand227 } from "citty";
|
|
51809
52880
|
registerSchema({
|
|
51810
52881
|
command: "winning-ads.winners",
|
|
51811
52882
|
description: "Top winning ads for one advertiser id (from `advertisers` or `following`). Returns lean winner cards; add --full for DNA + longevity.",
|
|
@@ -51815,7 +52886,7 @@ registerSchema({
|
|
|
51815
52886
|
platform: { type: "string", description: "Filter to a single platform: meta|linkedin", required: false }
|
|
51816
52887
|
}
|
|
51817
52888
|
});
|
|
51818
|
-
var winnersCommand =
|
|
52889
|
+
var winnersCommand = defineCommand227({
|
|
51819
52890
|
meta: {
|
|
51820
52891
|
name: "winners",
|
|
51821
52892
|
description: "Top winning ads for a specific advertiser id. Example: baker winning-ads winners adv_123 --top 15 --output md"
|
|
@@ -51865,7 +52936,7 @@ var winnersCommand = defineCommand225({
|
|
|
51865
52936
|
});
|
|
51866
52937
|
|
|
51867
52938
|
// src/commands/winning-ads/index.ts
|
|
51868
|
-
var winningAdsCommand =
|
|
52939
|
+
var winningAdsCommand = defineCommand228({
|
|
51869
52940
|
meta: {
|
|
51870
52941
|
name: "winning-ads",
|
|
51871
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.
|
|
@@ -52055,7 +53126,7 @@ function unknownFlagEnvelope(unknown, commandPath, suggestion) {
|
|
|
52055
53126
|
};
|
|
52056
53127
|
}
|
|
52057
53128
|
function commandPathOf(root, argv) {
|
|
52058
|
-
const
|
|
53129
|
+
const path43 = [];
|
|
52059
53130
|
let command = root;
|
|
52060
53131
|
for (const token of argv) {
|
|
52061
53132
|
if (token === "--" || token.startsWith("-")) {
|
|
@@ -52066,10 +53137,10 @@ function commandPathOf(root, argv) {
|
|
|
52066
53137
|
if (next === void 0 || typeof next !== "object") {
|
|
52067
53138
|
break;
|
|
52068
53139
|
}
|
|
52069
|
-
|
|
53140
|
+
path43.push(token);
|
|
52070
53141
|
command = next;
|
|
52071
53142
|
}
|
|
52072
|
-
return
|
|
53143
|
+
return path43.join(" ");
|
|
52073
53144
|
}
|
|
52074
53145
|
function refuseUnknownFlags(root, argv) {
|
|
52075
53146
|
const unknown = findUnknownFlags(root, argv);
|
|
@@ -52103,7 +53174,7 @@ function getCliVersion() {
|
|
|
52103
53174
|
}
|
|
52104
53175
|
|
|
52105
53176
|
// src/cli.ts
|
|
52106
|
-
var main =
|
|
53177
|
+
var main = defineCommand229({
|
|
52107
53178
|
meta: {
|
|
52108
53179
|
name: "baker",
|
|
52109
53180
|
version: getCliVersion(),
|
|
@@ -52122,6 +53193,7 @@ Introspection: Run 'baker schema <command>' to inspect argument schemas.`
|
|
|
52122
53193
|
ads: adsCommand2,
|
|
52123
53194
|
brand: brandCommand,
|
|
52124
53195
|
analytics: analyticsCommand2,
|
|
53196
|
+
experiment: experimentCommand,
|
|
52125
53197
|
ga4: ga4Command,
|
|
52126
53198
|
gsc: gscCommand,
|
|
52127
53199
|
research: researchCommand,
|