@koda-sl/baker-cli 0.142.0 → 0.145.0
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 +16 -2
- package/dist/{chunk-PEBP6G74.js → chunk-FBUVCOH6.js} +2 -2
- package/dist/{chunk-SCI57ZZ6.js → chunk-LVCBCJWF.js} +9 -3
- package/dist/chunk-LVCBCJWF.js.map +1 -0
- package/dist/{chunk-BFZELBTE.js → chunk-UFBKUMN2.js} +3 -3
- package/dist/cli.js +250 -30
- package/dist/cli.js.map +1 -1
- package/dist/{client-N7XWYCHT.js → client-R5ZSVIK3.js} +2 -2
- package/dist/{output-7M5VQPTZ.js → output-R534QTMO.js} +3 -3
- package/dist/{shared-6SZHAUM2.js → shared-MUWU7BK5.js} +4 -4
- package/package.json +1 -1
- package/dist/chunk-SCI57ZZ6.js.map +0 -1
- /package/dist/{chunk-PEBP6G74.js.map → chunk-FBUVCOH6.js.map} +0 -0
- /package/dist/{chunk-BFZELBTE.js.map → chunk-UFBKUMN2.js.map} +0 -0
- /package/dist/{client-N7XWYCHT.js.map → client-R5ZSVIK3.js.map} +0 -0
- /package/dist/{output-7M5VQPTZ.js.map → output-R534QTMO.js.map} +0 -0
- /package/dist/{shared-6SZHAUM2.js.map → shared-MUWU7BK5.js.map} +0 -0
package/README.md
CHANGED
|
@@ -42,7 +42,7 @@ export BAKER_CHAT_ID="<chat-id>"
|
|
|
42
42
|
- `BAKER_GA4_PROPERTY_ID` — default GA4 property ID. Used when `--property-id` is not passed. If neither is set and exactly one property is connected, it is auto-selected.
|
|
43
43
|
- `BAKER_GSC_SITE_URL` — default GSC site URL. Used when `--site-url` is not passed. If neither is set and exactly one site is verified, it is auto-selected.
|
|
44
44
|
- `BAKER_X_ADS_ACCOUNT_ID` — default X Ads account ID (base36). Used when `--account-id` is not passed. If neither is set and exactly one X Ads account is connected, it is auto-selected.
|
|
45
|
-
- `BAKER_CHAT_ID` — chat context for staged Work Action commands and staged Scheduled Action create/update/delete. `scheduled-actions trigger` does not require it.
|
|
45
|
+
- `BAKER_CHAT_ID` — chat context for staged Work Action commands and staged Scheduled Action create/update/delete. `scheduled-actions trigger` does not require it. When set, it is also sent as an `x-baker-chat-id` header on every request, so library additions that apply immediately (images, videos, followed brands) are listed in that chat's changes as already applied.
|
|
46
46
|
|
|
47
47
|
## Debug logging
|
|
48
48
|
|
|
@@ -2132,7 +2132,11 @@ baker videos search "tutorial" --tags explainer,ugc --limit 5
|
|
|
2132
2132
|
|
|
2133
2133
|
### `baker videos get <id>`
|
|
2134
2134
|
|
|
2135
|
-
Get a single video by ID.
|
|
2135
|
+
Get a single video by ID. `--full` includes the auto-generated **transcript** (Baker transcribes every upload with speech-to-text), a `transcript_segment_count`, and the titled/timestamped **scene breakdown** — so you can read what a clip says and shows without watching it. Default JSON also returns the raw `transcript` + timed `transcriptSegments`; `--full` is what surfaces them in `--output md`/`files` too.
|
|
2136
|
+
|
|
2137
|
+
```bash
|
|
2138
|
+
baker videos get j571abc123 --full --output md
|
|
2139
|
+
```
|
|
2136
2140
|
|
|
2137
2141
|
### `baker videos upload <file>`
|
|
2138
2142
|
|
|
@@ -2326,6 +2330,16 @@ Top winning ads for one advertiser id → `GET /api/ad-library/advertiser-winner
|
|
|
2326
2330
|
baker winning-ads winners adv_123 --top 15 --output md
|
|
2327
2331
|
```
|
|
2328
2332
|
|
|
2333
|
+
### `baker winning-ads content <ad-id>`
|
|
2334
|
+
|
|
2335
|
+
Read what's **inside** one ad → `GET /api/ad-library/ad-content`. `search`/`winners`/`feed` return a lean shortlist; this opens a single `ad_id` so you can understand a reference before reproducing it. For a **video** it returns the spoken `transcript`, the `on_screen_text` overlays, and the ad `copy` (`primary_text`/`headline`/`cta`); a **static** ad returns only the copy. Add `--full` for speech (voiceover style, speaker count, direct-address), pacing (scene count, shot length, key timeline moments), any recognised soundtrack, and each on-screen overlay's role + position (e.g. `GLOBAL PAYROLL @0.5s [hook, top]`) so you can place text faithfully. `--platform meta|linkedin` selects which platform the id is on (default `meta`).
|
|
2336
|
+
|
|
2337
|
+
```bash
|
|
2338
|
+
baker winning-ads content adg_123 # transcript + on-screen text + copy
|
|
2339
|
+
baker winning-ads content adg_123 --platform meta --full # + speech, pacing, soundtrack
|
|
2340
|
+
baker winning-ads content adg_123 --full --output md
|
|
2341
|
+
```
|
|
2342
|
+
|
|
2329
2343
|
### `baker winning-ads unfollow <advertiser>`
|
|
2330
2344
|
|
|
2331
2345
|
Stop following a brand by advertiser id → `POST /api/ad-library/unfollow`. Returns `{ removed }`.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
apiGet
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-LVCBCJWF.js";
|
|
4
4
|
import {
|
|
5
5
|
getEnv
|
|
6
6
|
} from "./chunk-RK67WL4O.js";
|
|
@@ -261,4 +261,4 @@ export {
|
|
|
261
261
|
writeAdsOutput,
|
|
262
262
|
resolveCustomerId
|
|
263
263
|
};
|
|
264
|
-
//# sourceMappingURL=chunk-
|
|
264
|
+
//# sourceMappingURL=chunk-FBUVCOH6.js.map
|
|
@@ -62,6 +62,10 @@ function sanitizeParams(params) {
|
|
|
62
62
|
}
|
|
63
63
|
return sanitized;
|
|
64
64
|
}
|
|
65
|
+
function chatHeader() {
|
|
66
|
+
const chatId = getEnv().BAKER_CHAT_ID;
|
|
67
|
+
return chatId ? { "x-baker-chat-id": chatId } : {};
|
|
68
|
+
}
|
|
65
69
|
function mapHttpError(status) {
|
|
66
70
|
if (status === 401 || status === 403) {
|
|
67
71
|
return "UNAUTHORIZED";
|
|
@@ -117,7 +121,8 @@ async function apiGet(path, params) {
|
|
|
117
121
|
method: "GET",
|
|
118
122
|
headers: {
|
|
119
123
|
Authorization: `Bearer ${env.BAKER_API_KEY}`,
|
|
120
|
-
Accept: "application/json"
|
|
124
|
+
Accept: "application/json",
|
|
125
|
+
...chatHeader()
|
|
121
126
|
},
|
|
122
127
|
signal: AbortSignal.timeout(6e4)
|
|
123
128
|
});
|
|
@@ -151,7 +156,8 @@ async function apiPost(path, body, opts) {
|
|
|
151
156
|
headers: {
|
|
152
157
|
Authorization: `Bearer ${env.BAKER_API_KEY}`,
|
|
153
158
|
"Content-Type": "application/json",
|
|
154
|
-
Accept: "application/json"
|
|
159
|
+
Accept: "application/json",
|
|
160
|
+
...chatHeader()
|
|
155
161
|
},
|
|
156
162
|
body: JSON.stringify(body),
|
|
157
163
|
signal: AbortSignal.timeout(timeoutMs)
|
|
@@ -189,4 +195,4 @@ export {
|
|
|
189
195
|
apiGet,
|
|
190
196
|
apiPost
|
|
191
197
|
};
|
|
192
|
-
//# sourceMappingURL=chunk-
|
|
198
|
+
//# sourceMappingURL=chunk-LVCBCJWF.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/client.ts"],"sourcesContent":["import { debugLogHttp, readBodyForLog } from \"./debugLog.ts\";\nimport { getEnv } from \"./env.ts\";\n\nconst MAX_RATE_LIMIT_RETRIES = 3;\nconst MAX_TOTAL_WAIT_MS = 2 * 60 * 1000;\n\nasync function fetchWithRateLimitRetry(url: string, init: RequestInit): Promise<Response> {\n let totalWaited = 0;\n\n for (let attempt = 0; attempt <= MAX_RATE_LIMIT_RETRIES; attempt++) {\n const response = await fetch(url, init);\n\n if (response.status !== 429 || attempt >= MAX_RATE_LIMIT_RETRIES) {\n return response;\n }\n\n const retryAfterHeader = response.headers.get(\"Retry-After\");\n const waitMs = retryAfterHeader ? Number(retryAfterHeader) * 1000 : 2000 * 2 ** attempt;\n\n if (totalWaited + waitMs > MAX_TOTAL_WAIT_MS) {\n return response;\n }\n\n totalWaited += waitMs;\n await new Promise((resolve) => setTimeout(resolve, waitMs));\n }\n\n return fetch(url, init);\n}\n\ntype ErrorCode =\n | \"UNAUTHORIZED\"\n | \"NOT_FOUND\"\n | \"VALIDATION_ERROR\"\n | \"RATE_LIMITED\"\n | \"INTERNAL_ERROR\"\n | \"NETWORK_ERROR\"\n | \"TIMEOUT\"\n | \"IMAGE_PROCESSING_ERROR\";\n\nexport class ApiError extends Error {\n code: ErrorCode;\n\n constructor(code: ErrorCode, message: string) {\n super(message);\n this.name = \"ApiError\";\n this.code = code;\n }\n}\n\nconst CONVEX_ID_RE = /^[a-zA-Z0-9_]+$/;\n\nfunction hasControlCharacters(value: string): boolean {\n for (let i = 0; i < value.length; i++) {\n const code = value.charCodeAt(i);\n // Allow tab (9), newline (10), carriage return (13)\n if (code < 32 && code !== 9 && code !== 10 && code !== 13) {\n return true;\n }\n }\n return false;\n}\n\nfunction validateStringValue(value: string): void {\n if (hasControlCharacters(value)) {\n throw new ApiError(\"VALIDATION_ERROR\", \"String value contains invalid control characters\");\n }\n}\n\nexport function validateConvexId(id: string): void {\n if (!CONVEX_ID_RE.test(id)) {\n throw new ApiError(\"VALIDATION_ERROR\", `Invalid ID format: \"${id}\". Expected alphanumeric string.`);\n }\n}\n\nfunction sanitizeParams(params: Record<string, string>): Record<string, string> {\n const sanitized: Record<string, string> = {};\n for (const [key, value] of Object.entries(params)) {\n validateStringValue(value);\n sanitized[key] = value;\n }\n return sanitized;\n}\n\n/**\n * Sent on every call so the backend can attribute immediately-applied writes\n * (library images and videos, followed advertisers) to the Session they were\n * made from. Absent outside a chat-attached environment.\n */\nfunction chatHeader(): Record<string, string> {\n const chatId = getEnv().BAKER_CHAT_ID;\n return chatId ? { \"x-baker-chat-id\": chatId } : {};\n}\n\nfunction mapHttpError(status: number): ErrorCode {\n if (status === 401 || status === 403) {\n return \"UNAUTHORIZED\";\n }\n if (status === 404) {\n return \"NOT_FOUND\";\n }\n if (status === 422 || status === 400) {\n return \"VALIDATION_ERROR\";\n }\n if (status === 429) {\n return \"RATE_LIMITED\";\n }\n return \"INTERNAL_ERROR\";\n}\n\nasync function handleResponse<T>(response: Response): Promise<T> {\n const body = await response.text();\n\n if (!response.ok) {\n let message = `HTTP ${response.status}: ${response.statusText}`;\n try {\n const parsed = JSON.parse(body) as { error?: string | { message?: string }; message?: string };\n if (typeof parsed.error === \"string\") {\n message = parsed.error;\n } else if (parsed.error?.message) {\n message = parsed.error.message;\n } else if (parsed.message) {\n message = parsed.message;\n }\n } catch {\n // Use default message\n }\n throw new ApiError(mapHttpError(response.status), message);\n }\n\n try {\n return JSON.parse(body) as T;\n } catch {\n throw new ApiError(\"INTERNAL_ERROR\", \"Failed to parse API response as JSON\");\n }\n}\n\nexport async function apiGet<T>(path: string, params?: Record<string, string>): Promise<T> {\n const env = getEnv();\n const url = new URL(path, env.BAKER_API_URL);\n if (params) {\n const clean = sanitizeParams(params);\n for (const [key, value] of Object.entries(clean)) {\n url.searchParams.set(key, value);\n }\n }\n\n const urlStr = url.toString();\n const startedAt = Date.now();\n let response: Response;\n try {\n response = await fetchWithRateLimitRetry(urlStr, {\n method: \"GET\",\n headers: {\n Authorization: `Bearer ${env.BAKER_API_KEY}`,\n Accept: \"application/json\",\n ...chatHeader(),\n },\n signal: AbortSignal.timeout(60_000),\n });\n } catch (err) {\n debugLogHttp({ source: \"cli\", method: \"GET\", url: urlStr, durationMs: Date.now() - startedAt, error: err });\n if (err instanceof Error && (err.name === \"TimeoutError\" || err.name === \"AbortError\")) {\n throw new ApiError(\"TIMEOUT\", \"Request timed out after 60 seconds\");\n }\n throw new ApiError(\"NETWORK_ERROR\", `Request failed: ${err instanceof Error ? err.message : \"Unknown error\"}`);\n }\n\n debugLogHttp({\n source: \"cli\",\n method: \"GET\",\n url: urlStr,\n status: response.status,\n ok: response.ok,\n responseBody: await readBodyForLog(response),\n durationMs: Date.now() - startedAt,\n });\n return handleResponse<T>(response);\n}\n\nexport async function apiPost<T>(path: string, body: unknown, opts?: { timeoutMs?: number }): Promise<T> {\n const env = getEnv();\n const timeoutMs = opts?.timeoutMs ?? 60_000;\n const urlStr = new URL(path, env.BAKER_API_URL).toString();\n const startedAt = Date.now();\n let response: Response;\n try {\n response = await fetchWithRateLimitRetry(urlStr, {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${env.BAKER_API_KEY}`,\n \"Content-Type\": \"application/json\",\n Accept: \"application/json\",\n ...chatHeader(),\n },\n body: JSON.stringify(body),\n signal: AbortSignal.timeout(timeoutMs),\n });\n } catch (err) {\n debugLogHttp({\n source: \"cli\",\n method: \"POST\",\n url: urlStr,\n requestBody: body,\n durationMs: Date.now() - startedAt,\n error: err,\n });\n if (err instanceof Error && (err.name === \"TimeoutError\" || err.name === \"AbortError\")) {\n throw new ApiError(\"TIMEOUT\", `Request timed out after ${Math.round(timeoutMs / 1000)} seconds`);\n }\n throw new ApiError(\"NETWORK_ERROR\", `Request failed: ${err instanceof Error ? err.message : \"Unknown error\"}`);\n }\n\n debugLogHttp({\n source: \"cli\",\n method: \"POST\",\n url: urlStr,\n requestBody: body,\n status: response.status,\n ok: response.ok,\n responseBody: await readBodyForLog(response),\n durationMs: Date.now() - startedAt,\n });\n return handleResponse<T>(response);\n}\n"],"mappings":";;;;;;;;;AAGA,IAAM,yBAAyB;AAC/B,IAAM,oBAAoB,IAAI,KAAK;AAEnC,eAAe,wBAAwB,KAAa,MAAsC;AACxF,MAAI,cAAc;AAElB,WAAS,UAAU,GAAG,WAAW,wBAAwB,WAAW;AAClE,UAAM,WAAW,MAAM,MAAM,KAAK,IAAI;AAEtC,QAAI,SAAS,WAAW,OAAO,WAAW,wBAAwB;AAChE,aAAO;AAAA,IACT;AAEA,UAAM,mBAAmB,SAAS,QAAQ,IAAI,aAAa;AAC3D,UAAM,SAAS,mBAAmB,OAAO,gBAAgB,IAAI,MAAO,MAAO,KAAK;AAEhF,QAAI,cAAc,SAAS,mBAAmB;AAC5C,aAAO;AAAA,IACT;AAEA,mBAAe;AACf,UAAM,IAAI,QAAQ,CAAC,YAAY,WAAW,SAAS,MAAM,CAAC;AAAA,EAC5D;AAEA,SAAO,MAAM,KAAK,IAAI;AACxB;AAYO,IAAM,WAAN,cAAuB,MAAM;AAAA,EAClC;AAAA,EAEA,YAAY,MAAiB,SAAiB;AAC5C,UAAM,OAAO;AACb,SAAK,OAAO;AACZ,SAAK,OAAO;AAAA,EACd;AACF;AAEA,IAAM,eAAe;AAErB,SAAS,qBAAqB,OAAwB;AACpD,WAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;AACrC,UAAM,OAAO,MAAM,WAAW,CAAC;AAE/B,QAAI,OAAO,MAAM,SAAS,KAAK,SAAS,MAAM,SAAS,IAAI;AACzD,aAAO;AAAA,IACT;AAAA,EACF;AACA,SAAO;AACT;AAEA,SAAS,oBAAoB,OAAqB;AAChD,MAAI,qBAAqB,KAAK,GAAG;AAC/B,UAAM,IAAI,SAAS,oBAAoB,kDAAkD;AAAA,EAC3F;AACF;AAEO,SAAS,iBAAiB,IAAkB;AACjD,MAAI,CAAC,aAAa,KAAK,EAAE,GAAG;AAC1B,UAAM,IAAI,SAAS,oBAAoB,uBAAuB,EAAE,kCAAkC;AAAA,EACpG;AACF;AAEA,SAAS,eAAe,QAAwD;AAC9E,QAAM,YAAoC,CAAC;AAC3C,aAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,MAAM,GAAG;AACjD,wBAAoB,KAAK;AACzB,cAAU,GAAG,IAAI;AAAA,EACnB;AACA,SAAO;AACT;AAOA,SAAS,aAAqC;AAC5C,QAAM,SAAS,OAAO,EAAE;AACxB,SAAO,SAAS,EAAE,mBAAmB,OAAO,IAAI,CAAC;AACnD;AAEA,SAAS,aAAa,QAA2B;AAC/C,MAAI,WAAW,OAAO,WAAW,KAAK;AACpC,WAAO;AAAA,EACT;AACA,MAAI,WAAW,KAAK;AAClB,WAAO;AAAA,EACT;AACA,MAAI,WAAW,OAAO,WAAW,KAAK;AACpC,WAAO;AAAA,EACT;AACA,MAAI,WAAW,KAAK;AAClB,WAAO;AAAA,EACT;AACA,SAAO;AACT;AAEA,eAAe,eAAkB,UAAgC;AAC/D,QAAM,OAAO,MAAM,SAAS,KAAK;AAEjC,MAAI,CAAC,SAAS,IAAI;AAChB,QAAI,UAAU,QAAQ,SAAS,MAAM,KAAK,SAAS,UAAU;AAC7D,QAAI;AACF,YAAM,SAAS,KAAK,MAAM,IAAI;AAC9B,UAAI,OAAO,OAAO,UAAU,UAAU;AACpC,kBAAU,OAAO;AAAA,MACnB,WAAW,OAAO,OAAO,SAAS;AAChC,kBAAU,OAAO,MAAM;AAAA,MACzB,WAAW,OAAO,SAAS;AACzB,kBAAU,OAAO;AAAA,MACnB;AAAA,IACF,QAAQ;AAAA,IAER;AACA,UAAM,IAAI,SAAS,aAAa,SAAS,MAAM,GAAG,OAAO;AAAA,EAC3D;AAEA,MAAI;AACF,WAAO,KAAK,MAAM,IAAI;AAAA,EACxB,QAAQ;AACN,UAAM,IAAI,SAAS,kBAAkB,sCAAsC;AAAA,EAC7E;AACF;AAEA,eAAsB,OAAU,MAAc,QAA6C;AACzF,QAAM,MAAM,OAAO;AACnB,QAAM,MAAM,IAAI,IAAI,MAAM,IAAI,aAAa;AAC3C,MAAI,QAAQ;AACV,UAAM,QAAQ,eAAe,MAAM;AACnC,eAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,KAAK,GAAG;AAChD,UAAI,aAAa,IAAI,KAAK,KAAK;AAAA,IACjC;AAAA,EACF;AAEA,QAAM,SAAS,IAAI,SAAS;AAC5B,QAAM,YAAY,KAAK,IAAI;AAC3B,MAAI;AACJ,MAAI;AACF,eAAW,MAAM,wBAAwB,QAAQ;AAAA,MAC/C,QAAQ;AAAA,MACR,SAAS;AAAA,QACP,eAAe,UAAU,IAAI,aAAa;AAAA,QAC1C,QAAQ;AAAA,QACR,GAAG,WAAW;AAAA,MAChB;AAAA,MACA,QAAQ,YAAY,QAAQ,GAAM;AAAA,IACpC,CAAC;AAAA,EACH,SAAS,KAAK;AACZ,iBAAa,EAAE,QAAQ,OAAO,QAAQ,OAAO,KAAK,QAAQ,YAAY,KAAK,IAAI,IAAI,WAAW,OAAO,IAAI,CAAC;AAC1G,QAAI,eAAe,UAAU,IAAI,SAAS,kBAAkB,IAAI,SAAS,eAAe;AACtF,YAAM,IAAI,SAAS,WAAW,oCAAoC;AAAA,IACpE;AACA,UAAM,IAAI,SAAS,iBAAiB,mBAAmB,eAAe,QAAQ,IAAI,UAAU,eAAe,EAAE;AAAA,EAC/G;AAEA,eAAa;AAAA,IACX,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,KAAK;AAAA,IACL,QAAQ,SAAS;AAAA,IACjB,IAAI,SAAS;AAAA,IACb,cAAc,MAAM,eAAe,QAAQ;AAAA,IAC3C,YAAY,KAAK,IAAI,IAAI;AAAA,EAC3B,CAAC;AACD,SAAO,eAAkB,QAAQ;AACnC;AAEA,eAAsB,QAAW,MAAc,MAAe,MAA2C;AACvG,QAAM,MAAM,OAAO;AACnB,QAAM,YAAY,MAAM,aAAa;AACrC,QAAM,SAAS,IAAI,IAAI,MAAM,IAAI,aAAa,EAAE,SAAS;AACzD,QAAM,YAAY,KAAK,IAAI;AAC3B,MAAI;AACJ,MAAI;AACF,eAAW,MAAM,wBAAwB,QAAQ;AAAA,MAC/C,QAAQ;AAAA,MACR,SAAS;AAAA,QACP,eAAe,UAAU,IAAI,aAAa;AAAA,QAC1C,gBAAgB;AAAA,QAChB,QAAQ;AAAA,QACR,GAAG,WAAW;AAAA,MAChB;AAAA,MACA,MAAM,KAAK,UAAU,IAAI;AAAA,MACzB,QAAQ,YAAY,QAAQ,SAAS;AAAA,IACvC,CAAC;AAAA,EACH,SAAS,KAAK;AACZ,iBAAa;AAAA,MACX,QAAQ;AAAA,MACR,QAAQ;AAAA,MACR,KAAK;AAAA,MACL,aAAa;AAAA,MACb,YAAY,KAAK,IAAI,IAAI;AAAA,MACzB,OAAO;AAAA,IACT,CAAC;AACD,QAAI,eAAe,UAAU,IAAI,SAAS,kBAAkB,IAAI,SAAS,eAAe;AACtF,YAAM,IAAI,SAAS,WAAW,2BAA2B,KAAK,MAAM,YAAY,GAAI,CAAC,UAAU;AAAA,IACjG;AACA,UAAM,IAAI,SAAS,iBAAiB,mBAAmB,eAAe,QAAQ,IAAI,UAAU,eAAe,EAAE;AAAA,EAC/G;AAEA,eAAa;AAAA,IACX,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,KAAK;AAAA,IACL,aAAa;AAAA,IACb,QAAQ,SAAS;AAAA,IACjB,IAAI,SAAS;AAAA,IACb,cAAc,MAAM,eAAe,QAAQ;AAAA,IAC3C,YAAY,KAAK,IAAI,IAAI;AAAA,EAC3B,CAAC;AACD,SAAO,eAAkB,QAAQ;AACnC;","names":[]}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import {
|
|
2
2
|
handleConnectionError,
|
|
3
3
|
writeAdsJson
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-FBUVCOH6.js";
|
|
5
5
|
import {
|
|
6
6
|
ApiError
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-LVCBCJWF.js";
|
|
8
8
|
import {
|
|
9
9
|
getEnv
|
|
10
10
|
} from "./chunk-RK67WL4O.js";
|
|
@@ -94,4 +94,4 @@ export {
|
|
|
94
94
|
csvOrJson,
|
|
95
95
|
resolveEffectiveStatus
|
|
96
96
|
};
|
|
97
|
-
//# sourceMappingURL=chunk-
|
|
97
|
+
//# sourceMappingURL=chunk-UFBKUMN2.js.map
|
package/dist/cli.js
CHANGED
|
@@ -43,7 +43,7 @@ import {
|
|
|
43
43
|
resolveAccountIdArg,
|
|
44
44
|
resolveEffectiveStatus,
|
|
45
45
|
todayIso
|
|
46
|
-
} from "./chunk-
|
|
46
|
+
} from "./chunk-UFBKUMN2.js";
|
|
47
47
|
import {
|
|
48
48
|
buildQueryCacheKey,
|
|
49
49
|
cacheGet,
|
|
@@ -56,13 +56,13 @@ import {
|
|
|
56
56
|
writeAdsJson,
|
|
57
57
|
writeAdsOutput,
|
|
58
58
|
writeJsonEnvelope
|
|
59
|
-
} from "./chunk-
|
|
59
|
+
} from "./chunk-FBUVCOH6.js";
|
|
60
60
|
import {
|
|
61
61
|
ApiError,
|
|
62
62
|
apiGet,
|
|
63
63
|
apiPost,
|
|
64
64
|
validateConvexId
|
|
65
|
-
} from "./chunk-
|
|
65
|
+
} from "./chunk-LVCBCJWF.js";
|
|
66
66
|
import {
|
|
67
67
|
installStreamTaps,
|
|
68
68
|
logInvocation
|
|
@@ -73,7 +73,7 @@ import {
|
|
|
73
73
|
} from "./chunk-RK67WL4O.js";
|
|
74
74
|
|
|
75
75
|
// src/cli.ts
|
|
76
|
-
import { defineCommand as
|
|
76
|
+
import { defineCommand as defineCommand178, runMain } from "citty";
|
|
77
77
|
|
|
78
78
|
// src/commands/actions/index.ts
|
|
79
79
|
import { defineCommand as defineCommand18 } from "citty";
|
|
@@ -145,6 +145,22 @@ function compactVideo(video) {
|
|
|
145
145
|
status: String(video.status ?? "")
|
|
146
146
|
};
|
|
147
147
|
}
|
|
148
|
+
function sceneLines(value) {
|
|
149
|
+
if (!Array.isArray(value)) {
|
|
150
|
+
return [];
|
|
151
|
+
}
|
|
152
|
+
return value.flatMap((s) => {
|
|
153
|
+
const scene = asRecord(s);
|
|
154
|
+
const title = typeof scene.title === "string" ? scene.title : "";
|
|
155
|
+
if (!title) {
|
|
156
|
+
return [];
|
|
157
|
+
}
|
|
158
|
+
const start = round2(scene.startSecond);
|
|
159
|
+
const end = round2(scene.endSecond);
|
|
160
|
+
const range = start !== null && end !== null ? ` (${start}-${end}s)` : "";
|
|
161
|
+
return [`${title}${range}`];
|
|
162
|
+
});
|
|
163
|
+
}
|
|
148
164
|
function fullVideo(video) {
|
|
149
165
|
const compact = compactVideo(video);
|
|
150
166
|
return {
|
|
@@ -152,6 +168,12 @@ function fullVideo(video) {
|
|
|
152
168
|
duration: video.duration ?? null,
|
|
153
169
|
muxPlaybackId: String(video.muxPlaybackId ?? ""),
|
|
154
170
|
source: String(video.source ?? ""),
|
|
171
|
+
// The spoken transcript + AI scene breakdown of the client's own clip — the
|
|
172
|
+
// "understand what's in this video" payload. Kept out of the compact/search
|
|
173
|
+
// projection (too large for a list) and surfaced here on --full.
|
|
174
|
+
transcript: video.transcript ?? null,
|
|
175
|
+
transcript_segment_count: Array.isArray(video.transcriptSegments) ? video.transcriptSegments.length : 0,
|
|
176
|
+
scenes: sceneLines(video.scenes),
|
|
155
177
|
createdAt: video.createdAt ?? null
|
|
156
178
|
};
|
|
157
179
|
}
|
|
@@ -243,6 +265,76 @@ function winningAdNormalizer(record, full) {
|
|
|
243
265
|
}
|
|
244
266
|
return compactWinningAd(record);
|
|
245
267
|
}
|
|
268
|
+
function overlayLines(value, detailed = false) {
|
|
269
|
+
if (!Array.isArray(value)) {
|
|
270
|
+
return [];
|
|
271
|
+
}
|
|
272
|
+
return value.flatMap((o) => {
|
|
273
|
+
const item = asRecord(o);
|
|
274
|
+
const text = typeof item.text === "string" ? item.text : "";
|
|
275
|
+
if (!text) {
|
|
276
|
+
return [];
|
|
277
|
+
}
|
|
278
|
+
const at = round2(item.appears_at_s);
|
|
279
|
+
let line = at !== null ? `${text} @${at}s` : text;
|
|
280
|
+
if (detailed) {
|
|
281
|
+
const tags = [item.role, item.position].filter((t) => typeof t === "string" && t.length > 0);
|
|
282
|
+
if (tags.length) {
|
|
283
|
+
line += ` [${tags.join(", ")}]`;
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
return [line];
|
|
287
|
+
});
|
|
288
|
+
}
|
|
289
|
+
function compactAdContent(record) {
|
|
290
|
+
return {
|
|
291
|
+
ad_id: record.ad_id ?? null,
|
|
292
|
+
advertiser: record.advertiser ?? null,
|
|
293
|
+
format: record.format ?? null,
|
|
294
|
+
media_kind: record.media_kind ?? null,
|
|
295
|
+
media_url: record.media_url ?? null,
|
|
296
|
+
summary: record.summary ?? null,
|
|
297
|
+
transcript: record.transcript ?? null,
|
|
298
|
+
on_screen_text: overlayLines(record.on_screen_text),
|
|
299
|
+
has_captions: typeof record.has_captions === "boolean" ? record.has_captions : null,
|
|
300
|
+
primary_text: record.primary_text ?? null,
|
|
301
|
+
headline: record.headline ?? null,
|
|
302
|
+
cta: record.cta ?? null
|
|
303
|
+
};
|
|
304
|
+
}
|
|
305
|
+
function fullAdContent(record) {
|
|
306
|
+
const speech = asRecord(record.speech);
|
|
307
|
+
const pacing = asRecord(record.pacing);
|
|
308
|
+
const music = asRecord(record.music);
|
|
309
|
+
return {
|
|
310
|
+
...compactAdContent(record),
|
|
311
|
+
platform: record.platform ?? null,
|
|
312
|
+
// Re-derive with role/position tags — the lean compact version dropped them.
|
|
313
|
+
on_screen_text: overlayLines(record.on_screen_text, true),
|
|
314
|
+
winner_score: round2(record.winner_score),
|
|
315
|
+
winner_category: record.winner_category ?? null,
|
|
316
|
+
angle: record.angle ?? null,
|
|
317
|
+
hook_archetype: record.hook_archetype ?? null,
|
|
318
|
+
caption_style: record.caption_style ?? null,
|
|
319
|
+
has_speech: typeof record.has_speech === "boolean" ? record.has_speech : null,
|
|
320
|
+
speech_pct_of_runtime: round2(speech.speech_pct_of_runtime),
|
|
321
|
+
speaker_count: speech.speaker_count ?? null,
|
|
322
|
+
voiceover_vs_sync: speech.voiceover_vs_sync ?? null,
|
|
323
|
+
is_direct_address: typeof speech.is_direct_address === "boolean" ? speech.is_direct_address : null,
|
|
324
|
+
speech_emotion_dominant: speech.speech_emotion_dominant ?? null,
|
|
325
|
+
scene_count: pacing.scene_count ?? null,
|
|
326
|
+
avg_shot_length_s: round2(pacing.avg_shot_length_s),
|
|
327
|
+
pacing_shape: pacing.pacing_shape ?? null,
|
|
328
|
+
hook_ends_at_s: round2(pacing.hook_ends_at_s),
|
|
329
|
+
cta_first_shown_at_s: round2(pacing.cta_first_shown_at_s),
|
|
330
|
+
product_first_appears_at_s: round2(pacing.product_first_appears_at_s),
|
|
331
|
+
music_track_title: music.track_title ?? null,
|
|
332
|
+
music_track_artist: music.track_artist ?? null
|
|
333
|
+
};
|
|
334
|
+
}
|
|
335
|
+
function adContentNormalizer(record, full) {
|
|
336
|
+
return full ? fullAdContent(record) : compactAdContent(record);
|
|
337
|
+
}
|
|
246
338
|
function applyFieldMask(data, fields) {
|
|
247
339
|
const result = {};
|
|
248
340
|
for (const field of fields) {
|
|
@@ -1135,6 +1227,65 @@ var seedCatalogResponseSchema = z2.object({
|
|
|
1135
1227
|
segment: seedCatalogSegmentSchema.nullable(),
|
|
1136
1228
|
patterns: z2.array(patternSeedSchema)
|
|
1137
1229
|
});
|
|
1230
|
+
var adContentRequestSchema = z2.object({
|
|
1231
|
+
ad_id: z2.string().min(1),
|
|
1232
|
+
platform: adLibraryPlatformSchema.optional()
|
|
1233
|
+
});
|
|
1234
|
+
var adContentOverlaySchema = z2.object({
|
|
1235
|
+
text: z2.string(),
|
|
1236
|
+
appears_at_s: z2.number().nullable(),
|
|
1237
|
+
position: z2.string().nullable(),
|
|
1238
|
+
role: z2.string().nullable()
|
|
1239
|
+
});
|
|
1240
|
+
var adContentSpeechSchema = z2.object({
|
|
1241
|
+
speech_pct_of_runtime: z2.number().nullable(),
|
|
1242
|
+
speaker_count: z2.number().nullable(),
|
|
1243
|
+
voiceover_vs_sync: z2.string().nullable(),
|
|
1244
|
+
is_direct_address: z2.boolean().nullable(),
|
|
1245
|
+
speech_emotion_dominant: z2.string().nullable()
|
|
1246
|
+
});
|
|
1247
|
+
var adContentPacingSchema = z2.object({
|
|
1248
|
+
scene_count: z2.number().nullable(),
|
|
1249
|
+
avg_shot_length_s: z2.number().nullable(),
|
|
1250
|
+
pacing_shape: z2.string().nullable(),
|
|
1251
|
+
hook_ends_at_s: z2.number().nullable(),
|
|
1252
|
+
cta_first_shown_at_s: z2.number().nullable(),
|
|
1253
|
+
product_first_appears_at_s: z2.number().nullable()
|
|
1254
|
+
});
|
|
1255
|
+
var adContentMusicSchema = z2.object({
|
|
1256
|
+
track_title: z2.string().nullable(),
|
|
1257
|
+
track_artist: z2.string().nullable()
|
|
1258
|
+
});
|
|
1259
|
+
var adContentSchema = z2.object({
|
|
1260
|
+
ad_id: z2.string().nullable(),
|
|
1261
|
+
advertiser: z2.string().nullable(),
|
|
1262
|
+
advertiser_id: z2.string().nullable(),
|
|
1263
|
+
platform: z2.string().nullable(),
|
|
1264
|
+
format: z2.string().nullable(),
|
|
1265
|
+
media_kind: z2.string().nullable(),
|
|
1266
|
+
media_url: z2.string().nullable(),
|
|
1267
|
+
winner_score: z2.number().nullable(),
|
|
1268
|
+
winner_category: z2.string().nullable(),
|
|
1269
|
+
summary: z2.string().nullable(),
|
|
1270
|
+
angle: z2.string().nullable(),
|
|
1271
|
+
hook_archetype: z2.string().nullable(),
|
|
1272
|
+
// Spoken words (video only; null when the ad has no speech track).
|
|
1273
|
+
transcript: z2.string().nullable(),
|
|
1274
|
+
has_speech: z2.boolean().nullable(),
|
|
1275
|
+
speech: adContentSpeechSchema,
|
|
1276
|
+
// On-screen text / captions.
|
|
1277
|
+
has_captions: z2.boolean().nullable(),
|
|
1278
|
+
caption_style: z2.string().nullable(),
|
|
1279
|
+
on_screen_text: z2.array(adContentOverlaySchema),
|
|
1280
|
+
// Copy the viewer reads outside the media.
|
|
1281
|
+
primary_text: z2.string().nullable(),
|
|
1282
|
+
headline: z2.string().nullable(),
|
|
1283
|
+
cta: z2.string().nullable(),
|
|
1284
|
+
// Structure + soundtrack.
|
|
1285
|
+
pacing: adContentPacingSchema,
|
|
1286
|
+
music: adContentMusicSchema
|
|
1287
|
+
});
|
|
1288
|
+
var adContentResponseSchema = z2.object({ ad: adContentSchema });
|
|
1138
1289
|
|
|
1139
1290
|
// ../api/src/ads-linkedin/limits.ts
|
|
1140
1291
|
var LINKEDIN_LIMITS = {
|
|
@@ -1980,6 +2131,10 @@ var chatChangeTypeSchema = z5.enum([
|
|
|
1980
2131
|
"linkedin-ads",
|
|
1981
2132
|
"google-ads",
|
|
1982
2133
|
"meta-ads",
|
|
2134
|
+
// Immediate (already-applied) library effects — see lib/chatChanges.ts.
|
|
2135
|
+
"image",
|
|
2136
|
+
"video",
|
|
2137
|
+
"followed-advertiser",
|
|
1983
2138
|
"briefs"
|
|
1984
2139
|
]);
|
|
1985
2140
|
var chatChangeActionSchema = z5.enum(["created", "updated", "deleted"]);
|
|
@@ -4923,7 +5078,7 @@ var demandGenAdSchema = z12.object({
|
|
|
4923
5078
|
squareImageAssets: z12.array(refSchema).optional(),
|
|
4924
5079
|
logoImageAssets: z12.array(refSchema).optional()
|
|
4925
5080
|
});
|
|
4926
|
-
var
|
|
5081
|
+
var adContentSchema2 = z12.discriminatedUnion("format", [
|
|
4927
5082
|
responsiveSearchAdSchema,
|
|
4928
5083
|
responsiveDisplayAdSchema,
|
|
4929
5084
|
callAdSchema,
|
|
@@ -4934,7 +5089,7 @@ var adContentSchema = z12.discriminatedUnion("format", [
|
|
|
4934
5089
|
var adCreateSchema = z12.object({
|
|
4935
5090
|
adGroup: refSchema,
|
|
4936
5091
|
status: stageableStatusSchema2.default("PAUSED"),
|
|
4937
|
-
content:
|
|
5092
|
+
content: adContentSchema2
|
|
4938
5093
|
});
|
|
4939
5094
|
var adUpdateSchema = z12.object({
|
|
4940
5095
|
status: z12.enum(["ENABLED", "PAUSED", "REMOVED"]).optional(),
|
|
@@ -12684,10 +12839,10 @@ function duplicateCommand2(entity, label) {
|
|
|
12684
12839
|
replace: { type: "boolean", description: "Pause the original once the copy publishes" }
|
|
12685
12840
|
},
|
|
12686
12841
|
run: async ({ args }) => {
|
|
12687
|
-
const { apiPost: apiPost2 } = await import("./client-
|
|
12842
|
+
const { apiPost: apiPost2 } = await import("./client-R5ZSVIK3.js");
|
|
12688
12843
|
const { requireChatId: requireChatId2 } = await import("./env-3JMYIH25.js");
|
|
12689
|
-
const { writeJsonEnvelope: writeJsonEnvelope2 } = await import("./output-
|
|
12690
|
-
const { handleMetaError: handleMetaError2 } = await import("./shared-
|
|
12844
|
+
const { writeJsonEnvelope: writeJsonEnvelope2 } = await import("./output-R534QTMO.js");
|
|
12845
|
+
const { handleMetaError: handleMetaError2 } = await import("./shared-MUWU7BK5.js");
|
|
12691
12846
|
try {
|
|
12692
12847
|
const accountId = bareAccountId2(args);
|
|
12693
12848
|
const chatId = requireChatId2();
|
|
@@ -29434,7 +29589,7 @@ Full guide: __tooling__/docs/tools/baker/videos.md`
|
|
|
29434
29589
|
});
|
|
29435
29590
|
|
|
29436
29591
|
// src/commands/winning-ads/index.ts
|
|
29437
|
-
import { defineCommand as
|
|
29592
|
+
import { defineCommand as defineCommand177 } from "citty";
|
|
29438
29593
|
|
|
29439
29594
|
// src/commands/winning-ads/advertisers.ts
|
|
29440
29595
|
import { defineCommand as defineCommand165 } from "citty";
|
|
@@ -29629,8 +29784,70 @@ var briefCommand = defineCommand166({
|
|
|
29629
29784
|
}
|
|
29630
29785
|
});
|
|
29631
29786
|
|
|
29632
|
-
// src/commands/winning-ads/
|
|
29787
|
+
// src/commands/winning-ads/content.ts
|
|
29633
29788
|
import { defineCommand as defineCommand167 } from "citty";
|
|
29789
|
+
registerSchema({
|
|
29790
|
+
command: "winning-ads.content",
|
|
29791
|
+
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.",
|
|
29792
|
+
args: {
|
|
29793
|
+
"ad-id": { type: "string", description: "The ad id to open (from search/winners/feed results)", required: true },
|
|
29794
|
+
platform: {
|
|
29795
|
+
type: "string",
|
|
29796
|
+
description: "Which platform the ad id is on: meta|linkedin (default meta)",
|
|
29797
|
+
required: false
|
|
29798
|
+
}
|
|
29799
|
+
}
|
|
29800
|
+
});
|
|
29801
|
+
var contentCommand = defineCommand167({
|
|
29802
|
+
meta: {
|
|
29803
|
+
name: "content",
|
|
29804
|
+
description: "Read the transcript + on-screen text + copy of one winning ad. Example: baker winning-ads content adg_123 --platform meta --full --output md"
|
|
29805
|
+
},
|
|
29806
|
+
args: {
|
|
29807
|
+
"ad-id": { type: "positional", description: "Ad id (from search/winners/feed)", required: true },
|
|
29808
|
+
platform: {
|
|
29809
|
+
type: "string",
|
|
29810
|
+
description: "Platform the ad id is on: meta|linkedin (default meta)",
|
|
29811
|
+
required: false
|
|
29812
|
+
},
|
|
29813
|
+
output: { type: "string", description: "Output format: json|files|md", required: false, default: "json" },
|
|
29814
|
+
fields: { type: "string", description: "Comma-separated field names to include", required: false },
|
|
29815
|
+
full: {
|
|
29816
|
+
type: "boolean",
|
|
29817
|
+
description: "Include speech, pacing, and soundtrack detail",
|
|
29818
|
+
required: false,
|
|
29819
|
+
default: false
|
|
29820
|
+
}
|
|
29821
|
+
},
|
|
29822
|
+
run: async ({ args }) => {
|
|
29823
|
+
try {
|
|
29824
|
+
const params = { id: String(args["ad-id"]) };
|
|
29825
|
+
if (args.platform) {
|
|
29826
|
+
params.platform = String(args.platform);
|
|
29827
|
+
}
|
|
29828
|
+
const data = await apiGet("/api/ad-library/ad-content", params);
|
|
29829
|
+
const output = args.output || "json";
|
|
29830
|
+
const full = args.full;
|
|
29831
|
+
const ad = data?.ad ?? {};
|
|
29832
|
+
if (output === "json") {
|
|
29833
|
+
writeJson({ ok: true, data: { ad: adContentNormalizer(ad, full) } });
|
|
29834
|
+
return;
|
|
29835
|
+
}
|
|
29836
|
+
writeOutput(
|
|
29837
|
+
{ ok: true, data: ad },
|
|
29838
|
+
output,
|
|
29839
|
+
args.fields ? args.fields.split(",") : void 0,
|
|
29840
|
+
full,
|
|
29841
|
+
adContentNormalizer
|
|
29842
|
+
);
|
|
29843
|
+
} catch (err) {
|
|
29844
|
+
reportError(err);
|
|
29845
|
+
}
|
|
29846
|
+
}
|
|
29847
|
+
});
|
|
29848
|
+
|
|
29849
|
+
// src/commands/winning-ads/feed.ts
|
|
29850
|
+
import { defineCommand as defineCommand168 } from "citty";
|
|
29634
29851
|
function buildFeedParams(input) {
|
|
29635
29852
|
const params = {};
|
|
29636
29853
|
const advertiser = splitList(input.advertiser);
|
|
@@ -29682,7 +29899,7 @@ registerSchema({
|
|
|
29682
29899
|
format: { type: "string", description: "Comma-separated formats to include (e.g. static,video)", required: false }
|
|
29683
29900
|
}
|
|
29684
29901
|
});
|
|
29685
|
-
var feedCommand =
|
|
29902
|
+
var feedCommand = defineCommand168({
|
|
29686
29903
|
meta: {
|
|
29687
29904
|
name: "feed",
|
|
29688
29905
|
description: "Winners across every brand you follow (browse, then trim per advertiser). Example: baker winning-ads feed --per-advertiser 5 --output md"
|
|
@@ -29767,7 +29984,7 @@ var feedCommand = defineCommand167({
|
|
|
29767
29984
|
});
|
|
29768
29985
|
|
|
29769
29986
|
// src/commands/winning-ads/follow.ts
|
|
29770
|
-
import { defineCommand as
|
|
29987
|
+
import { defineCommand as defineCommand169 } from "citty";
|
|
29771
29988
|
var PLATFORMS = ["meta", "linkedin"];
|
|
29772
29989
|
registerSchema({
|
|
29773
29990
|
command: "winning-ads.follow",
|
|
@@ -29782,7 +29999,7 @@ registerSchema({
|
|
|
29782
29999
|
label: { type: "string", description: "Optional display label (defaults to the resolved name)", required: false }
|
|
29783
30000
|
}
|
|
29784
30001
|
});
|
|
29785
|
-
var followCommand =
|
|
30002
|
+
var followCommand = defineCommand169({
|
|
29786
30003
|
meta: {
|
|
29787
30004
|
name: "follow",
|
|
29788
30005
|
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'
|
|
@@ -29829,7 +30046,7 @@ var followCommand = defineCommand168({
|
|
|
29829
30046
|
});
|
|
29830
30047
|
|
|
29831
30048
|
// src/commands/winning-ads/follow-competitors.ts
|
|
29832
|
-
import { defineCommand as
|
|
30049
|
+
import { defineCommand as defineCommand170 } from "citty";
|
|
29833
30050
|
var PLATFORMS2 = ["meta", "linkedin"];
|
|
29834
30051
|
var BATCH_TIMEOUT_MS = 3e5;
|
|
29835
30052
|
function buildFollowBatchBody(input) {
|
|
@@ -29862,7 +30079,7 @@ registerSchema({
|
|
|
29862
30079
|
}
|
|
29863
30080
|
}
|
|
29864
30081
|
});
|
|
29865
|
-
var followCompetitorsCommand =
|
|
30082
|
+
var followCompetitorsCommand = defineCommand170({
|
|
29866
30083
|
meta: {
|
|
29867
30084
|
name: "follow-competitors",
|
|
29868
30085
|
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"'
|
|
@@ -29937,7 +30154,7 @@ var followCompetitorsCommand = defineCommand169({
|
|
|
29937
30154
|
});
|
|
29938
30155
|
|
|
29939
30156
|
// src/commands/winning-ads/following.ts
|
|
29940
|
-
import { defineCommand as
|
|
30157
|
+
import { defineCommand as defineCommand171 } from "citty";
|
|
29941
30158
|
registerSchema({
|
|
29942
30159
|
command: "winning-ads.following",
|
|
29943
30160
|
description: "List the brands you follow in your ad-dna library, with each one's status (ready vs still adding) and cached ad counts.",
|
|
@@ -29970,7 +30187,7 @@ function followingNormalizer(record, full) {
|
|
|
29970
30187
|
platforms: Array.isArray(record.platforms) ? record.platforms : []
|
|
29971
30188
|
};
|
|
29972
30189
|
}
|
|
29973
|
-
var followingCommand =
|
|
30190
|
+
var followingCommand = defineCommand171({
|
|
29974
30191
|
meta: {
|
|
29975
30192
|
name: "following",
|
|
29976
30193
|
description: "List brands you follow, with status (ready / adding\u2026) and cached counts. Example: baker winning-ads following --output md"
|
|
@@ -30005,7 +30222,7 @@ var followingCommand = defineCommand170({
|
|
|
30005
30222
|
});
|
|
30006
30223
|
|
|
30007
30224
|
// src/commands/winning-ads/patterns.ts
|
|
30008
|
-
import { defineCommand as
|
|
30225
|
+
import { defineCommand as defineCommand172 } from "citty";
|
|
30009
30226
|
registerSchema({
|
|
30010
30227
|
command: "winning-ads.patterns",
|
|
30011
30228
|
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.",
|
|
@@ -30044,7 +30261,7 @@ function discriminatorRow(record) {
|
|
|
30044
30261
|
top_values_duds: Array.isArray(record.top_values_b) ? record.top_values_b.join(", ") : ""
|
|
30045
30262
|
};
|
|
30046
30263
|
}
|
|
30047
|
-
var patternsCommand =
|
|
30264
|
+
var patternsCommand = defineCommand172({
|
|
30048
30265
|
meta: {
|
|
30049
30266
|
name: "patterns",
|
|
30050
30267
|
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"
|
|
@@ -30100,7 +30317,7 @@ var patternsCommand = defineCommand171({
|
|
|
30100
30317
|
});
|
|
30101
30318
|
|
|
30102
30319
|
// src/commands/winning-ads/search.ts
|
|
30103
|
-
import { defineCommand as
|
|
30320
|
+
import { defineCommand as defineCommand173 } from "citty";
|
|
30104
30321
|
registerSchema({
|
|
30105
30322
|
command: "winning-ads.search",
|
|
30106
30323
|
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.",
|
|
@@ -30208,7 +30425,7 @@ function buildSearchBody(args) {
|
|
|
30208
30425
|
}
|
|
30209
30426
|
return body;
|
|
30210
30427
|
}
|
|
30211
|
-
var searchCommand4 =
|
|
30428
|
+
var searchCommand4 = defineCommand173({
|
|
30212
30429
|
meta: {
|
|
30213
30430
|
name: "search",
|
|
30214
30431
|
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"
|
|
@@ -30323,7 +30540,7 @@ var searchCommand4 = defineCommand172({
|
|
|
30323
30540
|
});
|
|
30324
30541
|
|
|
30325
30542
|
// src/commands/winning-ads/seeds.ts
|
|
30326
|
-
import { defineCommand as
|
|
30543
|
+
import { defineCommand as defineCommand174 } from "citty";
|
|
30327
30544
|
function leanRow(r) {
|
|
30328
30545
|
return {
|
|
30329
30546
|
key: r.key,
|
|
@@ -30351,7 +30568,7 @@ function makeSeedCommand(opts) {
|
|
|
30351
30568
|
limit: { type: "number", description: "Max keys 1-100 (default 20)", required: false, default: 20 }
|
|
30352
30569
|
}
|
|
30353
30570
|
});
|
|
30354
|
-
return
|
|
30571
|
+
return defineCommand174({
|
|
30355
30572
|
meta: { name: opts.name, description: opts.description },
|
|
30356
30573
|
args: {
|
|
30357
30574
|
platform: { type: "string", description: "Single platform to segment on", required: false },
|
|
@@ -30400,7 +30617,7 @@ var formatsCommand = makeSeedCommand({
|
|
|
30400
30617
|
});
|
|
30401
30618
|
|
|
30402
30619
|
// src/commands/winning-ads/unfollow.ts
|
|
30403
|
-
import { defineCommand as
|
|
30620
|
+
import { defineCommand as defineCommand175 } from "citty";
|
|
30404
30621
|
registerSchema({
|
|
30405
30622
|
command: "winning-ads.unfollow",
|
|
30406
30623
|
description: "Stop following a brand \u2014 removes it from your ad-dna library by advertiser id.",
|
|
@@ -30408,7 +30625,7 @@ registerSchema({
|
|
|
30408
30625
|
advertiser: { type: "string", description: "Advertiser id to unfollow", required: true }
|
|
30409
30626
|
}
|
|
30410
30627
|
});
|
|
30411
|
-
var unfollowCommand =
|
|
30628
|
+
var unfollowCommand = defineCommand175({
|
|
30412
30629
|
meta: {
|
|
30413
30630
|
name: "unfollow",
|
|
30414
30631
|
description: "Stop following a brand by advertiser id. Example: baker winning-ads unfollow adv_123"
|
|
@@ -30429,7 +30646,7 @@ var unfollowCommand = defineCommand174({
|
|
|
30429
30646
|
});
|
|
30430
30647
|
|
|
30431
30648
|
// src/commands/winning-ads/winners.ts
|
|
30432
|
-
import { defineCommand as
|
|
30649
|
+
import { defineCommand as defineCommand176 } from "citty";
|
|
30433
30650
|
registerSchema({
|
|
30434
30651
|
command: "winning-ads.winners",
|
|
30435
30652
|
description: "Top winning ads for one advertiser id (from `advertisers` or `following`). Returns lean winner cards; add --full for DNA + longevity.",
|
|
@@ -30439,7 +30656,7 @@ registerSchema({
|
|
|
30439
30656
|
platform: { type: "string", description: "Filter to a single platform: meta|linkedin", required: false }
|
|
30440
30657
|
}
|
|
30441
30658
|
});
|
|
30442
|
-
var winnersCommand =
|
|
30659
|
+
var winnersCommand = defineCommand176({
|
|
30443
30660
|
meta: {
|
|
30444
30661
|
name: "winners",
|
|
30445
30662
|
description: "Top winning ads for a specific advertiser id. Example: baker winning-ads winners adv_123 --top 15 --output md"
|
|
@@ -30489,7 +30706,7 @@ var winnersCommand = defineCommand175({
|
|
|
30489
30706
|
});
|
|
30490
30707
|
|
|
30491
30708
|
// src/commands/winning-ads/index.ts
|
|
30492
|
-
var winningAdsCommand =
|
|
30709
|
+
var winningAdsCommand = defineCommand177({
|
|
30493
30710
|
meta: {
|
|
30494
30711
|
name: "winning-ads",
|
|
30495
30712
|
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.
|
|
@@ -30504,6 +30721,7 @@ Subcommands:
|
|
|
30504
30721
|
baker winning-ads following \u2014 list brands you follow (status + counts)
|
|
30505
30722
|
baker winning-ads feed \u2014 winners across EVERY brand you follow; trim with --advertiser
|
|
30506
30723
|
baker winning-ads winners <advertiser> \u2014 top winners for one advertiser id
|
|
30724
|
+
baker winning-ads content <ad-id> \u2014 read ONE ad's transcript + on-screen text + copy (understand a reference before reproducing it)
|
|
30507
30725
|
baker winning-ads unfollow <advertiser> \u2014 stop following a brand
|
|
30508
30726
|
baker winning-ads brief \u2014 creative brief grounded in similar winners
|
|
30509
30727
|
baker winning-ads patterns --winners \u2026 --duds \u2026 \u2014 what separates a winning cohort from a weak one
|
|
@@ -30519,6 +30737,7 @@ Examples:
|
|
|
30519
30737
|
baker winning-ads feed --per-advertiser 5 --output md
|
|
30520
30738
|
baker winning-ads feed --advertiser adv_123,adv_456 --platform meta --output md
|
|
30521
30739
|
baker winning-ads winners adv_123 --top 15 --output md
|
|
30740
|
+
baker winning-ads content adg_123 --full --output md
|
|
30522
30741
|
baker winning-ads hooks --platform meta --awareness problem_aware --industry saas --output md
|
|
30523
30742
|
baker winning-ads search "fintech onboarding" --hook-archetype callout --winner-category winner --output md
|
|
30524
30743
|
baker winning-ads patterns --winners a_1,a_2 --duds a_9,a_8 --output md
|
|
@@ -30532,6 +30751,7 @@ Full guide: __tooling__/docs/tools/baker/winning-ads.md`
|
|
|
30532
30751
|
following: followingCommand,
|
|
30533
30752
|
feed: feedCommand,
|
|
30534
30753
|
winners: winnersCommand,
|
|
30754
|
+
content: contentCommand,
|
|
30535
30755
|
unfollow: unfollowCommand,
|
|
30536
30756
|
brief: briefCommand,
|
|
30537
30757
|
patterns: patternsCommand,
|
|
@@ -30558,7 +30778,7 @@ function getCliVersion() {
|
|
|
30558
30778
|
}
|
|
30559
30779
|
|
|
30560
30780
|
// src/cli.ts
|
|
30561
|
-
var main =
|
|
30781
|
+
var main = defineCommand178({
|
|
30562
30782
|
meta: {
|
|
30563
30783
|
name: "baker",
|
|
30564
30784
|
version: getCliVersion(),
|