@koda-sl/baker-cli 0.199.0 → 0.200.0-dev.0a9adf6f7
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 +38 -2
- package/dist/{chunk-5MPIOGRO.js → chunk-ONFW7HIK.js} +261 -215
- package/dist/chunk-ONFW7HIK.js.map +1 -0
- package/dist/cli.js +345 -105
- package/dist/cli.js.map +1 -1
- package/dist/engine/index.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-5MPIOGRO.js.map +0 -1
package/dist/cli.js
CHANGED
|
@@ -18,6 +18,7 @@ import {
|
|
|
18
18
|
SEEDANCE_PROFILE,
|
|
19
19
|
VIDEO_GENERATE_MODELS,
|
|
20
20
|
ValidationError,
|
|
21
|
+
YtDlpError,
|
|
21
22
|
clipParamRecipe,
|
|
22
23
|
clipProfileFor,
|
|
23
24
|
collectAssetRefLikes,
|
|
@@ -41,6 +42,7 @@ import {
|
|
|
41
42
|
promptMaxLength,
|
|
42
43
|
requireCredentialsFromEnv,
|
|
43
44
|
resolveConcurrency,
|
|
45
|
+
runYtDlp,
|
|
44
46
|
sha256Hex,
|
|
45
47
|
spineInputFlags,
|
|
46
48
|
spineInputOps,
|
|
@@ -50,7 +52,7 @@ import {
|
|
|
50
52
|
toModelSafeImage,
|
|
51
53
|
ulid,
|
|
52
54
|
validateCanvasDeep
|
|
53
|
-
} from "./chunk-
|
|
55
|
+
} from "./chunk-ONFW7HIK.js";
|
|
54
56
|
import {
|
|
55
57
|
csvOrJson,
|
|
56
58
|
daysAgoIso,
|
|
@@ -99,7 +101,7 @@ import {
|
|
|
99
101
|
} from "./chunk-6NZG2TCM.js";
|
|
100
102
|
|
|
101
103
|
// src/cli.ts
|
|
102
|
-
import { defineCommand as
|
|
104
|
+
import { defineCommand as defineCommand217, runMain } from "citty";
|
|
103
105
|
|
|
104
106
|
// src/commands/actions/index.ts
|
|
105
107
|
import { defineCommand as defineCommand18 } from "citty";
|
|
@@ -5686,6 +5688,7 @@ var testimonialsOutscraperWebhookResponseSchema = z18.object({
|
|
|
5686
5688
|
// ../api/src/videos.ts
|
|
5687
5689
|
import { z as z19 } from "zod";
|
|
5688
5690
|
var videoStatusSchema = z19.enum(["uploading", "uploaded", "processing", "ready", "error"]);
|
|
5691
|
+
var videoSourceSchema = z19.enum(["uploaded", "instagram", "url", "youtube"]);
|
|
5689
5692
|
var videoTranscriptSegmentSchema = z19.object({
|
|
5690
5693
|
text: z19.string(),
|
|
5691
5694
|
startSecond: z19.number(),
|
|
@@ -5786,6 +5789,19 @@ var videosUploadRequestSchema = z19.object({
|
|
|
5786
5789
|
descriptionContext: z19.string().optional()
|
|
5787
5790
|
});
|
|
5788
5791
|
var videosUploadResponseSchema = z19.object({ uploadUrl: z19.string(), videoId: z19.string() });
|
|
5792
|
+
var videosIngestRequestSchema = z19.object({
|
|
5793
|
+
// Must be a direct media file Mux can pull (mp4/mov/webm/…). A page URL —
|
|
5794
|
+
// a YouTube watch link, a TikTok post — is not one: the CLI resolves those to
|
|
5795
|
+
// a media file before calling this route.
|
|
5796
|
+
url: z19.string().url(),
|
|
5797
|
+
source: videoSourceSchema,
|
|
5798
|
+
externalId: z19.string().optional(),
|
|
5799
|
+
externalUrl: z19.string().optional()
|
|
5800
|
+
});
|
|
5801
|
+
var videosIngestResponseSchema = z19.object({
|
|
5802
|
+
videoId: z19.string(),
|
|
5803
|
+
deduped: z19.boolean()
|
|
5804
|
+
});
|
|
5789
5805
|
var videosDeleteRequestSchema = z19.object({ id: z19.string().min(1, "Missing video ID") });
|
|
5790
5806
|
var videosDeleteResponseSchema = z19.object({ ok: z19.literal(true) });
|
|
5791
5807
|
|
|
@@ -8746,11 +8762,11 @@ function rawTextEntries(value) {
|
|
|
8746
8762
|
const values = Array.isArray(value) ? value : typeof value === "string" ? [value] : [];
|
|
8747
8763
|
return values.filter((v) => typeof v === "string").flatMap((v) => v.split(",")).map((v) => v.trim()).filter(Boolean);
|
|
8748
8764
|
}
|
|
8749
|
-
function rawFileEntries(
|
|
8750
|
-
if (typeof
|
|
8765
|
+
function rawFileEntries(path36) {
|
|
8766
|
+
if (typeof path36 !== "string" || path36.length === 0) {
|
|
8751
8767
|
return [];
|
|
8752
8768
|
}
|
|
8753
|
-
return readFileSync2(
|
|
8769
|
+
return readFileSync2(path36, "utf8").split(/\r?\n/).map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#"));
|
|
8754
8770
|
}
|
|
8755
8771
|
function keywordEntries(args) {
|
|
8756
8772
|
const defaultMatch = typeof args["match-type"] === "string" ? args["match-type"].toUpperCase() : void 0;
|
|
@@ -8773,19 +8789,19 @@ function keywordEntries(args) {
|
|
|
8773
8789
|
}
|
|
8774
8790
|
return entries;
|
|
8775
8791
|
}
|
|
8776
|
-
function loadJsonFileArg(
|
|
8777
|
-
if (typeof
|
|
8792
|
+
function loadJsonFileArg(path36) {
|
|
8793
|
+
if (typeof path36 !== "string" || path36.length === 0) {
|
|
8778
8794
|
return {};
|
|
8779
8795
|
}
|
|
8780
8796
|
try {
|
|
8781
|
-
const parsed = JSON.parse(readFileSync2(
|
|
8797
|
+
const parsed = JSON.parse(readFileSync2(path36, "utf8"));
|
|
8782
8798
|
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
8783
|
-
failWriteValidation(`${
|
|
8799
|
+
failWriteValidation(`${path36} must contain a JSON object`);
|
|
8784
8800
|
}
|
|
8785
8801
|
return parsed;
|
|
8786
8802
|
} catch (err) {
|
|
8787
8803
|
if (err instanceof SyntaxError) {
|
|
8788
|
-
failWriteValidation(`${
|
|
8804
|
+
failWriteValidation(`${path36} is not valid JSON: ${err.message}`);
|
|
8789
8805
|
}
|
|
8790
8806
|
throw err;
|
|
8791
8807
|
}
|
|
@@ -8915,10 +8931,10 @@ async function stageUpdate(kind, customerId, target, payload, hints) {
|
|
|
8915
8931
|
async function stageTarget(kind, customerId, target, hints) {
|
|
8916
8932
|
await stageGoogleOp({ kind, customerId, target }, hints);
|
|
8917
8933
|
}
|
|
8918
|
-
async function draftAction(
|
|
8934
|
+
async function draftAction(path36, body, chat) {
|
|
8919
8935
|
try {
|
|
8920
8936
|
const chatId = resolveChatId(chat);
|
|
8921
|
-
const response = await apiPost(
|
|
8937
|
+
const response = await apiPost(path36, { chatId, ...body });
|
|
8922
8938
|
writeJsonEnvelope(response);
|
|
8923
8939
|
} catch (err) {
|
|
8924
8940
|
handleGoogleError(err);
|
|
@@ -11776,19 +11792,19 @@ function failWriteValidation2(message) {
|
|
|
11776
11792
|
writeJsonEnvelope({ ok: false, error: { code: "VALIDATION_ERROR", message } });
|
|
11777
11793
|
process.exit(1);
|
|
11778
11794
|
}
|
|
11779
|
-
function loadJsonFileArg2(
|
|
11780
|
-
if (typeof
|
|
11795
|
+
function loadJsonFileArg2(path36) {
|
|
11796
|
+
if (typeof path36 !== "string" || path36.length === 0) {
|
|
11781
11797
|
return {};
|
|
11782
11798
|
}
|
|
11783
11799
|
try {
|
|
11784
|
-
const parsed = JSON.parse(readFileSync4(
|
|
11800
|
+
const parsed = JSON.parse(readFileSync4(path36, "utf8"));
|
|
11785
11801
|
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
11786
|
-
failWriteValidation2(`${
|
|
11802
|
+
failWriteValidation2(`${path36} must contain a JSON object`);
|
|
11787
11803
|
}
|
|
11788
11804
|
return parsed;
|
|
11789
11805
|
} catch (err) {
|
|
11790
11806
|
if (err instanceof SyntaxError) {
|
|
11791
|
-
failWriteValidation2(`${
|
|
11807
|
+
failWriteValidation2(`${path36} is not valid JSON: ${err.message}`);
|
|
11792
11808
|
}
|
|
11793
11809
|
throw err;
|
|
11794
11810
|
}
|
|
@@ -11873,15 +11889,15 @@ function parseLocaleFlag(value) {
|
|
|
11873
11889
|
}
|
|
11874
11890
|
return { language: match[1], country: match[2].toUpperCase() };
|
|
11875
11891
|
}
|
|
11876
|
-
function loadTargetingFileArg(
|
|
11877
|
-
if (typeof
|
|
11892
|
+
function loadTargetingFileArg(path36) {
|
|
11893
|
+
if (typeof path36 !== "string" || path36.length === 0) {
|
|
11878
11894
|
return void 0;
|
|
11879
11895
|
}
|
|
11880
|
-
const parsed = loadJsonFileArg2(
|
|
11896
|
+
const parsed = loadJsonFileArg2(path36);
|
|
11881
11897
|
const criteria = parsed.targetingCriteria ?? parsed;
|
|
11882
11898
|
if (!criteria.include) {
|
|
11883
11899
|
failWriteValidation2(
|
|
11884
|
-
`${
|
|
11900
|
+
`${path36} must contain targeting criteria with an "include" block (see baker schema ads.linkedin.campaigns.create)`
|
|
11885
11901
|
);
|
|
11886
11902
|
}
|
|
11887
11903
|
return criteria;
|
|
@@ -11916,14 +11932,14 @@ function parseCsvLine(line) {
|
|
|
11916
11932
|
cells.push(current);
|
|
11917
11933
|
return cells.map((cell) => cell.trim());
|
|
11918
11934
|
}
|
|
11919
|
-
function parseListFileArg(
|
|
11920
|
-
if (typeof
|
|
11935
|
+
function parseListFileArg(path36, maxRows) {
|
|
11936
|
+
if (typeof path36 !== "string" || path36.length === 0) {
|
|
11921
11937
|
return void 0;
|
|
11922
11938
|
}
|
|
11923
|
-
const raw = readFileSync4(
|
|
11939
|
+
const raw = readFileSync4(path36, "utf8");
|
|
11924
11940
|
const lines = raw.split(/\r?\n/).filter((line) => line.trim().length > 0);
|
|
11925
11941
|
if (lines.length < 2) {
|
|
11926
|
-
failWriteValidation2(`${
|
|
11942
|
+
failWriteValidation2(`${path36} needs a header row and at least one data row`);
|
|
11927
11943
|
}
|
|
11928
11944
|
const columns = parseCsvLine(lines[0]).map((column) => column.trim());
|
|
11929
11945
|
const rows = [];
|
|
@@ -11942,7 +11958,7 @@ function parseListFileArg(path35, maxRows) {
|
|
|
11942
11958
|
}
|
|
11943
11959
|
}
|
|
11944
11960
|
if (rows.length > maxRows) {
|
|
11945
|
-
failWriteValidation2(`${
|
|
11961
|
+
failWriteValidation2(`${path36} has ${rows.length} rows \u2014 the inline limit is ${maxRows}. Split the list.`);
|
|
11946
11962
|
}
|
|
11947
11963
|
return { columns, rows };
|
|
11948
11964
|
}
|
|
@@ -12038,11 +12054,11 @@ function readPositionals(args) {
|
|
|
12038
12054
|
function splitIdList(raw) {
|
|
12039
12055
|
return raw.split(",").map((id) => id.trim()).filter(Boolean);
|
|
12040
12056
|
}
|
|
12041
|
-
function idsFileEntries(
|
|
12042
|
-
if (typeof
|
|
12057
|
+
function idsFileEntries(path36) {
|
|
12058
|
+
if (typeof path36 !== "string" || path36.length === 0) {
|
|
12043
12059
|
return [];
|
|
12044
12060
|
}
|
|
12045
|
-
return readFileSync4(
|
|
12061
|
+
return readFileSync4(path36, "utf8").split(/\r?\n/).map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#")).flatMap(splitIdList);
|
|
12046
12062
|
}
|
|
12047
12063
|
function requireTargets(args, entity) {
|
|
12048
12064
|
const positionals = readPositionals(args);
|
|
@@ -14665,9 +14681,9 @@ function compactRow(row) {
|
|
|
14665
14681
|
...destination.postUrn ? { postUrn: destination.postUrn } : {}
|
|
14666
14682
|
};
|
|
14667
14683
|
}
|
|
14668
|
-
function readPath(row,
|
|
14684
|
+
function readPath(row, path36) {
|
|
14669
14685
|
let current = row;
|
|
14670
|
-
for (const segment of
|
|
14686
|
+
for (const segment of path36.split(".")) {
|
|
14671
14687
|
const record = asRecord2(current);
|
|
14672
14688
|
if (!record) return void 0;
|
|
14673
14689
|
current = record[segment];
|
|
@@ -14677,10 +14693,10 @@ function readPath(row, path35) {
|
|
|
14677
14693
|
function projectFields(rows, paths) {
|
|
14678
14694
|
return rows.map((row) => {
|
|
14679
14695
|
const projected = {};
|
|
14680
|
-
for (const
|
|
14681
|
-
const value = readPath(row,
|
|
14696
|
+
for (const path36 of paths) {
|
|
14697
|
+
const value = readPath(row, path36);
|
|
14682
14698
|
if (value !== void 0) {
|
|
14683
|
-
projected[
|
|
14699
|
+
projected[path36] = value;
|
|
14684
14700
|
}
|
|
14685
14701
|
}
|
|
14686
14702
|
return projected;
|
|
@@ -15974,11 +15990,11 @@ var updateStatusSchema = z21.enum(UPDATE_STATUSES);
|
|
|
15974
15990
|
function currencyMinimums2(currencyCode) {
|
|
15975
15991
|
return CURRENCY_MINIMUMS2[currencyCode] ?? DEFAULT_CURRENCY_MINIMUM2;
|
|
15976
15992
|
}
|
|
15977
|
-
function validateDailyBudgetFloor(money, ctx,
|
|
15993
|
+
function validateDailyBudgetFloor(money, ctx, path36) {
|
|
15978
15994
|
if (money?.currencyCode) {
|
|
15979
15995
|
const min = currencyMinimums2(money.currencyCode).dailyBudgetMin;
|
|
15980
15996
|
if (Number(money.amount) < min) {
|
|
15981
|
-
ctx.addIssue({ code: "custom", path:
|
|
15997
|
+
ctx.addIssue({ code: "custom", path: path36, message: `below the ${min} ${money.currencyCode} daily minimum` });
|
|
15982
15998
|
}
|
|
15983
15999
|
}
|
|
15984
16000
|
}
|
|
@@ -16640,19 +16656,19 @@ function failWriteValidation3(message) {
|
|
|
16640
16656
|
writeJsonEnvelope({ ok: false, error: { code: "VALIDATION_ERROR", message } });
|
|
16641
16657
|
process.exit(1);
|
|
16642
16658
|
}
|
|
16643
|
-
function loadJsonFileArg3(
|
|
16644
|
-
if (typeof
|
|
16659
|
+
function loadJsonFileArg3(path36) {
|
|
16660
|
+
if (typeof path36 !== "string" || path36.length === 0) {
|
|
16645
16661
|
return {};
|
|
16646
16662
|
}
|
|
16647
16663
|
try {
|
|
16648
|
-
const parsed = JSON.parse(readFileSync8(
|
|
16664
|
+
const parsed = JSON.parse(readFileSync8(path36, "utf8"));
|
|
16649
16665
|
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
16650
|
-
failWriteValidation3(`${
|
|
16666
|
+
failWriteValidation3(`${path36} must contain a JSON object`);
|
|
16651
16667
|
}
|
|
16652
16668
|
return parsed;
|
|
16653
16669
|
} catch (err) {
|
|
16654
16670
|
if (err instanceof SyntaxError) {
|
|
16655
|
-
failWriteValidation3(`${
|
|
16671
|
+
failWriteValidation3(`${path36} is not valid JSON: ${err.message}`);
|
|
16656
16672
|
}
|
|
16657
16673
|
throw err;
|
|
16658
16674
|
}
|
|
@@ -27725,12 +27741,12 @@ function listFlowSlugs() {
|
|
|
27725
27741
|
return readdirSync2(dir, { withFileTypes: true }).filter((entry) => entry.isDirectory() && !entry.name.startsWith("_") && entry.name !== ".gitkeep").map((entry) => entry.name).sort();
|
|
27726
27742
|
}
|
|
27727
27743
|
function readFlowTree(slug) {
|
|
27728
|
-
const
|
|
27729
|
-
if (!existsSync4(
|
|
27744
|
+
const path36 = join3(flowsDir(), slug, "_data.json");
|
|
27745
|
+
if (!existsSync4(path36)) {
|
|
27730
27746
|
failLocal(`No form "${slug}". Run "baker flows list" to see the forms in this workspace.`);
|
|
27731
27747
|
}
|
|
27732
27748
|
try {
|
|
27733
|
-
return JSON.parse(readFileSync9(
|
|
27749
|
+
return JSON.parse(readFileSync9(path36, "utf-8"));
|
|
27734
27750
|
} catch (error) {
|
|
27735
27751
|
failLocal(`Could not read form "${slug}": ${error instanceof Error ? error.message : String(error)}`);
|
|
27736
27752
|
}
|
|
@@ -28143,10 +28159,10 @@ async function stageOps(ops) {
|
|
|
28143
28159
|
handleError(err);
|
|
28144
28160
|
}
|
|
28145
28161
|
}
|
|
28146
|
-
async function draftAction2(
|
|
28162
|
+
async function draftAction2(path36, body, chat) {
|
|
28147
28163
|
const chatId = resolveChatId(chat);
|
|
28148
28164
|
try {
|
|
28149
|
-
const data = await apiPost(
|
|
28165
|
+
const data = await apiPost(path36, { chatId, ...body });
|
|
28150
28166
|
writeJsonEnvelope({ ok: true, data });
|
|
28151
28167
|
return data;
|
|
28152
28168
|
} catch (err) {
|
|
@@ -30167,9 +30183,9 @@ async function readImageBuffer(pathOrUrl) {
|
|
|
30167
30183
|
}
|
|
30168
30184
|
return readFile19(pathOrUrl);
|
|
30169
30185
|
}
|
|
30170
|
-
async function isDirectory(
|
|
30186
|
+
async function isDirectory(path36) {
|
|
30171
30187
|
try {
|
|
30172
|
-
const s = await stat4(
|
|
30188
|
+
const s = await stat4(path36);
|
|
30173
30189
|
return s.isDirectory();
|
|
30174
30190
|
} catch {
|
|
30175
30191
|
return false;
|
|
@@ -30453,13 +30469,13 @@ function resolveDownloadPath({ baseName, extension, out, outIsDirectory: outIsDi
|
|
|
30453
30469
|
}
|
|
30454
30470
|
function disambiguate(paths) {
|
|
30455
30471
|
const taken = /* @__PURE__ */ new Set();
|
|
30456
|
-
return paths.map((
|
|
30457
|
-
if (!taken.has(
|
|
30458
|
-
taken.add(
|
|
30459
|
-
return
|
|
30472
|
+
return paths.map((path36) => {
|
|
30473
|
+
if (!taken.has(path36)) {
|
|
30474
|
+
taken.add(path36);
|
|
30475
|
+
return path36;
|
|
30460
30476
|
}
|
|
30461
|
-
const ext = extname3(
|
|
30462
|
-
const stem =
|
|
30477
|
+
const ext = extname3(path36);
|
|
30478
|
+
const stem = path36.slice(0, path36.length - ext.length);
|
|
30463
30479
|
let n = 2;
|
|
30464
30480
|
while (taken.has(`${stem}-${n}${ext}`)) n += 1;
|
|
30465
30481
|
const unique = `${stem}-${n}${ext}`;
|
|
@@ -30586,10 +30602,10 @@ async function runDownloads(plan) {
|
|
|
30586
30602
|
const paths = disambiguate(fetched.map((item) => item.path));
|
|
30587
30603
|
const downloaded = [];
|
|
30588
30604
|
for (const [index, item] of fetched.entries()) {
|
|
30589
|
-
const
|
|
30605
|
+
const path36 = paths[index] ?? item.path;
|
|
30590
30606
|
try {
|
|
30591
|
-
await atomicWrite(
|
|
30592
|
-
downloaded.push({ input: item.input, output:
|
|
30607
|
+
await atomicWrite(path36, item.buffer);
|
|
30608
|
+
downloaded.push({ input: item.input, output: path36, bytes: item.buffer.length, contentType: item.contentType });
|
|
30593
30609
|
} catch (err) {
|
|
30594
30610
|
failed.push({ input: item.input, error: failureMessage(err, "Write failed") });
|
|
30595
30611
|
}
|
|
@@ -40300,7 +40316,7 @@ function parseImageRefs(spec) {
|
|
|
40300
40316
|
}
|
|
40301
40317
|
var defaultDeps = {
|
|
40302
40318
|
ingest: (url) => apiPost("/api/images/ingest", { url, source: "uploaded" }),
|
|
40303
|
-
upload: (
|
|
40319
|
+
upload: (path36) => uploadLocalImage({ file: path36, contentType: detectImageContentType(path36), source: "uploaded" })
|
|
40304
40320
|
};
|
|
40305
40321
|
async function resolveLibraryImageIds(spec, limit, deps = defaultDeps) {
|
|
40306
40322
|
const refs = parseImageRefs(spec);
|
|
@@ -40320,10 +40336,10 @@ async function resolveLibraryImageIds(spec, limit, deps = defaultDeps) {
|
|
|
40320
40336
|
}
|
|
40321
40337
|
return { imageIds, added };
|
|
40322
40338
|
}
|
|
40323
|
-
function uploadFailure(
|
|
40339
|
+
function uploadFailure(path36) {
|
|
40324
40340
|
return (error) => {
|
|
40325
40341
|
if (error instanceof ApiError) throw error;
|
|
40326
|
-
throw new ApiError("VALIDATION_ERROR", `Could not read "${
|
|
40342
|
+
throw new ApiError("VALIDATION_ERROR", `Could not read "${path36}" as an image.`);
|
|
40327
40343
|
};
|
|
40328
40344
|
}
|
|
40329
40345
|
|
|
@@ -41373,10 +41389,10 @@ async function stageOp4(op) {
|
|
|
41373
41389
|
handleError4(err);
|
|
41374
41390
|
}
|
|
41375
41391
|
}
|
|
41376
|
-
async function draftAction3(
|
|
41392
|
+
async function draftAction3(path36, body, chat) {
|
|
41377
41393
|
const chatId = resolveChatId(chat);
|
|
41378
41394
|
try {
|
|
41379
|
-
const data = await apiPost(
|
|
41395
|
+
const data = await apiPost(path36, { chatId, ...body });
|
|
41380
41396
|
writeJsonEnvelope({ ok: true, data });
|
|
41381
41397
|
return data;
|
|
41382
41398
|
} catch (err) {
|
|
@@ -42292,7 +42308,7 @@ Full guide: __tooling__/docs/tools/baker/testimonials.md`
|
|
|
42292
42308
|
});
|
|
42293
42309
|
|
|
42294
42310
|
// src/commands/videos/index.ts
|
|
42295
|
-
import { defineCommand as
|
|
42311
|
+
import { defineCommand as defineCommand203 } from "citty";
|
|
42296
42312
|
|
|
42297
42313
|
// src/commands/videos/delete.ts
|
|
42298
42314
|
import { defineCommand as defineCommand197 } from "citty";
|
|
@@ -42423,8 +42439,214 @@ var groupCommand2 = defineCommand199({
|
|
|
42423
42439
|
}
|
|
42424
42440
|
});
|
|
42425
42441
|
|
|
42426
|
-
// src/commands/videos/
|
|
42442
|
+
// src/commands/videos/ingest.ts
|
|
42443
|
+
import { mkdtemp as mkdtemp2, readFile as readFile24, rm as rm7, stat as stat7 } from "fs/promises";
|
|
42444
|
+
import { tmpdir as tmpdir3 } from "os";
|
|
42445
|
+
import path35 from "path";
|
|
42427
42446
|
import { defineCommand as defineCommand200 } from "citty";
|
|
42447
|
+
|
|
42448
|
+
// src/commands/videos/ingestUrl.ts
|
|
42449
|
+
var MAX_VIDEO_INGEST_BYTES = 2 * 1024 * 1024 * 1024;
|
|
42450
|
+
var INGEST_MAX_HEIGHT = 1080;
|
|
42451
|
+
var DIRECT_MEDIA_EXTENSIONS = [".mp4", ".mov", ".webm", ".m4v", ".mkv", ".avi"];
|
|
42452
|
+
var YOUTUBE_HOSTS = ["youtube.com", "youtu.be", "m.youtube.com", "www.youtube.com"];
|
|
42453
|
+
function isDirectMediaUrl(rawUrl) {
|
|
42454
|
+
const pathname = safePathname(rawUrl);
|
|
42455
|
+
return DIRECT_MEDIA_EXTENSIONS.some((ext) => pathname.endsWith(ext));
|
|
42456
|
+
}
|
|
42457
|
+
function resolveIngestSource(explicit, rawUrl) {
|
|
42458
|
+
if (explicit) return explicit;
|
|
42459
|
+
return isYouTubeUrl(rawUrl) ? "youtube" : "url";
|
|
42460
|
+
}
|
|
42461
|
+
function isYouTubeUrl(rawUrl) {
|
|
42462
|
+
let host;
|
|
42463
|
+
try {
|
|
42464
|
+
host = new URL(rawUrl).hostname.toLowerCase();
|
|
42465
|
+
} catch {
|
|
42466
|
+
return false;
|
|
42467
|
+
}
|
|
42468
|
+
return YOUTUBE_HOSTS.includes(host);
|
|
42469
|
+
}
|
|
42470
|
+
var TOOLING_OR_BLOCK_SIGNATURES = [
|
|
42471
|
+
"requested format is not available",
|
|
42472
|
+
"only images are available",
|
|
42473
|
+
"nsig",
|
|
42474
|
+
"sign in to confirm",
|
|
42475
|
+
// bot check — distinct from "Private video. Sign in if…"
|
|
42476
|
+
"unable to download api page",
|
|
42477
|
+
"http error 403"
|
|
42478
|
+
];
|
|
42479
|
+
function isToolingOrBlockFailure(detail) {
|
|
42480
|
+
const haystack = detail.toLowerCase();
|
|
42481
|
+
return TOOLING_OR_BLOCK_SIGNATURES.some((signature) => haystack.includes(signature));
|
|
42482
|
+
}
|
|
42483
|
+
function safePathname(rawUrl) {
|
|
42484
|
+
try {
|
|
42485
|
+
return new URL(rawUrl).pathname.toLowerCase();
|
|
42486
|
+
} catch {
|
|
42487
|
+
return "";
|
|
42488
|
+
}
|
|
42489
|
+
}
|
|
42490
|
+
|
|
42491
|
+
// src/commands/videos/ingest.ts
|
|
42492
|
+
registerSchema({
|
|
42493
|
+
command: "videos.ingest",
|
|
42494
|
+
description: "Add a video to the library from a URL \u2014 a direct file, or a YouTube/TikTok/Vimeo page.",
|
|
42495
|
+
args: {
|
|
42496
|
+
url: { type: "string", description: "Video URL (direct file or a watch/post page)", required: true },
|
|
42497
|
+
source: { type: "string", description: "Provenance: url | youtube", required: false },
|
|
42498
|
+
"external-id": { type: "string", description: "Provider asset id (deduped on)", required: false },
|
|
42499
|
+
"external-url": { type: "string", description: "Canonical page URL", required: false },
|
|
42500
|
+
download: {
|
|
42501
|
+
type: "boolean",
|
|
42502
|
+
description: "Force the download path instead of letting Baker fetch the URL directly",
|
|
42503
|
+
required: false,
|
|
42504
|
+
default: false
|
|
42505
|
+
},
|
|
42506
|
+
"dry-run": { type: "boolean", description: "Preview the operation without executing", required: false }
|
|
42507
|
+
}
|
|
42508
|
+
});
|
|
42509
|
+
var ingestCommand2 = defineCommand200({
|
|
42510
|
+
meta: {
|
|
42511
|
+
name: "ingest",
|
|
42512
|
+
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"
|
|
42513
|
+
},
|
|
42514
|
+
args: {
|
|
42515
|
+
url: { type: "positional", description: "Video URL", required: false },
|
|
42516
|
+
source: { type: "string", description: "Provenance: url | youtube", required: false },
|
|
42517
|
+
"external-id": { type: "string", description: "Provider asset id", required: false },
|
|
42518
|
+
"external-url": { type: "string", description: "Canonical page URL", required: false },
|
|
42519
|
+
download: { type: "boolean", description: "Force the download path", required: false, default: false },
|
|
42520
|
+
"dry-run": { type: "boolean", description: "Preview without executing", required: false, default: false }
|
|
42521
|
+
},
|
|
42522
|
+
run: ({ args }) => runVideoIngest({
|
|
42523
|
+
url: args.url,
|
|
42524
|
+
source: args.source,
|
|
42525
|
+
externalId: args["external-id"],
|
|
42526
|
+
externalUrl: args["external-url"],
|
|
42527
|
+
forceDownload: args.download === true,
|
|
42528
|
+
dryRun: args["dry-run"] === true
|
|
42529
|
+
})
|
|
42530
|
+
});
|
|
42531
|
+
async function runVideoIngest(opts) {
|
|
42532
|
+
const url = opts.url;
|
|
42533
|
+
if (!url) {
|
|
42534
|
+
writeJson({ ok: false, error: { code: "VALIDATION_ERROR", message: "URL is required" } });
|
|
42535
|
+
process.exit(1);
|
|
42536
|
+
}
|
|
42537
|
+
const direct = !opts.forceDownload && isDirectMediaUrl(url);
|
|
42538
|
+
const args = {
|
|
42539
|
+
url,
|
|
42540
|
+
source: resolveIngestSource(opts.source, url),
|
|
42541
|
+
externalId: opts.externalId,
|
|
42542
|
+
externalUrl: opts.externalUrl ?? (direct ? void 0 : url)
|
|
42543
|
+
};
|
|
42544
|
+
if (opts.dryRun) {
|
|
42545
|
+
writeJson({
|
|
42546
|
+
ok: true,
|
|
42547
|
+
dryRun: true,
|
|
42548
|
+
operation: "videos.ingest",
|
|
42549
|
+
params: {
|
|
42550
|
+
url,
|
|
42551
|
+
source: args.source,
|
|
42552
|
+
externalId: args.externalId ?? null,
|
|
42553
|
+
externalUrl: args.externalUrl ?? null,
|
|
42554
|
+
direct
|
|
42555
|
+
}
|
|
42556
|
+
});
|
|
42557
|
+
return;
|
|
42558
|
+
}
|
|
42559
|
+
try {
|
|
42560
|
+
writeJson({ ok: true, data: await ingestByRoute(args, direct) });
|
|
42561
|
+
} catch (err) {
|
|
42562
|
+
reportIngestFailure(err);
|
|
42563
|
+
}
|
|
42564
|
+
}
|
|
42565
|
+
async function ingestByRoute(args, direct) {
|
|
42566
|
+
if (!direct) {
|
|
42567
|
+
return { ...await downloadThenIngest(args), via: "download" };
|
|
42568
|
+
}
|
|
42569
|
+
try {
|
|
42570
|
+
return { ...await ingestUrl(args), via: "direct" };
|
|
42571
|
+
} catch (err) {
|
|
42572
|
+
if (!(err instanceof ApiError)) throw err;
|
|
42573
|
+
return { ...await downloadThenIngest(args), via: "download", fallbackFrom: err.message };
|
|
42574
|
+
}
|
|
42575
|
+
}
|
|
42576
|
+
function reportIngestFailure(err) {
|
|
42577
|
+
if (err instanceof ApiError) {
|
|
42578
|
+
writeJson({ ok: false, error: { code: err.code, message: err.message } });
|
|
42579
|
+
process.exit(1);
|
|
42580
|
+
}
|
|
42581
|
+
if (err instanceof YtDlpError) {
|
|
42582
|
+
const toolingOrBlock = isToolingOrBlockFailure(`${err.stderrTail} ${err.message}`);
|
|
42583
|
+
writeJson({
|
|
42584
|
+
ok: false,
|
|
42585
|
+
error: {
|
|
42586
|
+
code: "INGEST_FAILED",
|
|
42587
|
+
message: `Couldn't download that video. ${err.stderrTail || err.message}`,
|
|
42588
|
+
fix: toolingOrBlock ? "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, age-restricted, or region-locked videos can't be downloaded \u2014 download the file yourself and use `baker videos upload <file>`."
|
|
42589
|
+
}
|
|
42590
|
+
});
|
|
42591
|
+
process.exit(1);
|
|
42592
|
+
}
|
|
42593
|
+
writeJson({ ok: false, error: { code: "INTERNAL_ERROR", message: "Unexpected error" } });
|
|
42594
|
+
process.exit(1);
|
|
42595
|
+
}
|
|
42596
|
+
function ingestUrl(args) {
|
|
42597
|
+
const body = {
|
|
42598
|
+
url: args.url,
|
|
42599
|
+
source: args.source
|
|
42600
|
+
};
|
|
42601
|
+
if (args.externalId) body.externalId = args.externalId;
|
|
42602
|
+
if (args.externalUrl) body.externalUrl = args.externalUrl;
|
|
42603
|
+
return apiPost("/api/videos/ingest", body);
|
|
42604
|
+
}
|
|
42605
|
+
async function downloadThenIngest(args) {
|
|
42606
|
+
const workDir = await mkdtemp2(path35.join(tmpdir3(), "videos-ingest-"));
|
|
42607
|
+
try {
|
|
42608
|
+
const { filePath, info } = await runYtDlp({
|
|
42609
|
+
url: args.url,
|
|
42610
|
+
audioOnly: false,
|
|
42611
|
+
workDir,
|
|
42612
|
+
maxHeight: INGEST_MAX_HEIGHT
|
|
42613
|
+
});
|
|
42614
|
+
const stats = await stat7(filePath);
|
|
42615
|
+
if (stats.size > MAX_VIDEO_INGEST_BYTES) {
|
|
42616
|
+
throw new ApiError(
|
|
42617
|
+
"VALIDATION_ERROR",
|
|
42618
|
+
`That video is ${Math.round(stats.size / 1e6)} MB, over the ${Math.round(MAX_VIDEO_INGEST_BYTES / 1e6)} MB limit for this route.`
|
|
42619
|
+
);
|
|
42620
|
+
}
|
|
42621
|
+
const bytes = await readFile24(filePath);
|
|
42622
|
+
const publicUrl = await uploadToAssetStore(bytes);
|
|
42623
|
+
const externalId = args.externalId ?? (typeof info.id === "string" ? `${args.source}:${info.id}` : void 0);
|
|
42624
|
+
return await ingestUrl({ ...args, url: publicUrl, externalId, externalUrl: args.externalUrl ?? args.url });
|
|
42625
|
+
} finally {
|
|
42626
|
+
await rm7(workDir, { recursive: true, force: true }).catch(() => {
|
|
42627
|
+
});
|
|
42628
|
+
}
|
|
42629
|
+
}
|
|
42630
|
+
async function uploadToAssetStore(bytes) {
|
|
42631
|
+
const sha256 = sha256Hex(bytes);
|
|
42632
|
+
const { putUrl, publicUrl } = await apiPost("/api/canvas/assets/presign", {
|
|
42633
|
+
sha256,
|
|
42634
|
+
mime: "video/mp4",
|
|
42635
|
+
purpose: "source"
|
|
42636
|
+
});
|
|
42637
|
+
const putRes = await fetch(putUrl, {
|
|
42638
|
+
method: "PUT",
|
|
42639
|
+
body: new Uint8Array(bytes),
|
|
42640
|
+
headers: { "Content-Type": "video/mp4" }
|
|
42641
|
+
});
|
|
42642
|
+
if (!putRes.ok) {
|
|
42643
|
+
throw new ApiError("INTERNAL_ERROR", `Storing the downloaded video failed: HTTP ${putRes.status}`);
|
|
42644
|
+
}
|
|
42645
|
+
return publicUrl;
|
|
42646
|
+
}
|
|
42647
|
+
|
|
42648
|
+
// src/commands/videos/search.ts
|
|
42649
|
+
import { defineCommand as defineCommand201 } from "citty";
|
|
42428
42650
|
registerSchema({
|
|
42429
42651
|
command: "videos.search",
|
|
42430
42652
|
description: "Search videos by text query. Only returns ready videos.",
|
|
@@ -42434,7 +42656,7 @@ registerSchema({
|
|
|
42434
42656
|
tags: { type: "string", description: "Comma-separated tags to filter by", required: false }
|
|
42435
42657
|
}
|
|
42436
42658
|
});
|
|
42437
|
-
var searchCommand4 =
|
|
42659
|
+
var searchCommand4 = defineCommand201({
|
|
42438
42660
|
meta: {
|
|
42439
42661
|
name: "search",
|
|
42440
42662
|
description: "Semantic search videos by text query. Uses hybrid BM25 + vector + reranking. Example: baker videos search 'product demo' --tags tutorial"
|
|
@@ -42484,9 +42706,9 @@ var searchCommand4 = defineCommand200({
|
|
|
42484
42706
|
var tagsCommand5 = makeTagsCommand("videos", "video", "/api/videos/tags");
|
|
42485
42707
|
|
|
42486
42708
|
// src/commands/videos/upload.ts
|
|
42487
|
-
import { readFile as
|
|
42709
|
+
import { readFile as readFile25, stat as stat8 } from "fs/promises";
|
|
42488
42710
|
import { basename as basename3, extname as extname4 } from "path";
|
|
42489
|
-
import { defineCommand as
|
|
42711
|
+
import { defineCommand as defineCommand202 } from "citty";
|
|
42490
42712
|
var MIME_MAP = {
|
|
42491
42713
|
".mp4": "video/mp4",
|
|
42492
42714
|
".mov": "video/quicktime",
|
|
@@ -42496,9 +42718,9 @@ var MIME_MAP = {
|
|
|
42496
42718
|
};
|
|
42497
42719
|
registerSchema({
|
|
42498
42720
|
command: "videos.upload",
|
|
42499
|
-
description: "Upload a video
|
|
42721
|
+
description: "Upload a video to Baker \u2014 a local file (via Mux direct upload) or a remote http(s) URL.",
|
|
42500
42722
|
args: {
|
|
42501
|
-
file: { type: "string", description: "
|
|
42723
|
+
file: { type: "string", description: "Local video file path, or a remote http(s) URL", required: true },
|
|
42502
42724
|
"content-type": {
|
|
42503
42725
|
type: "string",
|
|
42504
42726
|
description: "MIME type (auto-detected from extension if omitted)",
|
|
@@ -42525,33 +42747,49 @@ function detectContentType(filePath) {
|
|
|
42525
42747
|
}
|
|
42526
42748
|
return mime;
|
|
42527
42749
|
}
|
|
42528
|
-
|
|
42750
|
+
function isRemoteUrl3(value) {
|
|
42751
|
+
return /^https?:\/\//i.test(value);
|
|
42752
|
+
}
|
|
42753
|
+
var uploadCommand2 = defineCommand202({
|
|
42529
42754
|
meta: {
|
|
42530
42755
|
name: "upload",
|
|
42531
|
-
description: "Upload a video
|
|
42756
|
+
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"
|
|
42532
42757
|
},
|
|
42533
42758
|
args: {
|
|
42534
|
-
file: { type: "positional", description: "
|
|
42535
|
-
"content-type": { type: "string", description: "MIME type (auto-detected
|
|
42759
|
+
file: { type: "positional", description: "Local video file path or remote http(s) URL", required: false },
|
|
42760
|
+
"content-type": { type: "string", description: "MIME type (local only \u2014 auto-detected)", required: false },
|
|
42536
42761
|
context: {
|
|
42537
42762
|
type: "string",
|
|
42538
42763
|
description: "What this clip is and what it is for \u2014 who is on camera, which campaign, how it was shot. Start here: the analysis reads it, so it is what makes the clip findable later.",
|
|
42539
42764
|
required: false
|
|
42540
42765
|
},
|
|
42766
|
+
source: { type: "string", description: "Provenance (URL mode only): url | youtube", required: false },
|
|
42767
|
+
"external-id": { type: "string", description: "Provider asset id (URL mode only)", required: false },
|
|
42768
|
+
"external-url": { type: "string", description: "Canonical page URL (URL mode only)", required: false },
|
|
42541
42769
|
"dry-run": { type: "boolean", description: "Preview without executing", required: false, default: false }
|
|
42542
42770
|
},
|
|
42543
42771
|
run: async ({ args }) => {
|
|
42544
42772
|
try {
|
|
42545
42773
|
const filePath = args.file;
|
|
42546
42774
|
if (!filePath) {
|
|
42547
|
-
writeJson({ ok: false, error: { code: "VALIDATION_ERROR", message: "File path is required" } });
|
|
42775
|
+
writeJson({ ok: false, error: { code: "VALIDATION_ERROR", message: "File path or URL is required" } });
|
|
42548
42776
|
process.exit(1);
|
|
42549
42777
|
}
|
|
42778
|
+
if (isRemoteUrl3(filePath)) {
|
|
42779
|
+
await runVideoIngest({
|
|
42780
|
+
url: filePath,
|
|
42781
|
+
source: args.source,
|
|
42782
|
+
externalId: args["external-id"],
|
|
42783
|
+
externalUrl: args["external-url"],
|
|
42784
|
+
dryRun: args["dry-run"] === true
|
|
42785
|
+
});
|
|
42786
|
+
return;
|
|
42787
|
+
}
|
|
42550
42788
|
const contentType = args["content-type"] || detectContentType(filePath);
|
|
42551
42789
|
const originalFilename = basename3(filePath);
|
|
42552
42790
|
const descriptionContext = args.context;
|
|
42553
42791
|
if (args["dry-run"]) {
|
|
42554
|
-
const fileStats = await
|
|
42792
|
+
const fileStats = await stat8(filePath);
|
|
42555
42793
|
writeJson({
|
|
42556
42794
|
ok: true,
|
|
42557
42795
|
dryRun: true,
|
|
@@ -42564,7 +42802,7 @@ var uploadCommand2 = defineCommand201({
|
|
|
42564
42802
|
originalFilename,
|
|
42565
42803
|
descriptionContext
|
|
42566
42804
|
});
|
|
42567
|
-
const fileBuffer = await
|
|
42805
|
+
const fileBuffer = await readFile25(filePath);
|
|
42568
42806
|
const uploadResponse = await fetch(uploadUrl, {
|
|
42569
42807
|
method: "PUT",
|
|
42570
42808
|
headers: { "Content-Type": contentType },
|
|
@@ -42595,16 +42833,17 @@ var uploadCommand2 = defineCommand201({
|
|
|
42595
42833
|
});
|
|
42596
42834
|
|
|
42597
42835
|
// src/commands/videos/index.ts
|
|
42598
|
-
var videosCommand =
|
|
42836
|
+
var videosCommand = defineCommand203({
|
|
42599
42837
|
meta: {
|
|
42600
42838
|
name: "videos",
|
|
42601
|
-
description: `Find and manage videos in Baker. Subcommands: search, get, upload, delete, tags.
|
|
42839
|
+
description: `Find and manage videos in Baker. Subcommands: search, get, upload, ingest, delete, tags.
|
|
42602
42840
|
|
|
42603
42841
|
Examples:
|
|
42604
42842
|
baker videos search "product demo" --limit 5
|
|
42605
42843
|
baker videos search "tutorial" --tags explainer
|
|
42606
42844
|
baker videos get <video-id>
|
|
42607
42845
|
baker videos upload ./demo.mp4
|
|
42846
|
+
baker videos ingest https://www.youtube.com/watch?v=abc123
|
|
42608
42847
|
baker videos delete <video-id> --dry-run
|
|
42609
42848
|
baker videos tags
|
|
42610
42849
|
Full guide: __tooling__/docs/tools/baker/videos.md`
|
|
@@ -42614,16 +42853,17 @@ Full guide: __tooling__/docs/tools/baker/videos.md`
|
|
|
42614
42853
|
group: groupCommand2,
|
|
42615
42854
|
search: searchCommand4,
|
|
42616
42855
|
upload: uploadCommand2,
|
|
42856
|
+
ingest: ingestCommand2,
|
|
42617
42857
|
delete: deleteCommand3,
|
|
42618
42858
|
tags: tagsCommand5
|
|
42619
42859
|
}
|
|
42620
42860
|
});
|
|
42621
42861
|
|
|
42622
42862
|
// src/commands/winning-ads/index.ts
|
|
42623
|
-
import { defineCommand as
|
|
42863
|
+
import { defineCommand as defineCommand216 } from "citty";
|
|
42624
42864
|
|
|
42625
42865
|
// src/commands/winning-ads/advertisers.ts
|
|
42626
|
-
import { defineCommand as
|
|
42866
|
+
import { defineCommand as defineCommand204 } from "citty";
|
|
42627
42867
|
|
|
42628
42868
|
// src/commands/winning-ads/shared.ts
|
|
42629
42869
|
function splitList2(value) {
|
|
@@ -42676,7 +42916,7 @@ function advertiserNormalizer(record, full) {
|
|
|
42676
42916
|
last_synced_at: record.last_synced_at ?? null
|
|
42677
42917
|
};
|
|
42678
42918
|
}
|
|
42679
|
-
var advertisersCommand2 =
|
|
42919
|
+
var advertisersCommand2 = defineCommand204({
|
|
42680
42920
|
meta: {
|
|
42681
42921
|
name: "advertisers",
|
|
42682
42922
|
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'
|
|
@@ -42734,7 +42974,7 @@ var advertisersCommand2 = defineCommand203({
|
|
|
42734
42974
|
});
|
|
42735
42975
|
|
|
42736
42976
|
// src/commands/winning-ads/brief.ts
|
|
42737
|
-
import { defineCommand as
|
|
42977
|
+
import { defineCommand as defineCommand205 } from "citty";
|
|
42738
42978
|
registerSchema({
|
|
42739
42979
|
command: "winning-ads.brief",
|
|
42740
42980
|
description: "Generate a creative brief grounded in strategically-similar winning ads. Optionally describe the target creative with --dna (JSON) and steer with --notes.",
|
|
@@ -42780,7 +43020,7 @@ function parseDna(raw) {
|
|
|
42780
43020
|
}
|
|
42781
43021
|
return parsed;
|
|
42782
43022
|
}
|
|
42783
|
-
var briefCommand =
|
|
43023
|
+
var briefCommand = defineCommand205({
|
|
42784
43024
|
meta: {
|
|
42785
43025
|
name: "brief",
|
|
42786
43026
|
description: `Generate a creative brief from winning references. Example: baker winning-ads brief --dna '{"angle":"cost savings"}' --notes "B2B, LinkedIn video" --k 8`
|
|
@@ -42816,7 +43056,7 @@ var briefCommand = defineCommand204({
|
|
|
42816
43056
|
});
|
|
42817
43057
|
|
|
42818
43058
|
// src/commands/winning-ads/content.ts
|
|
42819
|
-
import { defineCommand as
|
|
43059
|
+
import { defineCommand as defineCommand206 } from "citty";
|
|
42820
43060
|
registerSchema({
|
|
42821
43061
|
command: "winning-ads.content",
|
|
42822
43062
|
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.",
|
|
@@ -42829,7 +43069,7 @@ registerSchema({
|
|
|
42829
43069
|
}
|
|
42830
43070
|
}
|
|
42831
43071
|
});
|
|
42832
|
-
var contentCommand =
|
|
43072
|
+
var contentCommand = defineCommand206({
|
|
42833
43073
|
meta: {
|
|
42834
43074
|
name: "content",
|
|
42835
43075
|
description: "Read the transcript + on-screen text + copy of one winning ad. Example: baker winning-ads content adg_123 --platform meta --full --output md"
|
|
@@ -42878,7 +43118,7 @@ var contentCommand = defineCommand205({
|
|
|
42878
43118
|
});
|
|
42879
43119
|
|
|
42880
43120
|
// src/commands/winning-ads/feed.ts
|
|
42881
|
-
import { defineCommand as
|
|
43121
|
+
import { defineCommand as defineCommand207 } from "citty";
|
|
42882
43122
|
function buildFeedParams(input) {
|
|
42883
43123
|
const params = {};
|
|
42884
43124
|
const advertiser = splitList2(input.advertiser);
|
|
@@ -42930,7 +43170,7 @@ registerSchema({
|
|
|
42930
43170
|
format: { type: "string", description: "Comma-separated formats to include (e.g. static,video)", required: false }
|
|
42931
43171
|
}
|
|
42932
43172
|
});
|
|
42933
|
-
var feedCommand =
|
|
43173
|
+
var feedCommand = defineCommand207({
|
|
42934
43174
|
meta: {
|
|
42935
43175
|
name: "feed",
|
|
42936
43176
|
description: "Winners across every brand you follow (browse, then trim per advertiser). Example: baker winning-ads feed --per-advertiser 5 --output md"
|
|
@@ -43015,7 +43255,7 @@ var feedCommand = defineCommand206({
|
|
|
43015
43255
|
});
|
|
43016
43256
|
|
|
43017
43257
|
// src/commands/winning-ads/follow.ts
|
|
43018
|
-
import { defineCommand as
|
|
43258
|
+
import { defineCommand as defineCommand208 } from "citty";
|
|
43019
43259
|
var PLATFORMS = ["meta", "linkedin"];
|
|
43020
43260
|
registerSchema({
|
|
43021
43261
|
command: "winning-ads.follow",
|
|
@@ -43030,7 +43270,7 @@ registerSchema({
|
|
|
43030
43270
|
label: { type: "string", description: "Optional display label (defaults to the resolved name)", required: false }
|
|
43031
43271
|
}
|
|
43032
43272
|
});
|
|
43033
|
-
var followCommand =
|
|
43273
|
+
var followCommand = defineCommand208({
|
|
43034
43274
|
meta: {
|
|
43035
43275
|
name: "follow",
|
|
43036
43276
|
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'
|
|
@@ -43077,7 +43317,7 @@ var followCommand = defineCommand207({
|
|
|
43077
43317
|
});
|
|
43078
43318
|
|
|
43079
43319
|
// src/commands/winning-ads/follow-competitors.ts
|
|
43080
|
-
import { defineCommand as
|
|
43320
|
+
import { defineCommand as defineCommand209 } from "citty";
|
|
43081
43321
|
var PLATFORMS2 = ["meta", "linkedin"];
|
|
43082
43322
|
var BATCH_TIMEOUT_MS = 3e5;
|
|
43083
43323
|
function buildFollowBatchBody(input) {
|
|
@@ -43110,7 +43350,7 @@ registerSchema({
|
|
|
43110
43350
|
}
|
|
43111
43351
|
}
|
|
43112
43352
|
});
|
|
43113
|
-
var followCompetitorsCommand =
|
|
43353
|
+
var followCompetitorsCommand = defineCommand209({
|
|
43114
43354
|
meta: {
|
|
43115
43355
|
name: "follow-competitors",
|
|
43116
43356
|
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"'
|
|
@@ -43185,7 +43425,7 @@ var followCompetitorsCommand = defineCommand208({
|
|
|
43185
43425
|
});
|
|
43186
43426
|
|
|
43187
43427
|
// src/commands/winning-ads/following.ts
|
|
43188
|
-
import { defineCommand as
|
|
43428
|
+
import { defineCommand as defineCommand210 } from "citty";
|
|
43189
43429
|
registerSchema({
|
|
43190
43430
|
command: "winning-ads.following",
|
|
43191
43431
|
description: "List the brands you follow in your ad-dna library, with each one's status (ready vs still adding) and cached ad counts.",
|
|
@@ -43218,7 +43458,7 @@ function followingNormalizer(record, full) {
|
|
|
43218
43458
|
platforms: Array.isArray(record.platforms) ? record.platforms : []
|
|
43219
43459
|
};
|
|
43220
43460
|
}
|
|
43221
|
-
var followingCommand =
|
|
43461
|
+
var followingCommand = defineCommand210({
|
|
43222
43462
|
meta: {
|
|
43223
43463
|
name: "following",
|
|
43224
43464
|
description: "List brands you follow, with status (ready / adding\u2026) and cached counts. Example: baker winning-ads following --output md"
|
|
@@ -43253,7 +43493,7 @@ var followingCommand = defineCommand209({
|
|
|
43253
43493
|
});
|
|
43254
43494
|
|
|
43255
43495
|
// src/commands/winning-ads/patterns.ts
|
|
43256
|
-
import { defineCommand as
|
|
43496
|
+
import { defineCommand as defineCommand211 } from "citty";
|
|
43257
43497
|
registerSchema({
|
|
43258
43498
|
command: "winning-ads.patterns",
|
|
43259
43499
|
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.",
|
|
@@ -43292,7 +43532,7 @@ function discriminatorRow(record) {
|
|
|
43292
43532
|
top_values_duds: Array.isArray(record.top_values_b) ? record.top_values_b.join(", ") : ""
|
|
43293
43533
|
};
|
|
43294
43534
|
}
|
|
43295
|
-
var patternsCommand =
|
|
43535
|
+
var patternsCommand = defineCommand211({
|
|
43296
43536
|
meta: {
|
|
43297
43537
|
name: "patterns",
|
|
43298
43538
|
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"
|
|
@@ -43348,7 +43588,7 @@ var patternsCommand = defineCommand210({
|
|
|
43348
43588
|
});
|
|
43349
43589
|
|
|
43350
43590
|
// src/commands/winning-ads/search.ts
|
|
43351
|
-
import { defineCommand as
|
|
43591
|
+
import { defineCommand as defineCommand212 } from "citty";
|
|
43352
43592
|
registerSchema({
|
|
43353
43593
|
command: "winning-ads.search",
|
|
43354
43594
|
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.",
|
|
@@ -43456,7 +43696,7 @@ function buildSearchBody2(args) {
|
|
|
43456
43696
|
}
|
|
43457
43697
|
return body;
|
|
43458
43698
|
}
|
|
43459
|
-
var searchCommand5 =
|
|
43699
|
+
var searchCommand5 = defineCommand212({
|
|
43460
43700
|
meta: {
|
|
43461
43701
|
name: "search",
|
|
43462
43702
|
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"
|
|
@@ -43571,7 +43811,7 @@ var searchCommand5 = defineCommand211({
|
|
|
43571
43811
|
});
|
|
43572
43812
|
|
|
43573
43813
|
// src/commands/winning-ads/seeds.ts
|
|
43574
|
-
import { defineCommand as
|
|
43814
|
+
import { defineCommand as defineCommand213 } from "citty";
|
|
43575
43815
|
function leanRow(r) {
|
|
43576
43816
|
return {
|
|
43577
43817
|
key: r.key,
|
|
@@ -43599,7 +43839,7 @@ function makeSeedCommand(opts) {
|
|
|
43599
43839
|
limit: { type: "number", description: "Max keys 1-100 (default 20)", required: false, default: 20 }
|
|
43600
43840
|
}
|
|
43601
43841
|
});
|
|
43602
|
-
return
|
|
43842
|
+
return defineCommand213({
|
|
43603
43843
|
meta: { name: opts.name, description: opts.description },
|
|
43604
43844
|
args: {
|
|
43605
43845
|
platform: { type: "string", description: "Single platform to segment on", required: false },
|
|
@@ -43648,7 +43888,7 @@ var formatsCommand = makeSeedCommand({
|
|
|
43648
43888
|
});
|
|
43649
43889
|
|
|
43650
43890
|
// src/commands/winning-ads/unfollow.ts
|
|
43651
|
-
import { defineCommand as
|
|
43891
|
+
import { defineCommand as defineCommand214 } from "citty";
|
|
43652
43892
|
registerSchema({
|
|
43653
43893
|
command: "winning-ads.unfollow",
|
|
43654
43894
|
description: "Stop following a brand \u2014 removes it from your ad-dna library by advertiser id.",
|
|
@@ -43656,7 +43896,7 @@ registerSchema({
|
|
|
43656
43896
|
advertiser: { type: "string", description: "Advertiser id to unfollow", required: true }
|
|
43657
43897
|
}
|
|
43658
43898
|
});
|
|
43659
|
-
var unfollowCommand =
|
|
43899
|
+
var unfollowCommand = defineCommand214({
|
|
43660
43900
|
meta: {
|
|
43661
43901
|
name: "unfollow",
|
|
43662
43902
|
description: "Stop following a brand by advertiser id. Example: baker winning-ads unfollow adv_123"
|
|
@@ -43677,7 +43917,7 @@ var unfollowCommand = defineCommand213({
|
|
|
43677
43917
|
});
|
|
43678
43918
|
|
|
43679
43919
|
// src/commands/winning-ads/winners.ts
|
|
43680
|
-
import { defineCommand as
|
|
43920
|
+
import { defineCommand as defineCommand215 } from "citty";
|
|
43681
43921
|
registerSchema({
|
|
43682
43922
|
command: "winning-ads.winners",
|
|
43683
43923
|
description: "Top winning ads for one advertiser id (from `advertisers` or `following`). Returns lean winner cards; add --full for DNA + longevity.",
|
|
@@ -43687,7 +43927,7 @@ registerSchema({
|
|
|
43687
43927
|
platform: { type: "string", description: "Filter to a single platform: meta|linkedin", required: false }
|
|
43688
43928
|
}
|
|
43689
43929
|
});
|
|
43690
|
-
var winnersCommand =
|
|
43930
|
+
var winnersCommand = defineCommand215({
|
|
43691
43931
|
meta: {
|
|
43692
43932
|
name: "winners",
|
|
43693
43933
|
description: "Top winning ads for a specific advertiser id. Example: baker winning-ads winners adv_123 --top 15 --output md"
|
|
@@ -43737,7 +43977,7 @@ var winnersCommand = defineCommand214({
|
|
|
43737
43977
|
});
|
|
43738
43978
|
|
|
43739
43979
|
// src/commands/winning-ads/index.ts
|
|
43740
|
-
var winningAdsCommand =
|
|
43980
|
+
var winningAdsCommand = defineCommand216({
|
|
43741
43981
|
meta: {
|
|
43742
43982
|
name: "winning-ads",
|
|
43743
43983
|
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.
|
|
@@ -43809,7 +44049,7 @@ function getCliVersion() {
|
|
|
43809
44049
|
}
|
|
43810
44050
|
|
|
43811
44051
|
// src/cli.ts
|
|
43812
|
-
var main =
|
|
44052
|
+
var main = defineCommand217({
|
|
43813
44053
|
meta: {
|
|
43814
44054
|
name: "baker",
|
|
43815
44055
|
version: getCliVersion(),
|