@koda-sl/baker-cli 0.248.0 → 0.249.0-dev.94b6a6a7e
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-SJPRTSGY.js → chunk-HBDGUVUH.js} +4 -4
- package/dist/chunk-HBDGUVUH.js.map +1 -0
- package/dist/cli.js +1617 -514
- package/dist/cli.js.map +1 -1
- package/dist/engine/index.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-SJPRTSGY.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-HBDGUVUH.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 = {
|
|
@@ -3936,10 +3936,35 @@ var capabilitySurfaceReportSchema = z9.object({
|
|
|
3936
3936
|
/** Where to look next: `baker ads google`, `baker ga4`, … */
|
|
3937
3937
|
commands: z9.string()
|
|
3938
3938
|
});
|
|
3939
|
+
var capabilityToolSchema = z9.object({
|
|
3940
|
+
/** `microsoft_clarity`, `hubspot`, or the name the user gave a custom server. */
|
|
3941
|
+
key: z9.string(),
|
|
3942
|
+
label: z9.string(),
|
|
3943
|
+
/** `mcp` — tools in the agent's own list. `platform` — signed in, read through the CLI. */
|
|
3944
|
+
kind: z9.enum(["mcp", "platform"]),
|
|
3945
|
+
/** How to actually reach it, in one phrase the agent can act on. */
|
|
3946
|
+
reach: z9.string(),
|
|
3947
|
+
/**
|
|
3948
|
+
* Whether it can be used right now.
|
|
3949
|
+
*
|
|
3950
|
+
* A connection that exists and is expired is worse than one that is absent:
|
|
3951
|
+
* it reads as available and fails at the moment it is needed, which on an
|
|
3952
|
+
* unattended run is a report with a hole in it and no explanation.
|
|
3953
|
+
*/
|
|
3954
|
+
ready: z9.boolean(),
|
|
3955
|
+
/** Why it is not ready, or what it is for. Absent when neither needs saying. */
|
|
3956
|
+
note: z9.string().optional()
|
|
3957
|
+
});
|
|
3939
3958
|
var capabilitiesResponseSchema = z9.object({
|
|
3940
3959
|
ok: z9.literal(true),
|
|
3941
3960
|
data: z9.object({
|
|
3942
|
-
surfaces: z9.array(capabilitySurfaceReportSchema)
|
|
3961
|
+
surfaces: z9.array(capabilitySurfaceReportSchema),
|
|
3962
|
+
/**
|
|
3963
|
+
* Everything else this company can reach. Shared connections only —
|
|
3964
|
+
* a teammate's private per-user connection belongs to whoever started the
|
|
3965
|
+
* session and is visible in the agent's own tool list, not here.
|
|
3966
|
+
*/
|
|
3967
|
+
tools: z9.array(capabilityToolSchema)
|
|
3943
3968
|
})
|
|
3944
3969
|
});
|
|
3945
3970
|
|
|
@@ -3986,6 +4011,8 @@ var chatChangeTypeSchema = z10.enum([
|
|
|
3986
4011
|
// what the edge captures and one held until Publish would not measure the
|
|
3987
4012
|
// campaign it was written for.
|
|
3988
4013
|
"analytics-mapping",
|
|
4014
|
+
// An A/B test between a landing and its alternative. Staged, not immediate.
|
|
4015
|
+
"experiment",
|
|
3989
4016
|
"briefs"
|
|
3990
4017
|
]);
|
|
3991
4018
|
var chatChangeActionSchema = z10.enum(["created", "updated", "deleted"]);
|
|
@@ -9780,11 +9807,11 @@ function rawTextEntries(value) {
|
|
|
9780
9807
|
const values = Array.isArray(value) ? value : typeof value === "string" ? [value] : [];
|
|
9781
9808
|
return values.filter((v) => typeof v === "string").flatMap((v) => v.split(",")).map((v) => v.trim()).filter(Boolean);
|
|
9782
9809
|
}
|
|
9783
|
-
function rawFileEntries(
|
|
9784
|
-
if (typeof
|
|
9810
|
+
function rawFileEntries(path41) {
|
|
9811
|
+
if (typeof path41 !== "string" || path41.length === 0) {
|
|
9785
9812
|
return [];
|
|
9786
9813
|
}
|
|
9787
|
-
return readFileSync2(
|
|
9814
|
+
return readFileSync2(path41, "utf8").split(/\r?\n/).map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#"));
|
|
9788
9815
|
}
|
|
9789
9816
|
function keywordEntries(args) {
|
|
9790
9817
|
const defaultMatch = typeof args["match-type"] === "string" ? args["match-type"].toUpperCase() : void 0;
|
|
@@ -9807,19 +9834,19 @@ function keywordEntries(args) {
|
|
|
9807
9834
|
}
|
|
9808
9835
|
return entries;
|
|
9809
9836
|
}
|
|
9810
|
-
function loadJsonFileArg(
|
|
9811
|
-
if (typeof
|
|
9837
|
+
function loadJsonFileArg(path41) {
|
|
9838
|
+
if (typeof path41 !== "string" || path41.length === 0) {
|
|
9812
9839
|
return {};
|
|
9813
9840
|
}
|
|
9814
9841
|
try {
|
|
9815
|
-
const parsed = JSON.parse(readFileSync2(
|
|
9842
|
+
const parsed = JSON.parse(readFileSync2(path41, "utf8"));
|
|
9816
9843
|
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
9817
|
-
failWriteValidation(`${
|
|
9844
|
+
failWriteValidation(`${path41} must contain a JSON object`);
|
|
9818
9845
|
}
|
|
9819
9846
|
return parsed;
|
|
9820
9847
|
} catch (err) {
|
|
9821
9848
|
if (err instanceof SyntaxError) {
|
|
9822
|
-
failWriteValidation(`${
|
|
9849
|
+
failWriteValidation(`${path41} is not valid JSON: ${err.message}`);
|
|
9823
9850
|
}
|
|
9824
9851
|
throw err;
|
|
9825
9852
|
}
|
|
@@ -9949,10 +9976,10 @@ async function stageUpdate(kind, customerId, target, payload, hints) {
|
|
|
9949
9976
|
async function stageTarget(kind, customerId, target, hints) {
|
|
9950
9977
|
await stageGoogleOp({ kind, customerId, target }, hints);
|
|
9951
9978
|
}
|
|
9952
|
-
async function draftAction(
|
|
9979
|
+
async function draftAction(path41, body, chat) {
|
|
9953
9980
|
try {
|
|
9954
9981
|
const chatId = resolveChatId(chat);
|
|
9955
|
-
const response = await apiPost(
|
|
9982
|
+
const response = await apiPost(path41, { chatId, ...body });
|
|
9956
9983
|
writeJsonEnvelope(response);
|
|
9957
9984
|
} catch (err) {
|
|
9958
9985
|
handleGoogleError(err);
|
|
@@ -15166,19 +15193,19 @@ function failWriteValidation2(message) {
|
|
|
15166
15193
|
writeJsonEnvelope({ ok: false, error: { code: "VALIDATION_ERROR", message } });
|
|
15167
15194
|
process.exit(1);
|
|
15168
15195
|
}
|
|
15169
|
-
function loadJsonFileArg2(
|
|
15170
|
-
if (typeof
|
|
15196
|
+
function loadJsonFileArg2(path41) {
|
|
15197
|
+
if (typeof path41 !== "string" || path41.length === 0) {
|
|
15171
15198
|
return {};
|
|
15172
15199
|
}
|
|
15173
15200
|
try {
|
|
15174
|
-
const parsed = JSON.parse(readFileSync4(
|
|
15201
|
+
const parsed = JSON.parse(readFileSync4(path41, "utf8"));
|
|
15175
15202
|
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
15176
|
-
failWriteValidation2(`${
|
|
15203
|
+
failWriteValidation2(`${path41} must contain a JSON object`);
|
|
15177
15204
|
}
|
|
15178
15205
|
return parsed;
|
|
15179
15206
|
} catch (err) {
|
|
15180
15207
|
if (err instanceof SyntaxError) {
|
|
15181
|
-
failWriteValidation2(`${
|
|
15208
|
+
failWriteValidation2(`${path41} is not valid JSON: ${err.message}`);
|
|
15182
15209
|
}
|
|
15183
15210
|
throw err;
|
|
15184
15211
|
}
|
|
@@ -15263,15 +15290,15 @@ function parseLocaleFlag(value) {
|
|
|
15263
15290
|
}
|
|
15264
15291
|
return { language: match[1], country: match[2].toUpperCase() };
|
|
15265
15292
|
}
|
|
15266
|
-
function loadTargetingFileArg(
|
|
15267
|
-
if (typeof
|
|
15293
|
+
function loadTargetingFileArg(path41) {
|
|
15294
|
+
if (typeof path41 !== "string" || path41.length === 0) {
|
|
15268
15295
|
return void 0;
|
|
15269
15296
|
}
|
|
15270
|
-
const parsed = loadJsonFileArg2(
|
|
15297
|
+
const parsed = loadJsonFileArg2(path41);
|
|
15271
15298
|
const criteria = parsed.targetingCriteria ?? parsed;
|
|
15272
15299
|
if (!criteria.include) {
|
|
15273
15300
|
failWriteValidation2(
|
|
15274
|
-
`${
|
|
15301
|
+
`${path41} must contain targeting criteria with an "include" block (see baker schema ads.linkedin.campaigns.create)`
|
|
15275
15302
|
);
|
|
15276
15303
|
}
|
|
15277
15304
|
return criteria;
|
|
@@ -15306,14 +15333,14 @@ function parseCsvLine(line) {
|
|
|
15306
15333
|
cells.push(current);
|
|
15307
15334
|
return cells.map((cell2) => cell2.trim());
|
|
15308
15335
|
}
|
|
15309
|
-
function parseListFileArg(
|
|
15310
|
-
if (typeof
|
|
15336
|
+
function parseListFileArg(path41, maxRows) {
|
|
15337
|
+
if (typeof path41 !== "string" || path41.length === 0) {
|
|
15311
15338
|
return void 0;
|
|
15312
15339
|
}
|
|
15313
|
-
const raw = readFileSync4(
|
|
15340
|
+
const raw = readFileSync4(path41, "utf8");
|
|
15314
15341
|
const lines = raw.split(/\r?\n/).filter((line) => line.trim().length > 0);
|
|
15315
15342
|
if (lines.length < 2) {
|
|
15316
|
-
failWriteValidation2(`${
|
|
15343
|
+
failWriteValidation2(`${path41} needs a header row and at least one data row`);
|
|
15317
15344
|
}
|
|
15318
15345
|
const columns = parseCsvLine(lines[0]).map((column) => column.trim());
|
|
15319
15346
|
const rows = [];
|
|
@@ -15332,7 +15359,7 @@ function parseListFileArg(path38, maxRows) {
|
|
|
15332
15359
|
}
|
|
15333
15360
|
}
|
|
15334
15361
|
if (rows.length > maxRows) {
|
|
15335
|
-
failWriteValidation2(`${
|
|
15362
|
+
failWriteValidation2(`${path41} has ${rows.length} rows \u2014 the inline limit is ${maxRows}. Split the list.`);
|
|
15336
15363
|
}
|
|
15337
15364
|
return { columns, rows };
|
|
15338
15365
|
}
|
|
@@ -15428,11 +15455,11 @@ function readPositionals(args) {
|
|
|
15428
15455
|
function splitIdList(raw) {
|
|
15429
15456
|
return raw.split(",").map((id) => id.trim()).filter(Boolean);
|
|
15430
15457
|
}
|
|
15431
|
-
function idsFileEntries(
|
|
15432
|
-
if (typeof
|
|
15458
|
+
function idsFileEntries(path41) {
|
|
15459
|
+
if (typeof path41 !== "string" || path41.length === 0) {
|
|
15433
15460
|
return [];
|
|
15434
15461
|
}
|
|
15435
|
-
return readFileSync4(
|
|
15462
|
+
return readFileSync4(path41, "utf8").split(/\r?\n/).map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#")).flatMap(splitIdList);
|
|
15436
15463
|
}
|
|
15437
15464
|
function requireTargets(args, entity) {
|
|
15438
15465
|
const positionals = readPositionals(args);
|
|
@@ -18053,9 +18080,9 @@ function compactRow(row) {
|
|
|
18053
18080
|
...destination.postUrn ? { postUrn: destination.postUrn } : {}
|
|
18054
18081
|
};
|
|
18055
18082
|
}
|
|
18056
|
-
function readPath(row,
|
|
18083
|
+
function readPath(row, path41) {
|
|
18057
18084
|
let current = row;
|
|
18058
|
-
for (const segment of
|
|
18085
|
+
for (const segment of path41.split(".")) {
|
|
18059
18086
|
const record = asRecord2(current);
|
|
18060
18087
|
if (!record) return void 0;
|
|
18061
18088
|
current = record[segment];
|
|
@@ -18065,10 +18092,10 @@ function readPath(row, path38) {
|
|
|
18065
18092
|
function projectFields(rows, paths) {
|
|
18066
18093
|
return rows.map((row) => {
|
|
18067
18094
|
const projected = {};
|
|
18068
|
-
for (const
|
|
18069
|
-
const value = readPath(row,
|
|
18095
|
+
for (const path41 of paths) {
|
|
18096
|
+
const value = readPath(row, path41);
|
|
18070
18097
|
if (value !== void 0) {
|
|
18071
|
-
projected[
|
|
18098
|
+
projected[path41] = value;
|
|
18072
18099
|
}
|
|
18073
18100
|
}
|
|
18074
18101
|
return projected;
|
|
@@ -19368,11 +19395,11 @@ var updateStatusSchema = z25.enum(UPDATE_STATUSES);
|
|
|
19368
19395
|
function currencyMinimums2(currencyCode) {
|
|
19369
19396
|
return CURRENCY_MINIMUMS2[currencyCode] ?? DEFAULT_CURRENCY_MINIMUM2;
|
|
19370
19397
|
}
|
|
19371
|
-
function validateDailyBudgetFloor(money, ctx,
|
|
19398
|
+
function validateDailyBudgetFloor(money, ctx, path41) {
|
|
19372
19399
|
if (money?.currencyCode) {
|
|
19373
19400
|
const min = currencyMinimums2(money.currencyCode).dailyBudgetMin;
|
|
19374
19401
|
if (Number(money.amount) < min) {
|
|
19375
|
-
ctx.addIssue({ code: "custom", path:
|
|
19402
|
+
ctx.addIssue({ code: "custom", path: path41, message: `below the ${min} ${money.currencyCode} daily minimum` });
|
|
19376
19403
|
}
|
|
19377
19404
|
}
|
|
19378
19405
|
}
|
|
@@ -20041,19 +20068,19 @@ function failWriteValidation3(message) {
|
|
|
20041
20068
|
writeJsonEnvelope({ ok: false, error: { code: "VALIDATION_ERROR", message } });
|
|
20042
20069
|
process.exit(1);
|
|
20043
20070
|
}
|
|
20044
|
-
function loadJsonFileArg3(
|
|
20045
|
-
if (typeof
|
|
20071
|
+
function loadJsonFileArg3(path41) {
|
|
20072
|
+
if (typeof path41 !== "string" || path41.length === 0) {
|
|
20046
20073
|
return {};
|
|
20047
20074
|
}
|
|
20048
20075
|
try {
|
|
20049
|
-
const parsed = JSON.parse(readFileSync8(
|
|
20076
|
+
const parsed = JSON.parse(readFileSync8(path41, "utf8"));
|
|
20050
20077
|
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
20051
|
-
failWriteValidation3(`${
|
|
20078
|
+
failWriteValidation3(`${path41} must contain a JSON object`);
|
|
20052
20079
|
}
|
|
20053
20080
|
return parsed;
|
|
20054
20081
|
} catch (err) {
|
|
20055
20082
|
if (err instanceof SyntaxError) {
|
|
20056
|
-
failWriteValidation3(`${
|
|
20083
|
+
failWriteValidation3(`${path41} is not valid JSON: ${err.message}`);
|
|
20057
20084
|
}
|
|
20058
20085
|
throw err;
|
|
20059
20086
|
}
|
|
@@ -24654,11 +24681,11 @@ function unwrap(response) {
|
|
|
24654
24681
|
}
|
|
24655
24682
|
return response.data;
|
|
24656
24683
|
}
|
|
24657
|
-
async function readAvatars(
|
|
24658
|
-
return unwrap(await apiGet(
|
|
24684
|
+
async function readAvatars(path41, params) {
|
|
24685
|
+
return unwrap(await apiGet(path41, params));
|
|
24659
24686
|
}
|
|
24660
|
-
async function writeAvatars(
|
|
24661
|
-
return unwrap(await apiPost(
|
|
24687
|
+
async function writeAvatars(path41, body) {
|
|
24688
|
+
return unwrap(await apiPost(path41, body));
|
|
24662
24689
|
}
|
|
24663
24690
|
|
|
24664
24691
|
// src/commands/avatars/create.ts
|
|
@@ -25492,12 +25519,12 @@ function missingFontFiles(urls, available) {
|
|
|
25492
25519
|
function planFontAdoption(sources, families) {
|
|
25493
25520
|
const wanted = new Map(families.map((family) => [normalizeFamily(family), family]));
|
|
25494
25521
|
const byFamily = /* @__PURE__ */ new Map();
|
|
25495
|
-
for (const { path:
|
|
25496
|
-
const dir = posix.dirname(
|
|
25522
|
+
for (const { path: path41, source } of sources) {
|
|
25523
|
+
const dir = posix.dirname(path41);
|
|
25497
25524
|
for (const face of declaredFontFaces(source)) {
|
|
25498
25525
|
if (!wanted.has(face.family)) continue;
|
|
25499
25526
|
const perFile = byFamily.get(face.family) ?? /* @__PURE__ */ new Map();
|
|
25500
|
-
perFile.set(
|
|
25527
|
+
perFile.set(path41, [...perFile.get(path41) ?? [], rebaseFontFaceSrc(face.block, dir)]);
|
|
25501
25528
|
byFamily.set(face.family, perFile);
|
|
25502
25529
|
}
|
|
25503
25530
|
}
|
|
@@ -26521,10 +26548,10 @@ import { defineCommand as defineCommand100 } from "citty";
|
|
|
26521
26548
|
import { existsSync as existsSync3, realpathSync } from "fs";
|
|
26522
26549
|
import { writeFile as writeFile2 } from "fs/promises";
|
|
26523
26550
|
import path5 from "path";
|
|
26524
|
-
function findWorkspaceRoot(startDir,
|
|
26551
|
+
function findWorkspaceRoot(startDir, exists2 = existsSync3, maxDepth = 12) {
|
|
26525
26552
|
let dir = path5.resolve(startDir);
|
|
26526
26553
|
for (let i = 0; i < maxDepth; i++) {
|
|
26527
|
-
if (
|
|
26554
|
+
if (exists2(path5.join(dir, "package.json"))) return dir;
|
|
26528
26555
|
const parent = path5.dirname(dir);
|
|
26529
26556
|
if (parent === dir) break;
|
|
26530
26557
|
dir = parent;
|
|
@@ -30471,10 +30498,10 @@ function runDirsToPrune(entries, keep, currentRunId) {
|
|
|
30471
30498
|
return runs.slice(0, Math.max(0, runs.length - keep));
|
|
30472
30499
|
}
|
|
30473
30500
|
async function pruneOldRuns(outputsDir, keep, currentRunId, log) {
|
|
30474
|
-
const { readdir:
|
|
30501
|
+
const { readdir: readdir11 } = await import("fs/promises");
|
|
30475
30502
|
let entries;
|
|
30476
30503
|
try {
|
|
30477
|
-
entries = await
|
|
30504
|
+
entries = await readdir11(outputsDir);
|
|
30478
30505
|
} catch {
|
|
30479
30506
|
return;
|
|
30480
30507
|
}
|
|
@@ -32266,12 +32293,12 @@ async function detectSceneCutsPySceneDetect(filePath, opts = {}) {
|
|
|
32266
32293
|
// src/commands/canvas/composition-path.ts
|
|
32267
32294
|
import { existsSync as existsSync4 } from "fs";
|
|
32268
32295
|
import path21 from "path";
|
|
32269
|
-
function resolveShippedCanvasDir(name, startDir,
|
|
32296
|
+
function resolveShippedCanvasDir(name, startDir, exists2 = existsSync4, maxDepth = 8) {
|
|
32270
32297
|
const rel = path21.join("canvas", name);
|
|
32271
32298
|
let dir = startDir;
|
|
32272
32299
|
for (let i = 0; i < maxDepth; i++) {
|
|
32273
32300
|
const candidate = path21.join(dir, rel);
|
|
32274
|
-
if (
|
|
32301
|
+
if (exists2(path21.join(candidate, "meta.json"))) return candidate;
|
|
32275
32302
|
const parent = path21.dirname(dir);
|
|
32276
32303
|
if (parent === dir) break;
|
|
32277
32304
|
dir = parent;
|
|
@@ -33097,7 +33124,7 @@ registerSchema({
|
|
|
33097
33124
|
}
|
|
33098
33125
|
}
|
|
33099
33126
|
});
|
|
33100
|
-
function capabilityHints(surfaces) {
|
|
33127
|
+
function capabilityHints(surfaces, tools = []) {
|
|
33101
33128
|
const hints = [];
|
|
33102
33129
|
const immediate = surfaces.filter((surface) => surface.writeMode === "applies-at-publish");
|
|
33103
33130
|
if (immediate.length > 0) {
|
|
@@ -33120,6 +33147,18 @@ function capabilityHints(surfaces) {
|
|
|
33120
33147
|
"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."
|
|
33121
33148
|
);
|
|
33122
33149
|
}
|
|
33150
|
+
const usable = tools.filter((tool) => tool.ready);
|
|
33151
|
+
if (usable.length > 0) {
|
|
33152
|
+
hints.push(
|
|
33153
|
+
`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.`
|
|
33154
|
+
);
|
|
33155
|
+
}
|
|
33156
|
+
const broken = tools.filter((tool) => !tool.ready);
|
|
33157
|
+
if (broken.length > 0) {
|
|
33158
|
+
hints.push(
|
|
33159
|
+
`CONNECTED BUT UNUSABLE: ${broken.map((tool) => `${tool.label} \u2014 ${tool.note ?? "not reachable"}`).join(" ")} Tell the user rather than planning around it silently.`
|
|
33160
|
+
);
|
|
33161
|
+
}
|
|
33123
33162
|
return hints;
|
|
33124
33163
|
}
|
|
33125
33164
|
var runCapabilities = defineCommand108({
|
|
@@ -33129,6 +33168,8 @@ var runCapabilities = defineCommand108({
|
|
|
33129
33168
|
|
|
33130
33169
|
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.
|
|
33131
33170
|
|
|
33171
|
+
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".
|
|
33172
|
+
|
|
33132
33173
|
Examples:
|
|
33133
33174
|
baker capabilities
|
|
33134
33175
|
baker capabilities google-ads
|
|
@@ -33164,7 +33205,7 @@ Full guide: __tooling__/docs/tools/baker/capabilities.md`
|
|
|
33164
33205
|
body.full = true;
|
|
33165
33206
|
}
|
|
33166
33207
|
const response = await apiPost("/api/capabilities", body);
|
|
33167
|
-
const hints = capabilityHints(response.data.surfaces);
|
|
33208
|
+
const hints = capabilityHints(response.data.surfaces, response.data.tools);
|
|
33168
33209
|
writeJson({
|
|
33169
33210
|
...response,
|
|
33170
33211
|
meta: { count: response.data.surfaces.length },
|
|
@@ -33634,12 +33675,793 @@ Full guide: __tooling__/docs/tools/baker/creatives.md`
|
|
|
33634
33675
|
}
|
|
33635
33676
|
});
|
|
33636
33677
|
|
|
33678
|
+
// src/commands/experiment/index.ts
|
|
33679
|
+
import { defineCommand as defineCommand112 } from "citty";
|
|
33680
|
+
|
|
33681
|
+
// src/commands/landing/import-graph.ts
|
|
33682
|
+
import { readdir as readdir8, readFile as readFile20, stat as stat4 } from "fs/promises";
|
|
33683
|
+
import path26 from "path";
|
|
33684
|
+
var SOURCE_EXT = /\.(astro|ts|tsx|js|jsx|mjs|cjs|vue|svelte)$/;
|
|
33685
|
+
var RESOLVE_CANDIDATES = [
|
|
33686
|
+
"",
|
|
33687
|
+
".astro",
|
|
33688
|
+
".ts",
|
|
33689
|
+
".tsx",
|
|
33690
|
+
".js",
|
|
33691
|
+
".jsx",
|
|
33692
|
+
".mjs",
|
|
33693
|
+
".cjs",
|
|
33694
|
+
"/index.astro",
|
|
33695
|
+
"/index.ts",
|
|
33696
|
+
"/index.js"
|
|
33697
|
+
];
|
|
33698
|
+
var IMPORT_RE = /(?<![A-Za-z0-9_$])(?:import|export)\s*(?:[\s\S]*?\bfrom\s*)?["']([^"']+)["']/g;
|
|
33699
|
+
var DYNAMIC_IMPORT_RE = /\bimport\s*\(\s*["']([^"']+)["']\s*\)/g;
|
|
33700
|
+
async function isFile(p) {
|
|
33701
|
+
try {
|
|
33702
|
+
return (await stat4(p)).isFile();
|
|
33703
|
+
} catch {
|
|
33704
|
+
return false;
|
|
33705
|
+
}
|
|
33706
|
+
}
|
|
33707
|
+
async function walkFiles(dir) {
|
|
33708
|
+
let entries;
|
|
33709
|
+
try {
|
|
33710
|
+
entries = await readdir8(dir, { withFileTypes: true });
|
|
33711
|
+
} catch {
|
|
33712
|
+
return [];
|
|
33713
|
+
}
|
|
33714
|
+
const out = [];
|
|
33715
|
+
for (const entry of entries) {
|
|
33716
|
+
const full = path26.join(dir, entry.name);
|
|
33717
|
+
if (entry.isDirectory()) {
|
|
33718
|
+
if (entry.name === "node_modules" || entry.name === "dist") continue;
|
|
33719
|
+
out.push(...await walkFiles(full));
|
|
33720
|
+
} else if (SOURCE_EXT.test(entry.name)) {
|
|
33721
|
+
out.push(full);
|
|
33722
|
+
}
|
|
33723
|
+
}
|
|
33724
|
+
return out;
|
|
33725
|
+
}
|
|
33726
|
+
async function resolveSpecifier(specifier, fromFile, root) {
|
|
33727
|
+
const bare = specifier.split("?")[0] ?? "";
|
|
33728
|
+
if (bare.includes("*")) return null;
|
|
33729
|
+
let base;
|
|
33730
|
+
if (bare.startsWith("/")) base = path26.join(root, bare.slice(1));
|
|
33731
|
+
else if (bare.startsWith(".")) base = path26.resolve(path26.dirname(fromFile), bare);
|
|
33732
|
+
else return null;
|
|
33733
|
+
for (const suffix of RESOLVE_CANDIDATES) {
|
|
33734
|
+
const candidate = base + suffix;
|
|
33735
|
+
if (SOURCE_EXT.test(candidate) && await isFile(candidate)) return candidate;
|
|
33736
|
+
}
|
|
33737
|
+
return null;
|
|
33738
|
+
}
|
|
33739
|
+
async function importedFiles(text2, file, root) {
|
|
33740
|
+
const out = [];
|
|
33741
|
+
for (const re2 of [IMPORT_RE, DYNAMIC_IMPORT_RE]) {
|
|
33742
|
+
for (const match of text2.matchAll(re2)) {
|
|
33743
|
+
const spec = match[1];
|
|
33744
|
+
if (spec === void 0) continue;
|
|
33745
|
+
const target = await resolveSpecifier(spec, file, root);
|
|
33746
|
+
if (target !== null) out.push(target);
|
|
33747
|
+
}
|
|
33748
|
+
}
|
|
33749
|
+
return out;
|
|
33750
|
+
}
|
|
33751
|
+
async function landingGraphFiles(root, slug) {
|
|
33752
|
+
const seen = /* @__PURE__ */ new Set();
|
|
33753
|
+
const queue = await walkFiles(path26.join(root, "src/pages", slug));
|
|
33754
|
+
while (queue.length > 0) {
|
|
33755
|
+
const file = queue.pop();
|
|
33756
|
+
if (file === void 0 || seen.has(file)) continue;
|
|
33757
|
+
let text2;
|
|
33758
|
+
try {
|
|
33759
|
+
text2 = await readFile20(file, "utf8");
|
|
33760
|
+
} catch {
|
|
33761
|
+
continue;
|
|
33762
|
+
}
|
|
33763
|
+
seen.add(file);
|
|
33764
|
+
for (const target of await importedFiles(text2, file, root)) {
|
|
33765
|
+
if (!seen.has(target)) queue.push(target);
|
|
33766
|
+
}
|
|
33767
|
+
}
|
|
33768
|
+
return new Set([...seen].map((abs) => path26.relative(root, abs).split(path26.sep).join("/")));
|
|
33769
|
+
}
|
|
33770
|
+
|
|
33771
|
+
// src/commands/experiment/composition.ts
|
|
33772
|
+
function sectionsUnder(files, slug) {
|
|
33773
|
+
const prefix = `src/pages/${slug}/`;
|
|
33774
|
+
return files.filter((file) => file.startsWith(prefix) && !file.slice(prefix.length).startsWith("index.")).sort();
|
|
33775
|
+
}
|
|
33776
|
+
function compositionOf(controlSlug, variantSlug, variantGraph) {
|
|
33777
|
+
return {
|
|
33778
|
+
forked: sectionsUnder(variantGraph, variantSlug),
|
|
33779
|
+
shared: sectionsUnder(variantGraph, controlSlug)
|
|
33780
|
+
};
|
|
33781
|
+
}
|
|
33782
|
+
async function readComposition(root, controlSlug, variantSlug) {
|
|
33783
|
+
try {
|
|
33784
|
+
const graph = await landingGraphFiles(root, variantSlug);
|
|
33785
|
+
return compositionOf(controlSlug, variantSlug, [...graph]);
|
|
33786
|
+
} catch {
|
|
33787
|
+
return void 0;
|
|
33788
|
+
}
|
|
33789
|
+
}
|
|
33790
|
+
|
|
33791
|
+
// src/commands/experiment/goal.ts
|
|
33792
|
+
function parseEventGoal(argument) {
|
|
33793
|
+
const [name, filter] = argument.split("/");
|
|
33794
|
+
if (!name) return { error: "Name the event: --goal event:request_demo" };
|
|
33795
|
+
if (!filter) return { kind: "custom_event", name };
|
|
33796
|
+
const separator = filter.indexOf("=");
|
|
33797
|
+
if (separator === -1) {
|
|
33798
|
+
return { error: `Write the property as key=value: --goal event:${name}/section=pricing` };
|
|
33799
|
+
}
|
|
33800
|
+
return { kind: "custom_event", name, property: filter.slice(0, separator), value: filter.slice(separator + 1) };
|
|
33801
|
+
}
|
|
33802
|
+
function parseGoal(raw) {
|
|
33803
|
+
if (raw === void 0 || raw === "") return void 0;
|
|
33804
|
+
const separator = raw.indexOf(":");
|
|
33805
|
+
const kind = separator === -1 ? raw : raw.slice(0, separator);
|
|
33806
|
+
const argument = separator === -1 ? "" : raw.slice(separator + 1);
|
|
33807
|
+
if (kind === "leads") return argument ? { kind: "conversion", flowSlug: argument } : { kind: "conversion" };
|
|
33808
|
+
if (kind === "click") return argument ? { kind: "outbound_click", targetHost: argument } : { kind: "outbound_click" };
|
|
33809
|
+
if (kind === "event") return parseEventGoal(argument);
|
|
33810
|
+
return {
|
|
33811
|
+
error: `Unknown goal "${raw}". Use leads, leads:<form>, event:<name>, event:<name>/<key>=<value>, click, or click:<host>`
|
|
33812
|
+
};
|
|
33813
|
+
}
|
|
33814
|
+
|
|
33815
|
+
// src/commands/experiment/hints.ts
|
|
33816
|
+
var RUNNING_HINT = {
|
|
33817
|
+
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)}`,
|
|
33818
|
+
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.`,
|
|
33819
|
+
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.`,
|
|
33820
|
+
stopped_early_harmful: (row) => `${row.experimentId} is doing damage. Finish it now \u2014 traffic goes back to the original page.`,
|
|
33821
|
+
// Unreachable in practice — only `finish --abandon` produces it, and that
|
|
33822
|
+
// leaves the test finished. The Record is exhaustive by type so a verdict
|
|
33823
|
+
// added upstream fails to compile here rather than printing nothing.
|
|
33824
|
+
abandoned: (row) => `${row.experimentId} was stopped before it concluded and found nothing.`,
|
|
33825
|
+
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.`
|
|
33826
|
+
};
|
|
33827
|
+
function describeWait(row) {
|
|
33828
|
+
if (row.willNotConclude) {
|
|
33829
|
+
return "This page's traffic will not settle it at any point, so abandon it with `--abandon` and test a bigger change.";
|
|
33830
|
+
}
|
|
33831
|
+
if (row.earliestDecisionAt === null) {
|
|
33832
|
+
return "This page has produced no visitors yet, so there is nothing to project a finish date from.";
|
|
33833
|
+
}
|
|
33834
|
+
return `Earliest it could say anything: ${new Date(row.earliestDecisionAt).toISOString().slice(0, 10)}.`;
|
|
33835
|
+
}
|
|
33836
|
+
function buildStatusHints(experiments) {
|
|
33837
|
+
const hints = experiments.filter((row) => row.status === "running").map((row) => RUNNING_HINT[row.verdict](row));
|
|
33838
|
+
if (experiments.length === 0) {
|
|
33839
|
+
hints.push(
|
|
33840
|
+
"No tests yet. `baker experiment plan --landing <slug> --variant <slug>` says whether a page has enough traffic for one before you build the alternative."
|
|
33841
|
+
);
|
|
33842
|
+
}
|
|
33843
|
+
return hints;
|
|
33844
|
+
}
|
|
33845
|
+
function asLift(value) {
|
|
33846
|
+
return `+${Math.round(value * 100)}%`;
|
|
33847
|
+
}
|
|
33848
|
+
function windowHint(days, detectable) {
|
|
33849
|
+
if (days === void 0) return null;
|
|
33850
|
+
if (detectable === null || detectable === void 0) {
|
|
33851
|
+
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.`;
|
|
33852
|
+
}
|
|
33853
|
+
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.`;
|
|
33854
|
+
}
|
|
33855
|
+
function buildPlanHints(plan, days) {
|
|
33856
|
+
const window2 = windowHint(days, plan.detectableLiftInDays);
|
|
33857
|
+
if (!plan.canRun) {
|
|
33858
|
+
return [
|
|
33859
|
+
plan.reason ?? "This test cannot run on this page.",
|
|
33860
|
+
...window2 ? [window2] : [],
|
|
33861
|
+
"A test that cannot conclude is worse than no test \u2014 it still produces a number, and somebody acts on it."
|
|
33862
|
+
];
|
|
33863
|
+
}
|
|
33864
|
+
const hints = [
|
|
33865
|
+
`This test measures ${plan.goalLabel}. That is fixed when it starts and cannot be changed later.`,
|
|
33866
|
+
// Fires at the exact moment the hypothesis gets invented, whether or not
|
|
33867
|
+
// the agent read the family doc. `plan` says a question CAN be settled here
|
|
33868
|
+
// and says nothing about which question is worth asking — and a well-run
|
|
33869
|
+
// test of a bad idea costs three weeks and returns `no_difference`.
|
|
33870
|
+
"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.",
|
|
33871
|
+
// The base always works, so a missing integration is never a reason to
|
|
33872
|
+
// stop. Naming what it would have shown is worth more to the client than
|
|
33873
|
+
// either a silent gap or a run that waited.
|
|
33874
|
+
"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."
|
|
33875
|
+
];
|
|
33876
|
+
if (plan.estimatedDays !== null && plan.estimatedDays > 60) {
|
|
33877
|
+
hints.push(
|
|
33878
|
+
`At this page's traffic it would take about ${plan.estimatedDays} days. Consider testing a bigger change, which needs less traffic to detect.`
|
|
33879
|
+
);
|
|
33880
|
+
}
|
|
33881
|
+
if (window2) hints.push(window2);
|
|
33882
|
+
return hints;
|
|
33883
|
+
}
|
|
33884
|
+
|
|
33885
|
+
// ../api/src/experiments/composition.ts
|
|
33886
|
+
import { z as z29 } from "zod";
|
|
33887
|
+
var experimentCompositionSchema = z29.object({
|
|
33888
|
+
/** Components the variant owns its own copy of — the thing under test. */
|
|
33889
|
+
forked: z29.array(z29.string().max(300)).max(50),
|
|
33890
|
+
/** Components both pages render from one file, so an edit reaches both arms. */
|
|
33891
|
+
shared: z29.array(z29.string().max(300)).max(200)
|
|
33892
|
+
});
|
|
33893
|
+
|
|
33894
|
+
// ../api/src/experiments/goal.ts
|
|
33895
|
+
import { z as z30 } from "zod";
|
|
33896
|
+
var eventNameSchema = z30.string().min(1).max(120);
|
|
33897
|
+
var experimentGoalSchema = z30.discriminatedUnion("kind", [
|
|
33898
|
+
z30.object({
|
|
33899
|
+
kind: z30.literal("conversion"),
|
|
33900
|
+
/**
|
|
33901
|
+
* Narrow to one Form, by the slug that is its identity.
|
|
33902
|
+
*
|
|
33903
|
+
* Absent means every marked conversion on the page counts, which is the
|
|
33904
|
+
* right default for a landing with one Form and the wrong one for a page
|
|
33905
|
+
* carrying both a newsletter signup and a demo request.
|
|
33906
|
+
*/
|
|
33907
|
+
flowSlug: z30.string().max(120).optional()
|
|
33908
|
+
}),
|
|
33909
|
+
z30.object({
|
|
33910
|
+
kind: z30.literal("custom_event"),
|
|
33911
|
+
name: eventNameSchema,
|
|
33912
|
+
/**
|
|
33913
|
+
* Narrow to one value of one of the event's own properties.
|
|
33914
|
+
*
|
|
33915
|
+
* `section_view` on its own is every section; `section_view` where
|
|
33916
|
+
* `section = pricing` is the one that matters. Without this a page would
|
|
33917
|
+
* have to encode the answer into the event name to be testable against it,
|
|
33918
|
+
* which is precisely what the properties map exists to stop.
|
|
33919
|
+
*/
|
|
33920
|
+
property: z30.string().max(64).optional(),
|
|
33921
|
+
value: z30.string().max(255).optional()
|
|
33922
|
+
}),
|
|
33923
|
+
z30.object({
|
|
33924
|
+
kind: z30.literal("outbound_click"),
|
|
33925
|
+
/**
|
|
33926
|
+
* The destination host, as the row records it. Absent means any exit.
|
|
33927
|
+
*
|
|
33928
|
+
* A host rather than a full URL because that is what `event_name` carries
|
|
33929
|
+
* on an `outbound_click` row — `target_url` has the rest, and matching on
|
|
33930
|
+
* it would make the goal depend on query parameters a campaign rewrites.
|
|
33931
|
+
*/
|
|
33932
|
+
targetHost: z30.string().max(255).optional()
|
|
33933
|
+
})
|
|
33934
|
+
]);
|
|
33935
|
+
|
|
33936
|
+
// ../api/src/experiments/hypothesis.ts
|
|
33937
|
+
import { z as z31 } from "zod";
|
|
33938
|
+
var experimentEvidenceSourceSchema = z31.enum([
|
|
33939
|
+
"baker_analytics",
|
|
33940
|
+
"clarity",
|
|
33941
|
+
"ads",
|
|
33942
|
+
"search_console",
|
|
33943
|
+
"research",
|
|
33944
|
+
"inspiration",
|
|
33945
|
+
"client",
|
|
33946
|
+
"hunch"
|
|
33947
|
+
]);
|
|
33948
|
+
var experimentEvidenceSchema = z31.object({
|
|
33949
|
+
source: experimentEvidenceSourceSchema,
|
|
33950
|
+
note: z31.string().min(1).max(200),
|
|
33951
|
+
/**
|
|
33952
|
+
* Where to go and look. Not a `z.url()` — a report inside Baker is reached by
|
|
33953
|
+
* a relative path, and rejecting those would push every in-product citation
|
|
33954
|
+
* into the note where nothing can link it.
|
|
33955
|
+
*/
|
|
33956
|
+
href: z31.string().max(500).optional()
|
|
33957
|
+
});
|
|
33958
|
+
var experimentHypothesisSchema = z31.object({
|
|
33959
|
+
/**
|
|
33960
|
+
* The observation. The one genuinely new field, and the reason for the rest.
|
|
33961
|
+
*
|
|
33962
|
+
* Without it a lost test teaches nothing, because there is no belief for the
|
|
33963
|
+
* result to be evidence against — which is how a testing programme ends up as
|
|
33964
|
+
* a list of tests instead of a list of things learned.
|
|
33965
|
+
*/
|
|
33966
|
+
because: z31.string().min(1).max(300),
|
|
33967
|
+
/**
|
|
33968
|
+
* The change, in the client's words.
|
|
33969
|
+
*
|
|
33970
|
+
* The machine-checkable truth lives in `plan.composition.forked`; this is what
|
|
33971
|
+
* a person reads. The two are allowed to differ in wording and cannot differ
|
|
33972
|
+
* in fact, because the brief shows both.
|
|
33973
|
+
*/
|
|
33974
|
+
change: z31.string().min(1).max(200),
|
|
33975
|
+
/** Which way the goal is expected to move. See the note at the top of this file. */
|
|
33976
|
+
expect: z31.enum(["increase", "decrease"]).default("increase"),
|
|
33977
|
+
evidence: z31.array(experimentEvidenceSchema).max(5).optional()
|
|
33978
|
+
});
|
|
33979
|
+
var experimentHypothesisOutcomeSchema = z31.enum([
|
|
33980
|
+
"supported",
|
|
33981
|
+
"refuted_opposite",
|
|
33982
|
+
"refuted_no_effect",
|
|
33983
|
+
"unanswered"
|
|
33984
|
+
]);
|
|
33985
|
+
|
|
33986
|
+
// ../api/src/experiments/wire.ts
|
|
33987
|
+
import { z as z32 } from "zod";
|
|
33988
|
+
var slugSchema = z32.string().min(1).max(120).regex(/^[a-z0-9]+(?:-[a-z0-9]+)*$/, "Use a page slug, like `oferta`");
|
|
33989
|
+
var experimentPlanRequestSchema = z32.object({
|
|
33990
|
+
landingSlug: slugSchema,
|
|
33991
|
+
variantSlug: slugSchema,
|
|
33992
|
+
goal: experimentGoalSchema.optional(),
|
|
33993
|
+
/**
|
|
33994
|
+
* The smallest lift worth detecting, relative to the baseline. `0.2` is +20%.
|
|
33995
|
+
*
|
|
33996
|
+
* The single most consequential number a caller supplies, and the one it is
|
|
33997
|
+
* most tempting to set small. A test sized for +5% on a 3% page needs a
|
|
33998
|
+
* quarter of a million visitors per arm; `plan` will say so rather than
|
|
33999
|
+
* running it.
|
|
34000
|
+
*/
|
|
34001
|
+
minDetectableRelativeLift: z32.number().gt(0).max(10).optional(),
|
|
34002
|
+
/**
|
|
34003
|
+
* "I have this many days — what could I even see?"
|
|
34004
|
+
*
|
|
34005
|
+
* Read-only, and deliberately not a setting. It does NOT shorten the test:
|
|
34006
|
+
* the finish line is a number of visitors fixed before the test starts, and a
|
|
34007
|
+
* horizon that can be moved is not a horizon. What it does is answer the
|
|
34008
|
+
* question a refusal leaves hanging — `plan` says a +20% lift needs three
|
|
34009
|
+
* weeks this page will not deliver, and this says what those weeks WOULD
|
|
34010
|
+
* settle, which is a change somebody can actually go and make.
|
|
34011
|
+
*/
|
|
34012
|
+
days: z32.number().int().gt(0).max(365).optional()
|
|
34013
|
+
});
|
|
34014
|
+
var experimentStartRequestSchema = experimentPlanRequestSchema.extend({
|
|
34015
|
+
hypothesis: experimentHypothesisSchema,
|
|
34016
|
+
/**
|
|
34017
|
+
* What the two arms are made of, read off the import graph by the caller at
|
|
34018
|
+
* the moment the test starts.
|
|
34019
|
+
*
|
|
34020
|
+
* Optional on the wire because the backend cannot compute it — only the
|
|
34021
|
+
* Company Workspace has the files — and a workspace that cannot answer must
|
|
34022
|
+
* not be blocked from running a test. The brief degrades to naming the two
|
|
34023
|
+
* pages instead of naming the section under test.
|
|
34024
|
+
*/
|
|
34025
|
+
composition: experimentCompositionSchema.optional()
|
|
34026
|
+
});
|
|
34027
|
+
var experimentFinishRequestSchema = z32.object({
|
|
34028
|
+
experimentId: z32.string().min(1).max(64),
|
|
34029
|
+
/**
|
|
34030
|
+
* Stop the test, keep the page that is live, and record no result.
|
|
34031
|
+
*
|
|
34032
|
+
* The only way a person or an agent can end a test that is still
|
|
34033
|
+
* `keep_running`, and it is deliberately its own field rather than a
|
|
34034
|
+
* `survivor` they get to pick: an abandoned test has no winner, keeps the
|
|
34035
|
+
* control, and is recorded as `abandoned` so it can never be reported later
|
|
34036
|
+
* as a result. Without the separate flag, "stop this" and "the variant won"
|
|
34037
|
+
* would be the same call.
|
|
34038
|
+
*
|
|
34039
|
+
* **It overrides the verdict, and does not defer to it.** The only outcome it
|
|
34040
|
+
* can actually disagree with is a variant win — every other one keeps the
|
|
34041
|
+
* control anyway — so reading it as "apply only when the numbers have nothing
|
|
34042
|
+
* to say" meant that abandoning a won test handed all the traffic to the
|
|
34043
|
+
* variant instead of keeping the original: the opposite of what this field
|
|
34044
|
+
* promises, on live visitors, from a flag whose whole purpose is to be the
|
|
34045
|
+
* conservative choice. The response reports `discardedVerdict` when it did
|
|
34046
|
+
* override one, because throwing away a real finding should be said out loud.
|
|
34047
|
+
*/
|
|
34048
|
+
abandon: z32.boolean().optional()
|
|
34049
|
+
});
|
|
34050
|
+
var experimentStatusRequestSchema = z32.object({
|
|
34051
|
+
experimentId: z32.string().max(64).optional(),
|
|
34052
|
+
full: z32.boolean().optional()
|
|
34053
|
+
});
|
|
34054
|
+
var experimentPlanResponseSchema = z32.object({
|
|
34055
|
+
canRun: z32.boolean(),
|
|
34056
|
+
/** Present when `canRun` is false. Product language, ready to show. */
|
|
34057
|
+
reason: z32.string().optional(),
|
|
34058
|
+
baselineRate: z32.number(),
|
|
34059
|
+
minDetectableRelativeLift: z32.number(),
|
|
34060
|
+
/** `null` when no amount of traffic could settle the hypothesis. */
|
|
34061
|
+
requiredPerVariant: z32.number().nullable(),
|
|
34062
|
+
/** Visitors a day the page has been getting, per arm once split. */
|
|
34063
|
+
dailyVisitorsPerVariant: z32.number(),
|
|
34064
|
+
/** `null` when the page has no traffic to project from. */
|
|
34065
|
+
estimatedDays: z32.number().nullable(),
|
|
34066
|
+
goal: experimentGoalSchema,
|
|
34067
|
+
goalLabel: z32.string(),
|
|
34068
|
+
/**
|
|
34069
|
+
* The smallest lift `days` of this page's traffic could detect, when `days`
|
|
34070
|
+
* was asked for. `null` when it was not, or when no lift at all would be
|
|
34071
|
+
* detectable in that window.
|
|
34072
|
+
*/
|
|
34073
|
+
detectableLiftInDays: z32.number().nullable().optional()
|
|
34074
|
+
});
|
|
34075
|
+
var experimentVerdictSchema = z32.enum([
|
|
34076
|
+
"keep_running",
|
|
34077
|
+
"winner",
|
|
34078
|
+
"no_difference",
|
|
34079
|
+
"invalid",
|
|
34080
|
+
"stopped_early_harmful",
|
|
34081
|
+
/**
|
|
34082
|
+
* Stopped by a person before it concluded. Never produced by the stopping
|
|
34083
|
+
* rule — only by `finish --abandon`.
|
|
34084
|
+
*
|
|
34085
|
+
* Its own verdict rather than folded into `no_difference`, because they are
|
|
34086
|
+
* opposite claims: one says the two versions were measured and found alike,
|
|
34087
|
+
* the other says nobody ever found out. Collapsing them would manufacture the
|
|
34088
|
+
* exact finding this feature exists to refuse.
|
|
34089
|
+
*/
|
|
34090
|
+
"abandoned"
|
|
34091
|
+
]);
|
|
34092
|
+
var experimentArmSchema = z32.object({
|
|
34093
|
+
slug: z32.string(),
|
|
34094
|
+
visitors: z32.number(),
|
|
34095
|
+
conversions: z32.number(),
|
|
34096
|
+
rate: z32.number()
|
|
34097
|
+
});
|
|
34098
|
+
var experimentStatusRowSchema = z32.object({
|
|
34099
|
+
experimentId: z32.string(),
|
|
34100
|
+
landingSlug: z32.string(),
|
|
34101
|
+
variantSlug: z32.string(),
|
|
34102
|
+
hypothesis: experimentHypothesisSchema,
|
|
34103
|
+
/**
|
|
34104
|
+
* The hypothesis as one sentence, built from its parts plus the goal and the
|
|
34105
|
+
* lift on this same row. Rendered backend-side so what a client reads in chat
|
|
34106
|
+
* and what they read on the dashboard can never be two different sentences.
|
|
34107
|
+
*/
|
|
34108
|
+
hypothesisStatement: z32.string(),
|
|
34109
|
+
/**
|
|
34110
|
+
* What the test found out about the belief — derived from `verdict` and
|
|
34111
|
+
* `winner`, never entered.
|
|
34112
|
+
*
|
|
34113
|
+
* Its own field because the verdict and the outcome come apart in exactly the
|
|
34114
|
+
* case worth catching: a `winner` the *control* won is not a win, it is the
|
|
34115
|
+
* change having made things worse.
|
|
34116
|
+
*/
|
|
34117
|
+
hypothesisOutcome: experimentHypothesisOutcomeSchema,
|
|
34118
|
+
/** What the two arms differ by. Absent for a test started before this was recorded. */
|
|
34119
|
+
composition: experimentCompositionSchema.optional(),
|
|
34120
|
+
/**
|
|
34121
|
+
* The finish line this test was sized for. On the row rather than only inside
|
|
34122
|
+
* the plan because the claim is not readable without it — "leads should go up"
|
|
34123
|
+
* is not a hypothesis, "leads should go up by at least 20%" is.
|
|
34124
|
+
*/
|
|
34125
|
+
minDetectableRelativeLift: z32.number(),
|
|
34126
|
+
goalLabel: z32.string(),
|
|
34127
|
+
status: z32.enum(["running", "finished"]),
|
|
34128
|
+
startedAt: z32.number(),
|
|
34129
|
+
finishedAt: z32.number().nullable(),
|
|
34130
|
+
verdict: experimentVerdictSchema,
|
|
34131
|
+
/** Set only on `winner`. Which page to keep. */
|
|
34132
|
+
winner: z32.enum(["control", "variant"]).nullable(),
|
|
34133
|
+
/** Set only on `invalid`. Why the numbers cannot be read. */
|
|
34134
|
+
invalidReason: z32.enum(["sample_ratio_mismatch", "crossover"]).nullable(),
|
|
34135
|
+
/** What a person should be told, in one sentence, in product language. */
|
|
34136
|
+
summary: z32.string(),
|
|
34137
|
+
control: experimentArmSchema,
|
|
34138
|
+
variant: experimentArmSchema,
|
|
34139
|
+
/**
|
|
34140
|
+
* How close this test is to being able to say anything at all, 0 to 1.
|
|
34141
|
+
*
|
|
34142
|
+
* Not progress toward a planned sample — there is no longer one to progress
|
|
34143
|
+
* toward. It is the traffic behind the test as a share of the traffic its own
|
|
34144
|
+
* measured variance says it needs, so it moves when the page gets noisier or
|
|
34145
|
+
* steadier rather than only when visitors arrive.
|
|
34146
|
+
*/
|
|
34147
|
+
precision: z32.number(),
|
|
34148
|
+
/** When the test could first say something. `null` with no traffic to project from. */
|
|
34149
|
+
earliestDecisionAt: z32.number().nullable(),
|
|
34150
|
+
/**
|
|
34151
|
+
* True when no amount of this page's traffic would settle the comparison.
|
|
34152
|
+
* A different thing from a page that is merely slow, and the reason to
|
|
34153
|
+
* abandon a test rather than leave it running.
|
|
34154
|
+
*/
|
|
34155
|
+
willNotConclude: z32.boolean(),
|
|
34156
|
+
/** Printing only. Never a reason to act — `verdict` is. */
|
|
34157
|
+
evidence: z32.object({
|
|
34158
|
+
relativeLift: z32.number(),
|
|
34159
|
+
probabilityVariantBetter: z32.number(),
|
|
34160
|
+
controlInterval: z32.tuple([z32.number(), z32.number()]),
|
|
34161
|
+
variantInterval: z32.tuple([z32.number(), z32.number()]),
|
|
34162
|
+
/** The anytime-valid interval on the relative lift — what the verdict is read from. */
|
|
34163
|
+
liftInterval: z32.tuple([z32.number(), z32.number()])
|
|
34164
|
+
}).optional()
|
|
34165
|
+
});
|
|
34166
|
+
var experimentStatusResponseSchema = z32.object({
|
|
34167
|
+
experiments: z32.array(experimentStatusRowSchema)
|
|
34168
|
+
});
|
|
34169
|
+
|
|
34170
|
+
// src/commands/experiment/hypothesis.ts
|
|
34171
|
+
var SOURCES = experimentEvidenceSourceSchema.options.join(", ");
|
|
34172
|
+
function parseOne(raw) {
|
|
34173
|
+
const separator = raw.indexOf(":");
|
|
34174
|
+
if (separator === -1) {
|
|
34175
|
+
return { error: `Write the evidence as source:note \u2014 for example \`${raw}:what you saw\`. Sources: ${SOURCES}` };
|
|
34176
|
+
}
|
|
34177
|
+
const source = experimentEvidenceSourceSchema.safeParse(raw.slice(0, separator).trim());
|
|
34178
|
+
if (!source.success) {
|
|
34179
|
+
return { error: `\u201C${raw.slice(0, separator).trim()}\u201D is not one of the evidence sources. Use one of: ${SOURCES}` };
|
|
34180
|
+
}
|
|
34181
|
+
const rest = raw.slice(separator + 1).trim();
|
|
34182
|
+
const pipe = rest.lastIndexOf("|");
|
|
34183
|
+
const note = (pipe === -1 ? rest : rest.slice(0, pipe)).trim();
|
|
34184
|
+
const href = pipe === -1 ? void 0 : rest.slice(pipe + 1).trim();
|
|
34185
|
+
if (note === "") {
|
|
34186
|
+
return { error: `Say what ${source.data} showed: --evidence "${source.data}:what you saw"` };
|
|
34187
|
+
}
|
|
34188
|
+
return href ? { source: source.data, note, href } : { source: source.data, note };
|
|
34189
|
+
}
|
|
34190
|
+
function parseEvidence(raw) {
|
|
34191
|
+
if (raw === void 0) return void 0;
|
|
34192
|
+
const entries = (Array.isArray(raw) ? raw : [raw]).filter((entry) => entry !== "");
|
|
34193
|
+
if (entries.length === 0) return void 0;
|
|
34194
|
+
const parsed = [];
|
|
34195
|
+
for (const entry of entries) {
|
|
34196
|
+
const one = parseOne(entry);
|
|
34197
|
+
if ("error" in one) return one;
|
|
34198
|
+
parsed.push(one);
|
|
34199
|
+
}
|
|
34200
|
+
return parsed;
|
|
34201
|
+
}
|
|
34202
|
+
|
|
34203
|
+
// src/commands/experiment/index.ts
|
|
34204
|
+
var GOAL_ARG = {
|
|
34205
|
+
type: "string",
|
|
34206
|
+
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.",
|
|
34207
|
+
required: false
|
|
34208
|
+
};
|
|
34209
|
+
var LIFT_ARG = {
|
|
34210
|
+
type: "string",
|
|
34211
|
+
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.",
|
|
34212
|
+
required: false
|
|
34213
|
+
};
|
|
34214
|
+
var DAYS_ARG = {
|
|
34215
|
+
type: "string",
|
|
34216
|
+
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.",
|
|
34217
|
+
required: false
|
|
34218
|
+
};
|
|
34219
|
+
var BECAUSE_ARG = {
|
|
34220
|
+
type: "string",
|
|
34221
|
+
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.",
|
|
34222
|
+
required: true
|
|
34223
|
+
};
|
|
34224
|
+
var CHANGE_ARG = {
|
|
34225
|
+
type: "string",
|
|
34226
|
+
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.",
|
|
34227
|
+
required: true
|
|
34228
|
+
};
|
|
34229
|
+
var EXPECT_ARG = {
|
|
34230
|
+
type: "string",
|
|
34231
|
+
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.",
|
|
34232
|
+
required: false
|
|
34233
|
+
};
|
|
34234
|
+
var EVIDENCE_ARG = {
|
|
34235
|
+
type: "string",
|
|
34236
|
+
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.",
|
|
34237
|
+
required: false
|
|
34238
|
+
};
|
|
34239
|
+
function fail5(message) {
|
|
34240
|
+
writeJsonEnvelope({ ok: false, error: { code: "VALIDATION_ERROR", message } });
|
|
34241
|
+
process.exit(1);
|
|
34242
|
+
}
|
|
34243
|
+
function reportApiError(error) {
|
|
34244
|
+
if (error instanceof ApiError) {
|
|
34245
|
+
writeJsonEnvelope({ ok: false, error: { code: error.code ?? "ERROR", message: error.message } });
|
|
34246
|
+
process.exit(1);
|
|
34247
|
+
}
|
|
34248
|
+
throw error;
|
|
34249
|
+
}
|
|
34250
|
+
registerSchema({
|
|
34251
|
+
command: "experiment.plan",
|
|
34252
|
+
description: "Whether a page has enough traffic to settle a question, before anything is built",
|
|
34253
|
+
args: {
|
|
34254
|
+
landing: { type: "string", description: "The page under test, by slug", required: true },
|
|
34255
|
+
variant: { type: "string", description: "The alternative page's slug", required: true },
|
|
34256
|
+
goal: GOAL_ARG,
|
|
34257
|
+
lift: LIFT_ARG,
|
|
34258
|
+
days: DAYS_ARG
|
|
34259
|
+
}
|
|
34260
|
+
});
|
|
34261
|
+
var planCommand = defineCommand112({
|
|
34262
|
+
meta: {
|
|
34263
|
+
name: "plan",
|
|
34264
|
+
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."
|
|
34265
|
+
},
|
|
34266
|
+
args: {
|
|
34267
|
+
landing: { type: "string", description: "The page under test, by slug", required: true },
|
|
34268
|
+
variant: { type: "string", description: "The alternative page's slug", required: true },
|
|
34269
|
+
goal: GOAL_ARG,
|
|
34270
|
+
lift: LIFT_ARG,
|
|
34271
|
+
days: DAYS_ARG
|
|
34272
|
+
},
|
|
34273
|
+
run: async ({ args }) => {
|
|
34274
|
+
const goal = parseGoal(args.goal ? String(args.goal) : void 0);
|
|
34275
|
+
if (goal && "error" in goal) fail5(goal.error);
|
|
34276
|
+
try {
|
|
34277
|
+
const response = await apiPost("/api/experiments/plan", {
|
|
34278
|
+
landingSlug: String(args.landing),
|
|
34279
|
+
variantSlug: String(args.variant),
|
|
34280
|
+
...goal ? { goal } : {},
|
|
34281
|
+
...args.lift ? { minDetectableRelativeLift: Number(args.lift) } : {},
|
|
34282
|
+
...args.days ? { days: Number(args.days) } : {}
|
|
34283
|
+
});
|
|
34284
|
+
writeJsonEnvelope({
|
|
34285
|
+
ok: true,
|
|
34286
|
+
data: response.data,
|
|
34287
|
+
hints: buildPlanHints(response.data, args.days ? Number(args.days) : void 0)
|
|
34288
|
+
});
|
|
34289
|
+
} catch (error) {
|
|
34290
|
+
reportApiError(error);
|
|
34291
|
+
}
|
|
34292
|
+
}
|
|
34293
|
+
});
|
|
34294
|
+
registerSchema({
|
|
34295
|
+
command: "experiment.start",
|
|
34296
|
+
description: "Stage an A/B test between a page and its alternative",
|
|
34297
|
+
args: {
|
|
34298
|
+
landing: { type: "string", description: "The page under test, by slug", required: true },
|
|
34299
|
+
variant: { type: "string", description: "The alternative page's slug", required: true },
|
|
34300
|
+
because: BECAUSE_ARG,
|
|
34301
|
+
change: CHANGE_ARG,
|
|
34302
|
+
expect: EXPECT_ARG,
|
|
34303
|
+
evidence: EVIDENCE_ARG,
|
|
34304
|
+
goal: GOAL_ARG,
|
|
34305
|
+
lift: LIFT_ARG
|
|
34306
|
+
}
|
|
34307
|
+
});
|
|
34308
|
+
var startCommand = defineCommand112({
|
|
34309
|
+
meta: {
|
|
34310
|
+
name: "start",
|
|
34311
|
+
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."
|
|
34312
|
+
},
|
|
34313
|
+
args: {
|
|
34314
|
+
landing: { type: "string", description: "The page under test, by slug", required: true },
|
|
34315
|
+
variant: { type: "string", description: "The alternative page's slug", required: true },
|
|
34316
|
+
because: BECAUSE_ARG,
|
|
34317
|
+
change: CHANGE_ARG,
|
|
34318
|
+
expect: EXPECT_ARG,
|
|
34319
|
+
evidence: EVIDENCE_ARG,
|
|
34320
|
+
goal: GOAL_ARG,
|
|
34321
|
+
lift: LIFT_ARG
|
|
34322
|
+
},
|
|
34323
|
+
run: async ({ args }) => {
|
|
34324
|
+
const goal = parseGoal(args.goal ? String(args.goal) : void 0);
|
|
34325
|
+
if (goal && "error" in goal) fail5(goal.error);
|
|
34326
|
+
const expect = args.expect === void 0 ? "increase" : String(args.expect);
|
|
34327
|
+
if (expect !== "increase" && expect !== "decrease") {
|
|
34328
|
+
fail5("`--expect` is either `increase` or `decrease` \u2014 which way should the goal move if you are right?");
|
|
34329
|
+
}
|
|
34330
|
+
const evidence = parseEvidence(args.evidence);
|
|
34331
|
+
if (evidence && "error" in evidence) fail5(evidence.error);
|
|
34332
|
+
const composition = await readComposition(process.cwd(), String(args.landing), String(args.variant));
|
|
34333
|
+
try {
|
|
34334
|
+
const response = await apiPost("/api/experiments/start", {
|
|
34335
|
+
landingSlug: String(args.landing),
|
|
34336
|
+
variantSlug: String(args.variant),
|
|
34337
|
+
hypothesis: {
|
|
34338
|
+
because: String(args.because),
|
|
34339
|
+
change: String(args.change),
|
|
34340
|
+
expect,
|
|
34341
|
+
...evidence ? { evidence } : {}
|
|
34342
|
+
},
|
|
34343
|
+
...composition ? { composition } : {},
|
|
34344
|
+
...goal ? { goal } : {},
|
|
34345
|
+
...args.lift ? { minDetectableRelativeLift: Number(args.lift) } : {}
|
|
34346
|
+
});
|
|
34347
|
+
writeJsonEnvelope({
|
|
34348
|
+
ok: true,
|
|
34349
|
+
data: response.data,
|
|
34350
|
+
hints: [
|
|
34351
|
+
// The one composition worth interrupting for: `baker landing variant`
|
|
34352
|
+
// with no `--fork` succeeds and produces a page that renders exactly
|
|
34353
|
+
// the control, so the test would run for weeks against itself.
|
|
34354
|
+
...composition && composition.forked.length === 0 ? [
|
|
34355
|
+
`\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.`
|
|
34356
|
+
] : [],
|
|
34357
|
+
"Staged. The split starts when this session is published \u2014 publishing is the review of the alternative page.",
|
|
34358
|
+
"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."
|
|
34359
|
+
]
|
|
34360
|
+
});
|
|
34361
|
+
} catch (error) {
|
|
34362
|
+
reportApiError(error);
|
|
34363
|
+
}
|
|
34364
|
+
}
|
|
34365
|
+
});
|
|
34366
|
+
registerSchema({
|
|
34367
|
+
command: "experiment.status",
|
|
34368
|
+
description: "The verdict on every A/B test, running and finished",
|
|
34369
|
+
args: {
|
|
34370
|
+
id: { type: "string", description: "One test, by its id", required: false },
|
|
34371
|
+
full: { type: "boolean", description: "Include the posteriors behind the verdict", required: false }
|
|
34372
|
+
}
|
|
34373
|
+
});
|
|
34374
|
+
var statusCommand4 = defineCommand112({
|
|
34375
|
+
meta: {
|
|
34376
|
+
name: "status",
|
|
34377
|
+
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."
|
|
34378
|
+
},
|
|
34379
|
+
args: {
|
|
34380
|
+
id: { type: "string", description: "One test, by its id", required: false },
|
|
34381
|
+
full: { type: "boolean", description: "Include the posteriors behind the verdict", required: false }
|
|
34382
|
+
},
|
|
34383
|
+
run: async ({ args }) => {
|
|
34384
|
+
try {
|
|
34385
|
+
const response = await apiPost("/api/experiments/status", {
|
|
34386
|
+
...args.id ? { experimentId: String(args.id) } : {},
|
|
34387
|
+
...args.full === true ? { full: true } : {}
|
|
34388
|
+
});
|
|
34389
|
+
writeJsonEnvelope({
|
|
34390
|
+
ok: true,
|
|
34391
|
+
data: response.data,
|
|
34392
|
+
hints: buildStatusHints(response.data.experiments)
|
|
34393
|
+
});
|
|
34394
|
+
} catch (error) {
|
|
34395
|
+
reportApiError(error);
|
|
34396
|
+
}
|
|
34397
|
+
}
|
|
34398
|
+
});
|
|
34399
|
+
registerSchema({
|
|
34400
|
+
command: "experiment.finish",
|
|
34401
|
+
description: "End a test and send all its traffic to the surviving page",
|
|
34402
|
+
args: {
|
|
34403
|
+
id: { type: "string", description: "The test, by its id", required: true },
|
|
34404
|
+
abandon: {
|
|
34405
|
+
type: "boolean",
|
|
34406
|
+
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.",
|
|
34407
|
+
required: false
|
|
34408
|
+
}
|
|
34409
|
+
}
|
|
34410
|
+
});
|
|
34411
|
+
var finishCommand = defineCommand112({
|
|
34412
|
+
meta: {
|
|
34413
|
+
name: "finish",
|
|
34414
|
+
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."
|
|
34415
|
+
},
|
|
34416
|
+
args: {
|
|
34417
|
+
id: { type: "string", description: "The test, by its id", required: true },
|
|
34418
|
+
abandon: {
|
|
34419
|
+
type: "boolean",
|
|
34420
|
+
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.",
|
|
34421
|
+
required: false
|
|
34422
|
+
}
|
|
34423
|
+
},
|
|
34424
|
+
run: async ({ args }) => {
|
|
34425
|
+
try {
|
|
34426
|
+
const response = await apiPost("/api/experiments/finish", {
|
|
34427
|
+
experimentId: String(args.id),
|
|
34428
|
+
...args.abandon === true ? { abandon: true } : {}
|
|
34429
|
+
});
|
|
34430
|
+
writeJsonEnvelope({
|
|
34431
|
+
ok: true,
|
|
34432
|
+
data: response.data,
|
|
34433
|
+
hints: [
|
|
34434
|
+
`Staged. All the traffic goes to the ${response.data.survivor === "variant" ? "alternative" : "original"} page when this session is published.`,
|
|
34435
|
+
...response.data.discardedVerdict ? [
|
|
34436
|
+
`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.`
|
|
34437
|
+
] : [],
|
|
34438
|
+
"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."
|
|
34439
|
+
]
|
|
34440
|
+
});
|
|
34441
|
+
} catch (error) {
|
|
34442
|
+
reportApiError(error);
|
|
34443
|
+
}
|
|
34444
|
+
}
|
|
34445
|
+
});
|
|
34446
|
+
var experimentCommand = defineCommand112({
|
|
34447
|
+
meta: {
|
|
34448
|
+
name: "experiment",
|
|
34449
|
+
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."
|
|
34450
|
+
},
|
|
34451
|
+
subCommands: {
|
|
34452
|
+
plan: planCommand,
|
|
34453
|
+
start: startCommand,
|
|
34454
|
+
status: statusCommand4,
|
|
34455
|
+
finish: finishCommand
|
|
34456
|
+
}
|
|
34457
|
+
});
|
|
34458
|
+
|
|
33637
34459
|
// src/commands/flows/index.ts
|
|
33638
|
-
import { defineCommand as
|
|
34460
|
+
import { defineCommand as defineCommand117 } from "citty";
|
|
33639
34461
|
|
|
33640
34462
|
// src/commands/flows/add.ts
|
|
33641
34463
|
import { randomUUID } from "crypto";
|
|
33642
|
-
import { defineCommand as
|
|
34464
|
+
import { defineCommand as defineCommand113 } from "citty";
|
|
33643
34465
|
|
|
33644
34466
|
// src/commands/flows/shared.ts
|
|
33645
34467
|
import { existsSync as existsSync5, readdirSync as readdirSync2, readFileSync as readFileSync9, writeFileSync as writeFileSync3 } from "fs";
|
|
@@ -33692,12 +34514,12 @@ function collectSideEffects(tree) {
|
|
|
33692
34514
|
);
|
|
33693
34515
|
}
|
|
33694
34516
|
function readFlowTree(slug) {
|
|
33695
|
-
const
|
|
33696
|
-
if (!existsSync5(
|
|
34517
|
+
const path41 = join3(flowsDir(), slug, "_data.json");
|
|
34518
|
+
if (!existsSync5(path41)) {
|
|
33697
34519
|
failLocal(`No form "${slug}". Run "baker flows list" to see the forms in this workspace.`);
|
|
33698
34520
|
}
|
|
33699
34521
|
try {
|
|
33700
|
-
return JSON.parse(readFileSync9(
|
|
34522
|
+
return JSON.parse(readFileSync9(path41, "utf-8"));
|
|
33701
34523
|
} catch (error) {
|
|
33702
34524
|
failLocal(`Could not read form "${slug}": ${error instanceof Error ? error.message : String(error)}`);
|
|
33703
34525
|
}
|
|
@@ -33849,7 +34671,7 @@ registerSchema({
|
|
|
33849
34671
|
parent: { type: "string", description: "Parent node id. Omit to append to the root's children.", required: false }
|
|
33850
34672
|
}
|
|
33851
34673
|
});
|
|
33852
|
-
var addNodeCommand =
|
|
34674
|
+
var addNodeCommand = defineCommand113({
|
|
33853
34675
|
meta: {
|
|
33854
34676
|
name: "add-node",
|
|
33855
34677
|
description: 'Add a step to a form, with the skeleton its type requires. Example: baker flows add-node contact --type customForm --name "Contact Info"'
|
|
@@ -33945,7 +34767,7 @@ registerSchema({
|
|
|
33945
34767
|
}
|
|
33946
34768
|
}
|
|
33947
34769
|
});
|
|
33948
|
-
var addSideEffectCommand =
|
|
34770
|
+
var addSideEffectCommand = defineCommand113({
|
|
33949
34771
|
meta: {
|
|
33950
34772
|
name: "add-side-effect",
|
|
33951
34773
|
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"
|
|
@@ -34027,7 +34849,7 @@ var addSideEffectCommand = defineCommand112({
|
|
|
34027
34849
|
|
|
34028
34850
|
// src/commands/flows/map.ts
|
|
34029
34851
|
import { readFileSync as readFileSync11 } from "fs";
|
|
34030
|
-
import { defineCommand as
|
|
34852
|
+
import { defineCommand as defineCommand114 } from "citty";
|
|
34031
34853
|
|
|
34032
34854
|
// src/commands/flows/value-expression.ts
|
|
34033
34855
|
import { existsSync as existsSync6, readFileSync as readFileSync10 } from "fs";
|
|
@@ -34089,10 +34911,10 @@ function parseValueExpression(raw) {
|
|
|
34089
34911
|
return parts.map(parsePart);
|
|
34090
34912
|
}
|
|
34091
34913
|
function trackingFieldIds() {
|
|
34092
|
-
const
|
|
34093
|
-
if (!existsSync6(
|
|
34914
|
+
const path41 = join4(flowsDir(), "..", "tracking.ts");
|
|
34915
|
+
if (!existsSync6(path41)) return null;
|
|
34094
34916
|
try {
|
|
34095
|
-
const source = readFileSync10(
|
|
34917
|
+
const source = readFileSync10(path41, "utf-8");
|
|
34096
34918
|
const block2 = source.match(/TRACKING_FIELD_IDS\s*=\s*\[([\s\S]*?)\]\s*as const/)?.[1];
|
|
34097
34919
|
if (!block2) return null;
|
|
34098
34920
|
const ids = [...block2.matchAll(/"(tracking\.[a-z0-9_]+)"/g)].map((match) => match[1]);
|
|
@@ -34644,13 +35466,13 @@ function specsFromFile(parsed) {
|
|
|
34644
35466
|
return `${destField}${type}=${entry?.value ?? ""}`;
|
|
34645
35467
|
});
|
|
34646
35468
|
}
|
|
34647
|
-
function readSpecFile(
|
|
35469
|
+
function readSpecFile(path41) {
|
|
34648
35470
|
let raw;
|
|
34649
35471
|
try {
|
|
34650
|
-
raw =
|
|
35472
|
+
raw = path41 === "-" ? readFileSync11(0, "utf-8") : readFileSync11(path41, "utf-8");
|
|
34651
35473
|
} catch (error) {
|
|
34652
35474
|
refuse(
|
|
34653
|
-
`Could not read ${
|
|
35475
|
+
`Could not read ${path41 === "-" ? "the mapping from stdin" : `"${path41}"`}: ${error instanceof Error ? error.message : String(error)}`
|
|
34654
35476
|
);
|
|
34655
35477
|
}
|
|
34656
35478
|
let parsed;
|
|
@@ -34658,7 +35480,7 @@ function readSpecFile(path38) {
|
|
|
34658
35480
|
parsed = JSON.parse(raw);
|
|
34659
35481
|
} catch (error) {
|
|
34660
35482
|
refuse(
|
|
34661
|
-
`${
|
|
35483
|
+
`${path41 === "-" ? "stdin" : `"${path41}"`} is not valid JSON: ${error instanceof Error ? error.message : String(error)}`,
|
|
34662
35484
|
'Expected { "map": { "<destField>": "<value>", \u2026 } }'
|
|
34663
35485
|
);
|
|
34664
35486
|
}
|
|
@@ -34800,7 +35622,7 @@ async function mapOptions(args, fromFile, into) {
|
|
|
34800
35622
|
destinationFields: hubspotForm ? await hubspotFieldCatalog(hubspotForm) : void 0
|
|
34801
35623
|
};
|
|
34802
35624
|
}
|
|
34803
|
-
var mapCommand2 =
|
|
35625
|
+
var mapCommand2 = defineCommand114({
|
|
34804
35626
|
meta: {
|
|
34805
35627
|
name: "map",
|
|
34806
35628
|
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"
|
|
@@ -34898,7 +35720,7 @@ var mapCommand2 = defineCommand113({
|
|
|
34898
35720
|
});
|
|
34899
35721
|
|
|
34900
35722
|
// src/commands/flows/normalize.ts
|
|
34901
|
-
import { defineCommand as
|
|
35723
|
+
import { defineCommand as defineCommand115 } from "citty";
|
|
34902
35724
|
var OPTIONAL_STRINGS = ["oauthProviderId"];
|
|
34903
35725
|
var ARRAY_FIELDS = [
|
|
34904
35726
|
"fieldMapping",
|
|
@@ -34912,18 +35734,18 @@ var ARRAY_FIELDS = [
|
|
|
34912
35734
|
"tagIds"
|
|
34913
35735
|
];
|
|
34914
35736
|
var ARRAY_OWNERS = ["", "body"];
|
|
34915
|
-
function dropUnsetOptionals(sideEffect,
|
|
35737
|
+
function dropUnsetOptionals(sideEffect, path41) {
|
|
34916
35738
|
return OPTIONAL_STRINGS.flatMap((key) => {
|
|
34917
35739
|
if (!(key in sideEffect) || sideEffect[key] !== null && sideEffect[key] !== "") return [];
|
|
34918
35740
|
delete sideEffect[key];
|
|
34919
|
-
return [{ path:
|
|
35741
|
+
return [{ path: path41, change: `dropped \`${key}\` (an optional string is absent, never null)` }];
|
|
34920
35742
|
});
|
|
34921
35743
|
}
|
|
34922
|
-
function fillNulledArrays(target, prefix,
|
|
35744
|
+
function fillNulledArrays(target, prefix, path41) {
|
|
34923
35745
|
return ARRAY_FIELDS.flatMap((key) => {
|
|
34924
35746
|
if (!(key in target) || target[key] !== null) return [];
|
|
34925
35747
|
target[key] = [];
|
|
34926
|
-
return [{ path:
|
|
35748
|
+
return [{ path: path41, change: `\`${prefix}${key}: null\` \u2192 \`[]\`` }];
|
|
34927
35749
|
});
|
|
34928
35750
|
}
|
|
34929
35751
|
function sideEffectsOf(node) {
|
|
@@ -34933,13 +35755,13 @@ function sideEffectsOf(node) {
|
|
|
34933
35755
|
);
|
|
34934
35756
|
}
|
|
34935
35757
|
function normalizeSideEffect(sideEffect, where) {
|
|
34936
|
-
const
|
|
35758
|
+
const path41 = `${where} \u2192 ${String(sideEffect.id ?? "side effect")}`;
|
|
34937
35759
|
const arrays = ARRAY_OWNERS.flatMap((owner) => {
|
|
34938
35760
|
const target = owner ? sideEffect[owner] : sideEffect;
|
|
34939
35761
|
if (!target || typeof target !== "object") return [];
|
|
34940
|
-
return fillNulledArrays(target, owner ? `${owner}.` : "",
|
|
35762
|
+
return fillNulledArrays(target, owner ? `${owner}.` : "", path41);
|
|
34941
35763
|
});
|
|
34942
|
-
return [...dropUnsetOptionals(sideEffect,
|
|
35764
|
+
return [...dropUnsetOptionals(sideEffect, path41), ...arrays];
|
|
34943
35765
|
}
|
|
34944
35766
|
function normalizeFlowTree(tree) {
|
|
34945
35767
|
const changes = [];
|
|
@@ -34967,7 +35789,7 @@ registerSchema({
|
|
|
34967
35789
|
}
|
|
34968
35790
|
}
|
|
34969
35791
|
});
|
|
34970
|
-
var normalizeCommand =
|
|
35792
|
+
var normalizeCommand = defineCommand115({
|
|
34971
35793
|
meta: {
|
|
34972
35794
|
name: "normalize",
|
|
34973
35795
|
description: "Repair the shapes a form file is allowed to get slightly wrong. Example: baker flows normalize contact --write"
|
|
@@ -34998,7 +35820,7 @@ var normalizeCommand = defineCommand114({
|
|
|
34998
35820
|
});
|
|
34999
35821
|
|
|
35000
35822
|
// src/commands/flows/schema.ts
|
|
35001
|
-
import { defineCommand as
|
|
35823
|
+
import { defineCommand as defineCommand116 } from "citty";
|
|
35002
35824
|
registerSchema({
|
|
35003
35825
|
command: "flows.schema",
|
|
35004
35826
|
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.",
|
|
@@ -35023,7 +35845,7 @@ function triggerHints(triggers) {
|
|
|
35023
35845
|
`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(", ")}.` : "")
|
|
35024
35846
|
];
|
|
35025
35847
|
}
|
|
35026
|
-
var schemaCommand =
|
|
35848
|
+
var schemaCommand = defineCommand116({
|
|
35027
35849
|
meta: {
|
|
35028
35850
|
name: "schema",
|
|
35029
35851
|
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)"
|
|
@@ -35157,7 +35979,7 @@ function displayName(slug) {
|
|
|
35157
35979
|
const name = tree.displayName;
|
|
35158
35980
|
return typeof name === "string" && name.trim() ? name.trim() : slug;
|
|
35159
35981
|
}
|
|
35160
|
-
var listCommand12 =
|
|
35982
|
+
var listCommand12 = defineCommand117({
|
|
35161
35983
|
meta: {
|
|
35162
35984
|
name: "list",
|
|
35163
35985
|
description: "List Forms in this workspace with the count of confidential fields still needing setup. Example: baker flows list"
|
|
@@ -35178,7 +36000,7 @@ var listCommand12 = defineCommand116({
|
|
|
35178
36000
|
});
|
|
35179
36001
|
}
|
|
35180
36002
|
});
|
|
35181
|
-
var showCommand3 =
|
|
36003
|
+
var showCommand3 = defineCommand117({
|
|
35182
36004
|
meta: {
|
|
35183
36005
|
name: "show",
|
|
35184
36006
|
description: "Show a Form's confidential fields and their configuration status (never secret values). Example: baker flows show contact"
|
|
@@ -35206,7 +36028,7 @@ var showCommand3 = defineCommand116({
|
|
|
35206
36028
|
});
|
|
35207
36029
|
}
|
|
35208
36030
|
});
|
|
35209
|
-
var flowsCommand =
|
|
36031
|
+
var flowsCommand = defineCommand117({
|
|
35210
36032
|
meta: {
|
|
35211
36033
|
name: "flows",
|
|
35212
36034
|
description: `Read and shape this workspace's Forms (flows).
|
|
@@ -35240,10 +36062,10 @@ Full guide: __tooling__/docs/tools/baker/flows.md`
|
|
|
35240
36062
|
});
|
|
35241
36063
|
|
|
35242
36064
|
// src/commands/ga4/index.ts
|
|
35243
|
-
import { defineCommand as
|
|
36065
|
+
import { defineCommand as defineCommand124 } from "citty";
|
|
35244
36066
|
|
|
35245
36067
|
// src/commands/ga4/audit.ts
|
|
35246
|
-
import { defineCommand as
|
|
36068
|
+
import { defineCommand as defineCommand118 } from "citty";
|
|
35247
36069
|
|
|
35248
36070
|
// src/commands/ga4/resolve.ts
|
|
35249
36071
|
async function fetchProperties(useCache = true) {
|
|
@@ -35309,7 +36131,7 @@ registerSchema({
|
|
|
35309
36131
|
"no-cache": { type: "boolean", description: "Skip cache, hit API directly", required: false }
|
|
35310
36132
|
}
|
|
35311
36133
|
});
|
|
35312
|
-
var auditCommand2 =
|
|
36134
|
+
var auditCommand2 = defineCommand118({
|
|
35313
36135
|
meta: {
|
|
35314
36136
|
name: "audit",
|
|
35315
36137
|
description: `Run all GA4 admin health checks. Returns property config with playbook warnings.
|
|
@@ -35364,7 +36186,7 @@ Examples:
|
|
|
35364
36186
|
});
|
|
35365
36187
|
|
|
35366
36188
|
// src/commands/ga4/config.ts
|
|
35367
|
-
import { defineCommand as
|
|
36189
|
+
import { defineCommand as defineCommand119 } from "citty";
|
|
35368
36190
|
|
|
35369
36191
|
// src/commands/ga4/shared.ts
|
|
35370
36192
|
import { readFileSync as readFileSync12 } from "fs";
|
|
@@ -35477,10 +36299,10 @@ async function stageOps(ops) {
|
|
|
35477
36299
|
handleError2(err);
|
|
35478
36300
|
}
|
|
35479
36301
|
}
|
|
35480
|
-
async function draftAction2(
|
|
36302
|
+
async function draftAction2(path41, body, chat) {
|
|
35481
36303
|
const chatId = resolveChatId(chat);
|
|
35482
36304
|
try {
|
|
35483
|
-
const data = await apiPost(
|
|
36305
|
+
const data = await apiPost(path41, { chatId, ...body });
|
|
35484
36306
|
writeJsonEnvelope({ ok: true, data });
|
|
35485
36307
|
return data;
|
|
35486
36308
|
} catch (err) {
|
|
@@ -35560,7 +36382,7 @@ function configHints(config) {
|
|
|
35560
36382
|
}
|
|
35561
36383
|
return hints;
|
|
35562
36384
|
}
|
|
35563
|
-
var configCommand =
|
|
36385
|
+
var configCommand = defineCommand119({
|
|
35564
36386
|
meta: {
|
|
35565
36387
|
name: "config",
|
|
35566
36388
|
description: `Read how the property is configured to measure \u2014 key events, custom definitions, custom events, retention
|
|
@@ -35592,7 +36414,7 @@ Examples:
|
|
|
35592
36414
|
});
|
|
35593
36415
|
|
|
35594
36416
|
// src/commands/ga4/draft.ts
|
|
35595
|
-
import { defineCommand as
|
|
36417
|
+
import { defineCommand as defineCommand120 } from "citty";
|
|
35596
36418
|
registerSchema({
|
|
35597
36419
|
command: "ga4.draft",
|
|
35598
36420
|
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.",
|
|
@@ -35601,13 +36423,13 @@ registerSchema({
|
|
|
35601
36423
|
chat: { type: "string", description: CHAT_READ_ARG.description, required: false }
|
|
35602
36424
|
}
|
|
35603
36425
|
});
|
|
35604
|
-
var draftCommand3 =
|
|
36426
|
+
var draftCommand3 = defineCommand120({
|
|
35605
36427
|
meta: {
|
|
35606
36428
|
name: "draft",
|
|
35607
36429
|
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."
|
|
35608
36430
|
},
|
|
35609
36431
|
subCommands: {
|
|
35610
|
-
list:
|
|
36432
|
+
list: defineCommand120({
|
|
35611
36433
|
meta: { name: "list", description: "Review everything staged on this chat (--json for the raw envelope)" },
|
|
35612
36434
|
args: {
|
|
35613
36435
|
json: { type: "boolean", description: "Print the raw JSON envelope", required: false },
|
|
@@ -35617,7 +36439,7 @@ var draftCommand3 = defineCommand119({
|
|
|
35617
36439
|
await draftList(args.json === true, args.chat);
|
|
35618
36440
|
}
|
|
35619
36441
|
}),
|
|
35620
|
-
show:
|
|
36442
|
+
show: defineCommand120({
|
|
35621
36443
|
meta: {
|
|
35622
36444
|
name: "show",
|
|
35623
36445
|
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)."
|
|
@@ -35634,7 +36456,7 @@ var draftCommand3 = defineCommand119({
|
|
|
35634
36456
|
);
|
|
35635
36457
|
}
|
|
35636
36458
|
}),
|
|
35637
|
-
amend:
|
|
36459
|
+
amend: defineCommand120({
|
|
35638
36460
|
meta: {
|
|
35639
36461
|
name: "amend",
|
|
35640
36462
|
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."
|
|
@@ -35651,7 +36473,7 @@ var draftCommand3 = defineCommand119({
|
|
|
35651
36473
|
});
|
|
35652
36474
|
}
|
|
35653
36475
|
}),
|
|
35654
|
-
remove:
|
|
36476
|
+
remove: defineCommand120({
|
|
35655
36477
|
meta: { name: "remove", description: "Remove one staged change" },
|
|
35656
36478
|
args: { ref: { type: "positional", description: "Staged ref or target id", required: false } },
|
|
35657
36479
|
run: async ({ args }) => {
|
|
@@ -35660,7 +36482,7 @@ var draftCommand3 = defineCommand119({
|
|
|
35660
36482
|
});
|
|
35661
36483
|
}
|
|
35662
36484
|
}),
|
|
35663
|
-
clear:
|
|
36485
|
+
clear: defineCommand120({
|
|
35664
36486
|
meta: { name: "clear", description: "Discard all Google Analytics changes staged on this chat" },
|
|
35665
36487
|
run: async () => {
|
|
35666
36488
|
await draftAction2("/api/ga4/draft/clear", {});
|
|
@@ -35670,7 +36492,7 @@ var draftCommand3 = defineCommand119({
|
|
|
35670
36492
|
});
|
|
35671
36493
|
|
|
35672
36494
|
// src/commands/ga4/properties.ts
|
|
35673
|
-
import { defineCommand as
|
|
36495
|
+
import { defineCommand as defineCommand121 } from "citty";
|
|
35674
36496
|
registerSchema({
|
|
35675
36497
|
command: "ga4.properties",
|
|
35676
36498
|
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.",
|
|
@@ -35686,7 +36508,7 @@ function propertyHints(properties) {
|
|
|
35686
36508
|
resources: properties.map((property) => ({ id: property.externalId, label: property.name }))
|
|
35687
36509
|
});
|
|
35688
36510
|
}
|
|
35689
|
-
var propertiesCommand =
|
|
36511
|
+
var propertiesCommand = defineCommand121({
|
|
35690
36512
|
meta: {
|
|
35691
36513
|
name: "properties",
|
|
35692
36514
|
description: `List accessible GA4 properties.
|
|
@@ -35736,7 +36558,7 @@ Examples:
|
|
|
35736
36558
|
// src/commands/ga4/query.ts
|
|
35737
36559
|
import { appendFileSync as appendFileSync2, existsSync as existsSync7, readFileSync as readFileSync13, writeFileSync as writeFileSync4 } from "fs";
|
|
35738
36560
|
import { resolve as resolve2 } from "path";
|
|
35739
|
-
import { defineCommand as
|
|
36561
|
+
import { defineCommand as defineCommand122 } from "citty";
|
|
35740
36562
|
|
|
35741
36563
|
// src/commands/ga4/presets.ts
|
|
35742
36564
|
var GA4_PRESETS = [
|
|
@@ -35871,7 +36693,7 @@ function handleError3(err) {
|
|
|
35871
36693
|
});
|
|
35872
36694
|
process.exit(1);
|
|
35873
36695
|
}
|
|
35874
|
-
var queryCommand2 =
|
|
36696
|
+
var queryCommand2 = defineCommand122({
|
|
35875
36697
|
meta: {
|
|
35876
36698
|
name: "query",
|
|
35877
36699
|
description: `Run GA4 Data API reports. Preset-first with free-form escape hatch.
|
|
@@ -35942,7 +36764,7 @@ Free-form (escape hatch):
|
|
|
35942
36764
|
});
|
|
35943
36765
|
|
|
35944
36766
|
// src/commands/ga4/write-commands.ts
|
|
35945
|
-
import { defineCommand as
|
|
36767
|
+
import { defineCommand as defineCommand123 } from "citty";
|
|
35946
36768
|
var PROPERTY_ARG_DESCRIPTION = "GA4 property id (optional only when one property is connected \u2014 run `baker ga4 properties`)";
|
|
35947
36769
|
var propertyArg = { type: "string", description: PROPERTY_ARG_DESCRIPTION, required: false };
|
|
35948
36770
|
function createJsonDescription(noun) {
|
|
@@ -35960,7 +36782,7 @@ registerSchema({
|
|
|
35960
36782
|
"property-id": { type: "string", description: PROPERTY_ARG_DESCRIPTION, required: false }
|
|
35961
36783
|
}
|
|
35962
36784
|
});
|
|
35963
|
-
var keyEventCommand =
|
|
36785
|
+
var keyEventCommand = defineCommand123({
|
|
35964
36786
|
meta: {
|
|
35965
36787
|
name: "key-event",
|
|
35966
36788
|
description: `Stage key-event (conversion) changes
|
|
@@ -35973,7 +36795,7 @@ Examples:
|
|
|
35973
36795
|
baker ga4 key-event delete 4185`
|
|
35974
36796
|
},
|
|
35975
36797
|
subCommands: {
|
|
35976
|
-
create:
|
|
36798
|
+
create: defineCommand123({
|
|
35977
36799
|
meta: {
|
|
35978
36800
|
name: "create",
|
|
35979
36801
|
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."
|
|
@@ -35994,7 +36816,7 @@ Examples:
|
|
|
35994
36816
|
);
|
|
35995
36817
|
}
|
|
35996
36818
|
}),
|
|
35997
|
-
update:
|
|
36819
|
+
update: defineCommand123({
|
|
35998
36820
|
meta: {
|
|
35999
36821
|
name: "update",
|
|
36000
36822
|
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."
|
|
@@ -36014,7 +36836,7 @@ Examples:
|
|
|
36014
36836
|
});
|
|
36015
36837
|
}
|
|
36016
36838
|
}),
|
|
36017
|
-
delete:
|
|
36839
|
+
delete: defineCommand123({
|
|
36018
36840
|
meta: {
|
|
36019
36841
|
name: "delete",
|
|
36020
36842
|
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."
|
|
@@ -36062,7 +36884,7 @@ for (const { kind, noun, createHint } of DEFINITIONS) {
|
|
|
36062
36884
|
}
|
|
36063
36885
|
function definitionCommand(definition) {
|
|
36064
36886
|
const { command, kind, noun, example } = definition;
|
|
36065
|
-
return
|
|
36887
|
+
return defineCommand123({
|
|
36066
36888
|
meta: {
|
|
36067
36889
|
name: command,
|
|
36068
36890
|
description: `Stage ${noun} changes
|
|
@@ -36074,7 +36896,7 @@ Examples:
|
|
|
36074
36896
|
baker ga4 ${command} archive 12`
|
|
36075
36897
|
},
|
|
36076
36898
|
subCommands: {
|
|
36077
|
-
create:
|
|
36899
|
+
create: defineCommand123({
|
|
36078
36900
|
meta: { name: "create", description: `Stage a new ${noun} (or a JSON array of them, staged together)` },
|
|
36079
36901
|
args: {
|
|
36080
36902
|
json: { type: "string", description: createJsonDescription(noun), required: false },
|
|
@@ -36092,7 +36914,7 @@ Examples:
|
|
|
36092
36914
|
);
|
|
36093
36915
|
}
|
|
36094
36916
|
}),
|
|
36095
|
-
update:
|
|
36917
|
+
update: defineCommand123({
|
|
36096
36918
|
meta: {
|
|
36097
36919
|
name: "update",
|
|
36098
36920
|
description: `Stage a change to an existing ${noun} (pass its id). The parameter name and scope cannot change \u2014 archive and recreate for that.`
|
|
@@ -36112,7 +36934,7 @@ Examples:
|
|
|
36112
36934
|
});
|
|
36113
36935
|
}
|
|
36114
36936
|
}),
|
|
36115
|
-
archive:
|
|
36937
|
+
archive: defineCommand123({
|
|
36116
36938
|
meta: {
|
|
36117
36939
|
name: "archive",
|
|
36118
36940
|
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.`
|
|
@@ -36157,7 +36979,7 @@ var dataStreamArg = {
|
|
|
36157
36979
|
function withStream(args) {
|
|
36158
36980
|
return typeof args["data-stream"] === "string" ? { dataStream: args["data-stream"] } : {};
|
|
36159
36981
|
}
|
|
36160
|
-
var customEventCommand =
|
|
36982
|
+
var customEventCommand = defineCommand123({
|
|
36161
36983
|
meta: {
|
|
36162
36984
|
name: "custom-event",
|
|
36163
36985
|
description: `Stage custom events \u2014 new events built from events the site already sends
|
|
@@ -36168,7 +36990,7 @@ Examples:
|
|
|
36168
36990
|
baker ga4 custom-event delete 7`
|
|
36169
36991
|
},
|
|
36170
36992
|
subCommands: {
|
|
36171
|
-
create:
|
|
36993
|
+
create: defineCommand123({
|
|
36172
36994
|
meta: {
|
|
36173
36995
|
name: "create",
|
|
36174
36996
|
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."
|
|
@@ -36191,7 +37013,7 @@ Examples:
|
|
|
36191
37013
|
);
|
|
36192
37014
|
}
|
|
36193
37015
|
}),
|
|
36194
|
-
update:
|
|
37016
|
+
update: defineCommand123({
|
|
36195
37017
|
meta: { name: "update", description: "Stage a change to an existing custom event (pass its id)" },
|
|
36196
37018
|
args: {
|
|
36197
37019
|
id: { type: "positional", description: "Custom event rule id", required: false },
|
|
@@ -36210,7 +37032,7 @@ Examples:
|
|
|
36210
37032
|
});
|
|
36211
37033
|
}
|
|
36212
37034
|
}),
|
|
36213
|
-
delete:
|
|
37035
|
+
delete: defineCommand123({
|
|
36214
37036
|
meta: {
|
|
36215
37037
|
name: "delete",
|
|
36216
37038
|
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."
|
|
@@ -36265,7 +37087,7 @@ function booleanArg(input, flag) {
|
|
|
36265
37087
|
if (input === "false") return false;
|
|
36266
37088
|
return failValidation3(`--${flag} must be true or false`);
|
|
36267
37089
|
}
|
|
36268
|
-
var dataRetentionCommand =
|
|
37090
|
+
var dataRetentionCommand = defineCommand123({
|
|
36269
37091
|
meta: {
|
|
36270
37092
|
name: "data-retention",
|
|
36271
37093
|
description: `Stage a change to how long Google Analytics keeps data
|
|
@@ -36275,7 +37097,7 @@ Examples:
|
|
|
36275
37097
|
baker ga4 data-retention set --event-data 14 --user-data 14 --reset-on-activity true`
|
|
36276
37098
|
},
|
|
36277
37099
|
subCommands: {
|
|
36278
|
-
set:
|
|
37100
|
+
set: defineCommand123({
|
|
36279
37101
|
meta: { name: "set", description: "Stage the retention window (months)" },
|
|
36280
37102
|
args: {
|
|
36281
37103
|
"event-data": { type: "string", description: "2, 14, 26, 38 or 50", required: false },
|
|
@@ -36311,7 +37133,7 @@ registerSchema({
|
|
|
36311
37133
|
"property-id": { type: "string", description: PROPERTY_ARG_DESCRIPTION, required: false }
|
|
36312
37134
|
}
|
|
36313
37135
|
});
|
|
36314
|
-
var audienceCommand =
|
|
37136
|
+
var audienceCommand = defineCommand123({
|
|
36315
37137
|
meta: {
|
|
36316
37138
|
name: "audience",
|
|
36317
37139
|
description: `Stage audience changes (saved groups of users, for retargeting and analysis)
|
|
@@ -36343,7 +37165,7 @@ Examples:
|
|
|
36343
37165
|
baker ga4 audience archive 8813`
|
|
36344
37166
|
},
|
|
36345
37167
|
subCommands: {
|
|
36346
|
-
create:
|
|
37168
|
+
create: defineCommand123({
|
|
36347
37169
|
meta: { name: "create", description: createJsonDescription("audience") },
|
|
36348
37170
|
args: {
|
|
36349
37171
|
json: { type: "string", description: createJsonDescription("audience"), required: false },
|
|
@@ -36361,7 +37183,7 @@ Examples:
|
|
|
36361
37183
|
);
|
|
36362
37184
|
}
|
|
36363
37185
|
}),
|
|
36364
|
-
update:
|
|
37186
|
+
update: defineCommand123({
|
|
36365
37187
|
meta: {
|
|
36366
37188
|
name: "update",
|
|
36367
37189
|
description: "Stage a new name or description (pass the audience id). Who is in it cannot be changed."
|
|
@@ -36382,7 +37204,7 @@ Examples:
|
|
|
36382
37204
|
});
|
|
36383
37205
|
}
|
|
36384
37206
|
}),
|
|
36385
|
-
archive:
|
|
37207
|
+
archive: defineCommand123({
|
|
36386
37208
|
meta: {
|
|
36387
37209
|
name: "archive",
|
|
36388
37210
|
description: "Stage retiring an audience. Cannot be undone \u2014 the users it gathered do not come back."
|
|
@@ -36411,7 +37233,7 @@ registerSchema({
|
|
|
36411
37233
|
"property-id": { type: "string", description: PROPERTY_ARG_DESCRIPTION, required: false }
|
|
36412
37234
|
}
|
|
36413
37235
|
});
|
|
36414
|
-
var propertyCommand =
|
|
37236
|
+
var propertyCommand = defineCommand123({
|
|
36415
37237
|
meta: {
|
|
36416
37238
|
name: "property",
|
|
36417
37239
|
description: `Stage a change to the property's own settings
|
|
@@ -36421,7 +37243,7 @@ Examples:
|
|
|
36421
37243
|
baker ga4 property set --name "Acme \u2014 Web" --industry REAL_ESTATE`
|
|
36422
37244
|
},
|
|
36423
37245
|
subCommands: {
|
|
36424
|
-
set:
|
|
37246
|
+
set: defineCommand123({
|
|
36425
37247
|
meta: { name: "set", description: "Stage the property name and/or industry category" },
|
|
36426
37248
|
args: {
|
|
36427
37249
|
name: { type: "string", description: "New property display name", required: false },
|
|
@@ -36461,7 +37283,7 @@ registerSchema({
|
|
|
36461
37283
|
"property-id": { type: "string", description: PROPERTY_ARG_DESCRIPTION, required: false }
|
|
36462
37284
|
}
|
|
36463
37285
|
});
|
|
36464
|
-
var adsLinkCommand =
|
|
37286
|
+
var adsLinkCommand = defineCommand123({
|
|
36465
37287
|
meta: {
|
|
36466
37288
|
name: "ads-link",
|
|
36467
37289
|
description: `Stage linking this property to a Google Ads account
|
|
@@ -36476,7 +37298,7 @@ Examples:
|
|
|
36476
37298
|
baker ga4 ads-link delete L1234`
|
|
36477
37299
|
},
|
|
36478
37300
|
subCommands: {
|
|
36479
|
-
create:
|
|
37301
|
+
create: defineCommand123({
|
|
36480
37302
|
meta: { name: "create", description: "Stage linking the property to a Google Ads account" },
|
|
36481
37303
|
args: {
|
|
36482
37304
|
"customer-id": { type: "string", description: "Google Ads customer id (123-456-7890)", required: false },
|
|
@@ -36495,7 +37317,7 @@ Examples:
|
|
|
36495
37317
|
await stageOp3({ kind: "ga4.googleAdsLink.create", payload, ...withProperty(args) });
|
|
36496
37318
|
}
|
|
36497
37319
|
}),
|
|
36498
|
-
update:
|
|
37320
|
+
update: defineCommand123({
|
|
36499
37321
|
meta: {
|
|
36500
37322
|
name: "update",
|
|
36501
37323
|
description: "Stage the ads-personalisation setting on an existing link (pass its id)"
|
|
@@ -36517,7 +37339,7 @@ Examples:
|
|
|
36517
37339
|
});
|
|
36518
37340
|
}
|
|
36519
37341
|
}),
|
|
36520
|
-
delete:
|
|
37342
|
+
delete: defineCommand123({
|
|
36521
37343
|
meta: {
|
|
36522
37344
|
name: "delete",
|
|
36523
37345
|
description: "Stage unlinking. Stops conversion imports and strands every audience on this property."
|
|
@@ -36568,7 +37390,7 @@ var MEASUREMENT_FLAGS = {
|
|
|
36568
37390
|
"page-changes": "pageChangesEnabled",
|
|
36569
37391
|
"form-interactions": "formInteractionsEnabled"
|
|
36570
37392
|
};
|
|
36571
|
-
var enhancedMeasurementCommand =
|
|
37393
|
+
var enhancedMeasurementCommand = defineCommand123({
|
|
36572
37394
|
meta: {
|
|
36573
37395
|
name: "enhanced-measurement",
|
|
36574
37396
|
description: `Stage the automatic events GA4 collects on a website stream
|
|
@@ -36582,7 +37404,7 @@ Examples:
|
|
|
36582
37404
|
baker ga4 enhanced-measurement set --video false`
|
|
36583
37405
|
},
|
|
36584
37406
|
subCommands: {
|
|
36585
|
-
set:
|
|
37407
|
+
set: defineCommand123({
|
|
36586
37408
|
meta: { name: "set", description: "Stage which automatic events the website stream collects" },
|
|
36587
37409
|
args: {
|
|
36588
37410
|
scrolls: { type: "string", description: "true|false", required: false },
|
|
@@ -36625,7 +37447,7 @@ Examples:
|
|
|
36625
37447
|
});
|
|
36626
37448
|
|
|
36627
37449
|
// src/commands/ga4/index.ts
|
|
36628
|
-
var ga4Command =
|
|
37450
|
+
var ga4Command = defineCommand124({
|
|
36629
37451
|
meta: {
|
|
36630
37452
|
name: "ga4",
|
|
36631
37453
|
description: `Google Analytics 4. Report on a property, audit its config, and change what it measures.
|
|
@@ -36674,12 +37496,12 @@ Full guide: __tooling__/docs/tools/baker/ga4.md`
|
|
|
36674
37496
|
});
|
|
36675
37497
|
|
|
36676
37498
|
// src/commands/gsc/index.ts
|
|
36677
|
-
import { defineCommand as
|
|
37499
|
+
import { defineCommand as defineCommand128 } from "citty";
|
|
36678
37500
|
|
|
36679
37501
|
// src/commands/gsc/query.ts
|
|
36680
37502
|
import { appendFileSync as appendFileSync3, existsSync as existsSync8, readFileSync as readFileSync14, writeFileSync as writeFileSync5 } from "fs";
|
|
36681
37503
|
import { resolve as resolve3 } from "path";
|
|
36682
|
-
import { defineCommand as
|
|
37504
|
+
import { defineCommand as defineCommand125 } from "citty";
|
|
36683
37505
|
|
|
36684
37506
|
// src/commands/gsc/presets.ts
|
|
36685
37507
|
var GSC_PRESETS = [
|
|
@@ -36873,7 +37695,7 @@ function handleError4(err) {
|
|
|
36873
37695
|
});
|
|
36874
37696
|
process.exit(1);
|
|
36875
37697
|
}
|
|
36876
|
-
var queryCommand3 =
|
|
37698
|
+
var queryCommand3 = defineCommand125({
|
|
36877
37699
|
meta: {
|
|
36878
37700
|
name: "query",
|
|
36879
37701
|
description: `Run GSC Search Analytics queries. Preset-first with free-form escape hatch.
|
|
@@ -36951,7 +37773,7 @@ Free-form (escape hatch):
|
|
|
36951
37773
|
});
|
|
36952
37774
|
|
|
36953
37775
|
// src/commands/gsc/sitemaps.ts
|
|
36954
|
-
import { defineCommand as
|
|
37776
|
+
import { defineCommand as defineCommand126 } from "citty";
|
|
36955
37777
|
registerSchema({
|
|
36956
37778
|
command: "gsc.sitemaps",
|
|
36957
37779
|
description: "List sitemaps for a Search Console site. Check sitemap health and errors.",
|
|
@@ -36960,7 +37782,7 @@ registerSchema({
|
|
|
36960
37782
|
"no-cache": { type: "boolean", description: "Skip cache, hit API directly", required: false }
|
|
36961
37783
|
}
|
|
36962
37784
|
});
|
|
36963
|
-
var sitemapsCommand =
|
|
37785
|
+
var sitemapsCommand = defineCommand126({
|
|
36964
37786
|
meta: {
|
|
36965
37787
|
name: "sitemaps",
|
|
36966
37788
|
description: `List sitemaps for a site. Check health and errors.
|
|
@@ -37013,7 +37835,7 @@ Examples:
|
|
|
37013
37835
|
});
|
|
37014
37836
|
|
|
37015
37837
|
// src/commands/gsc/sites.ts
|
|
37016
|
-
import { defineCommand as
|
|
37838
|
+
import { defineCommand as defineCommand127 } from "citty";
|
|
37017
37839
|
registerSchema({
|
|
37018
37840
|
command: "gsc.sites",
|
|
37019
37841
|
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.",
|
|
@@ -37029,7 +37851,7 @@ function siteHints(sites) {
|
|
|
37029
37851
|
resources: sites.map((site) => ({ id: site.siteUrl, label: site.permissionLevel }))
|
|
37030
37852
|
});
|
|
37031
37853
|
}
|
|
37032
|
-
var sitesCommand =
|
|
37854
|
+
var sitesCommand = defineCommand127({
|
|
37033
37855
|
meta: {
|
|
37034
37856
|
name: "sites",
|
|
37035
37857
|
description: `List verified Search Console sites.
|
|
@@ -37077,7 +37899,7 @@ Examples:
|
|
|
37077
37899
|
});
|
|
37078
37900
|
|
|
37079
37901
|
// src/commands/gsc/index.ts
|
|
37080
|
-
var gscCommand =
|
|
37902
|
+
var gscCommand = defineCommand128({
|
|
37081
37903
|
meta: {
|
|
37082
37904
|
name: "gsc",
|
|
37083
37905
|
description: `Google Search Console commands. PPC-SEO arbitrage, brand halo analysis, negative keyword discovery.
|
|
@@ -37101,7 +37923,7 @@ Full guide: __tooling__/docs/tools/baker/gsc.md`
|
|
|
37101
37923
|
});
|
|
37102
37924
|
|
|
37103
37925
|
// src/commands/history/index.ts
|
|
37104
|
-
import { defineCommand as
|
|
37926
|
+
import { defineCommand as defineCommand129 } from "citty";
|
|
37105
37927
|
registerSchema({
|
|
37106
37928
|
command: "history.list",
|
|
37107
37929
|
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.",
|
|
@@ -37156,7 +37978,7 @@ function parseBoundedInt2(raw, name, min, max) {
|
|
|
37156
37978
|
}
|
|
37157
37979
|
return value;
|
|
37158
37980
|
}
|
|
37159
|
-
var listCommand13 =
|
|
37981
|
+
var listCommand13 = defineCommand129({
|
|
37160
37982
|
meta: {
|
|
37161
37983
|
name: "list",
|
|
37162
37984
|
description: "List recent account changes (unified audit log), newest first."
|
|
@@ -37202,7 +38024,7 @@ var listCommand13 = defineCommand128({
|
|
|
37202
38024
|
}
|
|
37203
38025
|
}
|
|
37204
38026
|
});
|
|
37205
|
-
var historyCommand =
|
|
38027
|
+
var historyCommand = defineCommand129({
|
|
37206
38028
|
meta: {
|
|
37207
38029
|
name: "history",
|
|
37208
38030
|
description: `Unified account history (audit log): what changed, who did it, and when.
|
|
@@ -37212,7 +38034,7 @@ Full guide: __tooling__/docs/tools/baker/history.md`
|
|
|
37212
38034
|
});
|
|
37213
38035
|
|
|
37214
38036
|
// src/commands/hubspot/index.ts
|
|
37215
|
-
import { defineCommand as
|
|
38037
|
+
import { defineCommand as defineCommand130 } from "citty";
|
|
37216
38038
|
var EMBED_TYPES = ["legacy", "v4", "unknown"];
|
|
37217
38039
|
function failNotConnected(err) {
|
|
37218
38040
|
if (err instanceof ApiError && err.code === "FORBIDDEN" && err.message.includes(HUBSPOT_MISSING_GRANT_MARKER)) {
|
|
@@ -37366,7 +38188,7 @@ registerSchema({
|
|
|
37366
38188
|
}
|
|
37367
38189
|
}
|
|
37368
38190
|
});
|
|
37369
|
-
var formsListCommand =
|
|
38191
|
+
var formsListCommand = defineCommand130({
|
|
37370
38192
|
meta: {
|
|
37371
38193
|
name: "list",
|
|
37372
38194
|
description: "List HubSpot forms with embed type and post-submit action. Example: baker hubspot forms list --redirecting-only"
|
|
@@ -37411,7 +38233,7 @@ var formsListCommand = defineCommand129({
|
|
|
37411
38233
|
}
|
|
37412
38234
|
}
|
|
37413
38235
|
});
|
|
37414
|
-
var formsViewCommand =
|
|
38236
|
+
var formsViewCommand = defineCommand130({
|
|
37415
38237
|
meta: {
|
|
37416
38238
|
name: "view",
|
|
37417
38239
|
description: "Show one HubSpot form's fields and configuration. Example: baker hubspot forms view 1a2b3c"
|
|
@@ -37463,7 +38285,7 @@ var formsViewCommand = defineCommand129({
|
|
|
37463
38285
|
}
|
|
37464
38286
|
}
|
|
37465
38287
|
});
|
|
37466
|
-
var meetingsListCommand =
|
|
38288
|
+
var meetingsListCommand = defineCommand130({
|
|
37467
38289
|
meta: {
|
|
37468
38290
|
name: "list",
|
|
37469
38291
|
description: "List HubSpot meeting links (calendars). Example: baker hubspot meetings list"
|
|
@@ -37505,7 +38327,7 @@ var meetingsListCommand = defineCommand129({
|
|
|
37505
38327
|
}
|
|
37506
38328
|
}
|
|
37507
38329
|
});
|
|
37508
|
-
var meetingsViewCommand =
|
|
38330
|
+
var meetingsViewCommand = defineCommand130({
|
|
37509
38331
|
meta: {
|
|
37510
38332
|
name: "view",
|
|
37511
38333
|
description: "Show one HubSpot meeting link's booking fields. Example: baker hubspot meetings view discovery-call"
|
|
@@ -37549,7 +38371,7 @@ var meetingsViewCommand = defineCommand129({
|
|
|
37549
38371
|
}
|
|
37550
38372
|
}
|
|
37551
38373
|
});
|
|
37552
|
-
var formsSubmissionsCommand =
|
|
38374
|
+
var formsSubmissionsCommand = defineCommand130({
|
|
37553
38375
|
meta: {
|
|
37554
38376
|
name: "submissions",
|
|
37555
38377
|
description: "How many leads a form received, and when. Example: baker hubspot forms submissions <formId> --days 30"
|
|
@@ -37598,7 +38420,7 @@ var formsSubmissionsCommand = defineCommand129({
|
|
|
37598
38420
|
}
|
|
37599
38421
|
}
|
|
37600
38422
|
});
|
|
37601
|
-
var workflowsListCommand =
|
|
38423
|
+
var workflowsListCommand = defineCommand130({
|
|
37602
38424
|
meta: {
|
|
37603
38425
|
name: "list",
|
|
37604
38426
|
description: "List HubSpot workflows. Example: baker hubspot workflows list --enabled-only"
|
|
@@ -37663,7 +38485,7 @@ function workflowViewHints(workflow) {
|
|
|
37663
38485
|
}
|
|
37664
38486
|
return hints;
|
|
37665
38487
|
}
|
|
37666
|
-
var workflowsViewCommand =
|
|
38488
|
+
var workflowsViewCommand = defineCommand130({
|
|
37667
38489
|
meta: {
|
|
37668
38490
|
name: "view",
|
|
37669
38491
|
description: "Read one workflow's real configuration \u2014 enrolment, branches in order, and what each step writes. Example: baker hubspot workflows view 121183594 --full"
|
|
@@ -37690,7 +38512,7 @@ var workflowsViewCommand = defineCommand129({
|
|
|
37690
38512
|
}
|
|
37691
38513
|
}
|
|
37692
38514
|
});
|
|
37693
|
-
var pipelinesListCommand =
|
|
38515
|
+
var pipelinesListCommand = defineCommand130({
|
|
37694
38516
|
meta: {
|
|
37695
38517
|
name: "list",
|
|
37696
38518
|
description: "List HubSpot deal pipelines and their stages. Example: baker hubspot pipelines list"
|
|
@@ -37707,7 +38529,7 @@ var pipelinesListCommand = defineCommand129({
|
|
|
37707
38529
|
}
|
|
37708
38530
|
}
|
|
37709
38531
|
});
|
|
37710
|
-
var contactsSummaryCommand =
|
|
38532
|
+
var contactsSummaryCommand = defineCommand130({
|
|
37711
38533
|
meta: {
|
|
37712
38534
|
name: "summary",
|
|
37713
38535
|
description: "Whether recent leads are being worked, as counts. Example: baker hubspot contacts summary --days 30"
|
|
@@ -37777,7 +38599,7 @@ function contactLookupHints(data) {
|
|
|
37777
38599
|
}
|
|
37778
38600
|
return hints;
|
|
37779
38601
|
}
|
|
37780
|
-
var contactsLookupCommand =
|
|
38602
|
+
var contactsLookupCommand = defineCommand130({
|
|
37781
38603
|
meta: {
|
|
37782
38604
|
name: "lookup",
|
|
37783
38605
|
description: "Find one contact by email and see whether it was worked. Example: baker hubspot contacts lookup a@b.com"
|
|
@@ -37801,27 +38623,27 @@ var contactsLookupCommand = defineCommand129({
|
|
|
37801
38623
|
}
|
|
37802
38624
|
}
|
|
37803
38625
|
});
|
|
37804
|
-
var contactsCommand =
|
|
38626
|
+
var contactsCommand = defineCommand130({
|
|
37805
38627
|
meta: { name: "contacts", description: "Contacts on the connected HubSpot account." },
|
|
37806
38628
|
subCommands: { summary: contactsSummaryCommand, lookup: contactsLookupCommand }
|
|
37807
38629
|
});
|
|
37808
|
-
var formsCommand =
|
|
38630
|
+
var formsCommand = defineCommand130({
|
|
37809
38631
|
meta: { name: "forms", description: "HubSpot forms on the connected account." },
|
|
37810
38632
|
subCommands: { list: formsListCommand, view: formsViewCommand, submissions: formsSubmissionsCommand }
|
|
37811
38633
|
});
|
|
37812
|
-
var workflowsCommand =
|
|
38634
|
+
var workflowsCommand = defineCommand130({
|
|
37813
38635
|
meta: { name: "workflows", description: "HubSpot workflows on the connected account." },
|
|
37814
38636
|
subCommands: { list: workflowsListCommand, view: workflowsViewCommand }
|
|
37815
38637
|
});
|
|
37816
|
-
var pipelinesCommand =
|
|
38638
|
+
var pipelinesCommand = defineCommand130({
|
|
37817
38639
|
meta: { name: "pipelines", description: "HubSpot deal pipelines on the connected account." },
|
|
37818
38640
|
subCommands: { list: pipelinesListCommand }
|
|
37819
38641
|
});
|
|
37820
|
-
var meetingsCommand =
|
|
38642
|
+
var meetingsCommand = defineCommand130({
|
|
37821
38643
|
meta: { name: "meetings", description: "HubSpot meeting links (calendars) on the connected account." },
|
|
37822
38644
|
subCommands: { list: meetingsListCommand, view: meetingsViewCommand }
|
|
37823
38645
|
});
|
|
37824
|
-
var hubspotCommand =
|
|
38646
|
+
var hubspotCommand = defineCommand130({
|
|
37825
38647
|
meta: {
|
|
37826
38648
|
name: "hubspot",
|
|
37827
38649
|
description: `Read the connected HubSpot account \u2014 forms, the leads they received, workflows, deal pipelines and
|
|
@@ -37859,10 +38681,10 @@ Full guide: __tooling__/docs/tools/baker/hubspot.md`
|
|
|
37859
38681
|
});
|
|
37860
38682
|
|
|
37861
38683
|
// src/commands/images/index.ts
|
|
37862
|
-
import { defineCommand as
|
|
38684
|
+
import { defineCommand as defineCommand156 } from "citty";
|
|
37863
38685
|
|
|
37864
38686
|
// src/commands/images/crop.ts
|
|
37865
|
-
import { defineCommand as
|
|
38687
|
+
import { defineCommand as defineCommand131 } from "citty";
|
|
37866
38688
|
|
|
37867
38689
|
// src/lib/image/crop-sprite.ts
|
|
37868
38690
|
import sharp from "sharp";
|
|
@@ -37877,7 +38699,7 @@ function cropSprite(input, region) {
|
|
|
37877
38699
|
|
|
37878
38700
|
// src/lib/image/io.ts
|
|
37879
38701
|
import { randomBytes } from "crypto";
|
|
37880
|
-
import { glob as fsGlob, readFile as
|
|
38702
|
+
import { glob as fsGlob, readFile as readFile21, rename, stat as stat5, writeFile as writeFile11 } from "fs/promises";
|
|
37881
38703
|
import { dirname as dirname2, extname as extname2, join as join5, resolve as resolve4 } from "path";
|
|
37882
38704
|
var REMOTE_RE = /^https?:\/\//i;
|
|
37883
38705
|
var GLOB_RE = /[*?[\]{}]/;
|
|
@@ -37910,11 +38732,11 @@ async function readImageBuffer(pathOrUrl) {
|
|
|
37910
38732
|
const { buffer } = await fetchExternalBytes(pathOrUrl, { maxBytes: MAX_REMOTE_IMAGE_BYTES });
|
|
37911
38733
|
return buffer;
|
|
37912
38734
|
}
|
|
37913
|
-
return
|
|
38735
|
+
return readFile21(pathOrUrl);
|
|
37914
38736
|
}
|
|
37915
|
-
async function isDirectory(
|
|
38737
|
+
async function isDirectory(path41) {
|
|
37916
38738
|
try {
|
|
37917
|
-
const s = await
|
|
38739
|
+
const s = await stat5(path41);
|
|
37918
38740
|
return s.isDirectory();
|
|
37919
38741
|
} catch {
|
|
37920
38742
|
return false;
|
|
@@ -37984,7 +38806,7 @@ function emitError2(err) {
|
|
|
37984
38806
|
}
|
|
37985
38807
|
process.exit(1);
|
|
37986
38808
|
}
|
|
37987
|
-
var cropCommand =
|
|
38809
|
+
var cropCommand = defineCommand131({
|
|
37988
38810
|
meta: {
|
|
37989
38811
|
name: "crop",
|
|
37990
38812
|
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"
|
|
@@ -38020,7 +38842,7 @@ var cropCommand = defineCommand130({
|
|
|
38020
38842
|
});
|
|
38021
38843
|
|
|
38022
38844
|
// src/commands/images/delete.ts
|
|
38023
|
-
import { defineCommand as
|
|
38845
|
+
import { defineCommand as defineCommand132 } from "citty";
|
|
38024
38846
|
registerSchema({
|
|
38025
38847
|
command: "images.delete",
|
|
38026
38848
|
description: "Delete an image by ID",
|
|
@@ -38034,7 +38856,7 @@ registerSchema({
|
|
|
38034
38856
|
}
|
|
38035
38857
|
}
|
|
38036
38858
|
});
|
|
38037
|
-
var deleteCommand2 =
|
|
38859
|
+
var deleteCommand2 = defineCommand132({
|
|
38038
38860
|
meta: {
|
|
38039
38861
|
name: "delete",
|
|
38040
38862
|
description: "Delete an image by ID. Use --dry-run to preview. Example: baker images delete j571abc123 --dry-run"
|
|
@@ -38075,7 +38897,7 @@ var deleteCommand2 = defineCommand131({
|
|
|
38075
38897
|
});
|
|
38076
38898
|
|
|
38077
38899
|
// src/commands/images/dimensions.ts
|
|
38078
|
-
import { defineCommand as
|
|
38900
|
+
import { defineCommand as defineCommand133 } from "citty";
|
|
38079
38901
|
|
|
38080
38902
|
// src/lib/image/dimensions.ts
|
|
38081
38903
|
import { imageSize } from "image-size";
|
|
@@ -38104,7 +38926,7 @@ registerSchema({
|
|
|
38104
38926
|
}
|
|
38105
38927
|
}
|
|
38106
38928
|
});
|
|
38107
|
-
var dimensionsCommand =
|
|
38929
|
+
var dimensionsCommand = defineCommand133({
|
|
38108
38930
|
meta: {
|
|
38109
38931
|
name: "dimensions",
|
|
38110
38932
|
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"
|
|
@@ -38160,7 +38982,7 @@ var dimensionsCommand = defineCommand132({
|
|
|
38160
38982
|
});
|
|
38161
38983
|
|
|
38162
38984
|
// src/commands/images/download.ts
|
|
38163
|
-
import { defineCommand as
|
|
38985
|
+
import { defineCommand as defineCommand134 } from "citty";
|
|
38164
38986
|
|
|
38165
38987
|
// src/commands/images/downloadPaths.ts
|
|
38166
38988
|
import { basename as basename2, extname as extname3, join as join6 } from "path";
|
|
@@ -38216,13 +39038,13 @@ function resolveDownloadPath({ baseName, extension, out, outIsDirectory: outIsDi
|
|
|
38216
39038
|
}
|
|
38217
39039
|
function disambiguate(paths) {
|
|
38218
39040
|
const taken = /* @__PURE__ */ new Set();
|
|
38219
|
-
return paths.map((
|
|
38220
|
-
if (!taken.has(
|
|
38221
|
-
taken.add(
|
|
38222
|
-
return
|
|
39041
|
+
return paths.map((path41) => {
|
|
39042
|
+
if (!taken.has(path41)) {
|
|
39043
|
+
taken.add(path41);
|
|
39044
|
+
return path41;
|
|
38223
39045
|
}
|
|
38224
|
-
const ext = extname3(
|
|
38225
|
-
const stem =
|
|
39046
|
+
const ext = extname3(path41);
|
|
39047
|
+
const stem = path41.slice(0, path41.length - ext.length);
|
|
38226
39048
|
let n = 2;
|
|
38227
39049
|
while (taken.has(`${stem}-${n}${ext}`)) n += 1;
|
|
38228
39050
|
const unique = `${stem}-${n}${ext}`;
|
|
@@ -38349,10 +39171,10 @@ async function runDownloads(plan) {
|
|
|
38349
39171
|
const paths = disambiguate(fetched.map((item) => item.path));
|
|
38350
39172
|
const downloaded = [];
|
|
38351
39173
|
for (const [index, item] of fetched.entries()) {
|
|
38352
|
-
const
|
|
39174
|
+
const path41 = paths[index] ?? item.path;
|
|
38353
39175
|
try {
|
|
38354
|
-
await atomicWrite(
|
|
38355
|
-
downloaded.push({ input: item.input, output:
|
|
39176
|
+
await atomicWrite(path41, item.buffer);
|
|
39177
|
+
downloaded.push({ input: item.input, output: path41, bytes: item.buffer.length, contentType: item.contentType });
|
|
38356
39178
|
} catch (err) {
|
|
38357
39179
|
failed.push({ input: item.input, error: failureMessage(err, "Write failed") });
|
|
38358
39180
|
}
|
|
@@ -38385,7 +39207,7 @@ function emitError3(err) {
|
|
|
38385
39207
|
writeJson({ ok: false, error: { code: "INTERNAL_ERROR", message: "Unexpected error" } });
|
|
38386
39208
|
process.exit(1);
|
|
38387
39209
|
}
|
|
38388
|
-
var downloadCommand =
|
|
39210
|
+
var downloadCommand = defineCommand134({
|
|
38389
39211
|
meta: {
|
|
38390
39212
|
name: "download",
|
|
38391
39213
|
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"
|
|
@@ -38418,7 +39240,7 @@ var downloadCommand = defineCommand133({
|
|
|
38418
39240
|
});
|
|
38419
39241
|
|
|
38420
39242
|
// src/commands/images/extract.ts
|
|
38421
|
-
import { defineCommand as
|
|
39243
|
+
import { defineCommand as defineCommand135 } from "citty";
|
|
38422
39244
|
|
|
38423
39245
|
// src/commands/images/autoIngest.ts
|
|
38424
39246
|
var AUTO_INGEST_MAX = {
|
|
@@ -38465,7 +39287,7 @@ registerSchema({
|
|
|
38465
39287
|
}
|
|
38466
39288
|
}
|
|
38467
39289
|
});
|
|
38468
|
-
var extractCommand =
|
|
39290
|
+
var extractCommand = defineCommand135({
|
|
38469
39291
|
meta: {
|
|
38470
39292
|
name: "extract",
|
|
38471
39293
|
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"
|
|
@@ -38520,7 +39342,7 @@ var extractCommand = defineCommand134({
|
|
|
38520
39342
|
});
|
|
38521
39343
|
|
|
38522
39344
|
// src/commands/images/find.ts
|
|
38523
|
-
import { defineCommand as
|
|
39345
|
+
import { defineCommand as defineCommand136 } from "citty";
|
|
38524
39346
|
|
|
38525
39347
|
// src/commands/images/providerHits.ts
|
|
38526
39348
|
function asRecord3(value) {
|
|
@@ -38658,7 +39480,7 @@ registerSchema({
|
|
|
38658
39480
|
full: { type: "boolean", description: "Include full metadata", required: false, default: false }
|
|
38659
39481
|
}
|
|
38660
39482
|
});
|
|
38661
|
-
var findCommand =
|
|
39483
|
+
var findCommand = defineCommand136({
|
|
38662
39484
|
meta: {
|
|
38663
39485
|
name: "find",
|
|
38664
39486
|
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"
|
|
@@ -38731,7 +39553,7 @@ var findCommand = defineCommand135({
|
|
|
38731
39553
|
});
|
|
38732
39554
|
|
|
38733
39555
|
// src/commands/images/get.ts
|
|
38734
|
-
import { defineCommand as
|
|
39556
|
+
import { defineCommand as defineCommand137 } from "citty";
|
|
38735
39557
|
registerSchema({
|
|
38736
39558
|
command: "images.get",
|
|
38737
39559
|
description: "Get a single image by ID",
|
|
@@ -38739,7 +39561,7 @@ registerSchema({
|
|
|
38739
39561
|
id: { type: "string", description: "Image ID", required: true }
|
|
38740
39562
|
}
|
|
38741
39563
|
});
|
|
38742
|
-
var getCommand3 =
|
|
39564
|
+
var getCommand3 = defineCommand137({
|
|
38743
39565
|
meta: { name: "get", description: "Get a single image by ID. Example: baker images get j571abc123" },
|
|
38744
39566
|
args: {
|
|
38745
39567
|
id: { type: "positional", description: "Image ID", required: false },
|
|
@@ -38775,7 +39597,7 @@ var getCommand3 = defineCommand136({
|
|
|
38775
39597
|
});
|
|
38776
39598
|
|
|
38777
39599
|
// src/commands/images/gif.ts
|
|
38778
|
-
import { defineCommand as
|
|
39600
|
+
import { defineCommand as defineCommand138 } from "citty";
|
|
38779
39601
|
registerSchema({
|
|
38780
39602
|
command: "images.gif",
|
|
38781
39603
|
description: "Search Giphy for GIFs / reaction memes (paid social creative).",
|
|
@@ -38807,7 +39629,7 @@ registerSchema({
|
|
|
38807
39629
|
}
|
|
38808
39630
|
}
|
|
38809
39631
|
});
|
|
38810
|
-
var gifCommand =
|
|
39632
|
+
var gifCommand = defineCommand138({
|
|
38811
39633
|
meta: {
|
|
38812
39634
|
name: "gif",
|
|
38813
39635
|
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"
|
|
@@ -38854,7 +39676,7 @@ var gifCommand = defineCommand137({
|
|
|
38854
39676
|
});
|
|
38855
39677
|
|
|
38856
39678
|
// src/commands/images/google.ts
|
|
38857
|
-
import { defineCommand as
|
|
39679
|
+
import { defineCommand as defineCommand139 } from "citty";
|
|
38858
39680
|
var GOOGLE_ERROR_FIX = {
|
|
38859
39681
|
action: "use_different_resource",
|
|
38860
39682
|
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."
|
|
@@ -38894,7 +39716,7 @@ registerSchema({
|
|
|
38894
39716
|
}
|
|
38895
39717
|
}
|
|
38896
39718
|
});
|
|
38897
|
-
var googleCommand2 =
|
|
39719
|
+
var googleCommand2 = defineCommand139({
|
|
38898
39720
|
meta: {
|
|
38899
39721
|
name: "google",
|
|
38900
39722
|
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"
|
|
@@ -38967,7 +39789,7 @@ var googleCommand2 = defineCommand138({
|
|
|
38967
39789
|
});
|
|
38968
39790
|
|
|
38969
39791
|
// src/commands/images/group.ts
|
|
38970
|
-
import { defineCommand as
|
|
39792
|
+
import { defineCommand as defineCommand140 } from "citty";
|
|
38971
39793
|
|
|
38972
39794
|
// src/commands/mediaGroup.ts
|
|
38973
39795
|
async function runMediaGroupLookup(input) {
|
|
@@ -39020,7 +39842,7 @@ registerSchema({
|
|
|
39020
39842
|
"group-key": { type: "string", description: "The set key directly, when you already have it", required: false }
|
|
39021
39843
|
}
|
|
39022
39844
|
});
|
|
39023
|
-
var groupCommand =
|
|
39845
|
+
var groupCommand = defineCommand140({
|
|
39024
39846
|
meta: {
|
|
39025
39847
|
name: "group",
|
|
39026
39848
|
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>"
|
|
@@ -39040,7 +39862,7 @@ var groupCommand = defineCommand139({
|
|
|
39040
39862
|
});
|
|
39041
39863
|
|
|
39042
39864
|
// src/commands/images/icon.ts
|
|
39043
|
-
import { defineCommand as
|
|
39865
|
+
import { defineCommand as defineCommand141 } from "citty";
|
|
39044
39866
|
|
|
39045
39867
|
// src/commands/images/brandVerification.ts
|
|
39046
39868
|
function brandToken(domain) {
|
|
@@ -39137,7 +39959,7 @@ registerSchema({
|
|
|
39137
39959
|
full: { type: "boolean", description: "Include full metadata", required: false, default: false }
|
|
39138
39960
|
}
|
|
39139
39961
|
});
|
|
39140
|
-
var iconCommand =
|
|
39962
|
+
var iconCommand = defineCommand141({
|
|
39141
39963
|
meta: {
|
|
39142
39964
|
name: "icon",
|
|
39143
39965
|
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'"
|
|
@@ -39207,7 +40029,7 @@ var iconCommand = defineCommand140({
|
|
|
39207
40029
|
});
|
|
39208
40030
|
|
|
39209
40031
|
// src/commands/images/ingest.ts
|
|
39210
|
-
import { defineCommand as
|
|
40032
|
+
import { defineCommand as defineCommand142 } from "citty";
|
|
39211
40033
|
var SOURCE_VALUES = imageSourceSchema.options.join(" | ");
|
|
39212
40034
|
registerSchema({
|
|
39213
40035
|
command: "images.ingest",
|
|
@@ -39222,7 +40044,7 @@ registerSchema({
|
|
|
39222
40044
|
fields: { type: "string", description: "Comma-separated field names to include", required: false }
|
|
39223
40045
|
}
|
|
39224
40046
|
});
|
|
39225
|
-
var ingestCommand =
|
|
40047
|
+
var ingestCommand = defineCommand142({
|
|
39226
40048
|
meta: {
|
|
39227
40049
|
name: "ingest",
|
|
39228
40050
|
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"
|
|
@@ -39291,7 +40113,7 @@ var ingestCommand = defineCommand141({
|
|
|
39291
40113
|
});
|
|
39292
40114
|
|
|
39293
40115
|
// src/commands/images/layerize.ts
|
|
39294
|
-
import { defineCommand as
|
|
40116
|
+
import { defineCommand as defineCommand143 } from "citty";
|
|
39295
40117
|
registerSchema({
|
|
39296
40118
|
command: "images.layerize",
|
|
39297
40119
|
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.",
|
|
@@ -39355,7 +40177,7 @@ async function pollUntilSettled(imageId, maxWait) {
|
|
|
39355
40177
|
}
|
|
39356
40178
|
return null;
|
|
39357
40179
|
}
|
|
39358
|
-
var layerizeCommand =
|
|
40180
|
+
var layerizeCommand = defineCommand143({
|
|
39359
40181
|
meta: {
|
|
39360
40182
|
name: "layerize",
|
|
39361
40183
|
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'"
|
|
@@ -39424,7 +40246,7 @@ registerSchema({
|
|
|
39424
40246
|
full: { type: "boolean", description: "Include geometry and typography for every layer", required: false }
|
|
39425
40247
|
}
|
|
39426
40248
|
});
|
|
39427
|
-
var layersCommand =
|
|
40249
|
+
var layersCommand = defineCommand143({
|
|
39428
40250
|
meta: {
|
|
39429
40251
|
name: "layers",
|
|
39430
40252
|
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"
|
|
@@ -39464,7 +40286,7 @@ var layersCommand = defineCommand142({
|
|
|
39464
40286
|
});
|
|
39465
40287
|
|
|
39466
40288
|
// src/commands/images/library.ts
|
|
39467
|
-
import { defineCommand as
|
|
40289
|
+
import { defineCommand as defineCommand144 } from "citty";
|
|
39468
40290
|
registerSchema({
|
|
39469
40291
|
command: "images.library",
|
|
39470
40292
|
description: "Search the company image library. Returns only ready images.",
|
|
@@ -39490,7 +40312,7 @@ registerSchema({
|
|
|
39490
40312
|
}
|
|
39491
40313
|
}
|
|
39492
40314
|
});
|
|
39493
|
-
var libraryCommand =
|
|
40315
|
+
var libraryCommand = defineCommand144({
|
|
39494
40316
|
meta: {
|
|
39495
40317
|
name: "library",
|
|
39496
40318
|
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"
|
|
@@ -39553,7 +40375,7 @@ var libraryCommand = defineCommand143({
|
|
|
39553
40375
|
});
|
|
39554
40376
|
|
|
39555
40377
|
// src/commands/images/logo.ts
|
|
39556
|
-
import { defineCommand as
|
|
40378
|
+
import { defineCommand as defineCommand145 } from "citty";
|
|
39557
40379
|
registerSchema({
|
|
39558
40380
|
command: "images.logo",
|
|
39559
40381
|
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.",
|
|
@@ -39581,7 +40403,7 @@ registerSchema({
|
|
|
39581
40403
|
full: { type: "boolean", description: "Include full metadata", required: false, default: false }
|
|
39582
40404
|
}
|
|
39583
40405
|
});
|
|
39584
|
-
var logoCommand =
|
|
40406
|
+
var logoCommand = defineCommand145({
|
|
39585
40407
|
meta: {
|
|
39586
40408
|
name: "logo",
|
|
39587
40409
|
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"
|
|
@@ -39654,7 +40476,7 @@ var logoCommand = defineCommand144({
|
|
|
39654
40476
|
});
|
|
39655
40477
|
|
|
39656
40478
|
// src/commands/images/normalize.ts
|
|
39657
|
-
import { defineCommand as
|
|
40479
|
+
import { defineCommand as defineCommand146 } from "citty";
|
|
39658
40480
|
|
|
39659
40481
|
// src/lib/image/color-changer.ts
|
|
39660
40482
|
import quantize from "quantize";
|
|
@@ -40386,7 +41208,7 @@ function coerceRawArgs(args) {
|
|
|
40386
41208
|
"dry-run": bool(args["dry-run"])
|
|
40387
41209
|
};
|
|
40388
41210
|
}
|
|
40389
|
-
var normalizeCommand2 =
|
|
41211
|
+
var normalizeCommand2 = defineCommand146({
|
|
40390
41212
|
meta: {
|
|
40391
41213
|
name: "normalize",
|
|
40392
41214
|
description: `Normalize logos / images: declarative recolor + bg removal + trim + resize. Operates on local files; writes in-place by default.
|
|
@@ -40441,7 +41263,7 @@ Examples:
|
|
|
40441
41263
|
});
|
|
40442
41264
|
|
|
40443
41265
|
// src/commands/images/pinterest.ts
|
|
40444
|
-
import { defineCommand as
|
|
41266
|
+
import { defineCommand as defineCommand147 } from "citty";
|
|
40445
41267
|
registerSchema({
|
|
40446
41268
|
command: "images.pinterest",
|
|
40447
41269
|
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.",
|
|
@@ -40461,7 +41283,7 @@ registerSchema({
|
|
|
40461
41283
|
}
|
|
40462
41284
|
}
|
|
40463
41285
|
});
|
|
40464
|
-
var pinterestCommand =
|
|
41286
|
+
var pinterestCommand = defineCommand147({
|
|
40465
41287
|
meta: {
|
|
40466
41288
|
name: "pinterest",
|
|
40467
41289
|
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'"
|
|
@@ -40518,7 +41340,7 @@ var pinterestCommand = defineCommand146({
|
|
|
40518
41340
|
});
|
|
40519
41341
|
|
|
40520
41342
|
// src/commands/images/screenshot.ts
|
|
40521
|
-
import { defineCommand as
|
|
41343
|
+
import { defineCommand as defineCommand148 } from "citty";
|
|
40522
41344
|
registerSchema({
|
|
40523
41345
|
command: "images.screenshot",
|
|
40524
41346
|
description: "Capture a website screenshot via ScreenshotOne. Auto-ingests on success.",
|
|
@@ -40537,7 +41359,7 @@ registerSchema({
|
|
|
40537
41359
|
full: { type: "boolean", description: "Include full metadata", required: false, default: false }
|
|
40538
41360
|
}
|
|
40539
41361
|
});
|
|
40540
|
-
var screenshotCommand =
|
|
41362
|
+
var screenshotCommand = defineCommand148({
|
|
40541
41363
|
meta: {
|
|
40542
41364
|
name: "screenshot",
|
|
40543
41365
|
description: "Screenshot a URL via ScreenshotOne. $0.009/capture. Auto-ingests to library.\n\nExample: baker images screenshot https://stripe.com --full-page"
|
|
@@ -40601,7 +41423,7 @@ var screenshotCommand = defineCommand147({
|
|
|
40601
41423
|
});
|
|
40602
41424
|
|
|
40603
41425
|
// src/commands/images/search.ts
|
|
40604
|
-
import { defineCommand as
|
|
41426
|
+
import { defineCommand as defineCommand149 } from "citty";
|
|
40605
41427
|
registerSchema({
|
|
40606
41428
|
command: "images.search",
|
|
40607
41429
|
description: "Search images by text query. Only returns ready images.",
|
|
@@ -40617,7 +41439,7 @@ registerSchema({
|
|
|
40617
41439
|
tags: { type: "string", description: "Comma-separated tags to filter by", required: false }
|
|
40618
41440
|
}
|
|
40619
41441
|
});
|
|
40620
|
-
var searchCommand =
|
|
41442
|
+
var searchCommand = defineCommand149({
|
|
40621
41443
|
meta: {
|
|
40622
41444
|
name: "search",
|
|
40623
41445
|
description: "Semantic search images by text query. Uses hybrid BM25 + vector + reranking. Example: baker images search 'hero banner' --aspect-ratio 16:9 --tags logo"
|
|
@@ -40677,7 +41499,7 @@ var searchCommand = defineCommand148({
|
|
|
40677
41499
|
});
|
|
40678
41500
|
|
|
40679
41501
|
// src/commands/images/sticker.ts
|
|
40680
|
-
import { defineCommand as
|
|
41502
|
+
import { defineCommand as defineCommand150 } from "citty";
|
|
40681
41503
|
registerSchema({
|
|
40682
41504
|
command: "images.sticker",
|
|
40683
41505
|
description: "Search Giphy stickers \u2014 transparent-background overlays for ad creative.",
|
|
@@ -40709,7 +41531,7 @@ registerSchema({
|
|
|
40709
41531
|
}
|
|
40710
41532
|
}
|
|
40711
41533
|
});
|
|
40712
|
-
var stickerCommand =
|
|
41534
|
+
var stickerCommand = defineCommand150({
|
|
40713
41535
|
meta: {
|
|
40714
41536
|
name: "sticker",
|
|
40715
41537
|
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"
|
|
@@ -40756,7 +41578,7 @@ var stickerCommand = defineCommand149({
|
|
|
40756
41578
|
});
|
|
40757
41579
|
|
|
40758
41580
|
// src/commands/images/stock.ts
|
|
40759
|
-
import { defineCommand as
|
|
41581
|
+
import { defineCommand as defineCommand151 } from "citty";
|
|
40760
41582
|
var STOCK_ERROR_FIX = {
|
|
40761
41583
|
action: "use_different_resource",
|
|
40762
41584
|
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."
|
|
@@ -40833,7 +41655,7 @@ function buildStockRequest(query, args) {
|
|
|
40833
41655
|
if (args.context) body.descriptionContext = args.context;
|
|
40834
41656
|
return body;
|
|
40835
41657
|
}
|
|
40836
|
-
var stockCommand =
|
|
41658
|
+
var stockCommand = defineCommand151({
|
|
40837
41659
|
meta: {
|
|
40838
41660
|
name: "stock",
|
|
40839
41661
|
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"
|
|
@@ -40906,7 +41728,7 @@ var stockCommand = defineCommand150({
|
|
|
40906
41728
|
});
|
|
40907
41729
|
|
|
40908
41730
|
// src/lib/tags-command.ts
|
|
40909
|
-
import { defineCommand as
|
|
41731
|
+
import { defineCommand as defineCommand152 } from "citty";
|
|
40910
41732
|
function makeTagsCommand(command, label, endpoint) {
|
|
40911
41733
|
registerSchema({
|
|
40912
41734
|
command: `${command}.tags`,
|
|
@@ -40915,7 +41737,7 @@ function makeTagsCommand(command, label, endpoint) {
|
|
|
40915
41737
|
output: { type: "string", description: "Output format: md|json", required: false, default: "md" }
|
|
40916
41738
|
}
|
|
40917
41739
|
});
|
|
40918
|
-
return
|
|
41740
|
+
return defineCommand152({
|
|
40919
41741
|
meta: {
|
|
40920
41742
|
name: "tags",
|
|
40921
41743
|
description: `List the available ${label} tag names (defaults + company custom tags). Use before filtering with --tags. Example: baker ${command} tags`
|
|
@@ -40951,7 +41773,7 @@ function makeTagsCommand(command, label, endpoint) {
|
|
|
40951
41773
|
var tagsCommand3 = makeTagsCommand("images", "image", "/api/images/tags");
|
|
40952
41774
|
|
|
40953
41775
|
// src/commands/images/upload.ts
|
|
40954
|
-
import { defineCommand as
|
|
41776
|
+
import { defineCommand as defineCommand153 } from "citty";
|
|
40955
41777
|
registerSchema({
|
|
40956
41778
|
command: "images.upload",
|
|
40957
41779
|
description: "Upload an image to the library \u2014 local file path or remote http(s) URL.",
|
|
@@ -40989,7 +41811,7 @@ registerSchema({
|
|
|
40989
41811
|
function isRemoteUrl2(value) {
|
|
40990
41812
|
return /^https?:\/\//i.test(value);
|
|
40991
41813
|
}
|
|
40992
|
-
var uploadCommand =
|
|
41814
|
+
var uploadCommand = defineCommand153({
|
|
40993
41815
|
meta: {
|
|
40994
41816
|
name: "upload",
|
|
40995
41817
|
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'"
|
|
@@ -41082,7 +41904,7 @@ async function uploadLocal(target, args) {
|
|
|
41082
41904
|
}
|
|
41083
41905
|
|
|
41084
41906
|
// src/commands/images/upscale.ts
|
|
41085
|
-
import { defineCommand as
|
|
41907
|
+
import { defineCommand as defineCommand154 } from "citty";
|
|
41086
41908
|
registerSchema({
|
|
41087
41909
|
command: "images.upscale",
|
|
41088
41910
|
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).",
|
|
@@ -41097,7 +41919,7 @@ registerSchema({
|
|
|
41097
41919
|
}
|
|
41098
41920
|
});
|
|
41099
41921
|
var POLL_INTERVAL_MS4 = 1500;
|
|
41100
|
-
var upscaleCommand =
|
|
41922
|
+
var upscaleCommand = defineCommand154({
|
|
41101
41923
|
meta: {
|
|
41102
41924
|
name: "upscale",
|
|
41103
41925
|
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"
|
|
@@ -41152,7 +41974,7 @@ var upscaleCommand = defineCommand153({
|
|
|
41152
41974
|
});
|
|
41153
41975
|
|
|
41154
41976
|
// src/commands/images/use.ts
|
|
41155
|
-
import { defineCommand as
|
|
41977
|
+
import { defineCommand as defineCommand155 } from "citty";
|
|
41156
41978
|
registerSchema({
|
|
41157
41979
|
command: "images.use",
|
|
41158
41980
|
description: "Ingest a URL and wait for the library record to be ready.",
|
|
@@ -41181,7 +42003,7 @@ function emitReady(ingestResult, doc, args) {
|
|
|
41181
42003
|
args.full === true
|
|
41182
42004
|
);
|
|
41183
42005
|
}
|
|
41184
|
-
var useCommand =
|
|
42006
|
+
var useCommand = defineCommand155({
|
|
41185
42007
|
meta: {
|
|
41186
42008
|
name: "use",
|
|
41187
42009
|
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"
|
|
@@ -41230,7 +42052,7 @@ var useCommand = defineCommand154({
|
|
|
41230
42052
|
});
|
|
41231
42053
|
|
|
41232
42054
|
// src/commands/images/index.ts
|
|
41233
|
-
var imagesCommand =
|
|
42055
|
+
var imagesCommand = defineCommand156({
|
|
41234
42056
|
meta: {
|
|
41235
42057
|
name: "images",
|
|
41236
42058
|
description: `Find, source, and normalize images. Subcommands route by provider so cost + license are explicit.
|
|
@@ -41308,12 +42130,12 @@ Full guide: __tooling__/docs/tools/baker/images.md`
|
|
|
41308
42130
|
});
|
|
41309
42131
|
|
|
41310
42132
|
// src/commands/landing/index.ts
|
|
41311
|
-
import { defineCommand as
|
|
42133
|
+
import { defineCommand as defineCommand168 } from "citty";
|
|
41312
42134
|
|
|
41313
42135
|
// src/commands/landing/critique.ts
|
|
41314
42136
|
import { readdir as readdir9, stat as stat6 } from "fs/promises";
|
|
41315
|
-
import
|
|
41316
|
-
import { defineCommand as
|
|
42137
|
+
import path29 from "path";
|
|
42138
|
+
import { defineCommand as defineCommand157 } from "citty";
|
|
41317
42139
|
|
|
41318
42140
|
// src/engine/landing/lib/constants.ts
|
|
41319
42141
|
var OVERUSED_FONTS = /* @__PURE__ */ new Set([
|
|
@@ -42280,13 +43102,13 @@ function describeCounts(findings) {
|
|
|
42280
43102
|
|
|
42281
43103
|
// src/commands/landing/snapshot.ts
|
|
42282
43104
|
import { mkdir as mkdir8, rename as rename2, writeFile as writeFile12 } from "fs/promises";
|
|
42283
|
-
import
|
|
43105
|
+
import path27 from "path";
|
|
42284
43106
|
var CRITIC_VERSION = "2";
|
|
42285
43107
|
function critiqueCacheDir(projectRoot) {
|
|
42286
|
-
return
|
|
43108
|
+
return path27.join(projectRoot, ".cache", "landing-critique");
|
|
42287
43109
|
}
|
|
42288
43110
|
function snapshotPath(projectRoot, slug) {
|
|
42289
|
-
return
|
|
43111
|
+
return path27.join(critiqueCacheDir(projectRoot), `${slug}.json`);
|
|
42290
43112
|
}
|
|
42291
43113
|
async function writeCritiqueSnapshot(projectRoot, snapshot) {
|
|
42292
43114
|
await mkdir8(critiqueCacheDir(projectRoot), { recursive: true });
|
|
@@ -42298,30 +43120,26 @@ async function writeCritiqueSnapshot(projectRoot, snapshot) {
|
|
|
42298
43120
|
}
|
|
42299
43121
|
|
|
42300
43122
|
// src/commands/landing/source-version.ts
|
|
42301
|
-
import {
|
|
42302
|
-
import
|
|
42303
|
-
|
|
42304
|
-
|
|
42305
|
-
|
|
42306
|
-
|
|
42307
|
-
for (const abs of await walkAstro(componentsDir)) {
|
|
42308
|
-
rel.push(path27.relative(landingDir, abs).split(path27.sep).join("/"));
|
|
42309
|
-
}
|
|
42310
|
-
return rel.sort((a, b) => a < b ? -1 : a > b ? 1 : 0);
|
|
43123
|
+
import { readFile as readFile22 } from "fs/promises";
|
|
43124
|
+
import path28 from "path";
|
|
43125
|
+
var CRITIQUED_ROOTS = ["src/pages/", "src/components/"];
|
|
43126
|
+
async function landingSourceRelPaths(root, slug) {
|
|
43127
|
+
const files = await landingGraphFiles(root, slug);
|
|
43128
|
+
return [...files].filter((rel) => rel.endsWith(".astro") && CRITIQUED_ROOTS.some((r) => rel.startsWith(r))).sort((a, b) => a < b ? -1 : a > b ? 1 : 0);
|
|
42311
43129
|
}
|
|
42312
|
-
async function readLandingSources(
|
|
42313
|
-
const rel = await landingSourceRelPaths(
|
|
43130
|
+
async function readLandingSources(root, slug) {
|
|
43131
|
+
const rel = await landingSourceRelPaths(root, slug);
|
|
42314
43132
|
const out = [];
|
|
42315
|
-
for (const r of rel) out.push({ path: r, text: await
|
|
43133
|
+
for (const r of rel) out.push({ path: r, text: await readFile22(path28.join(root, r), "utf8") });
|
|
42316
43134
|
return out;
|
|
42317
43135
|
}
|
|
42318
|
-
async function computeLandingSourceSha(
|
|
42319
|
-
const rel = await landingSourceRelPaths(
|
|
43136
|
+
async function computeLandingSourceSha(root, slug) {
|
|
43137
|
+
const rel = await landingSourceRelPaths(root, slug);
|
|
42320
43138
|
const parts = [];
|
|
42321
43139
|
for (const r of rel) {
|
|
42322
43140
|
let bytes;
|
|
42323
43141
|
try {
|
|
42324
|
-
bytes = await
|
|
43142
|
+
bytes = await readFile22(path28.join(root, r));
|
|
42325
43143
|
} catch {
|
|
42326
43144
|
bytes = Buffer.alloc(0);
|
|
42327
43145
|
}
|
|
@@ -42329,28 +43147,6 @@ async function computeLandingSourceSha(landingDir) {
|
|
|
42329
43147
|
}
|
|
42330
43148
|
return sha256Hex(Buffer.concat(parts));
|
|
42331
43149
|
}
|
|
42332
|
-
async function isFile(p) {
|
|
42333
|
-
try {
|
|
42334
|
-
return (await stat5(p)).isFile();
|
|
42335
|
-
} catch {
|
|
42336
|
-
return false;
|
|
42337
|
-
}
|
|
42338
|
-
}
|
|
42339
|
-
async function walkAstro(dir) {
|
|
42340
|
-
let entries;
|
|
42341
|
-
try {
|
|
42342
|
-
entries = await readdir8(dir, { withFileTypes: true });
|
|
42343
|
-
} catch {
|
|
42344
|
-
return [];
|
|
42345
|
-
}
|
|
42346
|
-
const out = [];
|
|
42347
|
-
for (const entry of entries) {
|
|
42348
|
-
const abs = path27.join(dir, entry.name);
|
|
42349
|
-
if (entry.isDirectory()) out.push(...await walkAstro(abs));
|
|
42350
|
-
else if (entry.isFile() && entry.name.endsWith(".astro")) out.push(abs);
|
|
42351
|
-
}
|
|
42352
|
-
return out;
|
|
42353
|
-
}
|
|
42354
43150
|
|
|
42355
43151
|
// src/commands/landing/critique.ts
|
|
42356
43152
|
registerSchema({
|
|
@@ -42375,14 +43171,14 @@ function parseCritiqueSlugs(args) {
|
|
|
42375
43171
|
const all = [args.slug, ...rest].filter((s) => typeof s === "string" && s.length > 0);
|
|
42376
43172
|
return [...new Set(all.map(String))];
|
|
42377
43173
|
}
|
|
42378
|
-
function
|
|
43174
|
+
function fail6(code, message, fix) {
|
|
42379
43175
|
process.stderr.write(
|
|
42380
43176
|
`${JSON.stringify({ ok: false, error: { code, message, ...fix ? { fix } : {} } }, null, 2)}
|
|
42381
43177
|
`
|
|
42382
43178
|
);
|
|
42383
43179
|
process.exit(2);
|
|
42384
43180
|
}
|
|
42385
|
-
var critiqueCommand2 =
|
|
43181
|
+
var critiqueCommand2 = defineCommand157({
|
|
42386
43182
|
meta: {
|
|
42387
43183
|
name: "critique",
|
|
42388
43184
|
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."
|
|
@@ -42400,14 +43196,14 @@ var critiqueCommand2 = defineCommand156({
|
|
|
42400
43196
|
const projectRoot = process.cwd();
|
|
42401
43197
|
for (const slug of slugs) {
|
|
42402
43198
|
if (!SLUG_RE.test(slug) || slug.includes("..")) {
|
|
42403
|
-
|
|
43199
|
+
fail6(
|
|
42404
43200
|
"INVALID_SLUG",
|
|
42405
43201
|
`"${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).`,
|
|
42406
43202
|
{ availableSlugs: await listLandingSlugs(projectRoot) }
|
|
42407
43203
|
);
|
|
42408
43204
|
}
|
|
42409
|
-
if (!await isDir(
|
|
42410
|
-
|
|
43205
|
+
if (!await isDir(path29.resolve(projectRoot, "src", "pages", slug))) {
|
|
43206
|
+
fail6("NOT_FOUND", `No landing at src/pages/${slug}/`, {
|
|
42411
43207
|
availableSlugs: await listLandingSlugs(projectRoot)
|
|
42412
43208
|
});
|
|
42413
43209
|
}
|
|
@@ -42445,8 +43241,10 @@ var critiqueCommand2 = defineCommand156({
|
|
|
42445
43241
|
}
|
|
42446
43242
|
});
|
|
42447
43243
|
async function critiqueOne(projectRoot, slug, brand) {
|
|
42448
|
-
const
|
|
42449
|
-
|
|
43244
|
+
const [sources, sourceSha] = await Promise.all([
|
|
43245
|
+
readLandingSources(projectRoot, slug),
|
|
43246
|
+
computeLandingSourceSha(projectRoot, slug)
|
|
43247
|
+
]);
|
|
42450
43248
|
const report = critiqueLanding({ slug, sources, brand });
|
|
42451
43249
|
let snapshotFailed = false;
|
|
42452
43250
|
try {
|
|
@@ -42465,7 +43263,7 @@ async function critiqueOne(projectRoot, slug, brand) {
|
|
|
42465
43263
|
}
|
|
42466
43264
|
async function listLandingSlugs(projectRoot) {
|
|
42467
43265
|
try {
|
|
42468
|
-
const entries = await readdir9(
|
|
43266
|
+
const entries = await readdir9(path29.join(projectRoot, "src", "pages"), { withFileTypes: true });
|
|
42469
43267
|
return entries.filter((e) => e.isDirectory() && !e.name.startsWith("_") && !e.name.startsWith(".")).map((e) => e.name).sort();
|
|
42470
43268
|
} catch {
|
|
42471
43269
|
return [];
|
|
@@ -42491,10 +43289,10 @@ async function isDir(p) {
|
|
|
42491
43289
|
}
|
|
42492
43290
|
|
|
42493
43291
|
// src/commands/landing/inspiration/index.ts
|
|
42494
|
-
import { defineCommand as
|
|
43292
|
+
import { defineCommand as defineCommand166 } from "citty";
|
|
42495
43293
|
|
|
42496
43294
|
// src/commands/landing/inspiration/add.ts
|
|
42497
|
-
import { defineCommand as
|
|
43295
|
+
import { defineCommand as defineCommand158 } from "citty";
|
|
42498
43296
|
|
|
42499
43297
|
// src/commands/landing/inspiration/shared.ts
|
|
42500
43298
|
var INSPIRATION_HINTS = {
|
|
@@ -42576,7 +43374,7 @@ registerSchema({
|
|
|
42576
43374
|
note: { type: "string", description: "Why this page is worth keeping", required: false }
|
|
42577
43375
|
}
|
|
42578
43376
|
});
|
|
42579
|
-
var addCommand =
|
|
43377
|
+
var addCommand = defineCommand158({
|
|
42580
43378
|
meta: {
|
|
42581
43379
|
name: "add",
|
|
42582
43380
|
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'"
|
|
@@ -42617,8 +43415,8 @@ var addCommand = defineCommand157({
|
|
|
42617
43415
|
|
|
42618
43416
|
// src/commands/landing/inspiration/code.ts
|
|
42619
43417
|
import { mkdir as mkdir9, writeFile as writeFile13 } from "fs/promises";
|
|
42620
|
-
import
|
|
42621
|
-
import { defineCommand as
|
|
43418
|
+
import path30 from "path";
|
|
43419
|
+
import { defineCommand as defineCommand159 } from "citty";
|
|
42622
43420
|
registerSchema({
|
|
42623
43421
|
command: "landing.inspiration.code",
|
|
42624
43422
|
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.",
|
|
@@ -42627,7 +43425,7 @@ registerSchema({
|
|
|
42627
43425
|
full: { type: "boolean", description: "Print the markup inline as well as writing it", required: false }
|
|
42628
43426
|
}
|
|
42629
43427
|
});
|
|
42630
|
-
var codeCommand =
|
|
43428
|
+
var codeCommand = defineCommand159({
|
|
42631
43429
|
meta: {
|
|
42632
43430
|
name: "code",
|
|
42633
43431
|
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."
|
|
@@ -42640,9 +43438,9 @@ var codeCommand = defineCommand158({
|
|
|
42640
43438
|
try {
|
|
42641
43439
|
const id = args.id;
|
|
42642
43440
|
const data = await apiGet("/api/landing-inspiration/section-code", { id });
|
|
42643
|
-
const dir =
|
|
43441
|
+
const dir = path30.join(process.cwd(), ".baker", "inspiration", id);
|
|
42644
43442
|
await mkdir9(dir, { recursive: true });
|
|
42645
|
-
const file =
|
|
43443
|
+
const file = path30.join(dir, "section.html");
|
|
42646
43444
|
await writeFile13(file, data.html);
|
|
42647
43445
|
const hints = [INSPIRATION_HINTS.structureNotCopy, INSPIRATION_HINTS.adapt];
|
|
42648
43446
|
const fidelity = fidelityHint(data.fidelity);
|
|
@@ -42652,7 +43450,7 @@ var codeCommand = defineCommand158({
|
|
|
42652
43450
|
ok: true,
|
|
42653
43451
|
data: {
|
|
42654
43452
|
id,
|
|
42655
|
-
file:
|
|
43453
|
+
file: path30.relative(process.cwd(), file),
|
|
42656
43454
|
bytes: data.html.length,
|
|
42657
43455
|
fidelity: data.fidelity,
|
|
42658
43456
|
reproduction_notes: data.reproductionNotes,
|
|
@@ -42673,7 +43471,7 @@ var codeCommand = defineCommand158({
|
|
|
42673
43471
|
});
|
|
42674
43472
|
|
|
42675
43473
|
// src/commands/landing/inspiration/favorites.ts
|
|
42676
|
-
import { defineCommand as
|
|
43474
|
+
import { defineCommand as defineCommand160 } from "citty";
|
|
42677
43475
|
registerSchema({
|
|
42678
43476
|
command: "landing.inspiration.favorites",
|
|
42679
43477
|
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.",
|
|
@@ -42687,7 +43485,7 @@ registerSchema({
|
|
|
42687
43485
|
}
|
|
42688
43486
|
}
|
|
42689
43487
|
});
|
|
42690
|
-
var favoritesCommand =
|
|
43488
|
+
var favoritesCommand = defineCommand160({
|
|
42691
43489
|
meta: {
|
|
42692
43490
|
name: "favorites",
|
|
42693
43491
|
description: "List this company's saved reference sections. Example: baker landing inspiration favorites --type hero,pricing"
|
|
@@ -42767,7 +43565,7 @@ registerSchema({
|
|
|
42767
43565
|
note: { type: "string", description: "Why this is worth keeping", required: false }
|
|
42768
43566
|
}
|
|
42769
43567
|
});
|
|
42770
|
-
var favoriteCommand =
|
|
43568
|
+
var favoriteCommand = defineCommand160({
|
|
42771
43569
|
meta: {
|
|
42772
43570
|
name: "favorite",
|
|
42773
43571
|
description: "Save a reference section to this company. Example: baker landing inspiration favorite k57abc\u2026"
|
|
@@ -42805,7 +43603,7 @@ registerSchema({
|
|
|
42805
43603
|
page: { type: "boolean", description: "Treat the id as a page rather than a section", required: false }
|
|
42806
43604
|
}
|
|
42807
43605
|
});
|
|
42808
|
-
var unfavoriteCommand =
|
|
43606
|
+
var unfavoriteCommand = defineCommand160({
|
|
42809
43607
|
meta: {
|
|
42810
43608
|
name: "unfavorite",
|
|
42811
43609
|
description: "Remove a reference section from this company's saved set. Example: baker landing inspiration unfavorite k57abc\u2026"
|
|
@@ -42827,7 +43625,7 @@ var unfavoriteCommand = defineCommand159({
|
|
|
42827
43625
|
});
|
|
42828
43626
|
|
|
42829
43627
|
// src/commands/landing/inspiration/page.ts
|
|
42830
|
-
import { defineCommand as
|
|
43628
|
+
import { defineCommand as defineCommand161 } from "citty";
|
|
42831
43629
|
registerSchema({
|
|
42832
43630
|
command: "landing.inspiration.page",
|
|
42833
43631
|
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.",
|
|
@@ -42844,7 +43642,7 @@ registerSchema({
|
|
|
42844
43642
|
}
|
|
42845
43643
|
}
|
|
42846
43644
|
});
|
|
42847
|
-
var pageCommand2 =
|
|
43645
|
+
var pageCommand2 = defineCommand161({
|
|
42848
43646
|
meta: {
|
|
42849
43647
|
name: "page",
|
|
42850
43648
|
description: "Show how a reference page sequences its sections. Example: baker landing inspiration page j91xyz\u2026 \u2014 the blueprint, not the pixels."
|
|
@@ -42907,7 +43705,7 @@ var pageCommand2 = defineCommand160({
|
|
|
42907
43705
|
});
|
|
42908
43706
|
|
|
42909
43707
|
// src/commands/landing/inspiration/scrape.ts
|
|
42910
|
-
import { defineCommand as
|
|
43708
|
+
import { defineCommand as defineCommand162 } from "citty";
|
|
42911
43709
|
|
|
42912
43710
|
// src/engine/landing-library/proxyFailure.ts
|
|
42913
43711
|
var PROXY_STATUS = 407;
|
|
@@ -43084,7 +43882,7 @@ function classifyCaptureFailure(error) {
|
|
|
43084
43882
|
|
|
43085
43883
|
// src/engine/landing-library/run.ts
|
|
43086
43884
|
import { mkdir as mkdir10, writeFile as writeFile15 } from "fs/promises";
|
|
43087
|
-
import
|
|
43885
|
+
import path32 from "path";
|
|
43088
43886
|
|
|
43089
43887
|
// ../proxy/src/preflight.ts
|
|
43090
43888
|
import http from "http";
|
|
@@ -44500,9 +45298,9 @@ async function renderBundleToPng(browser, html, viewportWidth, options = {}) {
|
|
|
44500
45298
|
|
|
44501
45299
|
// src/engine/landing-library/report.ts
|
|
44502
45300
|
import { writeFile as writeFile14 } from "fs/promises";
|
|
44503
|
-
import
|
|
45301
|
+
import path31 from "path";
|
|
44504
45302
|
async function writeCaptureReport(manifest, outDir) {
|
|
44505
|
-
const file =
|
|
45303
|
+
const file = path31.join(outDir, "report.html");
|
|
44506
45304
|
await writeFile14(file, renderReport(manifest));
|
|
44507
45305
|
return file;
|
|
44508
45306
|
}
|
|
@@ -44681,32 +45479,32 @@ async function reproducePage(args) {
|
|
|
44681
45479
|
const { browser, page, outDir, pageUrl, livePageShot } = args;
|
|
44682
45480
|
const built = await buildSectionBundle(page, "body", pageUrl).catch(() => null);
|
|
44683
45481
|
if (!built) return { bundle: null, fidelity: null };
|
|
44684
|
-
await writeFile15(
|
|
45482
|
+
await writeFile15(path32.join(outDir, "page.html"), built.html);
|
|
44685
45483
|
const rendered = await renderBundleToPng(browser, built.html, DESKTOP_VIEWPORT.width, {
|
|
44686
45484
|
wholePage: true,
|
|
44687
45485
|
timeoutMs: 6e4
|
|
44688
45486
|
});
|
|
44689
45487
|
if (!rendered || !livePageShot) return { bundle: "page.html", fidelity: null };
|
|
44690
|
-
await writeFile15(
|
|
45488
|
+
await writeFile15(path32.join(outDir, "page-rendered.png"), rendered);
|
|
44691
45489
|
const { score, note } = await scoreFidelity(livePageShot, rendered);
|
|
44692
45490
|
return { bundle: "page.html", fidelity: score, ...note ? { fidelityNote: note } : {} };
|
|
44693
45491
|
}
|
|
44694
45492
|
async function captureOneSection(args) {
|
|
44695
45493
|
const { browser, page, candidate, sectionsDir, outDir, pageUrl, withCode } = args;
|
|
44696
|
-
const dir =
|
|
45494
|
+
const dir = path32.join(sectionsDir, String(candidate.index).padStart(2, "0"));
|
|
44697
45495
|
await mkdir10(dir, { recursive: true });
|
|
44698
45496
|
const desktop = await captureSection(page, candidate);
|
|
44699
|
-
if (desktop) await writeFile15(
|
|
45497
|
+
if (desktop) await writeFile15(path32.join(dir, "desktop.png"), desktop);
|
|
44700
45498
|
const visualHash = desktop ? await perceptualHash(desktop) : null;
|
|
44701
45499
|
const motion = await collectMotion(page, candidate.selector);
|
|
44702
45500
|
const built = withCode ? await buildSectionBundle(page, candidate.selector, pageUrl) : null;
|
|
44703
45501
|
let fidelity = null;
|
|
44704
45502
|
let fidelityNote;
|
|
44705
45503
|
if (built) {
|
|
44706
|
-
await writeFile15(
|
|
45504
|
+
await writeFile15(path32.join(dir, "section.html"), built.html);
|
|
44707
45505
|
const rendered = await renderBundleToPng(browser, built.html, DESKTOP_VIEWPORT.width);
|
|
44708
45506
|
if (rendered && desktop) {
|
|
44709
|
-
await writeFile15(
|
|
45507
|
+
await writeFile15(path32.join(dir, "section-rendered.png"), rendered);
|
|
44710
45508
|
const result = await scoreFidelity(desktop, rendered);
|
|
44711
45509
|
fidelity = result.score;
|
|
44712
45510
|
fidelityNote = result.note;
|
|
@@ -44714,9 +45512,9 @@ async function captureOneSection(args) {
|
|
|
44714
45512
|
}
|
|
44715
45513
|
return {
|
|
44716
45514
|
...candidate,
|
|
44717
|
-
desktopShot: desktop ?
|
|
45515
|
+
desktopShot: desktop ? path32.relative(outDir, path32.join(dir, "desktop.png")) : null,
|
|
44718
45516
|
mobileShot: null,
|
|
44719
|
-
bundle: built ?
|
|
45517
|
+
bundle: built ? path32.relative(outDir, path32.join(dir, "section.html")) : null,
|
|
44720
45518
|
fidelity,
|
|
44721
45519
|
...fidelityNote ? { fidelityNote } : {},
|
|
44722
45520
|
...built ? { cssStats: built.stats } : {},
|
|
@@ -44735,9 +45533,9 @@ async function captureMobileShots(args) {
|
|
|
44735
45533
|
for (const section of sections) {
|
|
44736
45534
|
const shot = await captureSectionOnMobile(mobile.page, section);
|
|
44737
45535
|
if (!shot) continue;
|
|
44738
|
-
const file =
|
|
45536
|
+
const file = path32.join(sectionsDir, String(section.index).padStart(2, "0"), "mobile.png");
|
|
44739
45537
|
await writeFile15(file, shot);
|
|
44740
|
-
section.mobileShot =
|
|
45538
|
+
section.mobileShot = path32.relative(outDir, file);
|
|
44741
45539
|
}
|
|
44742
45540
|
} finally {
|
|
44743
45541
|
await mobile.context.close();
|
|
@@ -44752,10 +45550,10 @@ async function captureMotionTakes(args) {
|
|
|
44752
45550
|
const filmOne = async (section) => {
|
|
44753
45551
|
const take = await captureMotionTake(browser, pageUrl, section.selector).catch(() => null);
|
|
44754
45552
|
if (!take) return;
|
|
44755
|
-
const dir =
|
|
44756
|
-
const file =
|
|
45553
|
+
const dir = path32.join(sectionsDir, String(section.index).padStart(2, "0"));
|
|
45554
|
+
const file = path32.join(dir, "motion-filmstrip.png");
|
|
44757
45555
|
await writeFile15(file, take.filmstrip);
|
|
44758
|
-
section.motionFilmstrip =
|
|
45556
|
+
section.motionFilmstrip = path32.relative(outDir, file);
|
|
44759
45557
|
log(` [${section.index}] ${section.motion.summary}`);
|
|
44760
45558
|
};
|
|
44761
45559
|
const queue = [...moving];
|
|
@@ -44812,7 +45610,7 @@ async function captureAlternateViews(args) {
|
|
|
44812
45610
|
async function reproduceWholePage(args) {
|
|
44813
45611
|
const { browser, page, outDir, pageUrl, withCode, log } = args;
|
|
44814
45612
|
const fullPage = await page.screenshot({ type: "png", fullPage: true }).catch(() => null);
|
|
44815
|
-
if (fullPage) await writeFile15(
|
|
45613
|
+
if (fullPage) await writeFile15(path32.join(outDir, "full-page.png"), fullPage);
|
|
44816
45614
|
if (!withCode) return { bundle: null, fidelity: null };
|
|
44817
45615
|
const reproduction = await reproducePage({ browser, page, outDir, pageUrl, livePageShot: fullPage });
|
|
44818
45616
|
log(`page reproduction: ${reproduction.fidelity === null ? "unavailable" : reproduction.fidelity.toFixed(2)}`);
|
|
@@ -44883,7 +45681,7 @@ async function openViaLadder(args) {
|
|
|
44883
45681
|
async function scrapeLanding(options) {
|
|
44884
45682
|
const timeoutMs = options.timeoutMs ?? 45e3;
|
|
44885
45683
|
const log = options.onProgress ?? (() => void 0);
|
|
44886
|
-
const sectionsDir =
|
|
45684
|
+
const sectionsDir = path32.join(options.outDir, "sections");
|
|
44887
45685
|
const nonPublic = refuseNonPublicUrl(options.url);
|
|
44888
45686
|
if (nonPublic) {
|
|
44889
45687
|
throw new BlockedPageError({
|
|
@@ -44945,7 +45743,7 @@ async function scrapeLanding(options) {
|
|
|
44945
45743
|
security: prepared.security,
|
|
44946
45744
|
captureTier: tier
|
|
44947
45745
|
};
|
|
44948
|
-
await writeFile15(
|
|
45746
|
+
await writeFile15(path32.join(options.outDir, "manifest.json"), `${JSON.stringify(manifest, null, 2)}
|
|
44949
45747
|
`);
|
|
44950
45748
|
if (options.report !== false) {
|
|
44951
45749
|
const reportPath = await writeCaptureReport(manifest, options.outDir);
|
|
@@ -44960,28 +45758,28 @@ async function scrapeLanding(options) {
|
|
|
44960
45758
|
|
|
44961
45759
|
// src/commands/landing/inspiration/captureOut.ts
|
|
44962
45760
|
import { existsSync as existsSync9 } from "fs";
|
|
44963
|
-
import
|
|
45761
|
+
import path33 from "path";
|
|
44964
45762
|
var SCRATCH_DIR = ".baker";
|
|
44965
45763
|
function isWithin(parent, target) {
|
|
44966
|
-
const relative =
|
|
44967
|
-
return relative === "" || !relative.startsWith("..") && !
|
|
45764
|
+
const relative = path33.relative(parent, target);
|
|
45765
|
+
return relative === "" || !relative.startsWith("..") && !path33.isAbsolute(relative);
|
|
44968
45766
|
}
|
|
44969
45767
|
function findRepoRoot(from) {
|
|
44970
|
-
let dir =
|
|
45768
|
+
let dir = path33.resolve(from);
|
|
44971
45769
|
for (; ; ) {
|
|
44972
|
-
if (existsSync9(
|
|
44973
|
-
const parent =
|
|
45770
|
+
if (existsSync9(path33.join(dir, ".git"))) return dir;
|
|
45771
|
+
const parent = path33.dirname(dir);
|
|
44974
45772
|
if (parent === dir) return null;
|
|
44975
45773
|
dir = parent;
|
|
44976
45774
|
}
|
|
44977
45775
|
}
|
|
44978
45776
|
function checkCaptureOut(out, options) {
|
|
44979
45777
|
const { cwd, repoRoot } = options;
|
|
44980
|
-
const resolved =
|
|
45778
|
+
const resolved = path33.resolve(cwd, out);
|
|
44981
45779
|
if (repoRoot === null || !isWithin(repoRoot, resolved)) return { ok: true };
|
|
44982
|
-
const scratch =
|
|
45780
|
+
const scratch = path33.join(repoRoot, SCRATCH_DIR);
|
|
44983
45781
|
if (isWithin(scratch, resolved)) return { ok: true };
|
|
44984
|
-
const suggestion =
|
|
45782
|
+
const suggestion = path33.posix.join(SCRATCH_DIR, "teardowns", path33.basename(resolved) || "capture");
|
|
44985
45783
|
return {
|
|
44986
45784
|
ok: false,
|
|
44987
45785
|
error: {
|
|
@@ -45047,7 +45845,7 @@ registerSchema({
|
|
|
45047
45845
|
report: { type: "boolean", description: "Write report.html. `--no-report` to skip", required: false }
|
|
45048
45846
|
}
|
|
45049
45847
|
});
|
|
45050
|
-
var scrapeCommand =
|
|
45848
|
+
var scrapeCommand = defineCommand162({
|
|
45051
45849
|
meta: {
|
|
45052
45850
|
name: "scrape",
|
|
45053
45851
|
description: "Capture a landing page to a directory, now. Example: baker landing inspiration scrape https://linear.app --out .baker/inspiration/linear.app"
|
|
@@ -45155,12 +45953,12 @@ var scrapeCommand = defineCommand161({
|
|
|
45155
45953
|
});
|
|
45156
45954
|
|
|
45157
45955
|
// src/commands/landing/inspiration/search.ts
|
|
45158
|
-
import
|
|
45159
|
-
import { defineCommand as
|
|
45956
|
+
import path35 from "path";
|
|
45957
|
+
import { defineCommand as defineCommand163 } from "citty";
|
|
45160
45958
|
|
|
45161
45959
|
// src/commands/landing/inspiration/shot.ts
|
|
45162
45960
|
import { mkdir as mkdir11, writeFile as writeFile16 } from "fs/promises";
|
|
45163
|
-
import
|
|
45961
|
+
import path34 from "path";
|
|
45164
45962
|
import sharp6 from "sharp";
|
|
45165
45963
|
var READABLE_SHOT = {
|
|
45166
45964
|
maxWidth: 1440,
|
|
@@ -45186,9 +45984,9 @@ async function downloadReadableShot(url, file) {
|
|
|
45186
45984
|
const response = await fetch(url);
|
|
45187
45985
|
if (!response.ok) return null;
|
|
45188
45986
|
const shot = await toReadableShot(Buffer.from(await response.arrayBuffer()));
|
|
45189
|
-
await mkdir11(
|
|
45987
|
+
await mkdir11(path34.dirname(file), { recursive: true });
|
|
45190
45988
|
await writeFile16(file, shot);
|
|
45191
|
-
return
|
|
45989
|
+
return path34.relative(process.cwd(), file);
|
|
45192
45990
|
} catch {
|
|
45193
45991
|
return null;
|
|
45194
45992
|
}
|
|
@@ -45280,20 +46078,20 @@ function buildSearchBody(args) {
|
|
|
45280
46078
|
return body;
|
|
45281
46079
|
}
|
|
45282
46080
|
async function downloadShots(results) {
|
|
45283
|
-
const dir =
|
|
46081
|
+
const dir = path35.join(process.cwd(), ".baker", "inspiration");
|
|
45284
46082
|
const saved = /* @__PURE__ */ new Map();
|
|
45285
46083
|
await Promise.all(
|
|
45286
46084
|
results.map(async (result) => {
|
|
45287
46085
|
const file = await downloadReadableShot(
|
|
45288
46086
|
result.desktopShotUrl,
|
|
45289
|
-
|
|
46087
|
+
path35.join(dir, `${result.id}.${READABLE_SHOT.extension}`)
|
|
45290
46088
|
);
|
|
45291
46089
|
if (file) saved.set(result.id, file);
|
|
45292
46090
|
})
|
|
45293
46091
|
);
|
|
45294
46092
|
return saved;
|
|
45295
46093
|
}
|
|
45296
|
-
var searchCommand2 =
|
|
46094
|
+
var searchCommand2 = defineCommand163({
|
|
45297
46095
|
meta: {
|
|
45298
46096
|
name: "search",
|
|
45299
46097
|
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"
|
|
@@ -45391,7 +46189,7 @@ var searchCommand2 = defineCommand162({
|
|
|
45391
46189
|
});
|
|
45392
46190
|
|
|
45393
46191
|
// src/commands/landing/inspiration/sequences.ts
|
|
45394
|
-
import { defineCommand as
|
|
46192
|
+
import { defineCommand as defineCommand164 } from "citty";
|
|
45395
46193
|
var COMPACT_TRANSITIONS = 12;
|
|
45396
46194
|
var COMPACT_ENDS = 5;
|
|
45397
46195
|
var MAX_PAGES = 50;
|
|
@@ -45465,7 +46263,7 @@ function sequencesHints(data, { scope, full }) {
|
|
|
45465
46263
|
if (scope === "favorites") hints.push(...favoritesScopeHints(data.favoritesHealth, data.pagesReturned));
|
|
45466
46264
|
return hints;
|
|
45467
46265
|
}
|
|
45468
|
-
var sequencesCommand =
|
|
46266
|
+
var sequencesCommand = defineCommand164({
|
|
45469
46267
|
meta: {
|
|
45470
46268
|
name: "sequences",
|
|
45471
46269
|
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."
|
|
@@ -45514,8 +46312,8 @@ var sequencesCommand = defineCommand163({
|
|
|
45514
46312
|
});
|
|
45515
46313
|
|
|
45516
46314
|
// src/commands/landing/inspiration/view.ts
|
|
45517
|
-
import
|
|
45518
|
-
import { defineCommand as
|
|
46315
|
+
import path36 from "path";
|
|
46316
|
+
import { defineCommand as defineCommand165 } from "citty";
|
|
45519
46317
|
registerSchema({
|
|
45520
46318
|
command: "landing.inspiration.view",
|
|
45521
46319
|
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.",
|
|
@@ -45528,7 +46326,7 @@ registerSchema({
|
|
|
45528
46326
|
}
|
|
45529
46327
|
}
|
|
45530
46328
|
});
|
|
45531
|
-
var viewCommand2 =
|
|
46329
|
+
var viewCommand2 = defineCommand165({
|
|
45532
46330
|
meta: {
|
|
45533
46331
|
name: "view",
|
|
45534
46332
|
description: "Full detail for one reference section. Example: baker landing inspiration view k57abc\u2026 \u2014 read the screenshots it saves before you build."
|
|
@@ -45547,12 +46345,12 @@ var viewCommand2 = defineCommand164({
|
|
|
45547
46345
|
const id = args.id;
|
|
45548
46346
|
const data = await apiGet("/api/landing-inspiration/section", { id });
|
|
45549
46347
|
const section = data.section;
|
|
45550
|
-
const dir =
|
|
46348
|
+
const dir = path36.join(process.cwd(), ".baker", "inspiration", id);
|
|
45551
46349
|
const ext = READABLE_SHOT.extension;
|
|
45552
46350
|
const [desktop, mobile, filmstrip] = await Promise.all([
|
|
45553
|
-
downloadReadableShot(section.desktopShotUrl,
|
|
45554
|
-
downloadReadableShot(section.mobileShotUrl,
|
|
45555
|
-
downloadReadableShot(section.motionFilmstripUrl,
|
|
46351
|
+
downloadReadableShot(section.desktopShotUrl, path36.join(dir, `desktop.${ext}`)),
|
|
46352
|
+
downloadReadableShot(section.mobileShotUrl, path36.join(dir, `mobile.${ext}`)),
|
|
46353
|
+
downloadReadableShot(section.motionFilmstripUrl, path36.join(dir, `motion-filmstrip.${ext}`))
|
|
45556
46354
|
]);
|
|
45557
46355
|
const full = args.full;
|
|
45558
46356
|
const hints = [INSPIRATION_HINTS.structureNotCopy, INSPIRATION_HINTS.adapt];
|
|
@@ -45612,7 +46410,7 @@ var viewCommand2 = defineCommand164({
|
|
|
45612
46410
|
});
|
|
45613
46411
|
|
|
45614
46412
|
// src/commands/landing/inspiration/index.ts
|
|
45615
|
-
var inspirationCommand =
|
|
46413
|
+
var inspirationCommand = defineCommand166({
|
|
45616
46414
|
meta: {
|
|
45617
46415
|
name: "inspiration",
|
|
45618
46416
|
description: `Reference library of real landing-page sections \u2014 look at how good pages actually solve a problem before you design one.
|
|
@@ -45656,8 +46454,310 @@ Full guide: __tooling__/docs/tools/baker/landing.md`
|
|
|
45656
46454
|
}
|
|
45657
46455
|
});
|
|
45658
46456
|
|
|
46457
|
+
// src/commands/landing/variant.ts
|
|
46458
|
+
import { randomUUID as randomUUID2 } from "crypto";
|
|
46459
|
+
import { mkdir as mkdir12, readdir as readdir10, readFile as readFile23, stat as stat7, writeFile as writeFile17 } from "fs/promises";
|
|
46460
|
+
import path38 from "path";
|
|
46461
|
+
import { defineCommand as defineCommand167 } from "citty";
|
|
46462
|
+
|
|
46463
|
+
// src/commands/landing/repoint.ts
|
|
46464
|
+
import path37 from "path";
|
|
46465
|
+
var QUOTED_RELATIVE = /(['"])(\.\.?\/[^'"]*)\1/g;
|
|
46466
|
+
function isInside3(abs, dir) {
|
|
46467
|
+
return abs === dir || abs.startsWith(dir + path37.sep);
|
|
46468
|
+
}
|
|
46469
|
+
function toSpecifier(from, to) {
|
|
46470
|
+
const rel = path37.relative(from, to).split(path37.sep).join("/");
|
|
46471
|
+
return rel.startsWith(".") ? rel : `./${rel}`;
|
|
46472
|
+
}
|
|
46473
|
+
function repointSpecifier(spec, opts) {
|
|
46474
|
+
if (!spec.startsWith(".")) return spec;
|
|
46475
|
+
const abs = path37.resolve(opts.fromDir, spec);
|
|
46476
|
+
if (opts.forkedTargets?.has(abs)) {
|
|
46477
|
+
const withinControl = path37.relative(opts.controlDir, abs);
|
|
46478
|
+
return toSpecifier(opts.toDir, path37.resolve(opts.variantDir, withinControl));
|
|
46479
|
+
}
|
|
46480
|
+
if (!isInside3(abs, opts.controlDir)) return spec;
|
|
46481
|
+
return toSpecifier(opts.toDir, abs);
|
|
46482
|
+
}
|
|
46483
|
+
function repointFile(text2, opts) {
|
|
46484
|
+
return text2.replace(QUOTED_RELATIVE, (_match, quote, spec) => {
|
|
46485
|
+
return `${quote}${repointSpecifier(spec, opts)}${quote}`;
|
|
46486
|
+
});
|
|
46487
|
+
}
|
|
46488
|
+
|
|
46489
|
+
// src/commands/landing/variant.ts
|
|
46490
|
+
registerSchema({
|
|
46491
|
+
command: "landing.variant",
|
|
46492
|
+
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.",
|
|
46493
|
+
args: {
|
|
46494
|
+
control: { type: "string", description: "The page under test, by slug", required: true },
|
|
46495
|
+
slug: { type: "string", description: "Slug for the alternative page (convention: <control>-b)", required: true },
|
|
46496
|
+
fork: {
|
|
46497
|
+
type: "string",
|
|
46498
|
+
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.",
|
|
46499
|
+
required: false
|
|
46500
|
+
},
|
|
46501
|
+
because: {
|
|
46502
|
+
type: "string",
|
|
46503
|
+
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.",
|
|
46504
|
+
required: true
|
|
46505
|
+
},
|
|
46506
|
+
change: {
|
|
46507
|
+
type: "string",
|
|
46508
|
+
description: "What is different about this version, in the client's words \u2014 \u201Cput the booking form in the hero\u201D.",
|
|
46509
|
+
required: true
|
|
46510
|
+
}
|
|
46511
|
+
}
|
|
46512
|
+
});
|
|
46513
|
+
var SLUG_RE2 = /^[a-z0-9][a-z0-9-]*$/;
|
|
46514
|
+
function fail7(code, message, fix) {
|
|
46515
|
+
process.stderr.write(
|
|
46516
|
+
`${JSON.stringify({ ok: false, error: { code, message, ...fix ? { fix } : {} } }, null, 2)}
|
|
46517
|
+
`
|
|
46518
|
+
);
|
|
46519
|
+
process.exit(2);
|
|
46520
|
+
}
|
|
46521
|
+
async function isDir2(p) {
|
|
46522
|
+
try {
|
|
46523
|
+
return (await stat7(p)).isDirectory();
|
|
46524
|
+
} catch {
|
|
46525
|
+
return false;
|
|
46526
|
+
}
|
|
46527
|
+
}
|
|
46528
|
+
async function exists(p) {
|
|
46529
|
+
try {
|
|
46530
|
+
await stat7(p);
|
|
46531
|
+
return true;
|
|
46532
|
+
} catch {
|
|
46533
|
+
return false;
|
|
46534
|
+
}
|
|
46535
|
+
}
|
|
46536
|
+
async function listLandingSlugs2(root) {
|
|
46537
|
+
try {
|
|
46538
|
+
const entries = await readdir10(path38.resolve(root, "src", "pages"), { withFileTypes: true });
|
|
46539
|
+
return entries.filter((e) => e.isDirectory() && !e.name.startsWith("_") && !e.name.startsWith(".")).map((e) => e.name).sort();
|
|
46540
|
+
} catch {
|
|
46541
|
+
return [];
|
|
46542
|
+
}
|
|
46543
|
+
}
|
|
46544
|
+
async function listComponents(componentsDir, prefix = "") {
|
|
46545
|
+
let entries;
|
|
46546
|
+
try {
|
|
46547
|
+
entries = await readdir10(componentsDir, { withFileTypes: true });
|
|
46548
|
+
} catch {
|
|
46549
|
+
return [];
|
|
46550
|
+
}
|
|
46551
|
+
const out = [];
|
|
46552
|
+
for (const entry of entries) {
|
|
46553
|
+
const rel = prefix ? `${prefix}/${entry.name}` : entry.name;
|
|
46554
|
+
if (entry.isDirectory()) out.push(...await listComponents(path38.join(componentsDir, entry.name), rel));
|
|
46555
|
+
else if (entry.name.endsWith(".astro")) out.push(rel);
|
|
46556
|
+
}
|
|
46557
|
+
return out.sort();
|
|
46558
|
+
}
|
|
46559
|
+
function resolveForkName(requested, available) {
|
|
46560
|
+
const cleaned = requested.replace(/^\.?\/?(_components\/)?/, "");
|
|
46561
|
+
for (const candidate of [cleaned, `${cleaned}.astro`]) {
|
|
46562
|
+
if (available.includes(candidate)) return candidate;
|
|
46563
|
+
}
|
|
46564
|
+
const basename4 = (p) => p.slice(p.lastIndexOf("/") + 1);
|
|
46565
|
+
const matches = available.filter((a) => basename4(a) === cleaned || basename4(a) === `${cleaned}.astro`);
|
|
46566
|
+
return matches.length === 1 ? matches[0] ?? null : null;
|
|
46567
|
+
}
|
|
46568
|
+
function parseForks(fork) {
|
|
46569
|
+
const list = Array.isArray(fork) ? fork : fork === void 0 || fork === null ? [] : [fork];
|
|
46570
|
+
const names = list.filter((s) => typeof s === "string").flatMap((s) => s.split(",")).map((s) => s.trim()).filter((s) => s.length > 0);
|
|
46571
|
+
return [...new Set(names)];
|
|
46572
|
+
}
|
|
46573
|
+
function buildVariantDefinition(controlDefinition, opts) {
|
|
46574
|
+
let out = controlDefinition;
|
|
46575
|
+
out = out.replace(/^internalId:.*$/m, `internalId: "${opts.internalId}"`);
|
|
46576
|
+
out = out.replace(/^internalTitle:\s*(.*)$/m, (_m, title) => `internalTitle: ${title.trim()} (variant)`);
|
|
46577
|
+
const shared = opts.forks.length > 0 ? opts.forks.join(", ") : "nothing yet";
|
|
46578
|
+
const note = [
|
|
46579
|
+
"",
|
|
46580
|
+
`## A/B test \u2014 alternative version of \`${opts.controlSlug}\``,
|
|
46581
|
+
"",
|
|
46582
|
+
`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.`,
|
|
46583
|
+
"",
|
|
46584
|
+
`**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.`,
|
|
46585
|
+
...opts.because ? ["", `**Because:** ${opts.because}`] : [],
|
|
46586
|
+
...opts.change ? ["", `**We changed:** ${opts.change}`] : [],
|
|
46587
|
+
"",
|
|
46588
|
+
`Run \`baker experiment start --landing ${opts.controlSlug} --variant ${opts.variantSlug} --because "\u2026" --change "\u2026"\` to begin, and read the result with \`baker experiment status\`.`,
|
|
46589
|
+
""
|
|
46590
|
+
].join("\n");
|
|
46591
|
+
return `${out.trimEnd()}
|
|
46592
|
+
${note}`;
|
|
46593
|
+
}
|
|
46594
|
+
async function resolveTargets(projectRoot, opts) {
|
|
46595
|
+
const { controlSlug, variantSlug } = opts;
|
|
46596
|
+
for (const [label, slug] of [
|
|
46597
|
+
["control", controlSlug],
|
|
46598
|
+
["slug", variantSlug]
|
|
46599
|
+
]) {
|
|
46600
|
+
if (!SLUG_RE2.test(slug)) {
|
|
46601
|
+
fail7(
|
|
46602
|
+
"INVALID_SLUG",
|
|
46603
|
+
`"${slug}" is not a landing slug \u2014 use the folder name directly under src/pages/ (lowercase letters, digits and dashes).`,
|
|
46604
|
+
{ argument: label, availableSlugs: await listLandingSlugs2(projectRoot) }
|
|
46605
|
+
);
|
|
46606
|
+
}
|
|
46607
|
+
}
|
|
46608
|
+
if (controlSlug === variantSlug) {
|
|
46609
|
+
fail7("INVALID_SLUG", `The alternative needs its own slug \u2014 try \`${controlSlug}-b\`.`);
|
|
46610
|
+
}
|
|
46611
|
+
const controlDir = path38.resolve(projectRoot, "src", "pages", controlSlug);
|
|
46612
|
+
const variantDir = path38.resolve(projectRoot, "src", "pages", variantSlug);
|
|
46613
|
+
if (!await isDir2(controlDir)) {
|
|
46614
|
+
fail7("NOT_FOUND", `No landing at src/pages/${controlSlug}/`, {
|
|
46615
|
+
availableSlugs: await listLandingSlugs2(projectRoot)
|
|
46616
|
+
});
|
|
46617
|
+
}
|
|
46618
|
+
if (await exists(variantDir)) {
|
|
46619
|
+
fail7(
|
|
46620
|
+
"ALREADY_EXISTS",
|
|
46621
|
+
`src/pages/${variantSlug}/ already exists \u2014 pick another slug, or delete that page if it was a false start.`
|
|
46622
|
+
);
|
|
46623
|
+
}
|
|
46624
|
+
if (!await exists(path38.join(controlDir, "index.astro"))) {
|
|
46625
|
+
fail7("NOT_FOUND", `src/pages/${controlSlug}/index.astro is missing, so there is no page to make a variant of.`);
|
|
46626
|
+
}
|
|
46627
|
+
const available = await listComponents(path38.join(controlDir, "_components"));
|
|
46628
|
+
const forks = [];
|
|
46629
|
+
for (const name of parseForks(opts.fork)) {
|
|
46630
|
+
const resolved = resolveForkName(name, available);
|
|
46631
|
+
if (resolved === null) {
|
|
46632
|
+
fail7("NOT_FOUND", `"${name}" is not a component of ${controlSlug}.`, {
|
|
46633
|
+
availableComponents: available,
|
|
46634
|
+
hint: "Name it as it appears under _components/ \u2014 `Hero.astro`, or `hero/Card.astro`."
|
|
46635
|
+
});
|
|
46636
|
+
}
|
|
46637
|
+
forks.push(resolved);
|
|
46638
|
+
}
|
|
46639
|
+
return { controlDir, variantDir, available, forks };
|
|
46640
|
+
}
|
|
46641
|
+
async function writeVariant(opts) {
|
|
46642
|
+
const { controlDir, variantDir, controlSlug, variantSlug, forks } = opts;
|
|
46643
|
+
const forkedTargets = new Set(forks.map((f) => path38.join(controlDir, "_components", f)));
|
|
46644
|
+
const written = [];
|
|
46645
|
+
const indexText = await readFile23(path38.join(controlDir, "index.astro"), "utf8");
|
|
46646
|
+
await mkdir12(variantDir, { recursive: true });
|
|
46647
|
+
await writeFile17(
|
|
46648
|
+
path38.join(variantDir, "index.astro"),
|
|
46649
|
+
repointFile(indexText, { fromDir: controlDir, toDir: variantDir, controlDir, variantDir, forkedTargets }),
|
|
46650
|
+
"utf8"
|
|
46651
|
+
);
|
|
46652
|
+
written.push(`src/pages/${variantSlug}/index.astro`);
|
|
46653
|
+
for (const fork of forks) {
|
|
46654
|
+
const fromFile = path38.join(controlDir, "_components", fork);
|
|
46655
|
+
const toFile = path38.join(variantDir, "_components", fork);
|
|
46656
|
+
const text2 = await readFile23(fromFile, "utf8");
|
|
46657
|
+
await mkdir12(path38.dirname(toFile), { recursive: true });
|
|
46658
|
+
await writeFile17(
|
|
46659
|
+
toFile,
|
|
46660
|
+
repointFile(text2, {
|
|
46661
|
+
fromDir: path38.dirname(fromFile),
|
|
46662
|
+
toDir: path38.dirname(toFile),
|
|
46663
|
+
controlDir,
|
|
46664
|
+
variantDir,
|
|
46665
|
+
forkedTargets
|
|
46666
|
+
}),
|
|
46667
|
+
"utf8"
|
|
46668
|
+
);
|
|
46669
|
+
written.push(`src/pages/${variantSlug}/_components/${fork}`);
|
|
46670
|
+
}
|
|
46671
|
+
const controlDefinitionPath = path38.join(controlDir, "_definition.md");
|
|
46672
|
+
if (await exists(controlDefinitionPath)) {
|
|
46673
|
+
const definition = buildVariantDefinition(await readFile23(controlDefinitionPath, "utf8"), {
|
|
46674
|
+
internalId: randomUUID2().replace(/-/g, "").slice(0, 8),
|
|
46675
|
+
variantSlug,
|
|
46676
|
+
controlSlug,
|
|
46677
|
+
forks,
|
|
46678
|
+
...opts.because ? { because: opts.because } : {},
|
|
46679
|
+
...opts.change ? { change: opts.change } : {}
|
|
46680
|
+
});
|
|
46681
|
+
await writeFile17(path38.join(variantDir, "_definition.md"), definition, "utf8");
|
|
46682
|
+
written.push(`src/pages/${variantSlug}/_definition.md`);
|
|
46683
|
+
}
|
|
46684
|
+
await mkdir12(path38.join(variantDir, "_images"), { recursive: true });
|
|
46685
|
+
await writeFile17(path38.join(variantDir, "_images", ".gitkeep"), "", "utf8");
|
|
46686
|
+
return written;
|
|
46687
|
+
}
|
|
46688
|
+
var variantCommand = defineCommand167({
|
|
46689
|
+
meta: {
|
|
46690
|
+
name: "variant",
|
|
46691
|
+
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."
|
|
46692
|
+
},
|
|
46693
|
+
args: {
|
|
46694
|
+
control: { type: "positional", required: true, description: "The page under test, by slug" },
|
|
46695
|
+
slug: {
|
|
46696
|
+
type: "positional",
|
|
46697
|
+
required: true,
|
|
46698
|
+
description: "Slug for the alternative page (convention: <control>-b)"
|
|
46699
|
+
},
|
|
46700
|
+
fork: {
|
|
46701
|
+
type: "string",
|
|
46702
|
+
description: "Component the variant owns its own copy of (`Hero.astro`). Repeat for several."
|
|
46703
|
+
},
|
|
46704
|
+
because: {
|
|
46705
|
+
type: "string",
|
|
46706
|
+
required: true,
|
|
46707
|
+
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`."
|
|
46708
|
+
},
|
|
46709
|
+
change: {
|
|
46710
|
+
type: "string",
|
|
46711
|
+
required: true,
|
|
46712
|
+
description: "What is different about this version, in the client's words \u2014 \u201Cput the booking form in the hero\u201D."
|
|
46713
|
+
}
|
|
46714
|
+
},
|
|
46715
|
+
async run({ args }) {
|
|
46716
|
+
const projectRoot = process.cwd();
|
|
46717
|
+
const controlSlug = String(args.control);
|
|
46718
|
+
const variantSlug = String(args.slug);
|
|
46719
|
+
const { controlDir, variantDir, available, forks } = await resolveTargets(projectRoot, {
|
|
46720
|
+
controlSlug,
|
|
46721
|
+
variantSlug,
|
|
46722
|
+
fork: args.fork
|
|
46723
|
+
});
|
|
46724
|
+
const written = await writeVariant({
|
|
46725
|
+
controlDir,
|
|
46726
|
+
variantDir,
|
|
46727
|
+
controlSlug,
|
|
46728
|
+
variantSlug,
|
|
46729
|
+
forks,
|
|
46730
|
+
because: String(args.because),
|
|
46731
|
+
change: String(args.change)
|
|
46732
|
+
});
|
|
46733
|
+
const hints = [
|
|
46734
|
+
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.`,
|
|
46735
|
+
`Preview both at http://localhost:4321/${controlSlug}/ and http://localhost:4321/${variantSlug}/ and check they differ only where you meant.`,
|
|
46736
|
+
`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.`
|
|
46737
|
+
];
|
|
46738
|
+
process.stdout.write(
|
|
46739
|
+
`${JSON.stringify(
|
|
46740
|
+
{
|
|
46741
|
+
ok: true,
|
|
46742
|
+
data: {
|
|
46743
|
+
control: controlSlug,
|
|
46744
|
+
slug: variantSlug,
|
|
46745
|
+
forked: forks,
|
|
46746
|
+
shared: available.filter((c) => !forks.includes(c)),
|
|
46747
|
+
written
|
|
46748
|
+
},
|
|
46749
|
+
hints
|
|
46750
|
+
},
|
|
46751
|
+
null,
|
|
46752
|
+
2
|
|
46753
|
+
)}
|
|
46754
|
+
`
|
|
46755
|
+
);
|
|
46756
|
+
}
|
|
46757
|
+
});
|
|
46758
|
+
|
|
45659
46759
|
// src/commands/landing/index.ts
|
|
45660
|
-
var landingCommand =
|
|
46760
|
+
var landingCommand = defineCommand168({
|
|
45661
46761
|
meta: {
|
|
45662
46762
|
name: "landing",
|
|
45663
46763
|
description: `Design-quality tools for landing pages (src/pages/<slug>/).
|
|
@@ -45666,16 +46766,18 @@ Start here: \`baker landing critique <slug>\` after building or editing a landin
|
|
|
45666
46766
|
|
|
45667
46767
|
Subcommands:
|
|
45668
46768
|
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.
|
|
46769
|
+
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.
|
|
45669
46770
|
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.`
|
|
45670
46771
|
},
|
|
45671
46772
|
subCommands: {
|
|
45672
46773
|
inspiration: inspirationCommand,
|
|
45673
|
-
critique: critiqueCommand2
|
|
46774
|
+
critique: critiqueCommand2,
|
|
46775
|
+
variant: variantCommand
|
|
45674
46776
|
}
|
|
45675
46777
|
});
|
|
45676
46778
|
|
|
45677
46779
|
// src/commands/mcp/index.ts
|
|
45678
|
-
import { defineCommand as
|
|
46780
|
+
import { defineCommand as defineCommand169 } from "citty";
|
|
45679
46781
|
|
|
45680
46782
|
// src/commands/mcp/platforms.ts
|
|
45681
46783
|
function readsKey(label) {
|
|
@@ -45730,7 +46832,7 @@ function parseHeaders(raw) {
|
|
|
45730
46832
|
}
|
|
45731
46833
|
return Object.keys(headers).length > 0 ? headers : void 0;
|
|
45732
46834
|
}
|
|
45733
|
-
function
|
|
46835
|
+
function fail8(err) {
|
|
45734
46836
|
if (err instanceof ApiError) {
|
|
45735
46837
|
writeJson({ ok: false, error: { code: err.code, message: err.message } });
|
|
45736
46838
|
process.exit(1);
|
|
@@ -45744,7 +46846,7 @@ registerSchema({
|
|
|
45744
46846
|
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.",
|
|
45745
46847
|
args: {}
|
|
45746
46848
|
});
|
|
45747
|
-
var connectedCommand =
|
|
46849
|
+
var connectedCommand = defineCommand169({
|
|
45748
46850
|
meta: {
|
|
45749
46851
|
name: "connected",
|
|
45750
46852
|
description: `Everything this chat can reach \u2014 managed integrations, custom MCP servers, and connected platforms.
|
|
@@ -45794,7 +46896,7 @@ A tool or platform the user names that is NOT listed anywhere here is simply not
|
|
|
45794
46896
|
hints
|
|
45795
46897
|
});
|
|
45796
46898
|
} catch (err) {
|
|
45797
|
-
|
|
46899
|
+
fail8(err);
|
|
45798
46900
|
}
|
|
45799
46901
|
}
|
|
45800
46902
|
});
|
|
@@ -45803,7 +46905,7 @@ registerSchema({
|
|
|
45803
46905
|
description: "List the custom MCP servers this company's chats see (org + company + your own user scope).",
|
|
45804
46906
|
args: {}
|
|
45805
46907
|
});
|
|
45806
|
-
var listCommand14 =
|
|
46908
|
+
var listCommand14 = defineCommand169({
|
|
45807
46909
|
meta: { name: "list", description: "List custom MCP servers visible to this company's chats." },
|
|
45808
46910
|
run: async () => {
|
|
45809
46911
|
try {
|
|
@@ -45822,7 +46924,7 @@ var listCommand14 = defineCommand167({
|
|
|
45822
46924
|
} : {}
|
|
45823
46925
|
});
|
|
45824
46926
|
} catch (err) {
|
|
45825
|
-
|
|
46927
|
+
fail8(err);
|
|
45826
46928
|
}
|
|
45827
46929
|
}
|
|
45828
46930
|
});
|
|
@@ -45840,7 +46942,7 @@ registerSchema({
|
|
|
45840
46942
|
header: { type: "string", description: 'Auth header "Key: Value" (repeatable)', required: false }
|
|
45841
46943
|
}
|
|
45842
46944
|
});
|
|
45843
|
-
var addCommand2 =
|
|
46945
|
+
var addCommand2 = defineCommand169({
|
|
45844
46946
|
meta: {
|
|
45845
46947
|
name: "add",
|
|
45846
46948
|
description: `Register a custom MCP server. Tools appear as mcp__<name>__* on the NEXT message.
|
|
@@ -45883,7 +46985,7 @@ Examples:
|
|
|
45883
46985
|
]
|
|
45884
46986
|
});
|
|
45885
46987
|
} catch (err) {
|
|
45886
|
-
|
|
46988
|
+
fail8(err);
|
|
45887
46989
|
}
|
|
45888
46990
|
}
|
|
45889
46991
|
});
|
|
@@ -45892,7 +46994,7 @@ registerSchema({
|
|
|
45892
46994
|
description: "Remove a company custom MCP server by name.",
|
|
45893
46995
|
args: { name: { type: "string", description: "Server name to remove", required: true } }
|
|
45894
46996
|
});
|
|
45895
|
-
var removeCommand4 =
|
|
46997
|
+
var removeCommand4 = defineCommand169({
|
|
45896
46998
|
meta: {
|
|
45897
46999
|
name: "remove",
|
|
45898
47000
|
description: `Remove a company custom MCP server by name.
|
|
@@ -45910,11 +47012,11 @@ Example:
|
|
|
45910
47012
|
});
|
|
45911
47013
|
writeJson({ ok: true, data });
|
|
45912
47014
|
} catch (err) {
|
|
45913
|
-
|
|
47015
|
+
fail8(err);
|
|
45914
47016
|
}
|
|
45915
47017
|
}
|
|
45916
47018
|
});
|
|
45917
|
-
var mcpCommand =
|
|
47019
|
+
var mcpCommand = defineCommand169({
|
|
45918
47020
|
meta: {
|
|
45919
47021
|
name: "mcp",
|
|
45920
47022
|
description: `Third-party tools for this company \u2014 see what's connected, register custom HTTPS MCP endpoints.
|
|
@@ -45940,10 +47042,10 @@ Full guide: __tooling__/docs/tools/baker/mcp.md`
|
|
|
45940
47042
|
});
|
|
45941
47043
|
|
|
45942
47044
|
// src/commands/research/index.ts
|
|
45943
|
-
import { defineCommand as
|
|
47045
|
+
import { defineCommand as defineCommand181 } from "citty";
|
|
45944
47046
|
|
|
45945
47047
|
// src/commands/research/advertisers.ts
|
|
45946
|
-
import { defineCommand as
|
|
47048
|
+
import { defineCommand as defineCommand170 } from "citty";
|
|
45947
47049
|
|
|
45948
47050
|
// src/commands/research/hints.ts
|
|
45949
47051
|
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.';
|
|
@@ -46126,7 +47228,7 @@ var FIELDS3 = {
|
|
|
46126
47228
|
etv: "Estimated traffic value (USD)",
|
|
46127
47229
|
visibility: "SERP visibility score (0-1)"
|
|
46128
47230
|
};
|
|
46129
|
-
var advertisersCommand =
|
|
47231
|
+
var advertisersCommand = defineCommand170({
|
|
46130
47232
|
meta: {
|
|
46131
47233
|
name: "advertisers",
|
|
46132
47234
|
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).
|
|
@@ -46182,7 +47284,7 @@ Examples:
|
|
|
46182
47284
|
});
|
|
46183
47285
|
|
|
46184
47286
|
// src/commands/research/autocomplete.ts
|
|
46185
|
-
import { defineCommand as
|
|
47287
|
+
import { defineCommand as defineCommand171 } from "citty";
|
|
46186
47288
|
registerSchema({
|
|
46187
47289
|
command: "research.autocomplete",
|
|
46188
47290
|
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).",
|
|
@@ -46205,7 +47307,7 @@ registerSchema({
|
|
|
46205
47307
|
var FIELDS4 = {
|
|
46206
47308
|
suggestion: "Autocomplete suggestion from Google"
|
|
46207
47309
|
};
|
|
46208
|
-
var autocompleteCommand =
|
|
47310
|
+
var autocompleteCommand = defineCommand171({
|
|
46209
47311
|
meta: {
|
|
46210
47312
|
name: "autocomplete",
|
|
46211
47313
|
description: `Get Google Autocomplete suggestions for keyword expansion.
|
|
@@ -46260,7 +47362,7 @@ Examples:
|
|
|
46260
47362
|
});
|
|
46261
47363
|
|
|
46262
47364
|
// src/commands/research/countries.ts
|
|
46263
|
-
import { defineCommand as
|
|
47365
|
+
import { defineCommand as defineCommand172 } from "citty";
|
|
46264
47366
|
registerSchema({
|
|
46265
47367
|
command: "research.countries",
|
|
46266
47368
|
description: "List all supported country codes for --location flag in research commands.",
|
|
@@ -46317,7 +47419,7 @@ var FIELDS5 = {
|
|
|
46317
47419
|
code: "Country code to pass as --location",
|
|
46318
47420
|
name: "Country name"
|
|
46319
47421
|
};
|
|
46320
|
-
var countriesCommand =
|
|
47422
|
+
var countriesCommand = defineCommand172({
|
|
46321
47423
|
meta: {
|
|
46322
47424
|
name: "countries",
|
|
46323
47425
|
description: "List all supported country codes for --location flag."
|
|
@@ -46328,7 +47430,7 @@ var countriesCommand = defineCommand170({
|
|
|
46328
47430
|
});
|
|
46329
47431
|
|
|
46330
47432
|
// src/commands/research/fetch.ts
|
|
46331
|
-
import { defineCommand as
|
|
47433
|
+
import { defineCommand as defineCommand173 } from "citty";
|
|
46332
47434
|
var CONTENT_PREVIEW_CHARS = 2e4;
|
|
46333
47435
|
var TIMEOUT_MS = 18e4;
|
|
46334
47436
|
registerSchema({
|
|
@@ -46401,7 +47503,7 @@ function fetchFix(code) {
|
|
|
46401
47503
|
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."
|
|
46402
47504
|
};
|
|
46403
47505
|
}
|
|
46404
|
-
var fetchCommand =
|
|
47506
|
+
var fetchCommand = defineCommand173({
|
|
46405
47507
|
meta: {
|
|
46406
47508
|
name: "fetch",
|
|
46407
47509
|
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.
|
|
@@ -46478,7 +47580,7 @@ Examples:
|
|
|
46478
47580
|
});
|
|
46479
47581
|
|
|
46480
47582
|
// src/commands/research/intent.ts
|
|
46481
|
-
import { defineCommand as
|
|
47583
|
+
import { defineCommand as defineCommand174 } from "citty";
|
|
46482
47584
|
registerSchema({
|
|
46483
47585
|
command: "research.intent",
|
|
46484
47586
|
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.",
|
|
@@ -46501,7 +47603,7 @@ var FIELDS7 = {
|
|
|
46501
47603
|
intent: "Primary Google Search intent: informational, navigational, commercial, transactional",
|
|
46502
47604
|
probability: "Confidence score 0.0-1.0"
|
|
46503
47605
|
};
|
|
46504
|
-
var intentCommand =
|
|
47606
|
+
var intentCommand = defineCommand174({
|
|
46505
47607
|
meta: {
|
|
46506
47608
|
name: "intent",
|
|
46507
47609
|
description: `Classify Google Search intent for keywords. Returns intent type and confidence.
|
|
@@ -46549,7 +47651,7 @@ Examples:
|
|
|
46549
47651
|
});
|
|
46550
47652
|
|
|
46551
47653
|
// src/commands/research/keyword-gap.ts
|
|
46552
|
-
import { defineCommand as
|
|
47654
|
+
import { defineCommand as defineCommand175 } from "citty";
|
|
46553
47655
|
registerSchema({
|
|
46554
47656
|
command: "research.keyword-gap",
|
|
46555
47657
|
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.",
|
|
@@ -46578,7 +47680,7 @@ var FIELDS8 = {
|
|
|
46578
47680
|
cpc: "Cost per click USD",
|
|
46579
47681
|
their_position: "Competitor's ranking position"
|
|
46580
47682
|
};
|
|
46581
|
-
var keywordGapCommand =
|
|
47683
|
+
var keywordGapCommand = defineCommand175({
|
|
46582
47684
|
meta: {
|
|
46583
47685
|
name: "keyword-gap",
|
|
46584
47686
|
description: `Find keywords a competitor has that you don't. Supports pagination via --offset.
|
|
@@ -46653,7 +47755,7 @@ Examples:
|
|
|
46653
47755
|
});
|
|
46654
47756
|
|
|
46655
47757
|
// src/commands/research/keywords-for-site.ts
|
|
46656
|
-
import { defineCommand as
|
|
47758
|
+
import { defineCommand as defineCommand176 } from "citty";
|
|
46657
47759
|
registerSchema({
|
|
46658
47760
|
command: "research.keywords-for-site",
|
|
46659
47761
|
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.",
|
|
@@ -46686,7 +47788,7 @@ var FIELDS9 = {
|
|
|
46686
47788
|
competition: "LOW, MEDIUM, or HIGH",
|
|
46687
47789
|
competition_index: "Competition score 0-100"
|
|
46688
47790
|
};
|
|
46689
|
-
var keywordsForSiteCommand =
|
|
47791
|
+
var keywordsForSiteCommand = defineCommand176({
|
|
46690
47792
|
meta: {
|
|
46691
47793
|
name: "keywords-for-site",
|
|
46692
47794
|
description: `Get keywords a competitor targets in Google. Use --type to filter paid/organic.
|
|
@@ -46748,7 +47850,7 @@ Examples:
|
|
|
46748
47850
|
});
|
|
46749
47851
|
|
|
46750
47852
|
// src/commands/research/languages.ts
|
|
46751
|
-
import { defineCommand as
|
|
47853
|
+
import { defineCommand as defineCommand177 } from "citty";
|
|
46752
47854
|
registerSchema({
|
|
46753
47855
|
command: "research.languages",
|
|
46754
47856
|
description: "List all supported language codes for --language flag in research commands.",
|
|
@@ -46778,7 +47880,7 @@ var FIELDS10 = {
|
|
|
46778
47880
|
code: "Language code to pass as --language",
|
|
46779
47881
|
name: "Language name (also accepted by --language)"
|
|
46780
47882
|
};
|
|
46781
|
-
var languagesCommand2 =
|
|
47883
|
+
var languagesCommand2 = defineCommand177({
|
|
46782
47884
|
meta: {
|
|
46783
47885
|
name: "languages",
|
|
46784
47886
|
description: "List all supported language codes for --language flag."
|
|
@@ -46789,7 +47891,7 @@ var languagesCommand2 = defineCommand175({
|
|
|
46789
47891
|
});
|
|
46790
47892
|
|
|
46791
47893
|
// src/commands/research/lighthouse.ts
|
|
46792
|
-
import { defineCommand as
|
|
47894
|
+
import { defineCommand as defineCommand178 } from "citty";
|
|
46793
47895
|
registerSchema({
|
|
46794
47896
|
command: "research.lighthouse",
|
|
46795
47897
|
description: "Landing page performance audit. Returns metrics that affect Google Ads Quality Score and CPC.",
|
|
@@ -46808,7 +47910,7 @@ var FIELDS11 = {
|
|
|
46808
47910
|
speed_index_ms: "Speed Index in ms (good: < 3400)",
|
|
46809
47911
|
interactive_ms: "Time to Interactive in ms (good: < 3800)"
|
|
46810
47912
|
};
|
|
46811
|
-
var lighthouseCommand =
|
|
47913
|
+
var lighthouseCommand = defineCommand178({
|
|
46812
47914
|
meta: {
|
|
46813
47915
|
name: "lighthouse",
|
|
46814
47916
|
description: `Landing page performance audit. Metrics affecting Google Ads Quality Score.
|
|
@@ -46846,7 +47948,7 @@ Examples:
|
|
|
46846
47948
|
});
|
|
46847
47949
|
|
|
46848
47950
|
// src/commands/research/relevant-pages.ts
|
|
46849
|
-
import { defineCommand as
|
|
47951
|
+
import { defineCommand as defineCommand179 } from "citty";
|
|
46850
47952
|
registerSchema({
|
|
46851
47953
|
command: "research.relevant-pages",
|
|
46852
47954
|
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).",
|
|
@@ -46872,7 +47974,7 @@ var FIELDS12 = {
|
|
|
46872
47974
|
keywords: "Total organic keywords the page ranks for",
|
|
46873
47975
|
top_10: "Keywords in positions 1-10"
|
|
46874
47976
|
};
|
|
46875
|
-
var relevantPagesCommand =
|
|
47977
|
+
var relevantPagesCommand = defineCommand179({
|
|
46876
47978
|
meta: {
|
|
46877
47979
|
name: "relevant-pages",
|
|
46878
47980
|
description: `Get the top pages of a competitor domain with traffic data.
|
|
@@ -46919,7 +48021,7 @@ Examples:
|
|
|
46919
48021
|
});
|
|
46920
48022
|
|
|
46921
48023
|
// src/commands/research/web.ts
|
|
46922
|
-
import { defineCommand as
|
|
48024
|
+
import { defineCommand as defineCommand180 } from "citty";
|
|
46923
48025
|
registerSchema({
|
|
46924
48026
|
command: "research.web",
|
|
46925
48027
|
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).",
|
|
@@ -46970,7 +48072,7 @@ async function runDeepResearch(question) {
|
|
|
46970
48072
|
}
|
|
46971
48073
|
throw new Error("Deep research timed out");
|
|
46972
48074
|
}
|
|
46973
|
-
var webCommand =
|
|
48075
|
+
var webCommand = defineCommand180({
|
|
46974
48076
|
meta: {
|
|
46975
48077
|
name: "web",
|
|
46976
48078
|
description: `Search the web with AI to answer any open-ended marketing question. Uses live internet data via Google Search.
|
|
@@ -47032,7 +48134,7 @@ Examples:
|
|
|
47032
48134
|
});
|
|
47033
48135
|
|
|
47034
48136
|
// src/commands/research/index.ts
|
|
47035
|
-
var researchCommand =
|
|
48137
|
+
var researchCommand = defineCommand181({
|
|
47036
48138
|
meta: {
|
|
47037
48139
|
name: "research",
|
|
47038
48140
|
description: `Competitive intelligence and AI-powered research commands.
|
|
@@ -47076,10 +48178,10 @@ Full guide: __tooling__/docs/tools/baker/research.md`
|
|
|
47076
48178
|
});
|
|
47077
48179
|
|
|
47078
48180
|
// src/commands/scheduled-actions/index.ts
|
|
47079
|
-
import { defineCommand as
|
|
48181
|
+
import { defineCommand as defineCommand189 } from "citty";
|
|
47080
48182
|
|
|
47081
48183
|
// src/commands/scheduled-actions/create.ts
|
|
47082
|
-
import { defineCommand as
|
|
48184
|
+
import { defineCommand as defineCommand182 } from "citty";
|
|
47083
48185
|
|
|
47084
48186
|
// src/commands/scheduled-actions/shared.ts
|
|
47085
48187
|
var TEMP_SCHEDULED_ACTION_PREFIX = "temp_sched_";
|
|
@@ -47226,7 +48328,7 @@ registerSchema({
|
|
|
47226
48328
|
}
|
|
47227
48329
|
}
|
|
47228
48330
|
});
|
|
47229
|
-
var createCommand3 =
|
|
48331
|
+
var createCommand3 = defineCommand182({
|
|
47230
48332
|
meta: {
|
|
47231
48333
|
name: "create",
|
|
47232
48334
|
description: 'Stage a scheduled action. Example: baker scheduled-actions create --name "Weekly report" --description "..." --cron "0 9 * * MON"'
|
|
@@ -47285,7 +48387,7 @@ var createCommand3 = defineCommand180({
|
|
|
47285
48387
|
});
|
|
47286
48388
|
|
|
47287
48389
|
// src/commands/scheduled-actions/delete.ts
|
|
47288
|
-
import { defineCommand as
|
|
48390
|
+
import { defineCommand as defineCommand183 } from "citty";
|
|
47289
48391
|
registerSchema({
|
|
47290
48392
|
command: "scheduled-actions.delete",
|
|
47291
48393
|
description: "Stage deletion of a published scheduled action or cancellation of a temp_sched_* draft creation.",
|
|
@@ -47293,7 +48395,7 @@ registerSchema({
|
|
|
47293
48395
|
id: { type: "string", description: "Published scheduled action ID or temp_sched_* draft ID", required: true }
|
|
47294
48396
|
}
|
|
47295
48397
|
});
|
|
47296
|
-
var deleteCommand3 =
|
|
48398
|
+
var deleteCommand3 = defineCommand183({
|
|
47297
48399
|
meta: {
|
|
47298
48400
|
name: "delete",
|
|
47299
48401
|
description: "Stage scheduled action deletion. Example: baker scheduled-actions delete <id-or-temp_sched_id>"
|
|
@@ -47322,7 +48424,7 @@ var deleteCommand3 = defineCommand181({
|
|
|
47322
48424
|
});
|
|
47323
48425
|
|
|
47324
48426
|
// src/commands/scheduled-actions/get.ts
|
|
47325
|
-
import { defineCommand as
|
|
48427
|
+
import { defineCommand as defineCommand184 } from "citty";
|
|
47326
48428
|
registerSchema({
|
|
47327
48429
|
command: "scheduled-actions.get",
|
|
47328
48430
|
description: "Get a published scheduled action or a temp_sched_* draft-created scheduled action.",
|
|
@@ -47331,7 +48433,7 @@ registerSchema({
|
|
|
47331
48433
|
chat: { type: "string", description: CHAT_READ_ARG.description, required: false }
|
|
47332
48434
|
}
|
|
47333
48435
|
});
|
|
47334
|
-
var getCommand4 =
|
|
48436
|
+
var getCommand4 = defineCommand184({
|
|
47335
48437
|
meta: {
|
|
47336
48438
|
name: "get",
|
|
47337
48439
|
description: "Get a scheduled action. Example: baker scheduled-actions get <id-or-temp_sched_id>"
|
|
@@ -47370,7 +48472,7 @@ var getCommand4 = defineCommand182({
|
|
|
47370
48472
|
});
|
|
47371
48473
|
|
|
47372
48474
|
// src/commands/scheduled-actions/list.ts
|
|
47373
|
-
import { defineCommand as
|
|
48475
|
+
import { defineCommand as defineCommand185 } from "citty";
|
|
47374
48476
|
registerSchema({
|
|
47375
48477
|
command: "scheduled-actions.list",
|
|
47376
48478
|
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.",
|
|
@@ -47378,7 +48480,7 @@ registerSchema({
|
|
|
47378
48480
|
chat: { type: "string", description: CHAT_READ_ARG.description, required: false }
|
|
47379
48481
|
}
|
|
47380
48482
|
});
|
|
47381
|
-
var listCommand15 =
|
|
48483
|
+
var listCommand15 = defineCommand185({
|
|
47382
48484
|
meta: {
|
|
47383
48485
|
name: "list",
|
|
47384
48486
|
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."
|
|
@@ -47401,9 +48503,9 @@ var listCommand15 = defineCommand183({
|
|
|
47401
48503
|
});
|
|
47402
48504
|
|
|
47403
48505
|
// src/commands/scheduled-actions/templates.ts
|
|
47404
|
-
import { readFile as
|
|
47405
|
-
import
|
|
47406
|
-
import { defineCommand as
|
|
48506
|
+
import { readFile as readFile24 } from "fs/promises";
|
|
48507
|
+
import path39 from "path";
|
|
48508
|
+
import { defineCommand as defineCommand186 } from "citty";
|
|
47407
48509
|
registerSchema({
|
|
47408
48510
|
command: "scheduled-actions.templates",
|
|
47409
48511
|
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.",
|
|
@@ -47458,7 +48560,7 @@ registerSchema({
|
|
|
47458
48560
|
}
|
|
47459
48561
|
}
|
|
47460
48562
|
});
|
|
47461
|
-
var templatesCommand =
|
|
48563
|
+
var templatesCommand = defineCommand186({
|
|
47462
48564
|
meta: {
|
|
47463
48565
|
name: "templates",
|
|
47464
48566
|
description: `The recipes this company can run \u2014 Baker's own plus theirs, with what each one produces.
|
|
@@ -47505,7 +48607,7 @@ Full guide: __tooling__/docs/tools/baker/scheduled-actions.md`
|
|
|
47505
48607
|
}
|
|
47506
48608
|
if (save.length > 0) {
|
|
47507
48609
|
const briefFile = flag("brief-file");
|
|
47508
|
-
const brief = briefFile.length > 0 ? await
|
|
48610
|
+
const brief = briefFile.length > 0 ? await readFile24(path39.resolve(briefFile), "utf8") : flag("brief");
|
|
47509
48611
|
if (brief.trim().length === 0) {
|
|
47510
48612
|
failValidation4("--brief-file (preferred) or --brief is required: the brief is the recipe.");
|
|
47511
48613
|
}
|
|
@@ -47550,7 +48652,7 @@ Full guide: __tooling__/docs/tools/baker/scheduled-actions.md`
|
|
|
47550
48652
|
});
|
|
47551
48653
|
|
|
47552
48654
|
// src/commands/scheduled-actions/trigger.ts
|
|
47553
|
-
import { defineCommand as
|
|
48655
|
+
import { defineCommand as defineCommand187 } from "citty";
|
|
47554
48656
|
registerSchema({
|
|
47555
48657
|
command: "scheduled-actions.trigger",
|
|
47556
48658
|
description: "Immediately trigger a published scheduled action. Does not require BAKER_CHAT_ID and rejects temp_sched_* IDs.",
|
|
@@ -47558,7 +48660,7 @@ registerSchema({
|
|
|
47558
48660
|
id: { type: "string", description: "Published scheduled action ID", required: true }
|
|
47559
48661
|
}
|
|
47560
48662
|
});
|
|
47561
|
-
var triggerCommand =
|
|
48663
|
+
var triggerCommand = defineCommand187({
|
|
47562
48664
|
meta: {
|
|
47563
48665
|
name: "trigger",
|
|
47564
48666
|
description: "Immediately trigger a published scheduled action. Example: baker scheduled-actions trigger <id>"
|
|
@@ -47595,7 +48697,7 @@ var triggerCommand = defineCommand185({
|
|
|
47595
48697
|
});
|
|
47596
48698
|
|
|
47597
48699
|
// src/commands/scheduled-actions/update.ts
|
|
47598
|
-
import { defineCommand as
|
|
48700
|
+
import { defineCommand as defineCommand188 } from "citty";
|
|
47599
48701
|
registerSchema({
|
|
47600
48702
|
command: "scheduled-actions.update",
|
|
47601
48703
|
description: "Stage an update to a published scheduled action or temp_sched_* draft-created scheduled action.",
|
|
@@ -47626,7 +48728,7 @@ registerSchema({
|
|
|
47626
48728
|
prompt: { type: "string", description: "Replacement additional spawned-agent instructions", required: false }
|
|
47627
48729
|
}
|
|
47628
48730
|
});
|
|
47629
|
-
var updateCommand3 =
|
|
48731
|
+
var updateCommand3 = defineCommand188({
|
|
47630
48732
|
meta: {
|
|
47631
48733
|
name: "update",
|
|
47632
48734
|
description: "Stage a scheduled action update. Examples: baker scheduled-actions update <id> --enabled false | baker scheduled-actions update <id> --mode publish"
|
|
@@ -47704,7 +48806,7 @@ var updateCommand3 = defineCommand186({
|
|
|
47704
48806
|
});
|
|
47705
48807
|
|
|
47706
48808
|
// src/commands/scheduled-actions/index.ts
|
|
47707
|
-
var scheduledActionsCommand =
|
|
48809
|
+
var scheduledActionsCommand = defineCommand189({
|
|
47708
48810
|
meta: {
|
|
47709
48811
|
name: "scheduled-actions",
|
|
47710
48812
|
description: `Manage Scheduled Actions. Subcommands: list, get, create, update, delete, trigger, templates.
|
|
@@ -47733,14 +48835,14 @@ Full guide: __tooling__/docs/tools/baker/scheduled-actions.md`
|
|
|
47733
48835
|
});
|
|
47734
48836
|
|
|
47735
48837
|
// src/commands/schema.ts
|
|
47736
|
-
import { defineCommand as
|
|
48838
|
+
import { defineCommand as defineCommand190 } from "citty";
|
|
47737
48839
|
function narrowToFamily(commandName, available) {
|
|
47738
48840
|
const segments = commandName.split(".");
|
|
47739
48841
|
const prefix = segments[0] === "ads" && segments[1] ? `ads.${segments[1]}.` : `${segments[0]}.`;
|
|
47740
48842
|
const siblings = available.filter((name) => name.startsWith(prefix));
|
|
47741
48843
|
return siblings.length > 0 ? siblings : available;
|
|
47742
48844
|
}
|
|
47743
|
-
var schemaCommand2 =
|
|
48845
|
+
var schemaCommand2 = defineCommand190({
|
|
47744
48846
|
meta: {
|
|
47745
48847
|
name: "schema",
|
|
47746
48848
|
description: "Inspect command argument schemas (for AI agent introspection). Lists all commands if no argument given. Example: baker schema images.search"
|
|
@@ -47784,10 +48886,10 @@ var schemaCommand2 = defineCommand188({
|
|
|
47784
48886
|
});
|
|
47785
48887
|
|
|
47786
48888
|
// src/commands/studio/index.ts
|
|
47787
|
-
import { defineCommand as
|
|
48889
|
+
import { defineCommand as defineCommand199 } from "citty";
|
|
47788
48890
|
|
|
47789
48891
|
// src/commands/studio/animate.ts
|
|
47790
|
-
import { defineCommand as
|
|
48892
|
+
import { defineCommand as defineCommand191 } from "citty";
|
|
47791
48893
|
|
|
47792
48894
|
// src/commands/studio/batch.ts
|
|
47793
48895
|
function projectBatch(generation, full) {
|
|
@@ -47976,7 +49078,7 @@ function parseImageRefs(spec) {
|
|
|
47976
49078
|
}
|
|
47977
49079
|
var defaultDeps = {
|
|
47978
49080
|
ingest: (url) => apiPost("/api/images/ingest", { url, source: "uploaded" }),
|
|
47979
|
-
upload: (
|
|
49081
|
+
upload: (path41) => uploadLocalImage({ file: path41, contentType: detectImageContentType(path41), source: "uploaded" })
|
|
47980
49082
|
};
|
|
47981
49083
|
async function resolveLibraryImageIds(spec, limit, deps = defaultDeps) {
|
|
47982
49084
|
const refs = parseImageRefs(spec);
|
|
@@ -47996,10 +49098,10 @@ async function resolveLibraryImageIds(spec, limit, deps = defaultDeps) {
|
|
|
47996
49098
|
}
|
|
47997
49099
|
return { imageIds, added };
|
|
47998
49100
|
}
|
|
47999
|
-
function uploadFailure(
|
|
49101
|
+
function uploadFailure(path41) {
|
|
48000
49102
|
return (error) => {
|
|
48001
49103
|
if (error instanceof ApiError) throw error;
|
|
48002
|
-
throw new ApiError("VALIDATION_ERROR", `Could not read "${
|
|
49104
|
+
throw new ApiError("VALIDATION_ERROR", `Could not read "${path41}" as an image.`);
|
|
48003
49105
|
};
|
|
48004
49106
|
}
|
|
48005
49107
|
|
|
@@ -48238,7 +49340,7 @@ function costHintsFor(body) {
|
|
|
48238
49340
|
}
|
|
48239
49341
|
return hints;
|
|
48240
49342
|
}
|
|
48241
|
-
var animateCommand =
|
|
49343
|
+
var animateCommand = defineCommand191({
|
|
48242
49344
|
meta: {
|
|
48243
49345
|
name: "animate",
|
|
48244
49346
|
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"
|
|
@@ -48357,7 +49459,7 @@ var animateCommand = defineCommand189({
|
|
|
48357
49459
|
});
|
|
48358
49460
|
|
|
48359
49461
|
// src/commands/studio/generate.ts
|
|
48360
|
-
import { defineCommand as
|
|
49462
|
+
import { defineCommand as defineCommand192 } from "citty";
|
|
48361
49463
|
var MODEL_LIST2 = IMAGE_MODEL_IDS;
|
|
48362
49464
|
var DEFAULT_MAX_WAIT_MS2 = 24e4;
|
|
48363
49465
|
registerSchema({
|
|
@@ -48493,7 +49595,7 @@ function buildGenerateBody(args, prompt) {
|
|
|
48493
49595
|
}
|
|
48494
49596
|
return body;
|
|
48495
49597
|
}
|
|
48496
|
-
var generateCommand =
|
|
49598
|
+
var generateCommand = defineCommand192({
|
|
48497
49599
|
meta: {
|
|
48498
49600
|
name: "generate",
|
|
48499
49601
|
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]]'"
|
|
@@ -48571,7 +49673,7 @@ var generateCommand = defineCommand190({
|
|
|
48571
49673
|
});
|
|
48572
49674
|
|
|
48573
49675
|
// src/commands/studio/get.ts
|
|
48574
|
-
import { defineCommand as
|
|
49676
|
+
import { defineCommand as defineCommand193 } from "citty";
|
|
48575
49677
|
registerSchema({
|
|
48576
49678
|
command: "studio.get",
|
|
48577
49679
|
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.",
|
|
@@ -48580,7 +49682,7 @@ registerSchema({
|
|
|
48580
49682
|
full: { type: "boolean", description: "Include settings, references and attribution", required: false }
|
|
48581
49683
|
}
|
|
48582
49684
|
});
|
|
48583
|
-
var getCommand5 =
|
|
49685
|
+
var getCommand5 = defineCommand193({
|
|
48584
49686
|
meta: {
|
|
48585
49687
|
name: "get",
|
|
48586
49688
|
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"
|
|
@@ -48609,7 +49711,7 @@ var getCommand5 = defineCommand191({
|
|
|
48609
49711
|
});
|
|
48610
49712
|
|
|
48611
49713
|
// src/commands/studio/improve.ts
|
|
48612
|
-
import { defineCommand as
|
|
49714
|
+
import { defineCommand as defineCommand194 } from "citty";
|
|
48613
49715
|
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.";
|
|
48614
49716
|
registerSchema({
|
|
48615
49717
|
command: "studio.improve",
|
|
@@ -48634,7 +49736,7 @@ registerSchema({
|
|
|
48634
49736
|
}
|
|
48635
49737
|
}
|
|
48636
49738
|
});
|
|
48637
|
-
var improveCommand =
|
|
49739
|
+
var improveCommand = defineCommand194({
|
|
48638
49740
|
meta: {
|
|
48639
49741
|
name: "improve",
|
|
48640
49742
|
description: `${DESCRIPTION}
|
|
@@ -48678,7 +49780,7 @@ Examples:
|
|
|
48678
49780
|
});
|
|
48679
49781
|
|
|
48680
49782
|
// src/commands/studio/keep.ts
|
|
48681
|
-
import { defineCommand as
|
|
49783
|
+
import { defineCommand as defineCommand195 } from "citty";
|
|
48682
49784
|
registerSchema({
|
|
48683
49785
|
command: "studio.keep",
|
|
48684
49786
|
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.",
|
|
@@ -48688,7 +49790,7 @@ registerSchema({
|
|
|
48688
49790
|
undo: { type: "boolean", description: "Un-star an image, or take a kept clip back out", required: false }
|
|
48689
49791
|
}
|
|
48690
49792
|
});
|
|
48691
|
-
var keepCommand =
|
|
49793
|
+
var keepCommand = defineCommand195({
|
|
48692
49794
|
meta: {
|
|
48693
49795
|
name: "keep",
|
|
48694
49796
|
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"
|
|
@@ -48739,7 +49841,7 @@ var keepCommand = defineCommand193({
|
|
|
48739
49841
|
});
|
|
48740
49842
|
|
|
48741
49843
|
// src/commands/studio/list.ts
|
|
48742
|
-
import { defineCommand as
|
|
49844
|
+
import { defineCommand as defineCommand196 } from "citty";
|
|
48743
49845
|
registerSchema({
|
|
48744
49846
|
command: "studio.list",
|
|
48745
49847
|
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.",
|
|
@@ -48750,7 +49852,7 @@ registerSchema({
|
|
|
48750
49852
|
full: { type: "boolean", description: "Include settings, references and attribution", required: false }
|
|
48751
49853
|
}
|
|
48752
49854
|
});
|
|
48753
|
-
var listCommand16 =
|
|
49855
|
+
var listCommand16 = defineCommand196({
|
|
48754
49856
|
meta: {
|
|
48755
49857
|
name: "list",
|
|
48756
49858
|
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"
|
|
@@ -48784,7 +49886,7 @@ var listCommand16 = defineCommand194({
|
|
|
48784
49886
|
});
|
|
48785
49887
|
|
|
48786
49888
|
// src/commands/studio/models.ts
|
|
48787
|
-
import { defineCommand as
|
|
49889
|
+
import { defineCommand as defineCommand197 } from "citty";
|
|
48788
49890
|
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.";
|
|
48789
49891
|
registerSchema({
|
|
48790
49892
|
command: "studio.models",
|
|
@@ -48871,7 +49973,7 @@ function buildModelCards(kind, model) {
|
|
|
48871
49973
|
const selected = model ? ids.filter((id) => id === model) : ids;
|
|
48872
49974
|
return selected.map((id) => build(id));
|
|
48873
49975
|
}
|
|
48874
|
-
var modelsCommand =
|
|
49976
|
+
var modelsCommand = defineCommand197({
|
|
48875
49977
|
meta: {
|
|
48876
49978
|
name: "models",
|
|
48877
49979
|
description: `${DESCRIPTION2}
|
|
@@ -48918,13 +50020,13 @@ Examples:
|
|
|
48918
50020
|
});
|
|
48919
50021
|
|
|
48920
50022
|
// src/commands/studio/skills.ts
|
|
48921
|
-
import { defineCommand as
|
|
50023
|
+
import { defineCommand as defineCommand198 } from "citty";
|
|
48922
50024
|
registerSchema({
|
|
48923
50025
|
command: "studio.skills",
|
|
48924
50026
|
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.",
|
|
48925
50027
|
args: {}
|
|
48926
50028
|
});
|
|
48927
|
-
var skillsCommand =
|
|
50029
|
+
var skillsCommand = defineCommand198({
|
|
48928
50030
|
meta: {
|
|
48929
50031
|
name: "skills",
|
|
48930
50032
|
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"
|
|
@@ -48948,7 +50050,7 @@ var skillsCommand = defineCommand196({
|
|
|
48948
50050
|
});
|
|
48949
50051
|
|
|
48950
50052
|
// src/commands/studio/index.ts
|
|
48951
|
-
var studioCommand =
|
|
50053
|
+
var studioCommand = defineCommand199({
|
|
48952
50054
|
meta: {
|
|
48953
50055
|
name: "studio",
|
|
48954
50056
|
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.
|
|
@@ -48991,10 +50093,10 @@ Full guide: __tooling__/docs/tools/baker/studio.md`
|
|
|
48991
50093
|
});
|
|
48992
50094
|
|
|
48993
50095
|
// src/commands/tag-manager/index.ts
|
|
48994
|
-
import { defineCommand as
|
|
50096
|
+
import { defineCommand as defineCommand203 } from "citty";
|
|
48995
50097
|
|
|
48996
50098
|
// src/commands/tag-manager/draft.ts
|
|
48997
|
-
import { defineCommand as
|
|
50099
|
+
import { defineCommand as defineCommand200 } from "citty";
|
|
48998
50100
|
|
|
48999
50101
|
// src/commands/tag-manager/shared.ts
|
|
49000
50102
|
import { readFileSync as readFileSync15 } from "fs";
|
|
@@ -49061,10 +50163,10 @@ async function stageOp4(op) {
|
|
|
49061
50163
|
handleError5(err);
|
|
49062
50164
|
}
|
|
49063
50165
|
}
|
|
49064
|
-
async function draftAction3(
|
|
50166
|
+
async function draftAction3(path41, body, chat) {
|
|
49065
50167
|
const chatId = resolveChatId(chat);
|
|
49066
50168
|
try {
|
|
49067
|
-
const data = await apiPost(
|
|
50169
|
+
const data = await apiPost(path41, { chatId, ...body });
|
|
49068
50170
|
writeJsonEnvelope({ ok: true, data });
|
|
49069
50171
|
return data;
|
|
49070
50172
|
} catch (err) {
|
|
@@ -49117,13 +50219,13 @@ registerSchema({
|
|
|
49117
50219
|
chat: { type: "string", description: CHAT_READ_ARG.description, required: false }
|
|
49118
50220
|
}
|
|
49119
50221
|
});
|
|
49120
|
-
var draftCommand4 =
|
|
50222
|
+
var draftCommand4 = defineCommand200({
|
|
49121
50223
|
meta: {
|
|
49122
50224
|
name: "draft",
|
|
49123
50225
|
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."
|
|
49124
50226
|
},
|
|
49125
50227
|
subCommands: {
|
|
49126
|
-
list:
|
|
50228
|
+
list: defineCommand200({
|
|
49127
50229
|
meta: {
|
|
49128
50230
|
name: "list",
|
|
49129
50231
|
description: "Review everything staged on this chat (--json for the raw envelope)"
|
|
@@ -49136,7 +50238,7 @@ var draftCommand4 = defineCommand198({
|
|
|
49136
50238
|
await draftList2(args.json === true, args.chat);
|
|
49137
50239
|
}
|
|
49138
50240
|
}),
|
|
49139
|
-
show:
|
|
50241
|
+
show: defineCommand200({
|
|
49140
50242
|
meta: {
|
|
49141
50243
|
name: "show",
|
|
49142
50244
|
description: "Print the full staged payload for one change \u2014 the receipt to verify it looks right before publish (never truncated)."
|
|
@@ -49153,7 +50255,7 @@ var draftCommand4 = defineCommand198({
|
|
|
49153
50255
|
);
|
|
49154
50256
|
}
|
|
49155
50257
|
}),
|
|
49156
|
-
amend:
|
|
50258
|
+
amend: defineCommand200({
|
|
49157
50259
|
meta: {
|
|
49158
50260
|
name: "amend",
|
|
49159
50261
|
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."
|
|
@@ -49170,7 +50272,7 @@ var draftCommand4 = defineCommand198({
|
|
|
49170
50272
|
});
|
|
49171
50273
|
}
|
|
49172
50274
|
}),
|
|
49173
|
-
remove:
|
|
50275
|
+
remove: defineCommand200({
|
|
49174
50276
|
meta: { name: "remove", description: "Remove one staged change (cascades to anything depending on it)" },
|
|
49175
50277
|
args: { ref: { type: "positional", description: "Staged ref (gtm_temp_*) or target", required: false } },
|
|
49176
50278
|
run: async ({ args }) => {
|
|
@@ -49179,7 +50281,7 @@ var draftCommand4 = defineCommand198({
|
|
|
49179
50281
|
});
|
|
49180
50282
|
}
|
|
49181
50283
|
}),
|
|
49182
|
-
clear:
|
|
50284
|
+
clear: defineCommand200({
|
|
49183
50285
|
meta: { name: "clear", description: "Discard all Tag Manager changes staged on this chat" },
|
|
49184
50286
|
run: async () => {
|
|
49185
50287
|
await draftAction3("/api/tag-manager/draft/clear", {});
|
|
@@ -49189,7 +50291,7 @@ var draftCommand4 = defineCommand198({
|
|
|
49189
50291
|
});
|
|
49190
50292
|
|
|
49191
50293
|
// src/commands/tag-manager/read.ts
|
|
49192
|
-
import { defineCommand as
|
|
50294
|
+
import { defineCommand as defineCommand201 } from "citty";
|
|
49193
50295
|
registerSchema({
|
|
49194
50296
|
command: "tagManager.containers",
|
|
49195
50297
|
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.",
|
|
@@ -49230,7 +50332,7 @@ function containersHints(containers) {
|
|
|
49230
50332
|
}))
|
|
49231
50333
|
});
|
|
49232
50334
|
}
|
|
49233
|
-
var containersCommand =
|
|
50335
|
+
var containersCommand = defineCommand201({
|
|
49234
50336
|
meta: {
|
|
49235
50337
|
name: "containers",
|
|
49236
50338
|
description: `List Tag Manager containers reachable by this company's connection.
|
|
@@ -49247,7 +50349,7 @@ Start here:
|
|
|
49247
50349
|
}
|
|
49248
50350
|
}
|
|
49249
50351
|
});
|
|
49250
|
-
var readCommand =
|
|
50352
|
+
var readCommand = defineCommand201({
|
|
49251
50353
|
meta: {
|
|
49252
50354
|
name: "read",
|
|
49253
50355
|
description: `Read the current contents of the Tag Manager container \u2014 always do this before staging changes.
|
|
@@ -49289,7 +50391,7 @@ Examples:
|
|
|
49289
50391
|
});
|
|
49290
50392
|
|
|
49291
50393
|
// src/commands/tag-manager/write-commands.ts
|
|
49292
|
-
import { defineCommand as
|
|
50394
|
+
import { defineCommand as defineCommand202 } from "citty";
|
|
49293
50395
|
var CONTAINER_ARG_DESCRIPTION = "Numeric container id (optional only when one container is connected \u2014 run `baker tag-manager containers`)";
|
|
49294
50396
|
var ENTITIES = [
|
|
49295
50397
|
{
|
|
@@ -49345,10 +50447,10 @@ for (const { entity, noun, createHint } of ENTITIES) {
|
|
|
49345
50447
|
});
|
|
49346
50448
|
}
|
|
49347
50449
|
function entityCommand(entity, noun, example) {
|
|
49348
|
-
return
|
|
50450
|
+
return defineCommand202({
|
|
49349
50451
|
meta: { name: entity, description: `Stage ${noun} changes on this chat's Tag Manager draft` },
|
|
49350
50452
|
subCommands: {
|
|
49351
|
-
create:
|
|
50453
|
+
create: defineCommand202({
|
|
49352
50454
|
meta: {
|
|
49353
50455
|
name: "create",
|
|
49354
50456
|
description: `Stage a new ${noun}
|
|
@@ -49370,7 +50472,7 @@ Examples:
|
|
|
49370
50472
|
});
|
|
49371
50473
|
}
|
|
49372
50474
|
}),
|
|
49373
|
-
update:
|
|
50475
|
+
update: defineCommand202({
|
|
49374
50476
|
meta: {
|
|
49375
50477
|
name: "update",
|
|
49376
50478
|
description: `Stage an update to an existing ${noun} (pass its id or path)`
|
|
@@ -49390,7 +50492,7 @@ Examples:
|
|
|
49390
50492
|
});
|
|
49391
50493
|
}
|
|
49392
50494
|
}),
|
|
49393
|
-
delete:
|
|
50495
|
+
delete: defineCommand202({
|
|
49394
50496
|
meta: { name: "delete", description: `Stage the deletion of a ${noun} (pass its id or path)` },
|
|
49395
50497
|
args: {
|
|
49396
50498
|
id: { type: "positional", description: `${noun} id or path`, required: false },
|
|
@@ -49431,7 +50533,7 @@ function builtinTypes(args) {
|
|
|
49431
50533
|
}
|
|
49432
50534
|
return raw.split(",").map((entry) => entry.trim());
|
|
49433
50535
|
}
|
|
49434
|
-
var builtinCommand =
|
|
50536
|
+
var builtinCommand = defineCommand202({
|
|
49435
50537
|
meta: {
|
|
49436
50538
|
name: "builtin",
|
|
49437
50539
|
description: `Enable or disable built-in variables
|
|
@@ -49441,7 +50543,7 @@ Examples:
|
|
|
49441
50543
|
baker tag-manager builtin disable --types formId`
|
|
49442
50544
|
},
|
|
49443
50545
|
subCommands: {
|
|
49444
|
-
enable:
|
|
50546
|
+
enable: defineCommand202({
|
|
49445
50547
|
meta: { name: "enable", description: "Stage enabling built-in variables" },
|
|
49446
50548
|
args: {
|
|
49447
50549
|
types: { type: "string", description: "Comma-separated types", required: false },
|
|
@@ -49455,7 +50557,7 @@ Examples:
|
|
|
49455
50557
|
});
|
|
49456
50558
|
}
|
|
49457
50559
|
}),
|
|
49458
|
-
disable:
|
|
50560
|
+
disable: defineCommand202({
|
|
49459
50561
|
meta: { name: "disable", description: "Stage disabling built-in variables" },
|
|
49460
50562
|
args: {
|
|
49461
50563
|
types: { type: "string", description: "Comma-separated types", required: false },
|
|
@@ -49473,7 +50575,7 @@ Examples:
|
|
|
49473
50575
|
});
|
|
49474
50576
|
|
|
49475
50577
|
// src/commands/tag-manager/index.ts
|
|
49476
|
-
var tagManagerCommand =
|
|
50578
|
+
var tagManagerCommand = defineCommand203({
|
|
49477
50579
|
meta: {
|
|
49478
50580
|
name: "tag-manager",
|
|
49479
50581
|
description: `Read and change what lives inside the client's Google Tag Manager container \u2014 tags, triggers, variables, folders and built-in variables.
|
|
@@ -49510,7 +50612,7 @@ Full guide: __tooling__/docs/tools/baker/tag-manager.md`
|
|
|
49510
50612
|
});
|
|
49511
50613
|
|
|
49512
50614
|
// src/commands/tags/index.ts
|
|
49513
|
-
import { defineCommand as
|
|
50615
|
+
import { defineCommand as defineCommand204 } from "citty";
|
|
49514
50616
|
|
|
49515
50617
|
// src/commands/tags/shared.ts
|
|
49516
50618
|
function failApi3(err) {
|
|
@@ -49579,7 +50681,7 @@ async function listTags(json) {
|
|
|
49579
50681
|
var listArgs9 = {
|
|
49580
50682
|
json: { type: "boolean", description: "Print the raw JSON envelope instead of the readable list" }
|
|
49581
50683
|
};
|
|
49582
|
-
var listCommand17 =
|
|
50684
|
+
var listCommand17 = defineCommand204({
|
|
49583
50685
|
meta: {
|
|
49584
50686
|
name: "list",
|
|
49585
50687
|
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"
|
|
@@ -49598,7 +50700,7 @@ async function listDraft3(chat) {
|
|
|
49598
50700
|
failApi3(err);
|
|
49599
50701
|
}
|
|
49600
50702
|
}
|
|
49601
|
-
var draftCommand5 =
|
|
50703
|
+
var draftCommand5 = defineCommand204({
|
|
49602
50704
|
meta: {
|
|
49603
50705
|
name: "draft",
|
|
49604
50706
|
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."
|
|
@@ -49608,7 +50710,7 @@ var draftCommand5 = defineCommand202({
|
|
|
49608
50710
|
await listDraft3(args.chat);
|
|
49609
50711
|
}
|
|
49610
50712
|
});
|
|
49611
|
-
var tagsCommand4 =
|
|
50713
|
+
var tagsCommand4 = defineCommand204({
|
|
49612
50714
|
meta: {
|
|
49613
50715
|
name: "tags",
|
|
49614
50716
|
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.
|
|
@@ -49637,10 +50739,10 @@ Full guide: __tooling__/docs/tools/baker/tags.md`
|
|
|
49637
50739
|
});
|
|
49638
50740
|
|
|
49639
50741
|
// src/commands/testimonials/index.ts
|
|
49640
|
-
import { defineCommand as
|
|
50742
|
+
import { defineCommand as defineCommand208 } from "citty";
|
|
49641
50743
|
|
|
49642
50744
|
// src/commands/testimonials/get.ts
|
|
49643
|
-
import { defineCommand as
|
|
50745
|
+
import { defineCommand as defineCommand205 } from "citty";
|
|
49644
50746
|
registerSchema({
|
|
49645
50747
|
command: "testimonials.get",
|
|
49646
50748
|
description: "Get a single testimonial by ID",
|
|
@@ -49648,7 +50750,7 @@ registerSchema({
|
|
|
49648
50750
|
id: { type: "string", description: "Testimonial ID", required: true }
|
|
49649
50751
|
}
|
|
49650
50752
|
});
|
|
49651
|
-
var getCommand6 =
|
|
50753
|
+
var getCommand6 = defineCommand205({
|
|
49652
50754
|
meta: { name: "get", description: "Get a single testimonial by ID. Example: baker testimonials get j571abc123" },
|
|
49653
50755
|
args: {
|
|
49654
50756
|
id: { type: "positional", description: "Testimonial ID", required: false },
|
|
@@ -49685,7 +50787,7 @@ var getCommand6 = defineCommand203({
|
|
|
49685
50787
|
});
|
|
49686
50788
|
|
|
49687
50789
|
// src/commands/testimonials/list.ts
|
|
49688
|
-
import { defineCommand as
|
|
50790
|
+
import { defineCommand as defineCommand206 } from "citty";
|
|
49689
50791
|
|
|
49690
50792
|
// src/commands/testimonials/emptyCorpusHints.ts
|
|
49691
50793
|
function resolveEmptyReason({
|
|
@@ -49820,7 +50922,7 @@ function buildListParams(args) {
|
|
|
49820
50922
|
}
|
|
49821
50923
|
return params;
|
|
49822
50924
|
}
|
|
49823
|
-
var listCommand18 =
|
|
50925
|
+
var listCommand18 = defineCommand206({
|
|
49824
50926
|
meta: {
|
|
49825
50927
|
name: "list",
|
|
49826
50928
|
description: "List testimonials with optional filters. Example: baker testimonials list --source google --sentiment positive"
|
|
@@ -49872,7 +50974,7 @@ var listCommand18 = defineCommand204({
|
|
|
49872
50974
|
});
|
|
49873
50975
|
|
|
49874
50976
|
// src/commands/testimonials/search.ts
|
|
49875
|
-
import { defineCommand as
|
|
50977
|
+
import { defineCommand as defineCommand207 } from "citty";
|
|
49876
50978
|
var FILTER_FLAGS2 = ["source", "rating-min", "rating-max", "status", "sentiment", "language", "tags"];
|
|
49877
50979
|
function languageBiasHint(results, requestedLanguage) {
|
|
49878
50980
|
if (requestedLanguage) {
|
|
@@ -49951,7 +51053,7 @@ function buildSearchRequest(query, args) {
|
|
|
49951
51053
|
}
|
|
49952
51054
|
return body;
|
|
49953
51055
|
}
|
|
49954
|
-
var searchCommand3 =
|
|
51056
|
+
var searchCommand3 = defineCommand207({
|
|
49955
51057
|
meta: {
|
|
49956
51058
|
name: "search",
|
|
49957
51059
|
description: "Semantic search testimonials by text query. Uses hybrid BM25 + vector + reranking. Example: baker testimonials search 'great service' --rating-min 4"
|
|
@@ -50015,7 +51117,7 @@ var searchCommand3 = defineCommand205({
|
|
|
50015
51117
|
var tagsCommand5 = makeTagsCommand("testimonials", "testimonial", "/api/testimonials/tags");
|
|
50016
51118
|
|
|
50017
51119
|
// src/commands/testimonials/index.ts
|
|
50018
|
-
var testimonialsCommand =
|
|
51120
|
+
var testimonialsCommand = defineCommand208({
|
|
50019
51121
|
meta: {
|
|
50020
51122
|
name: "testimonials",
|
|
50021
51123
|
description: `Find and browse testimonials in Baker. Subcommands: search, get, list, tags.
|
|
@@ -50037,10 +51139,10 @@ Full guide: __tooling__/docs/tools/baker/testimonials.md`
|
|
|
50037
51139
|
});
|
|
50038
51140
|
|
|
50039
51141
|
// src/commands/videos/index.ts
|
|
50040
|
-
import { defineCommand as
|
|
51142
|
+
import { defineCommand as defineCommand215 } from "citty";
|
|
50041
51143
|
|
|
50042
51144
|
// src/commands/videos/delete.ts
|
|
50043
|
-
import { defineCommand as
|
|
51145
|
+
import { defineCommand as defineCommand209 } from "citty";
|
|
50044
51146
|
registerSchema({
|
|
50045
51147
|
command: "videos.delete",
|
|
50046
51148
|
description: "Delete a video by ID",
|
|
@@ -50054,7 +51156,7 @@ registerSchema({
|
|
|
50054
51156
|
}
|
|
50055
51157
|
}
|
|
50056
51158
|
});
|
|
50057
|
-
var deleteCommand4 =
|
|
51159
|
+
var deleteCommand4 = defineCommand209({
|
|
50058
51160
|
meta: {
|
|
50059
51161
|
name: "delete",
|
|
50060
51162
|
description: "Delete a video by ID. Use --dry-run to preview. Example: baker videos delete j571abc123 --dry-run"
|
|
@@ -50095,7 +51197,7 @@ var deleteCommand4 = defineCommand207({
|
|
|
50095
51197
|
});
|
|
50096
51198
|
|
|
50097
51199
|
// src/commands/videos/get.ts
|
|
50098
|
-
import { defineCommand as
|
|
51200
|
+
import { defineCommand as defineCommand210 } from "citty";
|
|
50099
51201
|
registerSchema({
|
|
50100
51202
|
command: "videos.get",
|
|
50101
51203
|
description: "Get a single video by ID",
|
|
@@ -50103,7 +51205,7 @@ registerSchema({
|
|
|
50103
51205
|
id: { type: "string", description: "Video ID", required: true }
|
|
50104
51206
|
}
|
|
50105
51207
|
});
|
|
50106
|
-
var getCommand7 =
|
|
51208
|
+
var getCommand7 = defineCommand210({
|
|
50107
51209
|
meta: { name: "get", description: "Get a single video by ID. Example: baker videos get j571abc123" },
|
|
50108
51210
|
args: {
|
|
50109
51211
|
id: { type: "positional", description: "Video ID", required: false },
|
|
@@ -50140,7 +51242,7 @@ var getCommand7 = defineCommand208({
|
|
|
50140
51242
|
});
|
|
50141
51243
|
|
|
50142
51244
|
// src/commands/videos/group.ts
|
|
50143
|
-
import { defineCommand as
|
|
51245
|
+
import { defineCommand as defineCommand211 } from "citty";
|
|
50144
51246
|
registerSchema({
|
|
50145
51247
|
command: "videos.group",
|
|
50146
51248
|
description: "List every clip and image that arrived in the same set as this video (carousel slides, one page)",
|
|
@@ -50149,7 +51251,7 @@ registerSchema({
|
|
|
50149
51251
|
"group-key": { type: "string", description: "The set key directly, when you already have it", required: false }
|
|
50150
51252
|
}
|
|
50151
51253
|
});
|
|
50152
|
-
var groupCommand2 =
|
|
51254
|
+
var groupCommand2 = defineCommand211({
|
|
50153
51255
|
meta: {
|
|
50154
51256
|
name: "group",
|
|
50155
51257
|
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>"
|
|
@@ -50169,10 +51271,10 @@ var groupCommand2 = defineCommand209({
|
|
|
50169
51271
|
});
|
|
50170
51272
|
|
|
50171
51273
|
// src/commands/videos/ingest.ts
|
|
50172
|
-
import { mkdtemp as mkdtemp2, rm as rm7, stat as
|
|
51274
|
+
import { mkdtemp as mkdtemp2, rm as rm7, stat as stat8 } from "fs/promises";
|
|
50173
51275
|
import { tmpdir as tmpdir3 } from "os";
|
|
50174
|
-
import
|
|
50175
|
-
import { defineCommand as
|
|
51276
|
+
import path40 from "path";
|
|
51277
|
+
import { defineCommand as defineCommand212 } from "citty";
|
|
50176
51278
|
|
|
50177
51279
|
// src/lib/streamUpload.ts
|
|
50178
51280
|
import { createHash as createHash2 } from "crypto";
|
|
@@ -50336,7 +51438,7 @@ registerSchema({
|
|
|
50336
51438
|
"dry-run": { type: "boolean", description: "Preview the operation without executing", required: false }
|
|
50337
51439
|
}
|
|
50338
51440
|
});
|
|
50339
|
-
var ingestCommand2 =
|
|
51441
|
+
var ingestCommand2 = defineCommand212({
|
|
50340
51442
|
meta: {
|
|
50341
51443
|
name: "ingest",
|
|
50342
51444
|
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"
|
|
@@ -50452,35 +51554,35 @@ async function ingestByRoute(args, direct, country) {
|
|
|
50452
51554
|
function reportYtDlpFailure(err) {
|
|
50453
51555
|
const detail = `${err.stderrTail} ${err.message}`;
|
|
50454
51556
|
if (isProxyFailure(ytDlpBlockSignal(detail))) {
|
|
50455
|
-
|
|
51557
|
+
fail9(
|
|
50456
51558
|
"Couldn't download that video: our connection to the internet was refused.",
|
|
50457
51559
|
"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."
|
|
50458
51560
|
);
|
|
50459
51561
|
}
|
|
50460
51562
|
if (isDrmProtected(detail)) {
|
|
50461
|
-
|
|
51563
|
+
fail9(
|
|
50462
51564
|
"That video is copy-protected, so it can't be added to the library.",
|
|
50463
51565
|
"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>`."
|
|
50464
51566
|
);
|
|
50465
51567
|
}
|
|
50466
51568
|
if (isVimeoAuthFailure(detail)) {
|
|
50467
|
-
|
|
51569
|
+
fail9(
|
|
50468
51570
|
"Vimeo wouldn't hand over that video \u2014 Baker isn't signed in to Vimeo.",
|
|
50469
51571
|
"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>`."
|
|
50470
51572
|
);
|
|
50471
51573
|
}
|
|
50472
51574
|
if (isRegionBlocked(detail)) {
|
|
50473
|
-
|
|
51575
|
+
fail9(
|
|
50474
51576
|
`That video isn't available in the region we're fetching from. ${err.stderrTail || err.message}`,
|
|
50475
51577
|
"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."
|
|
50476
51578
|
);
|
|
50477
51579
|
}
|
|
50478
|
-
|
|
51580
|
+
fail9(
|
|
50479
51581
|
`Couldn't download that video. ${err.stderrTail || err.message}`,
|
|
50480
51582
|
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>`."
|
|
50481
51583
|
);
|
|
50482
51584
|
}
|
|
50483
|
-
function
|
|
51585
|
+
function fail9(message, fix, code = "INGEST_FAILED") {
|
|
50484
51586
|
writeJson({ ok: false, error: { code, message, fix } });
|
|
50485
51587
|
process.exit(1);
|
|
50486
51588
|
}
|
|
@@ -50492,13 +51594,13 @@ function reportIngestFailure(err) {
|
|
|
50492
51594
|
if (err instanceof YtDlpError) reportYtDlpFailure(err);
|
|
50493
51595
|
const detail = err instanceof Error ? err.message : String(err);
|
|
50494
51596
|
if (detail.includes("timed out after")) {
|
|
50495
|
-
|
|
51597
|
+
fail9(
|
|
50496
51598
|
"That video took too long to download and was stopped part-way.",
|
|
50497
51599
|
"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>`.",
|
|
50498
51600
|
"INTERNAL_ERROR"
|
|
50499
51601
|
);
|
|
50500
51602
|
}
|
|
50501
|
-
|
|
51603
|
+
fail9(
|
|
50502
51604
|
`Couldn't add that video. ${detail}`,
|
|
50503
51605
|
"This is a fault on Baker's side, not a problem with the link. Report it.",
|
|
50504
51606
|
"INTERNAL_ERROR"
|
|
@@ -50522,7 +51624,7 @@ function ingestUrl(args) {
|
|
|
50522
51624
|
}
|
|
50523
51625
|
async function downloadThenIngest(args, country) {
|
|
50524
51626
|
const vimeoCookie = captureVimeoCookie();
|
|
50525
|
-
const workDir = await mkdtemp2(
|
|
51627
|
+
const workDir = await mkdtemp2(path40.join(tmpdir3(), "videos-ingest-"));
|
|
50526
51628
|
try {
|
|
50527
51629
|
const probe = await probeYtDlp({ url: args.url, country, vimeoCookie, cookieDir: workDir });
|
|
50528
51630
|
if (isAudioOnly(probe.info)) {
|
|
@@ -50553,7 +51655,7 @@ async function downloadThenIngest(args, country) {
|
|
|
50553
51655
|
// we allow to fetch it cannot drift apart.
|
|
50554
51656
|
timeoutMs: downloadTimeoutMs(durationSeconds(probe.info))
|
|
50555
51657
|
});
|
|
50556
|
-
const stats = await
|
|
51658
|
+
const stats = await stat8(filePath);
|
|
50557
51659
|
if (stats.size > MAX_VIDEO_INGEST_BYTES) {
|
|
50558
51660
|
throw new ApiError(
|
|
50559
51661
|
"VALIDATION_ERROR",
|
|
@@ -50598,7 +51700,7 @@ async function uploadToAssetStore(filePath, sizeBytes) {
|
|
|
50598
51700
|
}
|
|
50599
51701
|
|
|
50600
51702
|
// src/commands/videos/search.ts
|
|
50601
|
-
import { defineCommand as
|
|
51703
|
+
import { defineCommand as defineCommand213 } from "citty";
|
|
50602
51704
|
registerSchema({
|
|
50603
51705
|
command: "videos.search",
|
|
50604
51706
|
description: "Search videos by text query. Only returns ready videos.",
|
|
@@ -50608,7 +51710,7 @@ registerSchema({
|
|
|
50608
51710
|
tags: { type: "string", description: "Comma-separated tags to filter by", required: false }
|
|
50609
51711
|
}
|
|
50610
51712
|
});
|
|
50611
|
-
var searchCommand4 =
|
|
51713
|
+
var searchCommand4 = defineCommand213({
|
|
50612
51714
|
meta: {
|
|
50613
51715
|
name: "search",
|
|
50614
51716
|
description: "Semantic search videos by text query. Uses hybrid BM25 + vector + reranking. Example: baker videos search 'product demo' --tags tutorial"
|
|
@@ -50658,9 +51760,9 @@ var searchCommand4 = defineCommand211({
|
|
|
50658
51760
|
var tagsCommand6 = makeTagsCommand("videos", "video", "/api/videos/tags");
|
|
50659
51761
|
|
|
50660
51762
|
// src/commands/videos/upload.ts
|
|
50661
|
-
import { readFile as
|
|
51763
|
+
import { readFile as readFile25, stat as stat9 } from "fs/promises";
|
|
50662
51764
|
import { basename as basename3, extname as extname4 } from "path";
|
|
50663
|
-
import { defineCommand as
|
|
51765
|
+
import { defineCommand as defineCommand214 } from "citty";
|
|
50664
51766
|
var MIME_MAP = {
|
|
50665
51767
|
".mp4": "video/mp4",
|
|
50666
51768
|
".mov": "video/quicktime",
|
|
@@ -50702,7 +51804,7 @@ function detectContentType(filePath) {
|
|
|
50702
51804
|
function isRemoteUrl3(value) {
|
|
50703
51805
|
return /^https?:\/\//i.test(value);
|
|
50704
51806
|
}
|
|
50705
|
-
var uploadCommand2 =
|
|
51807
|
+
var uploadCommand2 = defineCommand214({
|
|
50706
51808
|
meta: {
|
|
50707
51809
|
name: "upload",
|
|
50708
51810
|
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"
|
|
@@ -50742,7 +51844,7 @@ var uploadCommand2 = defineCommand212({
|
|
|
50742
51844
|
const originalFilename = basename3(filePath);
|
|
50743
51845
|
const descriptionContext = args.context;
|
|
50744
51846
|
if (args["dry-run"]) {
|
|
50745
|
-
const fileStats = await
|
|
51847
|
+
const fileStats = await stat9(filePath);
|
|
50746
51848
|
writeJson({
|
|
50747
51849
|
ok: true,
|
|
50748
51850
|
dryRun: true,
|
|
@@ -50755,7 +51857,7 @@ var uploadCommand2 = defineCommand212({
|
|
|
50755
51857
|
originalFilename,
|
|
50756
51858
|
descriptionContext
|
|
50757
51859
|
});
|
|
50758
|
-
const fileBuffer = await
|
|
51860
|
+
const fileBuffer = await readFile25(filePath);
|
|
50759
51861
|
const uploadResponse = await fetch(uploadUrl, {
|
|
50760
51862
|
method: "PUT",
|
|
50761
51863
|
headers: { "Content-Type": contentType },
|
|
@@ -50786,7 +51888,7 @@ var uploadCommand2 = defineCommand212({
|
|
|
50786
51888
|
});
|
|
50787
51889
|
|
|
50788
51890
|
// src/commands/videos/index.ts
|
|
50789
|
-
var videosCommand =
|
|
51891
|
+
var videosCommand = defineCommand215({
|
|
50790
51892
|
meta: {
|
|
50791
51893
|
name: "videos",
|
|
50792
51894
|
description: `Find and manage videos in Baker. Subcommands: search, get, upload, ingest, delete, tags.
|
|
@@ -50813,10 +51915,10 @@ Full guide: __tooling__/docs/tools/baker/videos.md`
|
|
|
50813
51915
|
});
|
|
50814
51916
|
|
|
50815
51917
|
// src/commands/winning-ads/index.ts
|
|
50816
|
-
import { defineCommand as
|
|
51918
|
+
import { defineCommand as defineCommand228 } from "citty";
|
|
50817
51919
|
|
|
50818
51920
|
// src/commands/winning-ads/advertisers.ts
|
|
50819
|
-
import { defineCommand as
|
|
51921
|
+
import { defineCommand as defineCommand216 } from "citty";
|
|
50820
51922
|
|
|
50821
51923
|
// src/commands/winning-ads/shared.ts
|
|
50822
51924
|
function splitList2(value) {
|
|
@@ -50869,7 +51971,7 @@ function advertiserNormalizer(record, full) {
|
|
|
50869
51971
|
last_synced_at: record.last_synced_at ?? null
|
|
50870
51972
|
};
|
|
50871
51973
|
}
|
|
50872
|
-
var advertisersCommand2 =
|
|
51974
|
+
var advertisersCommand2 = defineCommand216({
|
|
50873
51975
|
meta: {
|
|
50874
51976
|
name: "advertisers",
|
|
50875
51977
|
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'
|
|
@@ -50927,7 +52029,7 @@ var advertisersCommand2 = defineCommand214({
|
|
|
50927
52029
|
});
|
|
50928
52030
|
|
|
50929
52031
|
// src/commands/winning-ads/brief.ts
|
|
50930
|
-
import { defineCommand as
|
|
52032
|
+
import { defineCommand as defineCommand217 } from "citty";
|
|
50931
52033
|
registerSchema({
|
|
50932
52034
|
command: "winning-ads.brief",
|
|
50933
52035
|
description: "Generate a creative brief grounded in strategically-similar winning ads. Optionally describe the target creative with --dna (JSON) and steer with --notes.",
|
|
@@ -50973,7 +52075,7 @@ function parseDna(raw) {
|
|
|
50973
52075
|
}
|
|
50974
52076
|
return parsed;
|
|
50975
52077
|
}
|
|
50976
|
-
var briefCommand =
|
|
52078
|
+
var briefCommand = defineCommand217({
|
|
50977
52079
|
meta: {
|
|
50978
52080
|
name: "brief",
|
|
50979
52081
|
description: `Generate a creative brief from winning references. Example: baker winning-ads brief --dna '{"angle":"cost savings"}' --notes "B2B, LinkedIn video" --k 8`
|
|
@@ -51009,7 +52111,7 @@ var briefCommand = defineCommand215({
|
|
|
51009
52111
|
});
|
|
51010
52112
|
|
|
51011
52113
|
// src/commands/winning-ads/content.ts
|
|
51012
|
-
import { defineCommand as
|
|
52114
|
+
import { defineCommand as defineCommand218 } from "citty";
|
|
51013
52115
|
registerSchema({
|
|
51014
52116
|
command: "winning-ads.content",
|
|
51015
52117
|
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.",
|
|
@@ -51022,7 +52124,7 @@ registerSchema({
|
|
|
51022
52124
|
}
|
|
51023
52125
|
}
|
|
51024
52126
|
});
|
|
51025
|
-
var contentCommand =
|
|
52127
|
+
var contentCommand = defineCommand218({
|
|
51026
52128
|
meta: {
|
|
51027
52129
|
name: "content",
|
|
51028
52130
|
description: "Read the transcript + on-screen text + copy of one winning ad. Example: baker winning-ads content adg_123 --platform meta --full --output md"
|
|
@@ -51071,7 +52173,7 @@ var contentCommand = defineCommand216({
|
|
|
51071
52173
|
});
|
|
51072
52174
|
|
|
51073
52175
|
// src/commands/winning-ads/feed.ts
|
|
51074
|
-
import { defineCommand as
|
|
52176
|
+
import { defineCommand as defineCommand219 } from "citty";
|
|
51075
52177
|
function buildFeedParams(input) {
|
|
51076
52178
|
const params = {};
|
|
51077
52179
|
const advertiser = splitList2(input.advertiser);
|
|
@@ -51123,7 +52225,7 @@ registerSchema({
|
|
|
51123
52225
|
format: { type: "string", description: "Comma-separated formats to include (e.g. static,video)", required: false }
|
|
51124
52226
|
}
|
|
51125
52227
|
});
|
|
51126
|
-
var feedCommand =
|
|
52228
|
+
var feedCommand = defineCommand219({
|
|
51127
52229
|
meta: {
|
|
51128
52230
|
name: "feed",
|
|
51129
52231
|
description: "Winners across every brand you follow (browse, then trim per advertiser). Example: baker winning-ads feed --per-advertiser 5 --output md"
|
|
@@ -51208,7 +52310,7 @@ var feedCommand = defineCommand217({
|
|
|
51208
52310
|
});
|
|
51209
52311
|
|
|
51210
52312
|
// src/commands/winning-ads/follow.ts
|
|
51211
|
-
import { defineCommand as
|
|
52313
|
+
import { defineCommand as defineCommand220 } from "citty";
|
|
51212
52314
|
var PLATFORMS = ["meta", "linkedin"];
|
|
51213
52315
|
registerSchema({
|
|
51214
52316
|
command: "winning-ads.follow",
|
|
@@ -51223,7 +52325,7 @@ registerSchema({
|
|
|
51223
52325
|
label: { type: "string", description: "Optional display label (defaults to the resolved name)", required: false }
|
|
51224
52326
|
}
|
|
51225
52327
|
});
|
|
51226
|
-
var followCommand =
|
|
52328
|
+
var followCommand = defineCommand220({
|
|
51227
52329
|
meta: {
|
|
51228
52330
|
name: "follow",
|
|
51229
52331
|
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'
|
|
@@ -51270,7 +52372,7 @@ var followCommand = defineCommand218({
|
|
|
51270
52372
|
});
|
|
51271
52373
|
|
|
51272
52374
|
// src/commands/winning-ads/follow-competitors.ts
|
|
51273
|
-
import { defineCommand as
|
|
52375
|
+
import { defineCommand as defineCommand221 } from "citty";
|
|
51274
52376
|
var PLATFORMS2 = ["meta", "linkedin"];
|
|
51275
52377
|
var BATCH_TIMEOUT_MS = 3e5;
|
|
51276
52378
|
function buildFollowBatchBody(input) {
|
|
@@ -51303,7 +52405,7 @@ registerSchema({
|
|
|
51303
52405
|
}
|
|
51304
52406
|
}
|
|
51305
52407
|
});
|
|
51306
|
-
var followCompetitorsCommand =
|
|
52408
|
+
var followCompetitorsCommand = defineCommand221({
|
|
51307
52409
|
meta: {
|
|
51308
52410
|
name: "follow-competitors",
|
|
51309
52411
|
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"'
|
|
@@ -51378,7 +52480,7 @@ var followCompetitorsCommand = defineCommand219({
|
|
|
51378
52480
|
});
|
|
51379
52481
|
|
|
51380
52482
|
// src/commands/winning-ads/following.ts
|
|
51381
|
-
import { defineCommand as
|
|
52483
|
+
import { defineCommand as defineCommand222 } from "citty";
|
|
51382
52484
|
registerSchema({
|
|
51383
52485
|
command: "winning-ads.following",
|
|
51384
52486
|
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.",
|
|
@@ -51432,7 +52534,7 @@ function followingNormalizer(record, full) {
|
|
|
51432
52534
|
platforms: Array.isArray(record.platforms) ? record.platforms : []
|
|
51433
52535
|
};
|
|
51434
52536
|
}
|
|
51435
|
-
var followingCommand =
|
|
52537
|
+
var followingCommand = defineCommand222({
|
|
51436
52538
|
meta: {
|
|
51437
52539
|
name: "following",
|
|
51438
52540
|
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"
|
|
@@ -51468,7 +52570,7 @@ var followingCommand = defineCommand220({
|
|
|
51468
52570
|
});
|
|
51469
52571
|
|
|
51470
52572
|
// src/commands/winning-ads/patterns.ts
|
|
51471
|
-
import { defineCommand as
|
|
52573
|
+
import { defineCommand as defineCommand223 } from "citty";
|
|
51472
52574
|
registerSchema({
|
|
51473
52575
|
command: "winning-ads.patterns",
|
|
51474
52576
|
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.",
|
|
@@ -51507,7 +52609,7 @@ function discriminatorRow(record) {
|
|
|
51507
52609
|
top_values_duds: Array.isArray(record.top_values_b) ? record.top_values_b.join(", ") : ""
|
|
51508
52610
|
};
|
|
51509
52611
|
}
|
|
51510
|
-
var patternsCommand =
|
|
52612
|
+
var patternsCommand = defineCommand223({
|
|
51511
52613
|
meta: {
|
|
51512
52614
|
name: "patterns",
|
|
51513
52615
|
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"
|
|
@@ -51563,7 +52665,7 @@ var patternsCommand = defineCommand221({
|
|
|
51563
52665
|
});
|
|
51564
52666
|
|
|
51565
52667
|
// src/commands/winning-ads/search.ts
|
|
51566
|
-
import { defineCommand as
|
|
52668
|
+
import { defineCommand as defineCommand224 } from "citty";
|
|
51567
52669
|
registerSchema({
|
|
51568
52670
|
command: "winning-ads.search",
|
|
51569
52671
|
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.",
|
|
@@ -51671,7 +52773,7 @@ function buildSearchBody2(args) {
|
|
|
51671
52773
|
}
|
|
51672
52774
|
return body;
|
|
51673
52775
|
}
|
|
51674
|
-
var searchCommand5 =
|
|
52776
|
+
var searchCommand5 = defineCommand224({
|
|
51675
52777
|
meta: {
|
|
51676
52778
|
name: "search",
|
|
51677
52779
|
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"
|
|
@@ -51786,7 +52888,7 @@ var searchCommand5 = defineCommand222({
|
|
|
51786
52888
|
});
|
|
51787
52889
|
|
|
51788
52890
|
// src/commands/winning-ads/seeds.ts
|
|
51789
|
-
import { defineCommand as
|
|
52891
|
+
import { defineCommand as defineCommand225 } from "citty";
|
|
51790
52892
|
function leanRow(r) {
|
|
51791
52893
|
return {
|
|
51792
52894
|
key: r.key,
|
|
@@ -51814,7 +52916,7 @@ function makeSeedCommand(opts) {
|
|
|
51814
52916
|
limit: { type: "number", description: "Max keys 1-100 (default 20)", required: false, default: 20 }
|
|
51815
52917
|
}
|
|
51816
52918
|
});
|
|
51817
|
-
return
|
|
52919
|
+
return defineCommand225({
|
|
51818
52920
|
meta: { name: opts.name, description: opts.description },
|
|
51819
52921
|
args: {
|
|
51820
52922
|
platform: { type: "string", description: "Single platform to segment on", required: false },
|
|
@@ -51863,7 +52965,7 @@ var formatsCommand = makeSeedCommand({
|
|
|
51863
52965
|
});
|
|
51864
52966
|
|
|
51865
52967
|
// src/commands/winning-ads/unfollow.ts
|
|
51866
|
-
import { defineCommand as
|
|
52968
|
+
import { defineCommand as defineCommand226 } from "citty";
|
|
51867
52969
|
registerSchema({
|
|
51868
52970
|
command: "winning-ads.unfollow",
|
|
51869
52971
|
description: "Stop following a brand \u2014 removes it from your ad-dna library by advertiser id.",
|
|
@@ -51871,7 +52973,7 @@ registerSchema({
|
|
|
51871
52973
|
advertiser: { type: "string", description: "Advertiser id to unfollow", required: true }
|
|
51872
52974
|
}
|
|
51873
52975
|
});
|
|
51874
|
-
var unfollowCommand =
|
|
52976
|
+
var unfollowCommand = defineCommand226({
|
|
51875
52977
|
meta: {
|
|
51876
52978
|
name: "unfollow",
|
|
51877
52979
|
description: "Stop following a brand by advertiser id. Example: baker winning-ads unfollow adv_123"
|
|
@@ -51892,7 +52994,7 @@ var unfollowCommand = defineCommand224({
|
|
|
51892
52994
|
});
|
|
51893
52995
|
|
|
51894
52996
|
// src/commands/winning-ads/winners.ts
|
|
51895
|
-
import { defineCommand as
|
|
52997
|
+
import { defineCommand as defineCommand227 } from "citty";
|
|
51896
52998
|
registerSchema({
|
|
51897
52999
|
command: "winning-ads.winners",
|
|
51898
53000
|
description: "Top winning ads for one advertiser id (from `advertisers` or `following`). Returns lean winner cards; add --full for DNA + longevity.",
|
|
@@ -51902,7 +53004,7 @@ registerSchema({
|
|
|
51902
53004
|
platform: { type: "string", description: "Filter to a single platform: meta|linkedin", required: false }
|
|
51903
53005
|
}
|
|
51904
53006
|
});
|
|
51905
|
-
var winnersCommand =
|
|
53007
|
+
var winnersCommand = defineCommand227({
|
|
51906
53008
|
meta: {
|
|
51907
53009
|
name: "winners",
|
|
51908
53010
|
description: "Top winning ads for a specific advertiser id. Example: baker winning-ads winners adv_123 --top 15 --output md"
|
|
@@ -51952,7 +53054,7 @@ var winnersCommand = defineCommand225({
|
|
|
51952
53054
|
});
|
|
51953
53055
|
|
|
51954
53056
|
// src/commands/winning-ads/index.ts
|
|
51955
|
-
var winningAdsCommand =
|
|
53057
|
+
var winningAdsCommand = defineCommand228({
|
|
51956
53058
|
meta: {
|
|
51957
53059
|
name: "winning-ads",
|
|
51958
53060
|
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.
|
|
@@ -52142,7 +53244,7 @@ function unknownFlagEnvelope(unknown, commandPath, suggestion) {
|
|
|
52142
53244
|
};
|
|
52143
53245
|
}
|
|
52144
53246
|
function commandPathOf(root, argv) {
|
|
52145
|
-
const
|
|
53247
|
+
const path41 = [];
|
|
52146
53248
|
let command = root;
|
|
52147
53249
|
for (const token of argv) {
|
|
52148
53250
|
if (token === "--" || token.startsWith("-")) {
|
|
@@ -52153,10 +53255,10 @@ function commandPathOf(root, argv) {
|
|
|
52153
53255
|
if (next === void 0 || typeof next !== "object") {
|
|
52154
53256
|
break;
|
|
52155
53257
|
}
|
|
52156
|
-
|
|
53258
|
+
path41.push(token);
|
|
52157
53259
|
command = next;
|
|
52158
53260
|
}
|
|
52159
|
-
return
|
|
53261
|
+
return path41.join(" ");
|
|
52160
53262
|
}
|
|
52161
53263
|
function refuseUnknownFlags(root, argv) {
|
|
52162
53264
|
const unknown = findUnknownFlags(root, argv);
|
|
@@ -52190,7 +53292,7 @@ function getCliVersion() {
|
|
|
52190
53292
|
}
|
|
52191
53293
|
|
|
52192
53294
|
// src/cli.ts
|
|
52193
|
-
var main =
|
|
53295
|
+
var main = defineCommand229({
|
|
52194
53296
|
meta: {
|
|
52195
53297
|
name: "baker",
|
|
52196
53298
|
version: getCliVersion(),
|
|
@@ -52209,6 +53311,7 @@ Introspection: Run 'baker schema <command>' to inspect argument schemas.`
|
|
|
52209
53311
|
ads: adsCommand2,
|
|
52210
53312
|
brand: brandCommand,
|
|
52211
53313
|
analytics: analyticsCommand2,
|
|
53314
|
+
experiment: experimentCommand,
|
|
52212
53315
|
ga4: ga4Command,
|
|
52213
53316
|
gsc: gscCommand,
|
|
52214
53317
|
research: researchCommand,
|