@koda-sl/baker-cli 0.99.0-dev.40c99be71 → 0.99.1-dev.5b1957cc

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/dist/cli.js CHANGED
@@ -1,6 +1,5 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
- AssetRef,
4
3
  ELEVENLABS_MAX_MUSIC_LENGTH_MS,
5
4
  IMAGE_GENERATE_MODELS,
6
5
  MODEL_REGISTRY,
@@ -8,16 +7,15 @@ import {
8
7
  ValidationError,
9
8
  createEngineFromEnv,
10
9
  defaultRegistry,
11
- extForMime,
12
10
  generateCatalog,
13
11
  validateCanvasDeep
14
- } from "./chunk-FOK2JPRW.js";
12
+ } from "./chunk-26K7V346.js";
15
13
 
16
14
  // src/cli.ts
17
- import { defineCommand as defineCommand150, runMain } from "citty";
15
+ import { defineCommand as defineCommand152, runMain } from "citty";
18
16
 
19
17
  // src/commands/actions/index.ts
20
- import { defineCommand as defineCommand17 } from "citty";
18
+ import { defineCommand as defineCommand18 } from "citty";
21
19
 
22
20
  // src/commands/actions/claim.ts
23
21
  import { defineCommand } from "citty";
@@ -45,9 +43,6 @@ function getEnv() {
45
43
  }
46
44
  return cached;
47
45
  }
48
- function runtimeEnvVar(name) {
49
- return process.env[name];
50
- }
51
46
  function requireChatId() {
52
47
  const env = getEnv();
53
48
  if (!env.BAKER_CHAT_ID) {
@@ -152,9 +147,9 @@ async function handleResponse(response) {
152
147
  throw new ApiError("INTERNAL_ERROR", "Failed to parse API response as JSON");
153
148
  }
154
149
  }
155
- async function apiGet(path12, params) {
150
+ async function apiGet(path11, params) {
156
151
  const env = getEnv();
157
- const url = new URL(path12, env.BAKER_API_URL);
152
+ const url = new URL(path11, env.BAKER_API_URL);
158
153
  if (params) {
159
154
  const clean = sanitizeParams(params);
160
155
  for (const [key, value] of Object.entries(clean)) {
@@ -179,12 +174,12 @@ async function apiGet(path12, params) {
179
174
  }
180
175
  return handleResponse(response);
181
176
  }
182
- async function apiPost(path12, body, opts) {
177
+ async function apiPost(path11, body, opts) {
183
178
  const env = getEnv();
184
179
  const timeoutMs = opts?.timeoutMs ?? 6e4;
185
180
  let response;
186
181
  try {
187
- response = await fetchWithRateLimitRetry(new URL(path12, env.BAKER_API_URL).toString(), {
182
+ response = await fetchWithRateLimitRetry(new URL(path11, env.BAKER_API_URL).toString(), {
188
183
  method: "POST",
189
184
  headers: {
190
185
  Authorization: `Bearer ${env.BAKER_API_KEY}`,
@@ -562,12 +557,17 @@ var claimCommand = defineCommand({
562
557
 
563
558
  // src/commands/actions/complete.ts
564
559
  import { defineCommand as defineCommand2 } from "citty";
560
+ var COMPLETE_NOTE_MIN = 20;
565
561
  registerSchema({
566
562
  command: "actions.complete",
567
- description: "Stage completion of an action (it was DONE). Accepts a real action ID (must be claimed) or a temp ID from the same draft. The action becomes completed when the chat is published. Do NOT use this to drop an action you no longer want \u2014 to remove a staged op use `actions draft remove`, to close an unwanted published action use `actions discard`.",
563
+ description: "Stage completion of an action (it was DONE). Accepts a real action ID (must be claimed) or a temp ID from the same draft. The action becomes completed when the chat is published. --note is REQUIRED and must describe what you actually did to resolve it (what changed, where, and the outcome) \u2014 it is the client-facing record surfaced by `baker actions log`. Do NOT use this to drop an action you no longer want \u2014 to remove a staged op use `actions draft remove`, to close an unwanted published action use `actions discard`.",
568
564
  args: {
569
565
  id: { type: "string", description: "Action ID or temp ID", required: true },
570
- note: { type: "string", description: "What was done \u2014 context for the team and AI", required: false }
566
+ note: {
567
+ type: "string",
568
+ description: "REQUIRED. Detailed description of what you did to resolve this: what changed, where, and the outcome. This is the durable, client-facing record shown by `baker actions log`.",
569
+ required: true
570
+ }
571
571
  }
572
572
  });
573
573
  var completeCommand = defineCommand2({
@@ -578,7 +578,11 @@ var completeCommand = defineCommand2({
578
578
  args: {
579
579
  id: { type: "positional", description: "Action ID or temp ID", required: false },
580
580
  "action-id": { type: "string", description: "Action ID or temp ID", required: false },
581
- note: { type: "string", description: "What was done \u2014 context for the team and AI", required: false }
581
+ note: {
582
+ type: "string",
583
+ description: "REQUIRED. What you did to resolve this: what changed, where, and the outcome.",
584
+ required: false
585
+ }
582
586
  },
583
587
  run: async ({ args }) => {
584
588
  try {
@@ -589,8 +593,14 @@ var completeCommand = defineCommand2({
589
593
  if (!isTempId(id)) {
590
594
  validateConvexId(id);
591
595
  }
596
+ const note = args.note?.trim() ?? "";
597
+ if (note.length < COMPLETE_NOTE_MIN) {
598
+ failValidation(
599
+ `--note is required and must describe what you did (\u2265${COMPLETE_NOTE_MIN} chars): what changed, where, and the outcome. This becomes the client-facing record surfaced by \`baker actions log\`.`
600
+ );
601
+ }
592
602
  const chatId = requireChatId();
593
- await apiPost("/api/actions/complete", { chatId, actionRef: id, note: args.note });
603
+ await apiPost("/api/actions/complete", { chatId, actionRef: id, note });
594
604
  writeOk();
595
605
  } catch (err) {
596
606
  failApi(err);
@@ -970,8 +980,77 @@ var listCommand2 = defineCommand8({
970
980
  }
971
981
  });
972
982
 
973
- // src/commands/actions/release.ts
983
+ // src/commands/actions/log.ts
974
984
  import { defineCommand as defineCommand9 } from "citty";
985
+ var DAY_MS = 864e5;
986
+ function startOfLocalDay(date) {
987
+ return new Date(date.getFullYear(), date.getMonth(), date.getDate()).getTime();
988
+ }
989
+ function resolveWindow(args) {
990
+ const fromArg = args.from;
991
+ const toArg = args.to;
992
+ if (fromArg) {
993
+ const fromMs = Date.parse(fromArg);
994
+ if (Number.isNaN(fromMs)) {
995
+ failValidation(`--from is not a valid ISO date/datetime: ${fromArg}`);
996
+ }
997
+ const toMs = toArg ? Date.parse(toArg) : Date.now();
998
+ if (Number.isNaN(toMs)) {
999
+ failValidation(`--to is not a valid ISO date/datetime: ${toArg}`);
1000
+ }
1001
+ return { fromMs, toMs };
1002
+ }
1003
+ const since = (args.since ?? "today").trim().toLowerCase();
1004
+ const now = Date.now();
1005
+ if (since === "today") {
1006
+ return { fromMs: startOfLocalDay(new Date(now)), toMs: now };
1007
+ }
1008
+ if (since === "yesterday") {
1009
+ const todayStart = startOfLocalDay(new Date(now));
1010
+ return { fromMs: todayStart - DAY_MS, toMs: todayStart };
1011
+ }
1012
+ const days = /^(\d+)d$/.exec(since);
1013
+ if (days) {
1014
+ return { fromMs: now - Number(days[1]) * DAY_MS, toMs: now };
1015
+ }
1016
+ return failValidation(`--since must be one of: today | yesterday | <N>d (e.g. 7d). Got: ${since}`);
1017
+ }
1018
+ registerSchema({
1019
+ command: "actions.log",
1020
+ description: "List actions COMPLETED in a time window, newest first, each with its completion note (what was done). Use this to report to the client what was accomplished \u2014 e.g. `baker actions log` for today, or `baker actions log --since 7d` for the last week. Only published completions appear (not ops staged in an unpublished chat). Window defaults to today in local time.",
1021
+ args: {
1022
+ since: {
1023
+ type: "string",
1024
+ description: "Relative window: today | yesterday | <N>d (e.g. 7d). Default: today.",
1025
+ required: false
1026
+ },
1027
+ from: { type: "string", description: "Window start (ISO date/datetime). Overrides --since.", required: false },
1028
+ to: { type: "string", description: "Window end (ISO date/datetime). Defaults to now.", required: false }
1029
+ }
1030
+ });
1031
+ var logCommand = defineCommand9({
1032
+ meta: {
1033
+ name: "log",
1034
+ description: "List completed actions in a window with their completion notes (what was done). Example: baker actions log --since today"
1035
+ },
1036
+ args: {
1037
+ since: { type: "string", description: "today | yesterday | <N>d (default: today)", required: false },
1038
+ from: { type: "string", description: "Window start (ISO). Overrides --since", required: false },
1039
+ to: { type: "string", description: "Window end (ISO). Defaults to now", required: false }
1040
+ },
1041
+ run: async ({ args }) => {
1042
+ try {
1043
+ const { fromMs, toMs } = resolveWindow(args);
1044
+ const response = await apiPost("/api/actions/log", { fromMs, toMs });
1045
+ writeJson(response);
1046
+ } catch (err) {
1047
+ failApi(err);
1048
+ }
1049
+ }
1050
+ });
1051
+
1052
+ // src/commands/actions/release.ts
1053
+ import { defineCommand as defineCommand10 } from "citty";
975
1054
  registerSchema({
976
1055
  command: "actions.release",
977
1056
  description: "Release an action you previously claimed (no-op if you don't own the claim).",
@@ -979,7 +1058,7 @@ registerSchema({
979
1058
  id: { type: "string", description: "Action ID", required: true }
980
1059
  }
981
1060
  });
982
- var releaseCommand = defineCommand9({
1061
+ var releaseCommand = defineCommand10({
983
1062
  meta: {
984
1063
  name: "release",
985
1064
  description: "Release a claim you made on an action. Example: baker actions release <action-id>"
@@ -1005,7 +1084,7 @@ var releaseCommand = defineCommand9({
1005
1084
  });
1006
1085
 
1007
1086
  // src/commands/actions/status.ts
1008
- import { defineCommand as defineCommand10 } from "citty";
1087
+ import { defineCommand as defineCommand11 } from "citty";
1009
1088
  registerSchema({
1010
1089
  command: "actions.status",
1011
1090
  description: "Resolve one or more Work Action refs by real action ID or temp_* ref in a single batch call. When BAKER_CHAT_ID is set, a temp_* ref still staged in THIS chat resolves to status 'draft' (not 'not_found') \u2014 staged ops only become published actions on chat publish.",
@@ -1013,7 +1092,7 @@ registerSchema({
1013
1092
  refs: { type: "string", description: "One or more action refs: real action IDs or temp_* refs", required: true }
1014
1093
  }
1015
1094
  });
1016
- var statusCommand = defineCommand10({
1095
+ var statusCommand = defineCommand11({
1017
1096
  meta: {
1018
1097
  name: "status",
1019
1098
  description: "Resolve Work Action refs by real ID or temp_* ref. Example: baker actions status temp_hero jx123"
@@ -1038,10 +1117,10 @@ var statusCommand = defineCommand10({
1038
1117
  });
1039
1118
 
1040
1119
  // src/commands/actions/tags/index.ts
1041
- import { defineCommand as defineCommand14 } from "citty";
1120
+ import { defineCommand as defineCommand15 } from "citty";
1042
1121
 
1043
1122
  // src/commands/actions/tags/create.ts
1044
- import { defineCommand as defineCommand11 } from "citty";
1123
+ import { defineCommand as defineCommand12 } from "citty";
1045
1124
  registerSchema({
1046
1125
  command: "actions.tags.create",
1047
1126
  description: "Create a company custom action tag, minting a new slug usable with --tags. The name is slugified (lowercased, spaces\u2192hyphens). Use when no existing tag fits the work.",
@@ -1050,7 +1129,7 @@ registerSchema({
1050
1129
  description: { type: "string", description: "What this tag means", required: false }
1051
1130
  }
1052
1131
  });
1053
- var tagsCreateCommand = defineCommand11({
1132
+ var tagsCreateCommand = defineCommand12({
1054
1133
  meta: {
1055
1134
  name: "create",
1056
1135
  description: 'Create a custom action tag. Example: baker actions tags create --slug pmax --description "Performance Max work"'
@@ -1077,7 +1156,7 @@ var tagsCreateCommand = defineCommand11({
1077
1156
  });
1078
1157
 
1079
1158
  // src/commands/actions/tags/delete.ts
1080
- import { defineCommand as defineCommand12 } from "citty";
1159
+ import { defineCommand as defineCommand13 } from "citty";
1081
1160
  registerSchema({
1082
1161
  command: "actions.tags.delete",
1083
1162
  description: "Delete a company custom action tag by slug. Built-in default tags cannot be deleted. Existing actions keep the slug (it just stops appearing in the taxonomy).",
@@ -1085,7 +1164,7 @@ registerSchema({
1085
1164
  slug: { type: "string", description: "Custom tag slug to delete", required: true }
1086
1165
  }
1087
1166
  });
1088
- var tagsDeleteCommand = defineCommand12({
1167
+ var tagsDeleteCommand = defineCommand13({
1089
1168
  meta: {
1090
1169
  name: "delete",
1091
1170
  description: "Delete a custom action tag. Example: baker actions tags delete --slug pmax"
@@ -1108,13 +1187,13 @@ var tagsDeleteCommand = defineCommand12({
1108
1187
  });
1109
1188
 
1110
1189
  // src/commands/actions/tags/list.ts
1111
- import { defineCommand as defineCommand13 } from "citty";
1190
+ import { defineCommand as defineCommand14 } from "citty";
1112
1191
  registerSchema({
1113
1192
  command: "actions.tags.list",
1114
1193
  description: "List the available action tag taxonomy (built-in defaults + this company's custom tags). Tags are strict \u2014 only these names are accepted by --tags. Run before tagging.",
1115
1194
  args: { output: { type: "string", description: "Output format: md|json", required: false, default: "md" } }
1116
1195
  });
1117
- var tagsListCommand = defineCommand13({
1196
+ var tagsListCommand = defineCommand14({
1118
1197
  meta: {
1119
1198
  name: "list",
1120
1199
  description: "List available action tag names (defaults + company custom tags). Use before --tags. Example: baker actions tags list"
@@ -1144,7 +1223,7 @@ var tagsListCommand = defineCommand13({
1144
1223
  });
1145
1224
 
1146
1225
  // src/commands/actions/tags/index.ts
1147
- var tagsCommand = defineCommand14({
1226
+ var tagsCommand = defineCommand15({
1148
1227
  meta: {
1149
1228
  name: "tags",
1150
1229
  description: `Manage the action tag taxonomy (built-in defaults + company custom tags). Tags are strict \u2014 only listed names work with --tags on create/update.
@@ -1162,7 +1241,7 @@ Examples:
1162
1241
  });
1163
1242
 
1164
1243
  // src/commands/actions/unlink.ts
1165
- import { defineCommand as defineCommand15 } from "citty";
1244
+ import { defineCommand as defineCommand16 } from "citty";
1166
1245
  registerSchema({
1167
1246
  command: "actions.unlink",
1168
1247
  description: "Stage removal of a 'blocker -> blocked' dependency. The blocked action must be claimed by current chat.",
@@ -1171,7 +1250,7 @@ registerSchema({
1171
1250
  blocked: { type: "string", description: "Blocked action ID (must be claimed by current chat)", required: true }
1172
1251
  }
1173
1252
  });
1174
- var unlinkCommand = defineCommand15({
1253
+ var unlinkCommand = defineCommand16({
1175
1254
  meta: {
1176
1255
  name: "unlink",
1177
1256
  description: "Stage removal of a dependency. Example: baker actions unlink --blocker <id> --blocked <id>"
@@ -1203,7 +1282,7 @@ var unlinkCommand = defineCommand15({
1203
1282
  });
1204
1283
 
1205
1284
  // src/commands/actions/update.ts
1206
- import { defineCommand as defineCommand16 } from "citty";
1285
+ import { defineCommand as defineCommand17 } from "citty";
1207
1286
  registerSchema({
1208
1287
  command: "actions.update",
1209
1288
  description: "Stage an update on a claimed action (name, description, tags, and/or priority). Applies on publish. --tags REPLACES the tag set; pass --tags '' to clear. --priority accepts urgent|high|medium|low or 'none' to clear back to unset (== normal).",
@@ -1223,7 +1302,7 @@ registerSchema({
1223
1302
  }
1224
1303
  }
1225
1304
  });
1226
- var updateCommand = defineCommand16({
1305
+ var updateCommand = defineCommand17({
1227
1306
  meta: {
1228
1307
  name: "update",
1229
1308
  description: 'Stage an update on a claimed action. Example: baker actions update <id> --name "New name"'
@@ -1265,10 +1344,10 @@ var updateCommand = defineCommand16({
1265
1344
  });
1266
1345
 
1267
1346
  // src/commands/actions/index.ts
1268
- var actionsCommand = defineCommand17({
1347
+ var actionsCommand = defineCommand18({
1269
1348
  meta: {
1270
1349
  name: "actions",
1271
- description: `Manage action items for the current chat. Subcommands: list, draft, get, status, claim, release, create, update, complete, discard, link, unlink.
1350
+ description: `Manage action items for the current chat. Subcommands: list, log, draft, get, status, claim, release, create, update, complete, discard, link, unlink.
1272
1351
 
1273
1352
  Lifecycle: claim an action before working on it. Stage create/update/complete/discard/link via this CLI; they apply when the chat is published. Release if you decide not to work on it after all.
1274
1353
 
@@ -1276,6 +1355,8 @@ Staged vs published: create/update/complete/discard/link are STAGED in this chat
1276
1355
 
1277
1356
  Examples:
1278
1357
  baker actions list # bucketed view of PUBLISHED actions
1358
+ baker actions log # actions COMPLETED today, with what was done (client report)
1359
+ baker actions log --since 7d # everything completed in the last 7 days
1279
1360
  baker actions draft # review what THIS chat has staged (pre-publish)
1280
1361
  baker actions draft remove temp_hero # drop a staged create (cascades its complete/link ops)
1281
1362
  baker actions draft clear # drop everything staged in this chat
@@ -1288,6 +1369,7 @@ Examples:
1288
1369
  },
1289
1370
  subCommands: {
1290
1371
  list: listCommand2,
1372
+ log: logCommand,
1291
1373
  draft: draftCommand,
1292
1374
  get: getCommand,
1293
1375
  status: statusCommand,
@@ -1304,13 +1386,13 @@ Examples:
1304
1386
  });
1305
1387
 
1306
1388
  // src/commands/ads/index.ts
1307
- import { defineCommand as defineCommand77 } from "citty";
1389
+ import { defineCommand as defineCommand78 } from "citty";
1308
1390
 
1309
1391
  // src/commands/ads/google/index.ts
1310
- import { defineCommand as defineCommand28 } from "citty";
1392
+ import { defineCommand as defineCommand29 } from "citty";
1311
1393
 
1312
1394
  // src/commands/ads/google/accounts.ts
1313
- import { defineCommand as defineCommand18 } from "citty";
1395
+ import { defineCommand as defineCommand19 } from "citty";
1314
1396
 
1315
1397
  // src/commands/ads/cache.ts
1316
1398
  import { createHash } from "crypto";
@@ -1332,31 +1414,31 @@ function cachePath(category, key) {
1332
1414
  return join(dir, `${hashKey(key)}.json`);
1333
1415
  }
1334
1416
  function cacheGet(category, key) {
1335
- const path12 = cachePath(category, key);
1336
- if (!existsSync(path12)) {
1417
+ const path11 = cachePath(category, key);
1418
+ if (!existsSync(path11)) {
1337
1419
  return null;
1338
1420
  }
1339
1421
  try {
1340
- const raw = readFileSync(path12, "utf-8");
1422
+ const raw = readFileSync(path11, "utf-8");
1341
1423
  const entry = JSON.parse(raw);
1342
1424
  if (entry.expiresAt < Date.now()) {
1343
- rmSync(path12, { force: true });
1425
+ rmSync(path11, { force: true });
1344
1426
  return null;
1345
1427
  }
1346
1428
  return entry;
1347
1429
  } catch {
1348
- rmSync(path12, { force: true });
1430
+ rmSync(path11, { force: true });
1349
1431
  return null;
1350
1432
  }
1351
1433
  }
1352
1434
  function cacheSet(category, key, data, ttlMs, fields) {
1353
- const path12 = cachePath(category, key);
1435
+ const path11 = cachePath(category, key);
1354
1436
  const entry = {
1355
1437
  expiresAt: Date.now() + ttlMs,
1356
1438
  data,
1357
1439
  fields
1358
1440
  };
1359
- writeFileSync(path12, JSON.stringify(entry), "utf-8");
1441
+ writeFileSync(path11, JSON.stringify(entry), "utf-8");
1360
1442
  }
1361
1443
  var HOUR = 60 * 60 * 1e3;
1362
1444
  var MINUTE = 60 * 1e3;
@@ -1586,7 +1668,7 @@ function handleAccountsError(err) {
1586
1668
  writeAdsJson({ ok: false, error: { code: "NETWORK_ERROR", message: "Unexpected error" } });
1587
1669
  process.exit(1);
1588
1670
  }
1589
- var accountsCommand = defineCommand18({
1671
+ var accountsCommand = defineCommand19({
1590
1672
  meta: {
1591
1673
  name: "accounts",
1592
1674
  description: `List accessible Google Ads accounts. Returns customer IDs needed for all other commands.
@@ -1626,7 +1708,7 @@ Examples:
1626
1708
  });
1627
1709
 
1628
1710
  // src/commands/ads/google/changes.ts
1629
- import { defineCommand as defineCommand19 } from "citty";
1711
+ import { defineCommand as defineCommand20 } from "citty";
1630
1712
 
1631
1713
  // src/commands/ads/field-descriptions.ts
1632
1714
  var FIELD_DESCRIPTIONS = {
@@ -2178,7 +2260,7 @@ registerSchema({
2178
2260
  output: { type: "string", description: "Format: json|csv|jsonl|md", required: false, default: "json" }
2179
2261
  }
2180
2262
  });
2181
- var changesCommand = defineCommand19({
2263
+ var changesCommand = defineCommand20({
2182
2264
  meta: {
2183
2265
  name: "changes",
2184
2266
  description: `Get recent changes in a Google Ads account with performance data.
@@ -2229,7 +2311,7 @@ Examples:
2229
2311
  });
2230
2312
 
2231
2313
  // src/commands/ads/google/currency.ts
2232
- import { defineCommand as defineCommand20 } from "citty";
2314
+ import { defineCommand as defineCommand21 } from "citty";
2233
2315
  registerSchema({
2234
2316
  command: "ads.google.currency",
2235
2317
  description: "Get the currency code for a Google Ads account. Returns currency_code, customer_id, account_name, and access_type. Call this before interpreting cost_micros values.",
@@ -2241,7 +2323,7 @@ registerSchema({
2241
2323
  }
2242
2324
  }
2243
2325
  });
2244
- var currencyCommand = defineCommand20({
2326
+ var currencyCommand = defineCommand21({
2245
2327
  meta: {
2246
2328
  name: "currency",
2247
2329
  description: `Get account currency code. Use this to interpret metrics.cost_micros values.
@@ -2290,10 +2372,10 @@ Examples:
2290
2372
  });
2291
2373
 
2292
2374
  // src/commands/ads/google/keywords/index.ts
2293
- import { defineCommand as defineCommand25 } from "citty";
2375
+ import { defineCommand as defineCommand26 } from "citty";
2294
2376
 
2295
2377
  // src/commands/ads/google/keywords/discover.ts
2296
- import { defineCommand as defineCommand21 } from "citty";
2378
+ import { defineCommand as defineCommand22 } from "citty";
2297
2379
 
2298
2380
  // src/geo-context.ts
2299
2381
  var GOOGLE_ADS_LOCATIONS = [
@@ -2568,7 +2650,7 @@ function handleKeywordError(err) {
2568
2650
  writeAdsJson({ ok: false, error: { code: "NETWORK_ERROR", message: "Unexpected error" } });
2569
2651
  process.exit(1);
2570
2652
  }
2571
- var discoverCommand = defineCommand21({
2653
+ var discoverCommand = defineCommand22({
2572
2654
  meta: {
2573
2655
  name: "discover",
2574
2656
  description: `Discover new keyword ideas from seed keywords or competitor URLs.
@@ -2630,7 +2712,7 @@ Examples:
2630
2712
  });
2631
2713
 
2632
2714
  // src/commands/ads/google/keywords/languages.ts
2633
- import { defineCommand as defineCommand22 } from "citty";
2715
+ import { defineCommand as defineCommand23 } from "citty";
2634
2716
  registerSchema({
2635
2717
  command: "ads.google.keywords.languages",
2636
2718
  description: "List all supported language IDs for --language flag in Google Ads keyword commands.",
@@ -2640,7 +2722,7 @@ var FIELDS = {
2640
2722
  id: "Language ID to pass as --language",
2641
2723
  name: "Language name"
2642
2724
  };
2643
- var languagesCommand = defineCommand22({
2725
+ var languagesCommand = defineCommand23({
2644
2726
  meta: {
2645
2727
  name: "languages",
2646
2728
  description: "List all supported language IDs for --language flag."
@@ -2651,7 +2733,7 @@ var languagesCommand = defineCommand22({
2651
2733
  });
2652
2734
 
2653
2735
  // src/commands/ads/google/keywords/locations.ts
2654
- import { defineCommand as defineCommand23 } from "citty";
2736
+ import { defineCommand as defineCommand24 } from "citty";
2655
2737
  registerSchema({
2656
2738
  command: "ads.google.keywords.locations",
2657
2739
  description: "List all supported geo target IDs for --location flag in Google Ads keyword commands.",
@@ -2661,7 +2743,7 @@ var FIELDS2 = {
2661
2743
  id: "Geo target ID to pass as --location",
2662
2744
  name: "Country/region name"
2663
2745
  };
2664
- var locationsCommand = defineCommand23({
2746
+ var locationsCommand = defineCommand24({
2665
2747
  meta: {
2666
2748
  name: "locations",
2667
2749
  description: "List all supported geo target IDs for --location flag."
@@ -2672,7 +2754,7 @@ var locationsCommand = defineCommand23({
2672
2754
  });
2673
2755
 
2674
2756
  // src/commands/ads/google/keywords/metrics.ts
2675
- import { defineCommand as defineCommand24 } from "citty";
2757
+ import { defineCommand as defineCommand25 } from "citty";
2676
2758
  registerSchema({
2677
2759
  command: "ads.google.keywords.metrics",
2678
2760
  description: "Get historical metrics for specific keywords. Returns { historical_metrics: [...] } with snake_case fields matching the Google Ads API. IMPORTANT: If --location and --language are omitted, defaults to United States (2840) and English (1000). The response includes a query_context object showing which location/language were used.",
@@ -2696,7 +2778,7 @@ registerSchema({
2696
2778
  output: { type: "string", description: "Format: json|csv|jsonl|md", required: false, default: "json" }
2697
2779
  }
2698
2780
  });
2699
- var metricsCommand = defineCommand24({
2781
+ var metricsCommand = defineCommand25({
2700
2782
  meta: {
2701
2783
  name: "metrics",
2702
2784
  description: `Get historical search metrics for specific keywords.
@@ -2783,7 +2865,7 @@ Examples:
2783
2865
  });
2784
2866
 
2785
2867
  // src/commands/ads/google/keywords/index.ts
2786
- var keywordsCommand = defineCommand25({
2868
+ var keywordsCommand = defineCommand26({
2787
2869
  meta: {
2788
2870
  name: "keywords",
2789
2871
  description: `Keyword research tools. Subcommands: discover, metrics, locations, languages.
@@ -2803,8 +2885,8 @@ Examples:
2803
2885
  });
2804
2886
 
2805
2887
  // src/commands/ads/google/library/index.ts
2806
- import { defineCommand as defineCommand26 } from "citty";
2807
- var listAdvertisers = defineCommand26({
2888
+ import { defineCommand as defineCommand27 } from "citty";
2889
+ var listAdvertisers = defineCommand27({
2808
2890
  meta: {
2809
2891
  name: "list-advertisers",
2810
2892
  description: "List tracked Google advertisers and their accounts"
@@ -2821,7 +2903,7 @@ var listAdvertisers = defineCommand26({
2821
2903
  }
2822
2904
  }
2823
2905
  });
2824
- var syncStatus = defineCommand26({
2906
+ var syncStatus = defineCommand27({
2825
2907
  meta: {
2826
2908
  name: "sync-status",
2827
2909
  description: "Check the sync status and ad counts of a Google account"
@@ -2841,7 +2923,7 @@ var syncStatus = defineCommand26({
2841
2923
  writeAdsJson({ ok: true, data });
2842
2924
  }
2843
2925
  });
2844
- var searchAds = defineCommand26({
2926
+ var searchAds = defineCommand27({
2845
2927
  meta: {
2846
2928
  name: "search-ads",
2847
2929
  description: "Search and filter Google ads for an account"
@@ -2898,7 +2980,7 @@ var searchAds = defineCommand26({
2898
2980
  }
2899
2981
  }
2900
2982
  });
2901
- var searchAdvertiser = defineCommand26({
2983
+ var searchAdvertiser = defineCommand27({
2902
2984
  meta: {
2903
2985
  name: "search-advertiser",
2904
2986
  description: "Search for an advertiser on the Google Ads Transparency Center"
@@ -2933,7 +3015,7 @@ var searchAdvertiser = defineCommand26({
2933
3015
  function sleep(ms) {
2934
3016
  return new Promise((resolve5) => setTimeout(resolve5, ms));
2935
3017
  }
2936
- var track = defineCommand26({
3018
+ var track = defineCommand27({
2937
3019
  meta: {
2938
3020
  name: "track",
2939
3021
  description: "Track a new Google advertiser (from search results). Waits for initial sync to complete before returning."
@@ -2991,7 +3073,7 @@ var track = defineCommand26({
2991
3073
  process.exit(1);
2992
3074
  }
2993
3075
  });
2994
- var sync = defineCommand26({
3076
+ var sync = defineCommand27({
2995
3077
  meta: {
2996
3078
  name: "sync",
2997
3079
  description: "Trigger an immediate sync for a Google account. Waits for completion before returning."
@@ -3035,7 +3117,7 @@ var sync = defineCommand26({
3035
3117
  process.exit(1);
3036
3118
  }
3037
3119
  });
3038
- var searchCompetitors = defineCommand26({
3120
+ var searchCompetitors = defineCommand27({
3039
3121
  meta: {
3040
3122
  name: "search-competitors",
3041
3123
  description: "Search for competitors running Google ads for a keyword (DataForSEO)"
@@ -3067,7 +3149,7 @@ var searchCompetitors = defineCommand26({
3067
3149
  }
3068
3150
  }
3069
3151
  });
3070
- var library = defineCommand26({
3152
+ var library = defineCommand27({
3071
3153
  meta: {
3072
3154
  name: "library",
3073
3155
  description: "Manage and search the Google Ads Library"
@@ -3086,7 +3168,7 @@ var library = defineCommand26({
3086
3168
  // src/commands/ads/google/query.ts
3087
3169
  import { appendFileSync, existsSync as existsSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
3088
3170
  import { resolve } from "path";
3089
- import { defineCommand as defineCommand27 } from "citty";
3171
+ import { defineCommand as defineCommand28 } from "citty";
3090
3172
 
3091
3173
  // src/commands/ads/google/preflight.ts
3092
3174
  function buildCommand2(query, customerId) {
@@ -3542,7 +3624,7 @@ function handleQueryError(err, finalQuery, customerId) {
3542
3624
  });
3543
3625
  process.exit(1);
3544
3626
  }
3545
- var queryCommand = defineCommand27({
3627
+ var queryCommand = defineCommand28({
3546
3628
  meta: {
3547
3629
  name: "query",
3548
3630
  description: `Run GAQL queries against Google Ads. Supports raw GAQL, presets, pagination, file export, and caching.
@@ -3600,7 +3682,7 @@ Examples:
3600
3682
  });
3601
3683
 
3602
3684
  // src/commands/ads/google/index.ts
3603
- var googleCommand = defineCommand28({
3685
+ var googleCommand = defineCommand29({
3604
3686
  meta: {
3605
3687
  name: "google",
3606
3688
  description: `Google Ads commands. Query campaigns, keywords, search terms, and more via GAQL.
@@ -3628,7 +3710,7 @@ Examples:
3628
3710
  });
3629
3711
 
3630
3712
  // src/commands/ads/linkedin/index.ts
3631
- import { defineCommand as defineCommand46 } from "citty";
3713
+ import { defineCommand as defineCommand47 } from "citty";
3632
3714
 
3633
3715
  // src/commands/ads/linkedin/schemas.ts
3634
3716
  registerSchema({
@@ -3928,10 +4010,10 @@ registerSchema({
3928
4010
  });
3929
4011
 
3930
4012
  // src/commands/ads/linkedin/account.ts
3931
- import { defineCommand as defineCommand29 } from "citty";
4013
+ import { defineCommand as defineCommand30 } from "citty";
3932
4014
 
3933
4015
  // src/commands/ads/linkedin/shared.ts
3934
- var DAY_MS = 864e5;
4016
+ var DAY_MS2 = 864e5;
3935
4017
  function handleLinkedinError(err) {
3936
4018
  if (err instanceof ApiError) {
3937
4019
  if (err.code === "UNAUTHORIZED") {
@@ -4014,7 +4096,7 @@ function todayIso() {
4014
4096
  return (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
4015
4097
  }
4016
4098
  function daysAgoIso(days) {
4017
- return new Date(Date.now() - days * DAY_MS).toISOString().slice(0, 10);
4099
+ return new Date(Date.now() - days * DAY_MS2).toISOString().slice(0, 10);
4018
4100
  }
4019
4101
  function csvOrJson(args) {
4020
4102
  return args.output ?? "json";
@@ -4031,7 +4113,7 @@ function resolveStatusFilter(args) {
4031
4113
  }
4032
4114
 
4033
4115
  // src/commands/ads/linkedin/account.ts
4034
- var accountCommand = defineCommand29({
4116
+ var accountCommand = defineCommand30({
4035
4117
  meta: {
4036
4118
  name: "account",
4037
4119
  description: `Single LinkedIn ad account detail (currency, status, type).
@@ -4065,9 +4147,9 @@ Examples:
4065
4147
  });
4066
4148
 
4067
4149
  // src/commands/ads/linkedin/accounts.ts
4068
- import { defineCommand as defineCommand30 } from "citty";
4150
+ import { defineCommand as defineCommand31 } from "citty";
4069
4151
  var ACCOUNTS_TTL_MS = 60 * 60 * 1e3;
4070
- var accountsCommand2 = defineCommand30({
4152
+ var accountsCommand2 = defineCommand31({
4071
4153
  meta: {
4072
4154
  name: "accounts",
4073
4155
  description: `List LinkedIn ad accounts in this company's connected scope.
@@ -4115,7 +4197,7 @@ Examples:
4115
4197
  });
4116
4198
 
4117
4199
  // src/commands/ads/linkedin/analytics.ts
4118
- import { defineCommand as defineCommand31 } from "citty";
4200
+ import { defineCommand as defineCommand32 } from "citty";
4119
4201
 
4120
4202
  // src/commands/ads/linkedin/presets.ts
4121
4203
  var INTENTS = {
@@ -4387,7 +4469,7 @@ function numberOf(v) {
4387
4469
  }
4388
4470
  return 0;
4389
4471
  }
4390
- var analyticsCommand = defineCommand31({
4472
+ var analyticsCommand = defineCommand32({
4391
4473
  meta: {
4392
4474
  name: "analytics",
4393
4475
  description: `Performance reporting \u2014 the workhorse for AI agents.
@@ -4515,7 +4597,7 @@ Examples \u2014 common AI questions:
4515
4597
 
4516
4598
  // src/commands/ads/linkedin/audience-size.ts
4517
4599
  import { readFileSync as readFileSync3 } from "fs";
4518
- import { defineCommand as defineCommand32 } from "citty";
4600
+ import { defineCommand as defineCommand33 } from "citty";
4519
4601
  function loadTargeting(args) {
4520
4602
  const inline = args.targeting;
4521
4603
  if (inline) {
@@ -4537,7 +4619,7 @@ function loadTargeting(args) {
4537
4619
  }
4538
4620
  handleLinkedinError(new Error("Pass --targeting-file <path> or --targeting '{...JSON...}'"));
4539
4621
  }
4540
- var audienceSizeCommand = defineCommand32({
4622
+ var audienceSizeCommand = defineCommand33({
4541
4623
  meta: {
4542
4624
  name: "audience-size",
4543
4625
  description: `Estimate audience size for a targeting payload \u2014 pre-launch sanity check.
@@ -4582,7 +4664,7 @@ Examples:
4582
4664
  });
4583
4665
 
4584
4666
  // src/commands/ads/linkedin/audit.ts
4585
- import { defineCommand as defineCommand33 } from "citty";
4667
+ import { defineCommand as defineCommand34 } from "citty";
4586
4668
  var SEVERITY_RANK = {
4587
4669
  critical: 0,
4588
4670
  high: 1,
@@ -4641,7 +4723,7 @@ function noteOf(f) {
4641
4723
  const fix = f.fix?.explanation ?? "";
4642
4724
  return [fix, ev].filter(Boolean).join(" \u2014 ");
4643
4725
  }
4644
- var auditCommand = defineCommand33({
4726
+ var auditCommand = defineCommand34({
4645
4727
  meta: {
4646
4728
  name: "audit",
4647
4729
  description: `Run a LinkedIn Ads playbook audit \u2014 30+ checks across Settings, Tracking,
@@ -4708,7 +4790,7 @@ Examples:
4708
4790
 
4709
4791
  // src/commands/ads/linkedin/bid-pricing.ts
4710
4792
  import { readFileSync as readFileSync4 } from "fs";
4711
- import { defineCommand as defineCommand34 } from "citty";
4793
+ import { defineCommand as defineCommand35 } from "citty";
4712
4794
  function loadTargeting2(args) {
4713
4795
  const inline = args.targeting;
4714
4796
  if (inline) {
@@ -4730,7 +4812,7 @@ function loadTargeting2(args) {
4730
4812
  }
4731
4813
  handleLinkedinError(new Error("Pass --targeting-file <path> or --targeting '{...JSON...}'"));
4732
4814
  }
4733
- var bidPricingCommand = defineCommand34({
4815
+ var bidPricingCommand = defineCommand35({
4734
4816
  meta: {
4735
4817
  name: "bid-pricing",
4736
4818
  description: `Get LinkedIn's suggested bid range for a targeting + objective + cost type.
@@ -4780,8 +4862,8 @@ Examples:
4780
4862
  });
4781
4863
 
4782
4864
  // src/commands/ads/linkedin/campaign-groups.ts
4783
- import { defineCommand as defineCommand35 } from "citty";
4784
- var campaignGroupsCommand = defineCommand35({
4865
+ import { defineCommand as defineCommand36 } from "citty";
4866
+ var campaignGroupsCommand = defineCommand36({
4785
4867
  meta: {
4786
4868
  name: "campaign-groups",
4787
4869
  description: `List LinkedIn campaign groups.
@@ -4824,8 +4906,8 @@ Examples:
4824
4906
  });
4825
4907
 
4826
4908
  // src/commands/ads/linkedin/campaigns.ts
4827
- import { defineCommand as defineCommand36 } from "citty";
4828
- var campaignsCommand = defineCommand36({
4909
+ import { defineCommand as defineCommand37 } from "citty";
4910
+ var campaignsCommand = defineCommand37({
4829
4911
  meta: {
4830
4912
  name: "campaigns",
4831
4913
  description: `List LinkedIn campaigns.
@@ -4874,8 +4956,8 @@ Examples:
4874
4956
  });
4875
4957
 
4876
4958
  // src/commands/ads/linkedin/conversation.ts
4877
- import { defineCommand as defineCommand37 } from "citty";
4878
- var conversationCommand = defineCommand37({
4959
+ import { defineCommand as defineCommand38 } from "citty";
4960
+ var conversationCommand = defineCommand38({
4879
4961
  meta: {
4880
4962
  name: "conversation",
4881
4963
  description: `Per-button click rates inside Sponsored Messaging / Conversation Ads.
@@ -4938,14 +5020,14 @@ Examples:
4938
5020
  });
4939
5021
 
4940
5022
  // src/commands/ads/linkedin/conversions.ts
4941
- import { defineCommand as defineCommand38 } from "citty";
4942
- var DAY_MS2 = 864e5;
5023
+ import { defineCommand as defineCommand39 } from "citty";
5024
+ var DAY_MS3 = 864e5;
4943
5025
  function healthOf(rules) {
4944
5026
  const enabled = rules.filter((r) => r.enabled !== false);
4945
5027
  const capi = enabled.filter((r) => r.conversionMethod === "CONVERSIONS_API");
4946
5028
  const pixel = enabled.filter((r) => r.conversionMethod === "PIXEL");
4947
5029
  const staleCapi = capi.filter(
4948
- (r) => !r.lastConversionReportedAt || Date.now() - r.lastConversionReportedAt > 7 * DAY_MS2
5030
+ (r) => !r.lastConversionReportedAt || Date.now() - r.lastConversionReportedAt > 7 * DAY_MS3
4949
5031
  );
4950
5032
  const longView = enabled.filter(
4951
5033
  (r) => r.viewThroughAttributionWindowSize !== void 0 && r.viewThroughAttributionWindowSize > 7
@@ -4964,7 +5046,7 @@ function healthOf(rules) {
4964
5046
  wrongLeadDedup: wrongDedup
4965
5047
  };
4966
5048
  }
4967
- var listCmd = defineCommand38({
5049
+ var listCmd = defineCommand39({
4968
5050
  meta: {
4969
5051
  name: "list",
4970
5052
  description: `List conversion rules on the account.`
@@ -4992,7 +5074,7 @@ var listCmd = defineCommand38({
4992
5074
  }
4993
5075
  }
4994
5076
  });
4995
- var healthCmd = defineCommand38({
5077
+ var healthCmd = defineCommand39({
4996
5078
  meta: {
4997
5079
  name: "health",
4998
5080
  description: `5-point Insight Tag / CAPI health check (playbook \xA707).
@@ -5022,7 +5104,7 @@ Surfaces:
5022
5104
  }
5023
5105
  }
5024
5106
  });
5025
- var conversionsCommand = defineCommand38({
5107
+ var conversionsCommand = defineCommand39({
5026
5108
  meta: {
5027
5109
  name: "conversions",
5028
5110
  description: `Conversion rules \u2014 Insight Tag and Conversions API.
@@ -5038,8 +5120,8 @@ Subcommands:
5038
5120
  });
5039
5121
 
5040
5122
  // src/commands/ads/linkedin/creatives.ts
5041
- import { defineCommand as defineCommand39 } from "citty";
5042
- var creativesCommand = defineCommand39({
5123
+ import { defineCommand as defineCommand40 } from "citty";
5124
+ var creativesCommand = defineCommand40({
5043
5125
  meta: {
5044
5126
  name: "creatives",
5045
5127
  description: `List LinkedIn creatives (ads).
@@ -5088,7 +5170,7 @@ Examples:
5088
5170
  });
5089
5171
 
5090
5172
  // src/commands/ads/linkedin/demographics.ts
5091
- import { defineCommand as defineCommand40 } from "citty";
5173
+ import { defineCommand as defineCommand41 } from "citty";
5092
5174
  var DEFAULT_PIVOTS = ["job-title", "company", "industry", "seniority", "job-function", "company-size"];
5093
5175
  function numberOf2(v) {
5094
5176
  if (typeof v === "number") return Number.isFinite(v) ? v : 0;
@@ -5101,7 +5183,7 @@ function numberOf2(v) {
5101
5183
  function topByImpressions(rows, limit) {
5102
5184
  return [...rows].sort((a, b) => numberOf2(b.impressions) - numberOf2(a.impressions)).slice(0, limit);
5103
5185
  }
5104
- var demographicsCommand = defineCommand40({
5186
+ var demographicsCommand = defineCommand41({
5105
5187
  meta: {
5106
5188
  name: "demographics",
5107
5189
  description: `Sweep all firmographic pivots in one command \u2014 LinkedIn's superpower.
@@ -5198,8 +5280,8 @@ function resolveRange(args) {
5198
5280
  }
5199
5281
 
5200
5282
  // src/commands/ads/linkedin/facets.ts
5201
- import { defineCommand as defineCommand41 } from "citty";
5202
- var listCmd2 = defineCommand41({
5283
+ import { defineCommand as defineCommand42 } from "citty";
5284
+ var listCmd2 = defineCommand42({
5203
5285
  meta: {
5204
5286
  name: "list",
5205
5287
  description: `List every targeting facet LinkedIn supports.
@@ -5227,7 +5309,7 @@ seniorities, titles, employers, growthRate, companyCategory, skills, etc.).`
5227
5309
  }
5228
5310
  }
5229
5311
  });
5230
- var valuesCmd = defineCommand41({
5312
+ var valuesCmd = defineCommand42({
5231
5313
  meta: {
5232
5314
  name: "values",
5233
5315
  description: `Look up entity values for a single facet \u2014 full list or typeahead search.
@@ -5268,7 +5350,7 @@ or the full URN (urn:li:adTargetingFacet:industries).`
5268
5350
  }
5269
5351
  }
5270
5352
  });
5271
- var facetsCommand = defineCommand41({
5353
+ var facetsCommand = defineCommand42({
5272
5354
  meta: {
5273
5355
  name: "facets",
5274
5356
  description: `LinkedIn targeting facets and entity lookup.
@@ -5286,7 +5368,7 @@ Subcommands:
5286
5368
 
5287
5369
  // src/commands/ads/linkedin/forecast.ts
5288
5370
  import { readFileSync as readFileSync5 } from "fs";
5289
- import { defineCommand as defineCommand42 } from "citty";
5371
+ import { defineCommand as defineCommand43 } from "citty";
5290
5372
  function loadTargeting3(args) {
5291
5373
  const inline = args.targeting;
5292
5374
  if (inline) {
@@ -5316,7 +5398,7 @@ function parseMoney(raw) {
5316
5398
  }
5317
5399
  return { amount: m[1] ?? "0", currencyCode: m[2] ?? "USD" };
5318
5400
  }
5319
- var forecastCommand = defineCommand42({
5401
+ var forecastCommand = defineCommand43({
5320
5402
  meta: {
5321
5403
  name: "forecast",
5322
5404
  description: `Forecast reach + impressions + clicks + spend for a hypothetical campaign.
@@ -5363,9 +5445,9 @@ Examples:
5363
5445
  });
5364
5446
 
5365
5447
  // src/commands/ads/linkedin/leads.ts
5366
- import { defineCommand as defineCommand43 } from "citty";
5367
- var DAY_MS3 = 864e5;
5368
- var leadsCommand = defineCommand43({
5448
+ import { defineCommand as defineCommand44 } from "citty";
5449
+ var DAY_MS4 = 864e5;
5450
+ var leadsCommand = defineCommand44({
5369
5451
  meta: {
5370
5452
  name: "leads",
5371
5453
  description: `List Lead Gen Form responses (playbook \xA707).
@@ -5400,7 +5482,7 @@ Examples:
5400
5482
  if (args["form-id"]) params["form-id"] = String(args["form-id"]);
5401
5483
  if (args["campaign-id"]) params["campaign-id"] = String(args["campaign-id"]);
5402
5484
  const sinceDays = args["since-days"] ? Number(args["since-days"]) : void 0;
5403
- const sinceMs = args["since-ms"] ? Number(args["since-ms"]) : sinceDays ? Date.now() - sinceDays * DAY_MS3 : void 0;
5485
+ const sinceMs = args["since-ms"] ? Number(args["since-ms"]) : sinceDays ? Date.now() - sinceDays * DAY_MS4 : void 0;
5404
5486
  if (sinceMs) params["since-ms"] = String(sinceMs);
5405
5487
  if (args.limit) params.limit = String(args.limit);
5406
5488
  if (args["skip-cache"]) params["skip-cache"] = "true";
@@ -5425,7 +5507,7 @@ Examples:
5425
5507
  });
5426
5508
 
5427
5509
  // src/commands/ads/linkedin/resolve.ts
5428
- import { defineCommand as defineCommand44 } from "citty";
5510
+ import { defineCommand as defineCommand45 } from "citty";
5429
5511
  function toOrgUrn(raw) {
5430
5512
  const trimmed = raw.trim();
5431
5513
  if (trimmed.length === 0) {
@@ -5436,7 +5518,7 @@ function toOrgUrn(raw) {
5436
5518
  }
5437
5519
  return /^\d+$/.test(trimmed) ? `urn:li:organization:${trimmed}` : null;
5438
5520
  }
5439
- var resolveCommand = defineCommand44({
5521
+ var resolveCommand = defineCommand45({
5440
5522
  meta: {
5441
5523
  name: "resolve",
5442
5524
  description: `Resolve organization URNs to company names.
@@ -5478,8 +5560,8 @@ couldn't be resolved \u2014 typically an org outside LinkedIn's targetable set.`
5478
5560
  });
5479
5561
 
5480
5562
  // src/commands/ads/linkedin/top-companies.ts
5481
- import { defineCommand as defineCommand45 } from "citty";
5482
- var topCompaniesCommand = defineCommand45({
5563
+ import { defineCommand as defineCommand46 } from "citty";
5564
+ var topCompaniesCommand = defineCommand46({
5483
5565
  meta: {
5484
5566
  name: "top-companies",
5485
5567
  description: `Top companies whose employees saw / clicked / converted on a campaign.
@@ -5550,7 +5632,7 @@ Examples:
5550
5632
  });
5551
5633
 
5552
5634
  // src/commands/ads/linkedin/index.ts
5553
- var linkedinCommand = defineCommand46({
5635
+ var linkedinCommand = defineCommand47({
5554
5636
  meta: {
5555
5637
  name: "linkedin",
5556
5638
  description: `LinkedIn Marketing API \u2014 AI-first command surface for B2B ad insights.
@@ -5602,13 +5684,13 @@ Account ID format:
5602
5684
  });
5603
5685
 
5604
5686
  // src/commands/ads/meta/index.ts
5605
- import { defineCommand as defineCommand59 } from "citty";
5687
+ import { defineCommand as defineCommand60 } from "citty";
5606
5688
 
5607
5689
  // src/commands/ads/meta/account.ts
5608
- import { defineCommand as defineCommand47 } from "citty";
5690
+ import { defineCommand as defineCommand48 } from "citty";
5609
5691
 
5610
5692
  // src/commands/ads/meta/shared.ts
5611
- var DAY_MS4 = 864e5;
5693
+ var DAY_MS5 = 864e5;
5612
5694
  function handleMetaError(err) {
5613
5695
  if (err instanceof ApiError) {
5614
5696
  if (err.code === "UNAUTHORIZED" || err.code === "NOT_FOUND") {
@@ -5667,7 +5749,7 @@ function todayIso2() {
5667
5749
  return (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
5668
5750
  }
5669
5751
  function daysAgoIso2(days) {
5670
- return new Date(Date.now() - days * DAY_MS4).toISOString().slice(0, 10);
5752
+ return new Date(Date.now() - days * DAY_MS5).toISOString().slice(0, 10);
5671
5753
  }
5672
5754
  function csvOrJson2(args) {
5673
5755
  return args.output ?? "json";
@@ -5684,7 +5766,7 @@ function resolveEffectiveStatus(args) {
5684
5766
  }
5685
5767
 
5686
5768
  // src/commands/ads/meta/account.ts
5687
- var accountCommand2 = defineCommand47({
5769
+ var accountCommand2 = defineCommand48({
5688
5770
  meta: {
5689
5771
  name: "account",
5690
5772
  description: `Show single Meta ad account detail (currency, timezone, balance, business).
@@ -5711,8 +5793,8 @@ Examples:
5711
5793
  });
5712
5794
 
5713
5795
  // src/commands/ads/meta/accounts.ts
5714
- import { defineCommand as defineCommand48 } from "citty";
5715
- var accountsCommand3 = defineCommand48({
5796
+ import { defineCommand as defineCommand49 } from "citty";
5797
+ var accountsCommand3 = defineCommand49({
5716
5798
  meta: {
5717
5799
  name: "accounts",
5718
5800
  description: `List Meta ad accounts in this company's connected scope.
@@ -5760,8 +5842,8 @@ Examples:
5760
5842
  });
5761
5843
 
5762
5844
  // src/commands/ads/meta/activities.ts
5763
- import { defineCommand as defineCommand49 } from "citty";
5764
- var activitiesCommand = defineCommand49({
5845
+ import { defineCommand as defineCommand50 } from "citty";
5846
+ var activitiesCommand = defineCommand50({
5765
5847
  meta: {
5766
5848
  name: "activities",
5767
5849
  description: `Audit log of recent ad-account changes (created, paused, edited). Default lookback 7 days,
@@ -5798,8 +5880,8 @@ Examples:
5798
5880
  });
5799
5881
 
5800
5882
  // src/commands/ads/meta/ads.ts
5801
- import { defineCommand as defineCommand50 } from "citty";
5802
- var adsListCommand = defineCommand50({
5883
+ import { defineCommand as defineCommand51 } from "citty";
5884
+ var adsListCommand = defineCommand51({
5803
5885
  meta: {
5804
5886
  name: "ads",
5805
5887
  description: `List ads in a Meta ad account. Defaults to ACTIVE only \u2014 pass --all-statuses to widen.
@@ -5847,8 +5929,8 @@ Examples:
5847
5929
  });
5848
5930
 
5849
5931
  // src/commands/ads/meta/adsets.ts
5850
- import { defineCommand as defineCommand51 } from "citty";
5851
- var adsetsCommand = defineCommand51({
5932
+ import { defineCommand as defineCommand52 } from "citty";
5933
+ var adsetsCommand = defineCommand52({
5852
5934
  meta: {
5853
5935
  name: "adsets",
5854
5936
  description: `List ad sets in a Meta ad account, optionally scoped to one campaign. Defaults to ACTIVE only.
@@ -5890,8 +5972,8 @@ Examples:
5890
5972
  });
5891
5973
 
5892
5974
  // src/commands/ads/meta/audiences.ts
5893
- import { defineCommand as defineCommand52 } from "citty";
5894
- var audiencesCommand = defineCommand52({
5975
+ import { defineCommand as defineCommand53 } from "citty";
5976
+ var audiencesCommand = defineCommand53({
5895
5977
  meta: {
5896
5978
  name: "audiences",
5897
5979
  description: `List custom audiences for a Meta ad account. Includes lookalikes, website-pixel audiences,
@@ -5926,8 +6008,8 @@ Examples:
5926
6008
  });
5927
6009
 
5928
6010
  // src/commands/ads/meta/businesses.ts
5929
- import { defineCommand as defineCommand53 } from "citty";
5930
- var businessesCommand = defineCommand53({
6011
+ import { defineCommand as defineCommand54 } from "citty";
6012
+ var businessesCommand = defineCommand54({
5931
6013
  meta: {
5932
6014
  name: "businesses",
5933
6015
  description: `List Meta Business Manager accounts the connected user has access to. Required for ad-studies and product-catalogs commands.
@@ -5957,8 +6039,8 @@ Examples:
5957
6039
  });
5958
6040
 
5959
6041
  // src/commands/ads/meta/campaigns.ts
5960
- import { defineCommand as defineCommand54 } from "citty";
5961
- var campaignsCommand2 = defineCommand54({
6042
+ import { defineCommand as defineCommand55 } from "citty";
6043
+ var campaignsCommand2 = defineCommand55({
5962
6044
  meta: {
5963
6045
  name: "campaigns",
5964
6046
  description: `List campaigns for a Meta ad account. Defaults to ACTIVE only \u2014 pass --all-statuses to widen.
@@ -6002,8 +6084,8 @@ Examples:
6002
6084
  });
6003
6085
 
6004
6086
  // src/commands/ads/meta/creatives.ts
6005
- import { defineCommand as defineCommand55 } from "citty";
6006
- var creativesCommand2 = defineCommand55({
6087
+ import { defineCommand as defineCommand56 } from "citty";
6088
+ var creativesCommand2 = defineCommand56({
6007
6089
  meta: {
6008
6090
  name: "creatives",
6009
6091
  description: `List ad creatives in an account, or fetch a single creative by ID.
@@ -6047,7 +6129,7 @@ Examples:
6047
6129
  });
6048
6130
 
6049
6131
  // src/commands/ads/meta/insights.ts
6050
- import { defineCommand as defineCommand56 } from "citty";
6132
+ import { defineCommand as defineCommand57 } from "citty";
6051
6133
 
6052
6134
  // src/commands/ads/meta/presets.ts
6053
6135
  var INSIGHTS_INTENTS = {
@@ -6252,7 +6334,7 @@ function sortRowsBySpendDesc(rows) {
6252
6334
  return sb - sa;
6253
6335
  });
6254
6336
  }
6255
- var insightsCommand = defineCommand56({
6337
+ var insightsCommand = defineCommand57({
6256
6338
  meta: {
6257
6339
  name: "insights",
6258
6340
  description: `Performance reporting \u2014 the main Meta tool for AI agents.
@@ -6353,8 +6435,8 @@ Async is automatic for heavy queries; pass --async to force it, or --no-async to
6353
6435
  });
6354
6436
 
6355
6437
  // src/commands/ads/meta/pixels.ts
6356
- import { defineCommand as defineCommand57 } from "citty";
6357
- var pixelsCommand = defineCommand57({
6438
+ import { defineCommand as defineCommand58 } from "citty";
6439
+ var pixelsCommand = defineCommand58({
6358
6440
  meta: {
6359
6441
  name: "pixels",
6360
6442
  description: `List Meta Pixels for an ad account, or fetch firing stats for one pixel.
@@ -6425,7 +6507,7 @@ function emit(data, args) {
6425
6507
 
6426
6508
  // src/commands/ads/meta/preview.ts
6427
6509
  import { writeFileSync as writeFileSync3 } from "fs";
6428
- import { defineCommand as defineCommand58 } from "citty";
6510
+ import { defineCommand as defineCommand59 } from "citty";
6429
6511
  var VALID_AD_FORMATS = [
6430
6512
  "DESKTOP_FEED_STANDARD",
6431
6513
  "MOBILE_FEED_STANDARD",
@@ -6459,7 +6541,7 @@ var VALID_AD_FORMATS = [
6459
6541
  "MARKETPLACE_MOBILE",
6460
6542
  "BIZ_DISCO_FEED_MOBILE"
6461
6543
  ];
6462
- var previewCommand = defineCommand58({
6544
+ var previewCommand = defineCommand59({
6463
6545
  meta: {
6464
6546
  name: "preview",
6465
6547
  description: `Generate a Meta-hosted preview iframe for a creative or ad. Returns iframe HTML which you
@@ -6506,7 +6588,7 @@ Examples:
6506
6588
  });
6507
6589
 
6508
6590
  // src/commands/ads/meta/index.ts
6509
- var metaCommand = defineCommand59({
6591
+ var metaCommand = defineCommand60({
6510
6592
  meta: {
6511
6593
  name: "meta",
6512
6594
  description: `Meta Marketing API \u2014 AI-first command surface (Facebook + Instagram ads).
@@ -6558,10 +6640,10 @@ Audit & review:
6558
6640
  });
6559
6641
 
6560
6642
  // src/commands/ads/x/index.ts
6561
- import { defineCommand as defineCommand76 } from "citty";
6643
+ import { defineCommand as defineCommand77 } from "citty";
6562
6644
 
6563
6645
  // src/commands/ads/x/accounts.ts
6564
- import { defineCommand as defineCommand60 } from "citty";
6646
+ import { defineCommand as defineCommand61 } from "citty";
6565
6647
  registerSchema({
6566
6648
  command: "ads.x.accounts",
6567
6649
  description: "List all accessible X Ads accounts. Returns accounts with id (base36), name, approval_status, timezone, currency. Run this first to find account IDs for other commands.",
@@ -6589,7 +6671,7 @@ function handleAccountsError2(err) {
6589
6671
  writeAdsJson({ ok: false, error: { code: "NETWORK_ERROR", message: "Unexpected error" } });
6590
6672
  process.exit(1);
6591
6673
  }
6592
- var accountsCommand4 = defineCommand60({
6674
+ var accountsCommand4 = defineCommand61({
6593
6675
  meta: {
6594
6676
  name: "accounts",
6595
6677
  description: `List accessible X Ads accounts. Returns account IDs needed for all other commands.
@@ -6629,7 +6711,7 @@ Examples:
6629
6711
  });
6630
6712
 
6631
6713
  // src/commands/ads/x/active-entities.ts
6632
- import { defineCommand as defineCommand61 } from "citty";
6714
+ import { defineCommand as defineCommand62 } from "citty";
6633
6715
 
6634
6716
  // src/commands/ads/x/error-parser.ts
6635
6717
  function mapXErrorCode(message) {
@@ -6780,7 +6862,7 @@ function parseCsv(v) {
6780
6862
  const parts = v.split(",").map((s) => s.trim()).filter(Boolean);
6781
6863
  return parts.length > 0 ? parts : void 0;
6782
6864
  }
6783
- var activeEntitiesCommand = defineCommand61({
6865
+ var activeEntitiesCommand = defineCommand62({
6784
6866
  meta: {
6785
6867
  name: "active-entities",
6786
6868
  description: `List entities with metric activity in a time range.
@@ -6838,7 +6920,7 @@ Examples:
6838
6920
  });
6839
6921
 
6840
6922
  // src/commands/ads/x/audiences.ts
6841
- import { defineCommand as defineCommand62 } from "citty";
6923
+ import { defineCommand as defineCommand63 } from "citty";
6842
6924
  registerSchema({
6843
6925
  command: "ads.x.audiences",
6844
6926
  description: "List custom audiences for an X Ads account. Returns id, name, audience_size, audience_type, targetable status. Audiences need 100+ active users in the past 90 days to be targetable.",
@@ -6847,7 +6929,7 @@ registerSchema({
6847
6929
  "no-cache": { type: "boolean", description: "Skip cache", required: false }
6848
6930
  }
6849
6931
  });
6850
- var audiencesCommand2 = defineCommand62({
6932
+ var audiencesCommand2 = defineCommand63({
6851
6933
  meta: {
6852
6934
  name: "audiences",
6853
6935
  description: `List X Ads custom audiences.
@@ -6896,7 +6978,7 @@ Examples:
6896
6978
  });
6897
6979
 
6898
6980
  // src/commands/ads/x/campaigns.ts
6899
- import { defineCommand as defineCommand63 } from "citty";
6981
+ import { defineCommand as defineCommand64 } from "citty";
6900
6982
 
6901
6983
  // src/commands/ads/x/run-list.ts
6902
6984
  function buildCleanParams(opts) {
@@ -6959,7 +7041,7 @@ registerSchema({
6959
7041
  "no-cache": { type: "boolean", description: "Skip cache", required: false }
6960
7042
  }
6961
7043
  });
6962
- var campaignsCommand3 = defineCommand63({
7044
+ var campaignsCommand3 = defineCommand64({
6963
7045
  meta: {
6964
7046
  name: "campaigns",
6965
7047
  description: `List X Ads campaigns. Returns budget, schedule, funding instrument, status.
@@ -7001,7 +7083,7 @@ Examples:
7001
7083
  });
7002
7084
 
7003
7085
  // src/commands/ads/x/cards.ts
7004
- import { defineCommand as defineCommand64 } from "citty";
7086
+ import { defineCommand as defineCommand65 } from "citty";
7005
7087
  registerSchema({
7006
7088
  command: "ads.x.cards",
7007
7089
  description: "List website cards, video cards, and carousels for an X Ads account.",
@@ -7010,7 +7092,7 @@ registerSchema({
7010
7092
  "no-cache": { type: "boolean", description: "Skip cache", required: false }
7011
7093
  }
7012
7094
  });
7013
- var cardsCommand = defineCommand64({
7095
+ var cardsCommand = defineCommand65({
7014
7096
  meta: {
7015
7097
  name: "cards",
7016
7098
  description: `List X Ads cards (rich creatives).
@@ -7059,7 +7141,7 @@ Examples:
7059
7141
  });
7060
7142
 
7061
7143
  // src/commands/ads/x/funding.ts
7062
- import { defineCommand as defineCommand65 } from "citty";
7144
+ import { defineCommand as defineCommand66 } from "citty";
7063
7145
  registerSchema({
7064
7146
  command: "ads.x.funding",
7065
7147
  description: "List funding instruments for an X Ads account. Returns id, type, currency, credit_limit_local_micro, funded_amount_local_micro, status. Falls back to BAKER_X_ADS_ACCOUNT_ID env var.",
@@ -7068,7 +7150,7 @@ registerSchema({
7068
7150
  "no-cache": { type: "boolean", description: "Skip cache", required: false }
7069
7151
  }
7070
7152
  });
7071
- var fundingCommand = defineCommand65({
7153
+ var fundingCommand = defineCommand66({
7072
7154
  meta: {
7073
7155
  name: "funding",
7074
7156
  description: `List funding instruments for an X Ads account.
@@ -7117,7 +7199,7 @@ Examples:
7117
7199
  });
7118
7200
 
7119
7201
  // src/commands/ads/x/line-items.ts
7120
- import { defineCommand as defineCommand66 } from "citty";
7202
+ import { defineCommand as defineCommand67 } from "citty";
7121
7203
  registerSchema({
7122
7204
  command: "ads.x.lineItems",
7123
7205
  description: "List line items (ad groups) for an X Ads account. Returns bid, product_type, objective, placements, schedule. Filter by campaign-ids or line-item-ids (CSV).",
@@ -7129,7 +7211,7 @@ registerSchema({
7129
7211
  "no-cache": { type: "boolean", description: "Skip cache", required: false }
7130
7212
  }
7131
7213
  });
7132
- var lineItemsCommand = defineCommand66({
7214
+ var lineItemsCommand = defineCommand67({
7133
7215
  meta: {
7134
7216
  name: "line-items",
7135
7217
  description: `List X Ads line items (ad groups).
@@ -7170,7 +7252,7 @@ Examples:
7170
7252
  });
7171
7253
 
7172
7254
  // src/commands/ads/x/media.ts
7173
- import { defineCommand as defineCommand67 } from "citty";
7255
+ import { defineCommand as defineCommand68 } from "citty";
7174
7256
  registerSchema({
7175
7257
  command: "ads.x.media",
7176
7258
  description: "List media assets in the X Ads media library (images, GIFs, videos). Filter by media-type (IMAGE, GIF, VIDEO).",
@@ -7180,7 +7262,7 @@ registerSchema({
7180
7262
  "no-cache": { type: "boolean", description: "Skip cache", required: false }
7181
7263
  }
7182
7264
  });
7183
- var mediaCommand = defineCommand67({
7265
+ var mediaCommand = defineCommand68({
7184
7266
  meta: {
7185
7267
  name: "media",
7186
7268
  description: `List media assets in the X Ads media library.
@@ -7232,7 +7314,7 @@ Examples:
7232
7314
  });
7233
7315
 
7234
7316
  // src/commands/ads/x/promoted-tweets.ts
7235
- import { defineCommand as defineCommand68 } from "citty";
7317
+ import { defineCommand as defineCommand69 } from "citty";
7236
7318
  registerSchema({
7237
7319
  command: "ads.x.promotedTweets",
7238
7320
  description: "List promoted tweets for an X Ads account. Returns id, line_item_id, tweet_id, approval_status. Filter by line-item-ids (CSV).",
@@ -7243,7 +7325,7 @@ registerSchema({
7243
7325
  "no-cache": { type: "boolean", description: "Skip cache", required: false }
7244
7326
  }
7245
7327
  });
7246
- var promotedTweetsCommand = defineCommand68({
7328
+ var promotedTweetsCommand = defineCommand69({
7247
7329
  meta: {
7248
7330
  name: "promoted-tweets",
7249
7331
  description: `List X Ads promoted tweets.
@@ -7297,11 +7379,11 @@ Examples:
7297
7379
  });
7298
7380
 
7299
7381
  // src/commands/ads/x/stats/index.ts
7300
- import { defineCommand as defineCommand73 } from "citty";
7382
+ import { defineCommand as defineCommand74 } from "citty";
7301
7383
 
7302
7384
  // src/commands/ads/x/stats/job.ts
7303
7385
  import { gunzipSync } from "zlib";
7304
- import { defineCommand as defineCommand69 } from "citty";
7386
+ import { defineCommand as defineCommand70 } from "citty";
7305
7387
  var POLL_INTERVAL_MS2 = 1e4;
7306
7388
  var DEADLINE_MS = 12 * 60 * 1e3;
7307
7389
  var RESULT_CACHE_TTL_MS = 6 * 60 * 60 * 1e3;
@@ -7371,7 +7453,7 @@ async function pollUntilDone(accountId, jobId) {
7371
7453
  function buildCacheKey(body) {
7372
7454
  return `stats-job:${JSON.stringify(body)}`;
7373
7455
  }
7374
- var statsJobCommand = defineCommand69({
7456
+ var statsJobCommand = defineCommand70({
7375
7457
  meta: {
7376
7458
  name: "job",
7377
7459
  description: `Async X Ads stats job, sync from the CLI's perspective. Creates \u2192 polls \u2192 downloads \u2192 returns.
@@ -7476,7 +7558,7 @@ For fine-grained control (don't wait, poll yourself), use:
7476
7558
  });
7477
7559
 
7478
7560
  // src/commands/ads/x/stats/job-create.ts
7479
- import { defineCommand as defineCommand70 } from "citty";
7561
+ import { defineCommand as defineCommand71 } from "citty";
7480
7562
  registerSchema({
7481
7563
  command: "ads.x.statsJobCreate",
7482
7564
  description: "Create an asynchronous X Ads stats job (range up to 90 days non-segmented, 45 days segmented). Returns a job id; poll with `stats job-status`. Times must be ISO 8601 hour-aligned.",
@@ -7499,7 +7581,7 @@ function parseCsv3(v) {
7499
7581
  const parts = v.split(",").map((s) => s.trim()).filter(Boolean);
7500
7582
  return parts.length > 0 ? parts : void 0;
7501
7583
  }
7502
- var statsJobCreateCommand = defineCommand70({
7584
+ var statsJobCreateCommand = defineCommand71({
7503
7585
  meta: {
7504
7586
  name: "job-create",
7505
7587
  description: `Create an async X Ads stats job (up to 90 days, supports segmentation).
@@ -7562,7 +7644,7 @@ Examples:
7562
7644
  });
7563
7645
 
7564
7646
  // src/commands/ads/x/stats/job-status.ts
7565
- import { defineCommand as defineCommand71 } from "citty";
7647
+ import { defineCommand as defineCommand72 } from "citty";
7566
7648
  registerSchema({
7567
7649
  command: "ads.x.statsJobStatus",
7568
7650
  description: "Check the status of one or more X Ads stats jobs. Returns status (PROCESSING|SUCCESS|FAILED) and a downloadable url when SUCCESS. Pass --job-id or --job-ids (CSV).",
@@ -7572,7 +7654,7 @@ registerSchema({
7572
7654
  "job-ids": { type: "string", description: "CSV of job IDs", required: false }
7573
7655
  }
7574
7656
  });
7575
- var statsJobStatusCommand = defineCommand71({
7657
+ var statsJobStatusCommand = defineCommand72({
7576
7658
  meta: {
7577
7659
  name: "job-status",
7578
7660
  description: `Poll the status of an async X Ads stats job.
@@ -7613,7 +7695,7 @@ Examples:
7613
7695
  });
7614
7696
 
7615
7697
  // src/commands/ads/x/stats/sync.ts
7616
- import { defineCommand as defineCommand72 } from "citty";
7698
+ import { defineCommand as defineCommand73 } from "citty";
7617
7699
 
7618
7700
  // src/commands/ads/x/presets.ts
7619
7701
  var X_STATS_PRESETS = [
@@ -7772,7 +7854,7 @@ async function runSync(args, q) {
7772
7854
  process.exit(1);
7773
7855
  }
7774
7856
  }
7775
- var statsSyncCommand = defineCommand72({
7857
+ var statsSyncCommand = defineCommand73({
7776
7858
  meta: {
7777
7859
  name: "sync",
7778
7860
  description: `Synchronous X Ads analytics (max 7-day window).
@@ -7815,7 +7897,7 @@ Examples:
7815
7897
  });
7816
7898
 
7817
7899
  // src/commands/ads/x/stats/index.ts
7818
- var statsCommand = defineCommand73({
7900
+ var statsCommand = defineCommand74({
7819
7901
  meta: {
7820
7902
  name: "stats",
7821
7903
  description: `X Ads analytics. Sync (\u22647 days, no segmentation) or async jobs (\u226490 days, segmentable).
@@ -7843,7 +7925,7 @@ Examples:
7843
7925
  });
7844
7926
 
7845
7927
  // src/commands/ads/x/targeting-constants.ts
7846
- import { defineCommand as defineCommand74 } from "citty";
7928
+ import { defineCommand as defineCommand75 } from "citty";
7847
7929
  var ALLOWED_CONSTANTS = [
7848
7930
  "locations",
7849
7931
  "interests",
@@ -7871,7 +7953,7 @@ registerSchema({
7871
7953
  "no-cache": { type: "boolean", description: "Skip cache", required: false }
7872
7954
  }
7873
7955
  });
7874
- var targetingConstantsCommand = defineCommand74({
7956
+ var targetingConstantsCommand = defineCommand75({
7875
7957
  meta: {
7876
7958
  name: "targeting-constants",
7877
7959
  description: `Lookup X Ads targeting constants.
@@ -7921,7 +8003,7 @@ Examples:
7921
8003
  });
7922
8004
 
7923
8005
  // src/commands/ads/x/targeting-criteria.ts
7924
- import { defineCommand as defineCommand75 } from "citty";
8006
+ import { defineCommand as defineCommand76 } from "citty";
7925
8007
  registerSchema({
7926
8008
  command: "ads.x.targetingCriteria",
7927
8009
  description: "List targeting criteria attached to line items in an X Ads account. Returns targeting_type, targeting_value, name, operator_type per criterion. Filter by line-item-ids.",
@@ -7931,7 +8013,7 @@ registerSchema({
7931
8013
  "no-cache": { type: "boolean", description: "Skip cache", required: false }
7932
8014
  }
7933
8015
  });
7934
- var targetingCriteriaCommand = defineCommand75({
8016
+ var targetingCriteriaCommand = defineCommand76({
7935
8017
  meta: {
7936
8018
  name: "targeting-criteria",
7937
8019
  description: `List targeting criteria attached to line items.
@@ -7982,7 +8064,7 @@ Examples:
7982
8064
  });
7983
8065
 
7984
8066
  // src/commands/ads/x/index.ts
7985
- var xCommand = defineCommand76({
8067
+ var xCommand = defineCommand77({
7986
8068
  meta: {
7987
8069
  name: "x",
7988
8070
  description: `X (Twitter) Ads commands. Read campaigns, line items, promoted tweets, creatives, audiences, and analytics.
@@ -8020,7 +8102,7 @@ The CLI auto-detects --account-id when exactly one X Ads account is connected, o
8020
8102
  });
8021
8103
 
8022
8104
  // src/commands/ads/index.ts
8023
- var adsCommand = defineCommand77({
8105
+ var adsCommand = defineCommand78({
8024
8106
  meta: {
8025
8107
  name: "ads",
8026
8108
  description: `Ad platform commands. Each platform exposes its own native command surface \u2014 no forced parity.
@@ -8053,8 +8135,8 @@ Examples:
8053
8135
  import { defineCommand as defineCommand86 } from "citty";
8054
8136
 
8055
8137
  // src/commands/canvas/catalog.ts
8056
- import { defineCommand as defineCommand78 } from "citty";
8057
- var catalogCommand = defineCommand78({
8138
+ import { defineCommand as defineCommand79 } from "citty";
8139
+ var catalogCommand = defineCommand79({
8058
8140
  meta: {
8059
8141
  name: "catalog",
8060
8142
  description: "Print the agent-facing node catalog (JSON Schema). Includes every registered node grouped by category."
@@ -8066,293 +8148,76 @@ var catalogCommand = defineCommand78({
8066
8148
  }
8067
8149
  });
8068
8150
 
8069
- // src/commands/canvas/gallery.ts
8070
- import { readdir, readFile } from "fs/promises";
8151
+ // src/commands/canvas/inspect.ts
8152
+ import { execFile } from "child_process";
8153
+ import { readdir, readFile, stat } from "fs/promises";
8071
8154
  import path from "path";
8072
- import { defineCommand as defineCommand79 } from "citty";
8073
-
8074
- // src/engine/gallery/descriptor.ts
8075
- var KNOWN_RATIOS = [
8076
- ["9:16", 9 / 16],
8077
- ["4:5", 4 / 5],
8078
- ["1:1", 1],
8079
- ["1.91:1", 1.91],
8080
- ["16:9", 16 / 9],
8081
- ["4:1", 4]
8082
- ];
8083
- var RATIO_TOLERANCE = 0.06;
8084
- function aspectLabel(width, height) {
8085
- if (!width || !height) {
8086
- return "other";
8087
- }
8088
- const ratio = width / height;
8089
- let best = "other";
8090
- let bestErr = Number.POSITIVE_INFINITY;
8091
- for (const [label, value] of KNOWN_RATIOS) {
8092
- const err = Math.abs(ratio - value) / value;
8093
- if (err < bestErr) {
8094
- bestErr = err;
8095
- best = label;
8155
+ import { promisify } from "util";
8156
+ import { defineCommand as defineCommand80 } from "citty";
8157
+ var execFileAsync = promisify(execFile);
8158
+ var inspectCommand = defineCommand80({
8159
+ meta: {
8160
+ name: "inspect",
8161
+ description: "Dump a one-page summary of a canvas run: per-node duration + cache status, list of output files in the run dir, and optionally three thumbnail frames per video output. Pass either a run_id (resolved against --outputs-dir) or an absolute run directory."
8162
+ },
8163
+ args: {
8164
+ run: { type: "positional", required: true, description: "Run id (e.g. r_01K...) or absolute run directory" },
8165
+ "outputs-dir": { type: "string", description: "Per-run outputs root (default ./canvas)" },
8166
+ thumbnails: {
8167
+ type: "boolean",
8168
+ description: "Extract start/middle/end frames from every video output (requires ffmpeg)"
8096
8169
  }
8170
+ },
8171
+ async run({ args }) {
8172
+ const outputsDir = path.resolve(String(args["outputs-dir"] ?? "canvas"));
8173
+ const runArg = String(args.run);
8174
+ const runDir = await resolveRunDir(runArg, outputsDir);
8175
+ const manifest = await loadManifest(runDir);
8176
+ const files = await listRunFiles(runDir);
8177
+ const videos = files.filter((f) => isVideoFile(f.path));
8178
+ let thumbnails = [];
8179
+ if (args.thumbnails) {
8180
+ thumbnails = await extractThumbnails(videos.map((v) => v.path));
8181
+ }
8182
+ const summary = {
8183
+ ok: true,
8184
+ run_id: manifest.run_id ?? path.basename(runDir),
8185
+ run_dir: runDir,
8186
+ stats: manifest.stats ?? null,
8187
+ output: manifest.output ?? null,
8188
+ node_runs: manifest.node_runs ?? [],
8189
+ files: files.map((f) => ({ name: f.name, path: f.path, size_bytes: f.size })),
8190
+ thumbnails
8191
+ };
8192
+ process.stdout.write(`${JSON.stringify(summary, null, 2)}
8193
+ `);
8097
8194
  }
8098
- return bestErr <= RATIO_TOLERANCE ? best : `${width}x${height}`;
8099
- }
8100
- function visualRef(value) {
8101
- const parsed = AssetRef.safeParse(value);
8102
- if (!parsed.success) {
8103
- return null;
8104
- }
8105
- if (parsed.data.kind !== "image" && parsed.data.kind !== "video") {
8106
- return null;
8195
+ });
8196
+ async function resolveRunDir(run, outputsDir) {
8197
+ if (path.isAbsolute(run)) {
8198
+ const s2 = await stat(run).catch(() => null);
8199
+ if (s2?.isDirectory()) return run;
8200
+ throw new Error(`inspect: ${run} is not a directory`);
8107
8201
  }
8108
- return parsed.data;
8109
- }
8110
- function deliverableFor(ref, stem, resolveLocal) {
8111
- const width = "width" in ref ? ref.width : void 0;
8112
- const height = "height" in ref ? ref.height : void 0;
8113
- return {
8114
- kind: ref.kind,
8115
- format: aspectLabel(width, height),
8116
- // Remote-node outputs already carry a public R2 url; local composites are
8117
- // resolved against the mounted run dir's public base.
8118
- url: ref.url ?? resolveLocal(`${stem}.${extForMime(ref.mime)}`),
8119
- width,
8120
- height,
8121
- label: stem
8122
- };
8202
+ const candidate = path.join(outputsDir, run);
8203
+ const s = await stat(candidate).catch(() => null);
8204
+ if (s?.isDirectory()) return candidate;
8205
+ throw new Error(`inspect: no run directory at ${candidate}`);
8123
8206
  }
8124
- function deliverablesFromOutput(output, resolveLocal) {
8125
- if (Array.isArray(output)) {
8126
- const out = [];
8127
- output.forEach((entry, i) => {
8128
- const ref2 = visualRef(entry);
8129
- if (ref2) {
8130
- out.push(deliverableFor(ref2, `_final__${i}`, resolveLocal));
8131
- }
8132
- });
8133
- return out;
8134
- }
8135
- const ref = visualRef(output);
8136
- return ref ? [deliverableFor(ref, "_final", resolveLocal)] : [];
8137
- }
8138
- function buildGeneration(runId, manifest, resolveLocal) {
8139
- const m = manifest ?? {};
8140
- const credits = typeof m.stats?.total_credits === "number" ? m.stats.total_credits : 0;
8141
- return {
8142
- runId,
8143
- createdAt: typeof m.completed_at === "number" ? m.completed_at : 0,
8144
- credits,
8145
- deliverables: deliverablesFromOutput(m.output, resolveLocal)
8146
- };
8147
- }
8148
- function buildGalleryDescriptor(input) {
8149
- const generations = [...input.generations].sort((a, b) => b.createdAt - a.createdAt);
8150
- return {
8151
- slug: input.slug,
8152
- title: input.definition.title,
8153
- platform: input.definition.platform,
8154
- status: input.definition.status,
8155
- reference: input.definition.reference,
8156
- selectedRun: input.definition.selectedRun,
8157
- generations
8158
- };
8159
- }
8160
- function titleFromSlug(slug) {
8161
- return slug.split(/[-_/]+/).filter(Boolean).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join(" ");
8162
- }
8163
- function stripQuotes(raw) {
8164
- const trimmed = raw.trim();
8165
- if (trimmed.startsWith('"') && trimmed.endsWith('"') || trimmed.startsWith("'") && trimmed.endsWith("'")) {
8166
- return trimmed.slice(1, -1);
8167
- }
8168
- return trimmed;
8169
- }
8170
- function parseInlineList(raw) {
8171
- return raw.slice(1, -1).split(",").map((item) => stripQuotes(item)).filter(Boolean);
8172
- }
8173
- function parseFrontmatter(markdown) {
8174
- const match = markdown.match(/^---\r?\n([\s\S]*?)\r?\n---/);
8175
- const block = match?.[1];
8176
- if (!block) {
8177
- return {};
8178
- }
8179
- const out = {};
8180
- let listKey = null;
8181
- for (const line of block.split(/\r?\n/)) {
8182
- const item = line.match(/^\s+-\s+(.*)$/)?.[1];
8183
- if (listKey && item !== void 0) {
8184
- out[listKey].push(stripQuotes(item));
8185
- continue;
8186
- }
8187
- const kv = line.match(/^([A-Za-z0-9_]+):\s*(.*)$/);
8188
- if (!kv?.[1]) {
8189
- continue;
8190
- }
8191
- listKey = null;
8192
- const key = kv[1];
8193
- const value = (kv[2] ?? "").trim();
8194
- if (value === "") {
8195
- out[key] = [];
8196
- listKey = key;
8197
- } else if (value.startsWith("[") && value.endsWith("]")) {
8198
- out[key] = parseInlineList(value);
8199
- } else {
8200
- out[key] = stripQuotes(value);
8201
- }
8202
- }
8203
- return out;
8204
- }
8205
- function asString(value) {
8206
- if (typeof value === "string" && value.length > 0) {
8207
- return value;
8208
- }
8209
- return void 0;
8210
- }
8211
- function asList(value) {
8212
- if (Array.isArray(value)) {
8213
- return value;
8214
- }
8215
- return typeof value === "string" && value.length > 0 ? [value] : [];
8216
- }
8217
- function parseCreativeDefinition(markdown, slug) {
8218
- const fm = parseFrontmatter(markdown);
8219
- return {
8220
- title: asString(fm.title) ?? titleFromSlug(slug),
8221
- platform: asList(fm.platform),
8222
- formats: asList(fm.formats),
8223
- status: asString(fm.status) ?? "draft",
8224
- reference: asString(fm.reference),
8225
- selectedRun: asString(fm.selected_run)
8226
- };
8227
- }
8228
-
8229
- // src/commands/canvas/gallery.ts
8230
- async function readJson(file) {
8231
- try {
8232
- return JSON.parse(await readFile(file, "utf8"));
8233
- } catch {
8234
- return null;
8235
- }
8236
- }
8237
- async function listRunDirs(runsDir) {
8238
- try {
8239
- const entries = await readdir(runsDir, { withFileTypes: true });
8240
- return entries.filter((e) => e.isDirectory()).map((e) => e.name);
8241
- } catch {
8242
- return [];
8243
- }
8244
- }
8245
- var galleryCommand = defineCommand79({
8246
- meta: {
8247
- name: "gallery",
8248
- description: "Read a creative's _definition.md + every persisted run manifest and emit the gallery descriptor (JSON) the dashboard renders."
8249
- },
8250
- args: {
8251
- dir: { type: "positional", required: true, description: "Creative folder, e.g. src/creatives/<slug>" },
8252
- "workspace-dir": { type: "string", description: "R2-mounted workspace root (default ./.creatives-workspace)" },
8253
- "public-url": { type: "string", description: "R2 public base (default $R2_PUBLIC_URL)" },
8254
- "company-id": { type: "string", description: "Company id for the R2 prefix (default $BAKER_COMPANY_ID)" }
8255
- },
8256
- async run({ args }) {
8257
- const creativeDir = path.resolve(String(args.dir));
8258
- const slug = path.basename(creativeDir);
8259
- const workspaceDir = path.resolve(String(args["workspace-dir"] ?? ".creatives-workspace"));
8260
- const runsDir = path.join(workspaceDir, slug, "runs");
8261
- const publicUrl = (args["public-url"] ?? runtimeEnvVar("R2_PUBLIC_URL") ?? "").replace(/\/+$/, "");
8262
- const companyId = String(args["company-id"] ?? runtimeEnvVar("BAKER_COMPANY_ID") ?? "");
8263
- const definitionPath = path.join(creativeDir, "_definition.md");
8264
- let definitionMd = "";
8265
- try {
8266
- definitionMd = await readFile(definitionPath, "utf8");
8267
- } catch {
8268
- }
8269
- const definition = parseCreativeDefinition(definitionMd, slug);
8270
- const generations = [];
8271
- for (const runId of await listRunDirs(runsDir)) {
8272
- const manifest = await readJson(path.join(runsDir, runId, "manifest.json"));
8273
- if (!manifest) {
8274
- continue;
8275
- }
8276
- const runDir = path.join(runsDir, runId);
8277
- const resolveLocal = (filename) => publicUrl && companyId ? `${publicUrl}/creatives/${companyId}/${slug}/runs/${runId}/${filename}` : path.join(runDir, filename);
8278
- generations.push(buildGeneration(runId, manifest, resolveLocal));
8279
- }
8280
- const descriptor = buildGalleryDescriptor({ slug, definition, generations });
8281
- process.stdout.write(`${JSON.stringify({ ok: true, descriptor }, null, 2)}
8282
- `);
8283
- }
8284
- });
8285
-
8286
- // src/commands/canvas/inspect.ts
8287
- import { execFile } from "child_process";
8288
- import { readdir as readdir2, readFile as readFile2, stat } from "fs/promises";
8289
- import path2 from "path";
8290
- import { promisify } from "util";
8291
- import { defineCommand as defineCommand80 } from "citty";
8292
- var execFileAsync = promisify(execFile);
8293
- var inspectCommand = defineCommand80({
8294
- meta: {
8295
- name: "inspect",
8296
- description: "Dump a one-page summary of a canvas run: per-node duration + cache status, list of output files in the run dir, and optionally three thumbnail frames per video output. Pass either a run_id (resolved against --outputs-dir) or an absolute run directory."
8297
- },
8298
- args: {
8299
- run: { type: "positional", required: true, description: "Run id (e.g. r_01K...) or absolute run directory" },
8300
- "outputs-dir": { type: "string", description: "Per-run outputs root (default ./canvas)" },
8301
- thumbnails: {
8302
- type: "boolean",
8303
- description: "Extract start/middle/end frames from every video output (requires ffmpeg)"
8304
- }
8305
- },
8306
- async run({ args }) {
8307
- const outputsDir = path2.resolve(String(args["outputs-dir"] ?? "canvas"));
8308
- const runArg = String(args.run);
8309
- const runDir = await resolveRunDir(runArg, outputsDir);
8310
- const manifest = await loadManifest(runDir);
8311
- const files = await listRunFiles(runDir);
8312
- const videos = files.filter((f) => isVideoFile(f.path));
8313
- let thumbnails = [];
8314
- if (args.thumbnails) {
8315
- thumbnails = await extractThumbnails(videos.map((v) => v.path));
8316
- }
8317
- const summary = {
8318
- ok: true,
8319
- run_id: manifest.run_id ?? path2.basename(runDir),
8320
- run_dir: runDir,
8321
- stats: manifest.stats ?? null,
8322
- output: manifest.output ?? null,
8323
- node_runs: manifest.node_runs ?? [],
8324
- files: files.map((f) => ({ name: f.name, path: f.path, size_bytes: f.size })),
8325
- thumbnails
8326
- };
8327
- process.stdout.write(`${JSON.stringify(summary, null, 2)}
8328
- `);
8329
- }
8330
- });
8331
- async function resolveRunDir(run, outputsDir) {
8332
- if (path2.isAbsolute(run)) {
8333
- const s2 = await stat(run).catch(() => null);
8334
- if (s2?.isDirectory()) return run;
8335
- throw new Error(`inspect: ${run} is not a directory`);
8336
- }
8337
- const candidate = path2.join(outputsDir, run);
8338
- const s = await stat(candidate).catch(() => null);
8339
- if (s?.isDirectory()) return candidate;
8340
- throw new Error(`inspect: no run directory at ${candidate}`);
8341
- }
8342
- async function loadManifest(runDir) {
8343
- const manifestPath = path2.join(runDir, "manifest.json");
8344
- try {
8345
- const raw = await readFile2(manifestPath, "utf-8");
8346
- return JSON.parse(raw);
8347
- } catch {
8348
- return {};
8207
+ async function loadManifest(runDir) {
8208
+ const manifestPath = path.join(runDir, "manifest.json");
8209
+ try {
8210
+ const raw = await readFile(manifestPath, "utf-8");
8211
+ return JSON.parse(raw);
8212
+ } catch {
8213
+ return {};
8349
8214
  }
8350
8215
  }
8351
8216
  async function listRunFiles(runDir) {
8352
8217
  const out = [];
8353
- const names = await readdir2(runDir);
8218
+ const names = await readdir(runDir);
8354
8219
  for (const name of names) {
8355
- const abs = path2.join(runDir, name);
8220
+ const abs = path.join(runDir, name);
8356
8221
  const s = await stat(abs).catch(() => null);
8357
8222
  if (!s?.isFile()) continue;
8358
8223
  out.push({ name, path: abs, size: s.size });
@@ -8397,8 +8262,8 @@ async function probeDuration(filePath) {
8397
8262
  }
8398
8263
 
8399
8264
  // src/commands/canvas/run.ts
8400
- import { readFile as readFile3 } from "fs/promises";
8401
- import path5 from "path";
8265
+ import { readFile as readFile2 } from "fs/promises";
8266
+ import path4 from "path";
8402
8267
  import { defineCommand as defineCommand81 } from "citty";
8403
8268
 
8404
8269
  // src/commands/canvas/placeholders.ts
@@ -8418,7 +8283,7 @@ function unsuppliedPlaceholderAssets(canvas) {
8418
8283
  }
8419
8284
 
8420
8285
  // src/commands/canvas/resolve-paths.ts
8421
- import path3 from "path";
8286
+ import path2 from "path";
8422
8287
  function resolveRelativeCanvasPaths(canvas, baseDir) {
8423
8288
  if (!canvas || typeof canvas !== "object") return canvas;
8424
8289
  const c = canvas;
@@ -8431,37 +8296,37 @@ function resolveNode(node, baseDir) {
8431
8296
  const params = n.params;
8432
8297
  if (!params || typeof params !== "object") return node;
8433
8298
  if (n.type === "ingest" && params.source === "path" && isResolvableRelative(params.path)) {
8434
- return { ...node, params: { ...params, path: path3.resolve(baseDir, params.path) } };
8299
+ return { ...node, params: { ...params, path: path2.resolve(baseDir, params.path) } };
8435
8300
  }
8436
8301
  if (n.type === "hyperframe_render" && isResolvableRelative(params.composition)) {
8437
- return { ...node, params: { ...params, composition: path3.resolve(baseDir, params.composition) } };
8302
+ return { ...node, params: { ...params, composition: path2.resolve(baseDir, params.composition) } };
8438
8303
  }
8439
8304
  return node;
8440
8305
  }
8441
8306
  function isResolvableRelative(value) {
8442
- return typeof value === "string" && value.length > 0 && !value.includes("[TODO") && !path3.isAbsolute(value);
8307
+ return typeof value === "string" && value.length > 0 && !value.includes("[TODO") && !path2.isAbsolute(value);
8443
8308
  }
8444
8309
 
8445
8310
  // src/commands/canvas/run-retention.ts
8446
8311
  import { rm } from "fs/promises";
8447
- import path4 from "path";
8312
+ import path3 from "path";
8448
8313
  function runDirsToPrune(entries, keep, currentRunId) {
8449
8314
  const runs = entries.filter((e) => /^r_[0-9A-Za-z]+$/.test(e) && e !== currentRunId).sort();
8450
8315
  if (keep <= 0) return runs;
8451
8316
  return runs.slice(0, Math.max(0, runs.length - keep));
8452
8317
  }
8453
8318
  async function pruneOldRuns(outputsDir, keep, currentRunId, log) {
8454
- const { readdir: readdir4 } = await import("fs/promises");
8319
+ const { readdir: readdir3 } = await import("fs/promises");
8455
8320
  let entries;
8456
8321
  try {
8457
- entries = await readdir4(outputsDir);
8322
+ entries = await readdir3(outputsDir);
8458
8323
  } catch {
8459
8324
  return;
8460
8325
  }
8461
8326
  const toPrune = runDirsToPrune(entries, keep, currentRunId);
8462
8327
  if (toPrune.length === 0) return;
8463
8328
  for (const dir of toPrune) {
8464
- await rm(path4.join(outputsDir, dir), { recursive: true, force: true }).catch(
8329
+ await rm(path3.join(outputsDir, dir), { recursive: true, force: true }).catch(
8465
8330
  (e) => log(`[prune ] could not remove ${dir}: ${e.message}`)
8466
8331
  );
8467
8332
  }
@@ -8483,8 +8348,8 @@ var runCommand = defineCommand81({
8483
8348
  }
8484
8349
  },
8485
8350
  async run({ args }) {
8486
- const filePath = path5.resolve(String(args.file));
8487
- const raw = await readFile3(filePath, "utf8");
8351
+ const filePath = path4.resolve(String(args.file));
8352
+ const raw = await readFile2(filePath, "utf8");
8488
8353
  let parsed;
8489
8354
  try {
8490
8355
  parsed = JSON.parse(raw);
@@ -8494,7 +8359,7 @@ var runCommand = defineCommand81({
8494
8359
  `);
8495
8360
  process.exit(2);
8496
8361
  }
8497
- parsed = resolveRelativeCanvasPaths(parsed, path5.dirname(filePath));
8362
+ parsed = resolveRelativeCanvasPaths(parsed, path4.dirname(filePath));
8498
8363
  const pending = unsuppliedPlaceholderAssets(parsed);
8499
8364
  if (pending.length > 0) {
8500
8365
  process.stderr.write(
@@ -8528,7 +8393,7 @@ var runCommand = defineCommand81({
8528
8393
  });
8529
8394
  const keepRuns = args["keep-runs"] !== void 0 ? Number(args["keep-runs"]) : void 0;
8530
8395
  if (keepRuns !== void 0 && Number.isFinite(keepRuns)) {
8531
- const outputsDir = args["outputs-dir"] ? path5.resolve(String(args["outputs-dir"])) : path5.resolve("canvas");
8396
+ const outputsDir = args["outputs-dir"] ? path4.resolve(String(args["outputs-dir"])) : path4.resolve("canvas");
8532
8397
  await pruneOldRuns(outputsDir, keepRuns, result.run_id, (line) => process.stdout.write(`${line}
8533
8398
  `));
8534
8399
  }
@@ -8563,8 +8428,8 @@ var runCommand = defineCommand81({
8563
8428
  });
8564
8429
 
8565
8430
  // src/commands/canvas/scaffold-static-ad.ts
8566
- import { readFile as readFile4, writeFile } from "fs/promises";
8567
- import path6 from "path";
8431
+ import { readFile as readFile3, writeFile } from "fs/promises";
8432
+ import path5 from "path";
8568
8433
  import { defineCommand as defineCommand82 } from "citty";
8569
8434
 
8570
8435
  // src/engine/scaffold/staticAd.ts
@@ -8778,7 +8643,7 @@ var SELECT_SYSTEM = 'You identify the MAIN, identity-critical visual elements of
8778
8643
  var SELECT_PROMPT = 'AD BLUEPRINT (from image_describe):\n{{blueprint}}\n\nFrom this blueprint, list ONLY the elements that are prominent, important, and identity-bearing \u2014 the ones a reproduction must ground in a real asset:\n- the brand logo/wordmark (from brands_logos with function_in_image = advertiser_brand) -> type "logo"\n- trust/rating/certification/app-store/review badges (brands_logos with function_in_image = trust_badge | review_platform | certification_or_seal | app_store_badge | payment_method) -> type "badge"\n- a showcased/hero product or package (a foreground entry in subjects that the ad is selling) -> type "product"\n- a foreground person whose identity matters (from people) -> type "person"\n- a foreground animal/character with a specific expression (from subjects) -> type "animal"\n\nDROP background extras, decorative props, generic scenery, and anything small or incidental. Keep at most ~6. If there are none, return an empty list.\n\nFor each kept element return: { "type": one of logo|product|person|animal|badge, "label": a short UPPER_SNAKE_CASE name (e.g. LOGO, PRODUCT, HERO_DOG, TRUSTPILOT), "description": a concrete reusable description to source/shoot the real asset (include the exact expression for a living subject), "expression": the facial expression for a living subject or null, "reason": why it is identity-critical, "locator": the blueprint entry this element came from as { "collection": one of "subjects" | "people" | "brands_logos", "index": its 0-based position in that array } (people -> people; logos/badges -> brands_logos; products/animals/objects -> subjects). Output ONLY the JSON object.';
8779
8644
  async function loadAssetText(ref, label) {
8780
8645
  const r = ref;
8781
- if (typeof r?.path === "string") return readFile4(r.path, "utf8");
8646
+ if (typeof r?.path === "string") return readFile3(r.path, "utf8");
8782
8647
  if (typeof r?.url === "string") {
8783
8648
  const res = await fetch(r.url);
8784
8649
  if (!res.ok) throw new Error(`failed to fetch ${label} (${res.status})`);
@@ -8901,10 +8766,10 @@ var scaffoldStaticAdCommand = defineCommand82({
8901
8766
  "skip-font": { type: "boolean", description: "Skip the brand-font \u2192 type-specimen slot" }
8902
8767
  },
8903
8768
  async run({ args }) {
8904
- const imagePath = path6.resolve(String(args.file));
8905
- const outPath = args.out ? path6.resolve(String(args.out)) : path6.join(path6.dirname(imagePath), "static-ad.canvas.json");
8906
- const outDir = path6.dirname(outPath);
8907
- const blueprintPath = path6.join(outDir, "prompt.json");
8769
+ const imagePath = path5.resolve(String(args.file));
8770
+ const outPath = args.out ? path5.resolve(String(args.out)) : path5.join(path5.dirname(imagePath), "static-ad.canvas.json");
8771
+ const outDir = path5.dirname(outPath);
8772
+ const blueprintPath = path5.join(outDir, "prompt.json");
8908
8773
  const { describeModel, selectModel, layoutModel, genModel } = resolveModels(args);
8909
8774
  const describeCanvas = buildDescribeCanvas(
8910
8775
  imagePath,
@@ -8961,7 +8826,7 @@ var scaffoldStaticAdCommand = defineCommand82({
8961
8826
  run_estimated_credits: validation.estimatedCredits
8962
8827
  },
8963
8828
  checklist: {
8964
- edit_prompt: `Edit ${path6.basename(blueprintPath)} \u2014 it is the blueprint generated from your image; rewrite it into the ad you want (palette, copy, claims, subjects). It feeds the generator directly.`,
8829
+ edit_prompt: `Edit ${path5.basename(blueprintPath)} \u2014 it is the blueprint generated from your image; rewrite it into the ad you want (palette, copy, claims, subjects). It feeds the generator directly.`,
8965
8830
  assets_to_supply: report.elements,
8966
8831
  font_slot: report.includes_font ? "Drop a brand font at the [TODO] brandfont path, or delete the brandfont + type_ref nodes to skip it." : "skipped (--skip-font)",
8967
8832
  note: "Replace every [TODO] ingest path with a real file, then `baker canvas validate` and `baker canvas run`. Running generates a billed image \u2014 it is not free."
@@ -8976,13 +8841,13 @@ var scaffoldStaticAdCommand = defineCommand82({
8976
8841
  });
8977
8842
 
8978
8843
  // src/commands/canvas/scaffold-video.ts
8979
- import { cp, mkdir, readFile as readFile7, writeFile as writeFile2 } from "fs/promises";
8980
- import path9 from "path";
8844
+ import { cp, mkdir, readFile as readFile6, writeFile as writeFile2 } from "fs/promises";
8845
+ import path8 from "path";
8981
8846
  import { defineCommand as defineCommand83 } from "citty";
8982
8847
 
8983
8848
  // src/engine/nodes/local/lib/sceneDetect.ts
8984
8849
  import { execFile as execFile2 } from "child_process";
8985
- import { mkdtemp, readdir as readdir3, readFile as readFile5, rm as rm2 } from "fs/promises";
8850
+ import { mkdtemp, readdir as readdir2, readFile as readFile4, rm as rm2 } from "fs/promises";
8986
8851
  import { tmpdir } from "os";
8987
8852
  import { join as join2 } from "path";
8988
8853
  import { promisify as promisify2 } from "util";
@@ -9046,9 +8911,9 @@ async function runSceneDetectOnce(filePath, threshold, minSceneLenS, timeoutMs)
9046
8911
  ],
9047
8912
  { encoding: "utf-8", maxBuffer: 32 * 1024 * 1024, timeout: timeoutMs }
9048
8913
  );
9049
- const csvName = (await readdir3(outDir)).find((f) => f.toLowerCase().endsWith(".csv"));
8914
+ const csvName = (await readdir2(outDir)).find((f) => f.toLowerCase().endsWith(".csv"));
9050
8915
  if (!csvName) return [];
9051
- return parsePySceneDetectCsvCuts(await readFile5(join2(outDir, csvName), "utf-8"));
8916
+ return parsePySceneDetectCsvCuts(await readFile4(join2(outDir, csvName), "utf-8"));
9052
8917
  } finally {
9053
8918
  await rm2(outDir, { recursive: true, force: true });
9054
8919
  }
@@ -11593,23 +11458,23 @@ function videoReport(input, elementsInput) {
11593
11458
 
11594
11459
  // src/commands/canvas/composition-path.ts
11595
11460
  import { existsSync as existsSync3 } from "fs";
11596
- import path7 from "path";
11461
+ import path6 from "path";
11597
11462
  function resolveShippedCanvasDir(name, startDir, exists = existsSync3, maxDepth = 8) {
11598
- const rel = path7.join("canvas", name);
11463
+ const rel = path6.join("canvas", name);
11599
11464
  let dir = startDir;
11600
11465
  for (let i = 0; i < maxDepth; i++) {
11601
- const candidate = path7.join(dir, rel);
11602
- if (exists(path7.join(candidate, "meta.json"))) return candidate;
11603
- const parent = path7.dirname(dir);
11466
+ const candidate = path6.join(dir, rel);
11467
+ if (exists(path6.join(candidate, "meta.json"))) return candidate;
11468
+ const parent = path6.dirname(dir);
11604
11469
  if (parent === dir) break;
11605
11470
  dir = parent;
11606
11471
  }
11607
- return path7.resolve(startDir, "../../../", rel);
11472
+ return path6.resolve(startDir, "../../../", rel);
11608
11473
  }
11609
11474
 
11610
11475
  // src/commands/canvas/gitignore.ts
11611
- import { appendFile, readFile as readFile6 } from "fs/promises";
11612
- import path8 from "path";
11476
+ import { appendFile, readFile as readFile5 } from "fs/promises";
11477
+ import path7 from "path";
11613
11478
  function missingGitignoreEntries(existing, entries) {
11614
11479
  const present = new Set(
11615
11480
  existing.split("\n").map((l) => l.trim().replace(/\/+$/, "")).filter((l) => l.length > 0 && !l.startsWith("#"))
@@ -11617,10 +11482,10 @@ function missingGitignoreEntries(existing, entries) {
11617
11482
  return entries.filter((e) => !present.has(e.trim().replace(/\/+$/, "")));
11618
11483
  }
11619
11484
  async function ensureGitignore(dir, entries) {
11620
- const file = path8.join(dir, ".gitignore");
11485
+ const file = path7.join(dir, ".gitignore");
11621
11486
  let existing;
11622
11487
  try {
11623
- existing = await readFile6(file, "utf8");
11488
+ existing = await readFile5(file, "utf8");
11624
11489
  } catch {
11625
11490
  return;
11626
11491
  }
@@ -11659,7 +11524,7 @@ ONE PERSON, MULTIPLE LOOKS: if a single individual plays MULTIPLE personas or wa
11659
11524
  For each kept element return: { "type": one of person|animal|product|logo|badge|location, "label": a short UPPER_SNAKE_CASE name (e.g. HERO, CREATOR_SKEPTIC, INSURANCE_CARD, LOGO), "description": a concrete reusable description to source/shoot the real asset \u2014 for a person/animal give a NEUTRAL castable role (e.g. "hero pet-owner, woman in her 30s" or "a small beagle"), NOT the original individual's literal face/identity: we RECAST with a FRESH person/animal, so never tell the agent to reuse the original. "expression": a living subject's typical expression or null, "cast_id": the global.cast id if it maps to one else null, "same_as": the label of another element this is the SAME individual as (different wardrobe/persona) else null, "scenes": the 0-based indices of ONLY the scenes where the element is ACTUALLY VISIBLE ON SCREEN \u2014 judged from that scene's start_frame_prompt / end_frame_prompt subjects and its action_detail, NOT from who is merely speaking. A narrator heard over b-roll is NOT present in that b-roll scene; a dog-running cutaway does NOT contain the couch creator just because she talks across it. Do NOT pad the list \u2014 an element wrongly listed in a scene makes the reproduction render the wrong subject there (e.g. the creator appearing in a pure-dog b-roll). When in doubt, leave a scene OUT. Output ONLY the JSON object.`;
11660
11525
  async function loadAssetText2(ref, label) {
11661
11526
  const r = ref;
11662
- if (typeof r?.path === "string") return readFile7(r.path, "utf8");
11527
+ if (typeof r?.path === "string") return readFile6(r.path, "utf8");
11663
11528
  if (typeof r?.url === "string") {
11664
11529
  const res = await fetch(r.url);
11665
11530
  if (!res.ok) throw new Error(`failed to fetch ${label} (${res.status})`);
@@ -11678,7 +11543,7 @@ async function loadTranscriptBestEffort(ref) {
11678
11543
  async function stageCaptions(outDir, transcript) {
11679
11544
  const text = transcript?.trim();
11680
11545
  if (!text || text === "[]") return {};
11681
- const compositionPath = path9.join(outDir, "tiktok-captions-composition");
11546
+ const compositionPath = path8.join(outDir, "tiktok-captions-composition");
11682
11547
  await cp(SHIPPED_CAPTIONS_DIR, compositionPath, { recursive: true });
11683
11548
  return { compositionPath };
11684
11549
  }
@@ -11840,11 +11705,11 @@ var scaffoldVideoCommand = defineCommand83({
11840
11705
  }
11841
11706
  },
11842
11707
  async run({ args }) {
11843
- const videoPath = path9.resolve(String(args.file));
11844
- const base = path9.basename(videoPath, path9.extname(videoPath));
11845
- const outPath = args.out ? path9.resolve(String(args.out)) : path9.join(path9.dirname(videoPath), `${base}.video.canvas.json`);
11846
- const outDir = path9.dirname(outPath);
11847
- const blueprintPath = path9.join(outDir, "prompt.json");
11708
+ const videoPath = path8.resolve(String(args.file));
11709
+ const base = path8.basename(videoPath, path8.extname(videoPath));
11710
+ const outPath = args.out ? path8.resolve(String(args.out)) : path8.join(path8.dirname(videoPath), `${base}.video.canvas.json`);
11711
+ const outDir = path8.dirname(outPath);
11712
+ const blueprintPath = path8.join(outDir, "prompt.json");
11848
11713
  const frames = args.frames === "reuse" ? "reuse" : "generate";
11849
11714
  const maxScenes = args["max-scenes"] ? Number(args["max-scenes"]) : void 0;
11850
11715
  if (Number.isFinite(maxScenes)) {
@@ -11867,11 +11732,11 @@ var scaffoldVideoCommand = defineCommand83({
11867
11732
  const annotated = annotateBlueprintWithElements(blueprint, elements);
11868
11733
  await writeFile2(blueprintPath, `${JSON.stringify(annotated, null, 2)}
11869
11734
  `, "utf8");
11870
- const compositionDest = path9.join(outDir, "video-overlay-composition");
11735
+ const compositionDest = path8.join(outDir, "video-overlay-composition");
11871
11736
  await cp(SHIPPED_COMPOSITION_DIR, compositionDest, { recursive: true });
11872
- const indexPath = path9.join(compositionDest, "index.html");
11737
+ const indexPath = path8.join(compositionDest, "index.html");
11873
11738
  const overlayHtml = buildOverlayHtml(blueprint);
11874
- const indexHtml = await readFile7(indexPath, "utf8");
11739
+ const indexHtml = await readFile6(indexPath, "utf8");
11875
11740
  const injected = indexHtml.replace("<!--OVERLAYS-->", () => overlayHtml);
11876
11741
  if (injected === indexHtml && overlayHtml.trim()) {
11877
11742
  fail2(
@@ -11884,9 +11749,9 @@ var scaffoldVideoCommand = defineCommand83({
11884
11749
  const opts = {
11885
11750
  imageModel,
11886
11751
  videoModel,
11887
- overlayCompositionPath: path9.relative(outDir, compositionDest),
11888
- captionsCompositionPath: captions.compositionPath ? path9.relative(outDir, captions.compositionPath) : void 0,
11889
- blueprintPath: path9.relative(outDir, blueprintPath),
11752
+ overlayCompositionPath: path8.relative(outDir, compositionDest),
11753
+ captionsCompositionPath: captions.compositionPath ? path8.relative(outDir, captions.compositionPath) : void 0,
11754
+ blueprintPath: path8.relative(outDir, blueprintPath),
11890
11755
  frames,
11891
11756
  ambient: Boolean(args.ambient),
11892
11757
  ...args.resolution ? { resolution: String(args.resolution) } : {}
@@ -11927,7 +11792,7 @@ var scaffoldVideoCommand = defineCommand83({
11927
11792
  run_estimated_credits: validation.estimatedCredits
11928
11793
  },
11929
11794
  checklist: {
11930
- edit_prompt: `Edit ${path9.basename(blueprintPath)} \u2014 the blueprint deconstructed from your video; rewrite it into the ad you want (cast, palette, copy, claims). Every scene frame reads it via target_blueprint.`,
11795
+ edit_prompt: `Edit ${path8.basename(blueprintPath)} \u2014 the blueprint deconstructed from your video; rewrite it into the ad you want (cast, palette, copy, claims). Every scene frame reads it via target_blueprint.`,
11931
11796
  recurring_elements_to_supply: report.elements,
11932
11797
  voices_to_confirm: report.dialogue.map((d) => ({
11933
11798
  scene: d.scene,
@@ -11953,8 +11818,8 @@ var scaffoldVideoCommand = defineCommand83({
11953
11818
  });
11954
11819
 
11955
11820
  // src/commands/canvas/set-prompt.ts
11956
- import { readFile as readFile8, writeFile as writeFile3 } from "fs/promises";
11957
- import path10 from "path";
11821
+ import { readFile as readFile7, writeFile as writeFile3 } from "fs/promises";
11822
+ import path9 from "path";
11958
11823
  import { defineCommand as defineCommand84 } from "citty";
11959
11824
  function setNodePrompt(canvas, nodeId, text) {
11960
11825
  const nodes = canvas?.nodes;
@@ -11982,17 +11847,17 @@ var setPromptCommand = defineCommand84({
11982
11847
  "text-file": { type: "string", description: "Read the new prompt from a UTF-8 file (preserves accents/newlines)" }
11983
11848
  },
11984
11849
  async run({ args }) {
11985
- const filePath = path10.resolve(String(args.file));
11850
+ const filePath = path9.resolve(String(args.file));
11986
11851
  let canvas;
11987
11852
  try {
11988
- canvas = JSON.parse(await readFile8(filePath, "utf8"));
11853
+ canvas = JSON.parse(await readFile7(filePath, "utf8"));
11989
11854
  } catch (e) {
11990
11855
  process.stderr.write(`${JSON.stringify({ ok: false, error: { code: "parse", message: String(e) } }, null, 2)}
11991
11856
  `);
11992
11857
  process.exit(2);
11993
11858
  }
11994
11859
  let text;
11995
- if (args["text-file"]) text = await readFile8(path10.resolve(String(args["text-file"])), "utf8");
11860
+ if (args["text-file"]) text = await readFile7(path9.resolve(String(args["text-file"])), "utf8");
11996
11861
  else if (args.text !== void 0) text = String(args.text);
11997
11862
  else {
11998
11863
  process.stderr.write(
@@ -12013,7 +11878,7 @@ var setPromptCommand = defineCommand84({
12013
11878
  process.exit(2);
12014
11879
  return;
12015
11880
  }
12016
- const validation = await validateCanvasDeep(resolveRelativeCanvasPaths(updated, path10.dirname(filePath)), defaultRegistry());
11881
+ const validation = await validateCanvasDeep(resolveRelativeCanvasPaths(updated, path9.dirname(filePath)), defaultRegistry());
12017
11882
  if (!validation.ok) {
12018
11883
  process.stderr.write(`${JSON.stringify({ ok: false, error: { code: "validation", issues: validation.issues } }, null, 2)}
12019
11884
  `);
@@ -12028,8 +11893,8 @@ var setPromptCommand = defineCommand84({
12028
11893
  });
12029
11894
 
12030
11895
  // src/commands/canvas/validate.ts
12031
- import { readFile as readFile9 } from "fs/promises";
12032
- import path11 from "path";
11896
+ import { readFile as readFile8 } from "fs/promises";
11897
+ import path10 from "path";
12033
11898
  import { defineCommand as defineCommand85 } from "citty";
12034
11899
  var validateCommand = defineCommand85({
12035
11900
  meta: {
@@ -12038,8 +11903,8 @@ var validateCommand = defineCommand85({
12038
11903
  },
12039
11904
  args: { file: { type: "positional", required: true, description: "Path to canvas JSON" } },
12040
11905
  async run({ args }) {
12041
- const filePath = path11.resolve(String(args.file));
12042
- const raw = await readFile9(filePath, "utf8");
11906
+ const filePath = path10.resolve(String(args.file));
11907
+ const raw = await readFile8(filePath, "utf8");
12043
11908
  let parsed;
12044
11909
  try {
12045
11910
  parsed = JSON.parse(raw);
@@ -12049,7 +11914,7 @@ var validateCommand = defineCommand85({
12049
11914
  `);
12050
11915
  process.exit(2);
12051
11916
  }
12052
- parsed = resolveRelativeCanvasPaths(parsed, path11.dirname(filePath));
11917
+ parsed = resolveRelativeCanvasPaths(parsed, path10.dirname(filePath));
12053
11918
  const result = await validateCanvasDeep(parsed, defaultRegistry());
12054
11919
  if (!result.ok) {
12055
11920
  process.stderr.write(`${JSON.stringify({ ok: false, issues: result.issues }, null, 2)}
@@ -12086,7 +11951,6 @@ Subcommands:
12086
11951
  baker canvas run <file.json> \u2014 execute the canvas, write outputs to ./canvas/<run_id>/
12087
11952
  baker canvas catalog \u2014 print the agent-facing node + composition catalog (JSON Schema)
12088
11953
  baker canvas inspect <run_id> \u2014 one-page summary of a completed run
12089
- baker canvas gallery <dir> \u2014 read a creative folder's _definition.md + run manifests into the dashboard gallery descriptor (JSON)
12090
11954
  baker canvas scaffold-video <video> \u2014 turn a reference video into a runnable reproduction canvas (deconstruct + recurring-element detection)
12091
11955
  baker canvas scaffold-static-ad <image> \u2014 turn a source image into a runnable static-ad canvas (describe + element detection)`
12092
11956
  },
@@ -12095,18 +11959,333 @@ Subcommands:
12095
11959
  validate: validateCommand,
12096
11960
  catalog: catalogCommand,
12097
11961
  inspect: inspectCommand,
12098
- gallery: galleryCommand,
12099
11962
  "scaffold-video": scaffoldVideoCommand,
12100
11963
  "scaffold-static-ad": scaffoldStaticAdCommand,
12101
11964
  "set-prompt": setPromptCommand
12102
11965
  }
12103
11966
  });
12104
11967
 
11968
+ // src/commands/creatives/index.ts
11969
+ import { defineCommand as defineCommand88 } from "citty";
11970
+
11971
+ // src/commands/creatives/publish.ts
11972
+ import { extname as extname2 } from "path";
11973
+ import { defineCommand as defineCommand87 } from "citty";
11974
+
11975
+ // src/commands/images/api.ts
11976
+ import { readFile as readFile9 } from "fs/promises";
11977
+ import { extname } from "path";
11978
+ var imageProcessingTimeoutMs = 18e4;
11979
+ var imageReadyPollIntervalMs = 2e3;
11980
+ var mimeMap = {
11981
+ ".png": "image/png",
11982
+ ".jpg": "image/jpeg",
11983
+ ".jpeg": "image/jpeg",
11984
+ ".gif": "image/gif",
11985
+ ".webp": "image/webp",
11986
+ ".svg": "image/svg+xml",
11987
+ ".avif": "image/avif"
11988
+ };
11989
+ var defaultImageApiDeps = {
11990
+ readFile: readFile9,
11991
+ post: apiPost,
11992
+ get: apiGet,
11993
+ sleep: (ms) => new Promise((resolve5) => setTimeout(resolve5, ms))
11994
+ };
11995
+ function detectImageContentType(filePath, opts = {}) {
11996
+ const ext = extname(filePath).toLowerCase();
11997
+ const contentType = mimeMap[ext];
11998
+ if (!contentType || opts.allowedContentTypes && !opts.allowedContentTypes.includes(contentType)) {
11999
+ throw new ApiError(
12000
+ "VALIDATION_ERROR",
12001
+ opts.unsupportedMessage ?? `Cannot detect content type for extension "${ext}". Use --content-type.`
12002
+ );
12003
+ }
12004
+ return contentType;
12005
+ }
12006
+ async function uploadLocalImage(args, deps = defaultImageApiDeps) {
12007
+ const fileBuffer = await deps.readFile(args.file);
12008
+ const body = {
12009
+ base64: fileBuffer.toString("base64"),
12010
+ contentType: args.contentType
12011
+ };
12012
+ if (args.source) body.source = args.source;
12013
+ if (args.descriptionContext) body.descriptionContext = args.descriptionContext;
12014
+ return deps.post("/api/images/upload", body, { timeoutMs: imageProcessingTimeoutMs });
12015
+ }
12016
+ function getImage(deps, imageId) {
12017
+ return deps.get("/api/images/get", { id: imageId });
12018
+ }
12019
+ async function waitForReadyImage(deps, imageId, opts = {}) {
12020
+ const timeoutMs = opts.timeoutMs ?? imageProcessingTimeoutMs;
12021
+ const pollIntervalMs = opts.pollIntervalMs ?? imageReadyPollIntervalMs;
12022
+ const deadline = Date.now() + timeoutMs;
12023
+ let lastStatus = "unknown";
12024
+ while (Date.now() <= deadline) {
12025
+ const image = await getImage(deps, imageId);
12026
+ lastStatus = image.status ?? "unknown";
12027
+ if (image.status === "ready") {
12028
+ return image;
12029
+ }
12030
+ if (image.status === "error") {
12031
+ throw new ApiError("IMAGE_PROCESSING_ERROR", "Image processing failed");
12032
+ }
12033
+ await deps.sleep(pollIntervalMs);
12034
+ }
12035
+ throw new ApiError("TIMEOUT", `Image was not ready before timeout; last status: ${lastStatus}`);
12036
+ }
12037
+
12038
+ // src/commands/creatives/publish.ts
12039
+ var creativeTag = "creative";
12040
+ var winningAdsTag = "winning-ads";
12041
+ var creativeTags = [creativeTag, winningAdsTag];
12042
+ var creativeImageContentTypes = ["image/png", "image/jpeg", "image/webp"];
12043
+ var creativeVideoContentTypesByExtension = {
12044
+ ".mp4": "video/mp4",
12045
+ ".mov": "video/quicktime",
12046
+ ".webm": "video/webm"
12047
+ };
12048
+ var videoProcessingTimeoutMs = 5 * 60 * 1e3;
12049
+ var videoReadyPollIntervalMs = 5e3;
12050
+ var defaultCreativePublishDeps = {
12051
+ ...defaultImageApiDeps,
12052
+ fetch
12053
+ };
12054
+ registerSchema({
12055
+ command: "creatives.publish",
12056
+ description: "Publish a final creative image or video, apply creative and winning-ads tags, and return an asset reference.",
12057
+ args: {
12058
+ file: { type: "string", description: "Local PNG/JPG/WebP/MP4/MOV/WebM creative path", required: true },
12059
+ title: { type: "string", description: "Human title for the creative output", required: true },
12060
+ body: { type: "string", description: "Reference traceability body for the creative output", required: true }
12061
+ }
12062
+ });
12063
+ function uniqueTags(tags) {
12064
+ return [...new Set(tags.map((tag) => tag.trim()).filter(Boolean))];
12065
+ }
12066
+ function requiredMetadata(args) {
12067
+ const title = args.title.trim();
12068
+ const body = args.body.trim();
12069
+ if (!title) {
12070
+ throw new ApiError("VALIDATION_ERROR", "--title is required");
12071
+ }
12072
+ if (!body) {
12073
+ throw new ApiError("VALIDATION_ERROR", "--body is required");
12074
+ }
12075
+ return { title, body };
12076
+ }
12077
+ function detectCreativeContentType(filePath) {
12078
+ const ext = extname2(filePath).toLowerCase();
12079
+ const videoContentType = creativeVideoContentTypesByExtension[ext];
12080
+ if (videoContentType) {
12081
+ return videoContentType;
12082
+ }
12083
+ return detectImageContentType(filePath, {
12084
+ allowedContentTypes: creativeImageContentTypes,
12085
+ unsupportedMessage: "Unsupported creative extension. Use PNG, JPG, WebP, MP4, MOV, or WebM."
12086
+ });
12087
+ }
12088
+ function isVideoContentType(contentType) {
12089
+ return contentType.startsWith("video/");
12090
+ }
12091
+ function imageToCreativeReference(image, title) {
12092
+ if (!image.imageUrl) {
12093
+ throw new ApiError("IMAGE_PROCESSING_ERROR", "Published image is missing imageUrl");
12094
+ }
12095
+ const tags = uniqueTags([...image.tags ?? [], ...creativeTags]);
12096
+ return {
12097
+ type: "image",
12098
+ slug: image._id,
12099
+ title,
12100
+ body: image.description,
12101
+ tags,
12102
+ imageUrl: image.imageUrl,
12103
+ thumbnailUrl: image.thumbnailUrl ?? image.imageUrl,
12104
+ storageKey: image.storageKey,
12105
+ width: image.width,
12106
+ height: image.height,
12107
+ aspectRatio: image.aspectRatio,
12108
+ source: image.source
12109
+ };
12110
+ }
12111
+ function videoToCreativeReference(video, title) {
12112
+ const tags = uniqueTags([...video.tags, ...creativeTags]);
12113
+ return {
12114
+ type: "video",
12115
+ slug: video._id,
12116
+ title,
12117
+ body: video.description,
12118
+ tags,
12119
+ thumbnailUrl: video.thumbnailUrl,
12120
+ muxPlaybackId: video.muxPlaybackId,
12121
+ playbackUrl: video.muxPlaybackId ? `https://stream.mux.com/${video.muxPlaybackId}.m3u8` : void 0,
12122
+ duration: video.duration,
12123
+ width: video.width,
12124
+ height: video.height,
12125
+ aspectRatio: video.aspectRatio,
12126
+ source: video.source
12127
+ };
12128
+ }
12129
+ async function updateImageMetadata(deps, args) {
12130
+ await deps.post("/api/images/update-description", {
12131
+ imageId: args.imageId,
12132
+ name: args.title,
12133
+ description: args.body,
12134
+ tags: args.tags
12135
+ });
12136
+ }
12137
+ function createVideoUpload(deps) {
12138
+ return deps.post("/api/videos/upload", {});
12139
+ }
12140
+ function getVideo(deps, videoId) {
12141
+ return deps.get("/api/videos/get", { id: videoId });
12142
+ }
12143
+ async function updateVideoMetadata(deps, args) {
12144
+ await deps.post("/api/videos/update-description", {
12145
+ videoId: args.videoId,
12146
+ name: args.title,
12147
+ description: args.body,
12148
+ tags: args.tags
12149
+ });
12150
+ }
12151
+ async function waitForReadyVideo(deps, videoId, opts = {}) {
12152
+ const timeoutMs = opts.timeoutMs ?? videoProcessingTimeoutMs;
12153
+ const pollIntervalMs = opts.pollIntervalMs ?? videoReadyPollIntervalMs;
12154
+ const deadline = Date.now() + timeoutMs;
12155
+ let lastStatus = "unknown";
12156
+ while (Date.now() <= deadline) {
12157
+ const video = await getVideo(deps, videoId);
12158
+ lastStatus = video.status ?? "unknown";
12159
+ if (video.status === "ready") {
12160
+ return video;
12161
+ }
12162
+ if (video.status === "error") {
12163
+ throw new ApiError(
12164
+ "INTERNAL_ERROR",
12165
+ `Video processing failed for videoId ${videoId}: ${video.errorMessage ?? "unknown error"}`
12166
+ );
12167
+ }
12168
+ await deps.sleep(pollIntervalMs);
12169
+ }
12170
+ throw new ApiError("TIMEOUT", `Video was not ready before timeout; videoId: ${videoId}; last status: ${lastStatus}`);
12171
+ }
12172
+ async function uploadLocalVideo(args, deps) {
12173
+ const { uploadUrl, videoId } = await createVideoUpload(deps);
12174
+ const fileBuffer = await deps.readFile(args.file);
12175
+ const uploadResponse = await deps.fetch(uploadUrl, {
12176
+ method: "PUT",
12177
+ headers: { "Content-Type": args.contentType },
12178
+ body: fileBuffer
12179
+ });
12180
+ if (!uploadResponse.ok) {
12181
+ throw new ApiError(
12182
+ "INTERNAL_ERROR",
12183
+ `Mux upload failed: HTTP ${uploadResponse.status} ${uploadResponse.statusText}`
12184
+ );
12185
+ }
12186
+ return { videoId };
12187
+ }
12188
+ async function publishCreativeImage(args, deps) {
12189
+ const upload = await uploadLocalImage(
12190
+ {
12191
+ file: args.file,
12192
+ contentType: args.contentType,
12193
+ source: "ai_generated",
12194
+ descriptionContext: args.body
12195
+ },
12196
+ deps
12197
+ );
12198
+ const readyImage = await waitForReadyImage(deps, upload.imageId, { timeoutMs: imageProcessingTimeoutMs });
12199
+ const tags = uniqueTags([...readyImage.tags ?? [], ...creativeTags]);
12200
+ await updateImageMetadata(deps, { imageId: upload.imageId, title: args.title, body: args.body, tags });
12201
+ const taggedImage = await getImage(deps, upload.imageId);
12202
+ return {
12203
+ imageId: upload.imageId,
12204
+ reference: imageToCreativeReference({ ...readyImage, ...taggedImage }, args.title)
12205
+ };
12206
+ }
12207
+ async function publishCreativeVideo(args, deps) {
12208
+ const { videoId } = await uploadLocalVideo({ file: args.file, contentType: args.contentType }, deps);
12209
+ const readyVideo = await waitForReadyVideo(deps, videoId);
12210
+ const tags = uniqueTags([...readyVideo.tags, ...creativeTags]);
12211
+ await updateVideoMetadata(deps, { videoId, title: args.title, body: args.body, tags });
12212
+ const taggedVideo = await getVideo(deps, videoId);
12213
+ return { videoId, reference: videoToCreativeReference(taggedVideo, args.title) };
12214
+ }
12215
+ function publishCreative(args, deps = defaultCreativePublishDeps) {
12216
+ try {
12217
+ const metadata = requiredMetadata(args);
12218
+ const contentType = detectCreativeContentType(args.file);
12219
+ if (isVideoContentType(contentType)) {
12220
+ return publishCreativeVideo({ file: args.file, ...metadata, contentType }, deps);
12221
+ }
12222
+ return publishCreativeImage({ file: args.file, ...metadata, contentType }, deps);
12223
+ } catch (error) {
12224
+ return Promise.reject(error);
12225
+ }
12226
+ }
12227
+ var publishCommand = defineCommand87({
12228
+ meta: {
12229
+ name: "publish",
12230
+ description: "Publish a final creative image or video, tag it as creative and winning-ads, and print the asset reference JSON."
12231
+ },
12232
+ args: {
12233
+ file: { type: "positional", description: "Local PNG/JPG/WebP/MP4/MOV/WebM creative path", required: false },
12234
+ title: { type: "string", description: "Human title for the creative output", required: false },
12235
+ body: { type: "string", description: "Reference traceability body for the creative output", required: false }
12236
+ },
12237
+ run: async ({ args }) => {
12238
+ try {
12239
+ const file = args.file;
12240
+ const title = args.title;
12241
+ const body = args.body;
12242
+ if (!file) {
12243
+ writeJson({ ok: false, error: { code: "VALIDATION_ERROR", message: "File path is required" } });
12244
+ process.exit(1);
12245
+ }
12246
+ if (!title) {
12247
+ writeJson({ ok: false, error: { code: "VALIDATION_ERROR", message: "--title is required" } });
12248
+ process.exit(1);
12249
+ }
12250
+ if (!body) {
12251
+ writeJson({ ok: false, error: { code: "VALIDATION_ERROR", message: "--body is required" } });
12252
+ process.exit(1);
12253
+ }
12254
+ const data = await publishCreative({ file, title, body });
12255
+ writeJson({ ok: true, data });
12256
+ } catch (err) {
12257
+ if (err instanceof ApiError) {
12258
+ writeJson({ ok: false, error: { code: err.code, message: err.message } });
12259
+ process.exit(1);
12260
+ }
12261
+ writeJson({ ok: false, error: { code: "INTERNAL_ERROR", message: "Unexpected error" } });
12262
+ process.exit(1);
12263
+ }
12264
+ }
12265
+ });
12266
+
12267
+ // src/commands/creatives/index.ts
12268
+ var creativesCommand3 = defineCommand88({
12269
+ meta: {
12270
+ name: "creatives",
12271
+ description: `Publish ad creatives as first-class Baker outputs.
12272
+
12273
+ Creative handoff:
12274
+ baker creatives publish ./canvas/run/final.png --title "Winning Ads: Offer + Meta 4:5 + Angle" --body "Reference: Advertiser[ad_id]: https://example.com/ad.mp4
12275
+ Angle adapted: Client-safe angle"
12276
+
12277
+ Publishing routes images to Images and videos to the video library, applies creative + winning-ads tags, and returns an asset reference for chat previews.`
12278
+ },
12279
+ subCommands: {
12280
+ publish: publishCommand
12281
+ }
12282
+ });
12283
+
12105
12284
  // src/commands/ga4/index.ts
12106
- import { defineCommand as defineCommand90 } from "citty";
12285
+ import { defineCommand as defineCommand92 } from "citty";
12107
12286
 
12108
12287
  // src/commands/ga4/audit.ts
12109
- import { defineCommand as defineCommand87 } from "citty";
12288
+ import { defineCommand as defineCommand89 } from "citty";
12110
12289
 
12111
12290
  // src/commands/ga4/resolve.ts
12112
12291
  async function fetchProperties(useCache = true) {
@@ -12169,7 +12348,7 @@ registerSchema({
12169
12348
  "no-cache": { type: "boolean", description: "Skip cache, hit API directly", required: false }
12170
12349
  }
12171
12350
  });
12172
- var auditCommand2 = defineCommand87({
12351
+ var auditCommand2 = defineCommand89({
12173
12352
  meta: {
12174
12353
  name: "audit",
12175
12354
  description: `Run all GA4 admin health checks. Returns property config with playbook warnings.
@@ -12221,7 +12400,7 @@ Examples:
12221
12400
  });
12222
12401
 
12223
12402
  // src/commands/ga4/properties.ts
12224
- import { defineCommand as defineCommand88 } from "citty";
12403
+ import { defineCommand as defineCommand90 } from "citty";
12225
12404
  registerSchema({
12226
12405
  command: "ga4.properties",
12227
12406
  description: "List all accessible GA4 properties. Returns property IDs needed for query and audit commands. Run this first to find property IDs.",
@@ -12229,7 +12408,7 @@ registerSchema({
12229
12408
  "no-cache": { type: "boolean", description: "Skip cache, hit API directly", required: false }
12230
12409
  }
12231
12410
  });
12232
- var propertiesCommand = defineCommand88({
12411
+ var propertiesCommand = defineCommand90({
12233
12412
  meta: {
12234
12413
  name: "properties",
12235
12414
  description: `List accessible GA4 properties.
@@ -12279,7 +12458,7 @@ Examples:
12279
12458
  // src/commands/ga4/query.ts
12280
12459
  import { appendFileSync as appendFileSync2, existsSync as existsSync4, readFileSync as readFileSync6, writeFileSync as writeFileSync4 } from "fs";
12281
12460
  import { resolve as resolve2 } from "path";
12282
- import { defineCommand as defineCommand89 } from "citty";
12461
+ import { defineCommand as defineCommand91 } from "citty";
12283
12462
 
12284
12463
  // src/commands/ga4/presets.ts
12285
12464
  var GA4_PRESETS = [
@@ -12411,7 +12590,7 @@ function handleError(err) {
12411
12590
  });
12412
12591
  process.exit(1);
12413
12592
  }
12414
- var queryCommand2 = defineCommand89({
12593
+ var queryCommand2 = defineCommand91({
12415
12594
  meta: {
12416
12595
  name: "query",
12417
12596
  description: `Run GA4 Data API reports. Preset-first with free-form escape hatch.
@@ -12482,7 +12661,7 @@ Free-form (escape hatch):
12482
12661
  });
12483
12662
 
12484
12663
  // src/commands/ga4/index.ts
12485
- var ga4Command = defineCommand90({
12664
+ var ga4Command = defineCommand92({
12486
12665
  meta: {
12487
12666
  name: "ga4",
12488
12667
  description: `Google Analytics 4 commands. Audit property config, run playbook-aligned reports.
@@ -12505,12 +12684,12 @@ Examples:
12505
12684
  });
12506
12685
 
12507
12686
  // src/commands/gsc/index.ts
12508
- import { defineCommand as defineCommand94 } from "citty";
12687
+ import { defineCommand as defineCommand96 } from "citty";
12509
12688
 
12510
12689
  // src/commands/gsc/query.ts
12511
12690
  import { appendFileSync as appendFileSync3, existsSync as existsSync5, readFileSync as readFileSync7, writeFileSync as writeFileSync5 } from "fs";
12512
12691
  import { resolve as resolve3 } from "path";
12513
- import { defineCommand as defineCommand91 } from "citty";
12692
+ import { defineCommand as defineCommand93 } from "citty";
12514
12693
 
12515
12694
  // src/commands/gsc/presets.ts
12516
12695
  var GSC_PRESETS = [
@@ -12698,7 +12877,7 @@ function handleError2(err) {
12698
12877
  });
12699
12878
  process.exit(1);
12700
12879
  }
12701
- var queryCommand3 = defineCommand91({
12880
+ var queryCommand3 = defineCommand93({
12702
12881
  meta: {
12703
12882
  name: "query",
12704
12883
  description: `Run GSC Search Analytics queries. Preset-first with free-form escape hatch.
@@ -12776,7 +12955,7 @@ Free-form (escape hatch):
12776
12955
  });
12777
12956
 
12778
12957
  // src/commands/gsc/sitemaps.ts
12779
- import { defineCommand as defineCommand92 } from "citty";
12958
+ import { defineCommand as defineCommand94 } from "citty";
12780
12959
  registerSchema({
12781
12960
  command: "gsc.sitemaps",
12782
12961
  description: "List sitemaps for a Search Console site. Check sitemap health and errors.",
@@ -12785,7 +12964,7 @@ registerSchema({
12785
12964
  "no-cache": { type: "boolean", description: "Skip cache, hit API directly", required: false }
12786
12965
  }
12787
12966
  });
12788
- var sitemapsCommand = defineCommand92({
12967
+ var sitemapsCommand = defineCommand94({
12789
12968
  meta: {
12790
12969
  name: "sitemaps",
12791
12970
  description: `List sitemaps for a site. Check health and errors.
@@ -12835,7 +13014,7 @@ Examples:
12835
13014
  });
12836
13015
 
12837
13016
  // src/commands/gsc/sites.ts
12838
- import { defineCommand as defineCommand93 } from "citty";
13017
+ import { defineCommand as defineCommand95 } from "citty";
12839
13018
  registerSchema({
12840
13019
  command: "gsc.sites",
12841
13020
  description: "List all verified Google Search Console sites. Returns site URLs needed for query and sitemaps commands.",
@@ -12843,7 +13022,7 @@ registerSchema({
12843
13022
  "no-cache": { type: "boolean", description: "Skip cache, hit API directly", required: false }
12844
13023
  }
12845
13024
  });
12846
- var sitesCommand = defineCommand93({
13025
+ var sitesCommand = defineCommand95({
12847
13026
  meta: {
12848
13027
  name: "sites",
12849
13028
  description: `List verified Search Console sites.
@@ -12891,7 +13070,7 @@ Examples:
12891
13070
  });
12892
13071
 
12893
13072
  // src/commands/gsc/index.ts
12894
- var gscCommand = defineCommand94({
13073
+ var gscCommand = defineCommand96({
12895
13074
  meta: {
12896
13075
  name: "gsc",
12897
13076
  description: `Google Search Console commands. PPC-SEO arbitrage, brand halo analysis, negative keyword discovery.
@@ -12914,10 +13093,10 @@ Examples:
12914
13093
  });
12915
13094
 
12916
13095
  // src/commands/images/index.ts
12917
- import { defineCommand as defineCommand118 } from "citty";
13096
+ import { defineCommand as defineCommand120 } from "citty";
12918
13097
 
12919
13098
  // src/commands/images/crop.ts
12920
- import { defineCommand as defineCommand95 } from "citty";
13099
+ import { defineCommand as defineCommand97 } from "citty";
12921
13100
 
12922
13101
  // src/lib/image/crop-sprite.ts
12923
13102
  import sharp from "sharp";
@@ -12933,7 +13112,7 @@ function cropSprite(input, region) {
12933
13112
  // src/lib/image/io.ts
12934
13113
  import { randomBytes } from "crypto";
12935
13114
  import { glob as fsGlob, readFile as readFile10, rename, stat as stat2, writeFile as writeFile4 } from "fs/promises";
12936
- import { dirname, extname, join as join3, resolve as resolve4 } from "path";
13115
+ import { dirname, extname as extname3, join as join3, resolve as resolve4 } from "path";
12937
13116
  var REMOTE_RE = /^https?:\/\//i;
12938
13117
  var GLOB_RE = /[*?[\]{}]/;
12939
13118
  function isRemoteUrl(value) {
@@ -12970,16 +13149,16 @@ async function readImageBuffer(pathOrUrl) {
12970
13149
  }
12971
13150
  return readFile10(pathOrUrl);
12972
13151
  }
12973
- async function isDirectory(path12) {
13152
+ async function isDirectory(path11) {
12974
13153
  try {
12975
- const s = await stat2(path12);
13154
+ const s = await stat2(path11);
12976
13155
  return s.isDirectory();
12977
13156
  } catch {
12978
13157
  return false;
12979
13158
  }
12980
13159
  }
12981
13160
  async function resolveOutputPath(inputPath, outputArg, options) {
12982
- const base = options.newExtension ? inputPath.slice(0, -extname(inputPath).length) + options.newExtension : inputPath;
13161
+ const base = options.newExtension ? inputPath.slice(0, -extname3(inputPath).length) + options.newExtension : inputPath;
12983
13162
  if (!outputArg) return base;
12984
13163
  if (options.multipleInputs || await isDirectory(outputArg)) {
12985
13164
  const filename = base.split("/").pop() ?? "out.png";
@@ -13042,7 +13221,7 @@ function emitError2(err) {
13042
13221
  }
13043
13222
  process.exit(1);
13044
13223
  }
13045
- var cropCommand = defineCommand95({
13224
+ var cropCommand = defineCommand97({
13046
13225
  meta: {
13047
13226
  name: "crop",
13048
13227
  description: "Crop a rectangular region from an image.\n\nExample: baker images crop sprite.png --x 0 --y 0 --width 64 --height 64 --output icon.png"
@@ -13078,7 +13257,7 @@ var cropCommand = defineCommand95({
13078
13257
  });
13079
13258
 
13080
13259
  // src/commands/images/delete.ts
13081
- import { defineCommand as defineCommand96 } from "citty";
13260
+ import { defineCommand as defineCommand98 } from "citty";
13082
13261
  registerSchema({
13083
13262
  command: "images.delete",
13084
13263
  description: "Delete an image by ID",
@@ -13092,7 +13271,7 @@ registerSchema({
13092
13271
  }
13093
13272
  }
13094
13273
  });
13095
- var deleteCommand = defineCommand96({
13274
+ var deleteCommand = defineCommand98({
13096
13275
  meta: {
13097
13276
  name: "delete",
13098
13277
  description: "Delete an image by ID. Use --dry-run to preview. Example: baker images delete j571abc123 --dry-run"
@@ -13133,7 +13312,7 @@ var deleteCommand = defineCommand96({
13133
13312
  });
13134
13313
 
13135
13314
  // src/commands/images/dimensions.ts
13136
- import { defineCommand as defineCommand97 } from "citty";
13315
+ import { defineCommand as defineCommand99 } from "citty";
13137
13316
 
13138
13317
  // src/lib/image/dimensions.ts
13139
13318
  import { imageSize } from "image-size";
@@ -13156,7 +13335,7 @@ registerSchema({
13156
13335
  target: { type: "string", description: "Local file path or remote http(s) URL", required: true }
13157
13336
  }
13158
13337
  });
13159
- var dimensionsCommand = defineCommand97({
13338
+ var dimensionsCommand = defineCommand99({
13160
13339
  meta: {
13161
13340
  name: "dimensions",
13162
13341
  description: "Read image dimensions without decoding the full file.\n\nExample: baker images dimensions ./logo.png\nExample: baker images dimensions https://acme.com/hero.png"
@@ -13200,7 +13379,7 @@ var dimensionsCommand = defineCommand97({
13200
13379
  });
13201
13380
 
13202
13381
  // src/commands/images/extract.ts
13203
- import { defineCommand as defineCommand98 } from "citty";
13382
+ import { defineCommand as defineCommand100 } from "citty";
13204
13383
  registerSchema({
13205
13384
  command: "images.extract",
13206
13385
  description: "Extract images from a URL via Firecrawl (formats: images).",
@@ -13216,7 +13395,7 @@ registerSchema({
13216
13395
  }
13217
13396
  }
13218
13397
  });
13219
- var extractCommand = defineCommand98({
13398
+ var extractCommand = defineCommand100({
13220
13399
  meta: {
13221
13400
  name: "extract",
13222
13401
  description: "Pull every image from a single URL via Firecrawl. ~$0.001/scrape. Cap auto-ingest at 20.\n\nExample: baker images extract https://stripe.com --auto-ingest 5"
@@ -13254,7 +13433,7 @@ var extractCommand = defineCommand98({
13254
13433
  });
13255
13434
 
13256
13435
  // src/commands/images/find.ts
13257
- import { defineCommand as defineCommand99 } from "citty";
13436
+ import { defineCommand as defineCommand101 } from "citty";
13258
13437
  registerSchema({
13259
13438
  command: "images.find",
13260
13439
  description: "Fanout image search: library first, then opted-in external providers.",
@@ -13286,7 +13465,7 @@ registerSchema({
13286
13465
  }
13287
13466
  }
13288
13467
  });
13289
- var findCommand = defineCommand99({
13468
+ var findCommand = defineCommand101({
13290
13469
  meta: {
13291
13470
  name: "find",
13292
13471
  description: "Library-first fanout image search. Opt in to providers with --sources. `--fallback` short-circuits to externals only when library is thin. With --auto-ingest, ingested external hits return Baker-owned URLs.\n\nExample: baker images find 'office' --sources library,magnific --limit 20"
@@ -13333,7 +13512,7 @@ var findCommand = defineCommand99({
13333
13512
 
13334
13513
  // src/commands/images/generate.ts
13335
13514
  import { readFile as readFile11 } from "fs/promises";
13336
- import { defineCommand as defineCommand100 } from "citty";
13515
+ import { defineCommand as defineCommand102 } from "citty";
13337
13516
  import sharp2 from "sharp";
13338
13517
  var GENERATE_TIMEOUT_MS = 18e4;
13339
13518
  var REFERENCE_MAX_EDGE = 1536;
@@ -13429,7 +13608,7 @@ async function resolveReferences(spec) {
13429
13608
  }
13430
13609
  return out;
13431
13610
  }
13432
- var generateCommand = defineCommand100({
13611
+ var generateCommand = defineCommand102({
13433
13612
  meta: {
13434
13613
  name: "generate",
13435
13614
  description: "Generate an image with AI and store it in the library (cost-tracked per request via OpenRouter usage). Models mirror the canvas: openai/gpt-5.4-image-2 (default \u2014 photoreal, cleanest text, best for ad/landing reproduction), google/gemini-3-pro-image-preview (Nano Banana Pro), google/gemini-3.5-flash & google/gemini-3.1-flash-image-preview (fast, extreme aspect ratios), recraft/recraft-v4.1-pro-vector (vector/SVG-style with palette control). The result is auto-ingested (describe + embed), so the next `baker images library` query finds it. Pass --reference with image URLs and/or local file paths (Pinterest, stock, brand assets, sandbox files) to ground generation in reality.\n\nExamples:\n baker images generate 'a friendly golden retriever sitting in a bright modern living room' --aspect-ratio 16:9\n baker images generate 'hero shot of a matte black water bottle on marble' --model google/gemini-3-pro-image-preview --image-size 2K\n baker images generate 'lifestyle photo matching this mood' --reference 'https://\u2026/ref1.jpg,https://\u2026/ref2.jpg'\n baker images generate 'put this product on a marble countertop, soft daylight' --reference './src/brand/logos/product.png,./refs/kitchen-mood.jpg'\n baker images generate 'flat geometric mascot, brand palette' --model recraft/recraft-v4.1-pro-vector --rgb-colors '[[10,10,10],[255,80,0]]'"
@@ -13481,7 +13660,7 @@ var generateCommand = defineCommand100({
13481
13660
  });
13482
13661
 
13483
13662
  // src/commands/images/get.ts
13484
- import { defineCommand as defineCommand101 } from "citty";
13663
+ import { defineCommand as defineCommand103 } from "citty";
13485
13664
  registerSchema({
13486
13665
  command: "images.get",
13487
13666
  description: "Get a single image by ID",
@@ -13489,7 +13668,7 @@ registerSchema({
13489
13668
  id: { type: "string", description: "Image ID", required: true }
13490
13669
  }
13491
13670
  });
13492
- var getCommand2 = defineCommand101({
13671
+ var getCommand2 = defineCommand103({
13493
13672
  meta: { name: "get", description: "Get a single image by ID. Example: baker images get j571abc123" },
13494
13673
  args: {
13495
13674
  id: { type: "positional", description: "Image ID", required: false },
@@ -13525,7 +13704,7 @@ var getCommand2 = defineCommand101({
13525
13704
  });
13526
13705
 
13527
13706
  // src/commands/images/gif.ts
13528
- import { defineCommand as defineCommand102 } from "citty";
13707
+ import { defineCommand as defineCommand104 } from "citty";
13529
13708
  registerSchema({
13530
13709
  command: "images.gif",
13531
13710
  description: "Search Giphy for GIFs / reaction memes (paid social creative).",
@@ -13557,7 +13736,7 @@ registerSchema({
13557
13736
  }
13558
13737
  }
13559
13738
  });
13560
- var gifCommand = defineCommand102({
13739
+ var gifCommand = defineCommand104({
13561
13740
  meta: {
13562
13741
  name: "gif",
13563
13742
  description: "Search Giphy for GIFs / reaction memes \u2014 built for paid-social creative (Meta, TikTok, LinkedIn, X). Free API. Each hit carries WebP + GIF + MP4 URLs in providerMeta so you can pick the right format per platform.\n\nExample: baker images gif 'this is fine' --limit 10\nExample: baker images gif 'office reaction' --rating pg --auto-ingest 2\nExample: baker images gif --trending --limit 25"
@@ -13604,7 +13783,7 @@ var gifCommand = defineCommand102({
13604
13783
  });
13605
13784
 
13606
13785
  // src/commands/images/google.ts
13607
- import { defineCommand as defineCommand103 } from "citty";
13786
+ import { defineCommand as defineCommand105 } from "citty";
13608
13787
  registerSchema({
13609
13788
  command: "images.google",
13610
13789
  description: "Google Images search via the official Custom Search JSON API. Unverified source \u2014 inspect before placing.",
@@ -13640,7 +13819,7 @@ registerSchema({
13640
13819
  }
13641
13820
  }
13642
13821
  });
13643
- var googleCommand2 = defineCommand103({
13822
+ var googleCommand2 = defineCommand105({
13644
13823
  meta: {
13645
13824
  name: "google",
13646
13825
  description: "Google Images via the official Custom Search JSON API ($0.005/query, free 100/day). \u26A0 Source unverified \u2014 watermarks, low-res, mislabeled results are common. Use as last resort. With --auto-ingest, ingested hits return Baker-owned URLs.\n\nExample: baker images google 'industrial workshop' --type photo --size large --limit 20"
@@ -13688,7 +13867,7 @@ var googleCommand2 = defineCommand103({
13688
13867
  });
13689
13868
 
13690
13869
  // src/commands/images/icon.ts
13691
- import { defineCommand as defineCommand104 } from "citty";
13870
+ import { defineCommand as defineCommand106 } from "citty";
13692
13871
  registerSchema({
13693
13872
  command: "images.icon",
13694
13873
  description: "Icon lookup via Iconify (200+ icon sets, free CDN).",
@@ -13714,7 +13893,7 @@ registerSchema({
13714
13893
  }
13715
13894
  }
13716
13895
  });
13717
- var iconCommand = defineCommand104({
13896
+ var iconCommand = defineCommand106({
13718
13897
  meta: {
13719
13898
  name: "icon",
13720
13899
  description: "Icon via Iconify (simple-icons, logos, lucide, devicon, heroicons, tabler, phosphor, material-symbols, \u2026). Free CDN, no API key.\n\nExample: baker images icon react --set devicon\nExample: baker images icon lucide:check --color '#0a0a0a'"
@@ -13754,7 +13933,7 @@ var iconCommand = defineCommand104({
13754
13933
  });
13755
13934
 
13756
13935
  // src/commands/images/ingest.ts
13757
- import { defineCommand as defineCommand105 } from "citty";
13936
+ import { defineCommand as defineCommand107 } from "citty";
13758
13937
  registerSchema({
13759
13938
  command: "images.ingest",
13760
13939
  description: "Ingest a remote image URL into the library (full describe + embed).",
@@ -13766,7 +13945,7 @@ registerSchema({
13766
13945
  context: { type: "string", description: "Description context hint", required: false }
13767
13946
  }
13768
13947
  });
13769
- var ingestCommand = defineCommand105({
13948
+ var ingestCommand = defineCommand107({
13770
13949
  meta: {
13771
13950
  name: "ingest",
13772
13951
  description: "Download a remote URL and store it in the library. Hash-deduped on bytes + externalId.\n\nExample: baker images ingest https://img.freepik.com/free-photo/xyz.jpg --source magnific --external-id 12345"
@@ -13808,7 +13987,7 @@ var ingestCommand = defineCommand105({
13808
13987
  });
13809
13988
 
13810
13989
  // src/commands/images/library.ts
13811
- import { defineCommand as defineCommand106 } from "citty";
13990
+ import { defineCommand as defineCommand108 } from "citty";
13812
13991
  registerSchema({
13813
13992
  command: "images.library",
13814
13993
  description: "Search the company image library. Returns only ready images.",
@@ -13834,7 +14013,7 @@ registerSchema({
13834
14013
  }
13835
14014
  }
13836
14015
  });
13837
- var libraryCommand = defineCommand106({
14016
+ var libraryCommand = defineCommand108({
13838
14017
  meta: {
13839
14018
  name: "library",
13840
14019
  description: "Search the company image library (hybrid BM25 + vector + Cohere rerank). Use this BEFORE any external provider.\n\nExample: baker images library 'hero banner' --aspect-ratio 16:9 --source magnific"
@@ -13891,7 +14070,7 @@ var libraryCommand = defineCommand106({
13891
14070
  });
13892
14071
 
13893
14072
  // src/commands/images/logo.ts
13894
- import { defineCommand as defineCommand107 } from "citty";
14073
+ import { defineCommand as defineCommand109 } from "citty";
13895
14074
  registerSchema({
13896
14075
  command: "images.logo",
13897
14076
  description: "Brand logo lookup via Brandfetch CDN (fallback/404). Auto-ingests by default.",
@@ -13916,7 +14095,7 @@ registerSchema({
13916
14095
  }
13917
14096
  }
13918
14097
  });
13919
- var logoCommand = defineCommand107({
14098
+ var logoCommand = defineCommand109({
13920
14099
  meta: {
13921
14100
  name: "logo",
13922
14101
  description: "Brand logo via Brandfetch CDN. Returns up to 5 variants (icon, light/dark logo, light/dark symbol). Auto-ingests the first variant.\n\nExample: baker images logo stripe.com --variant logo"
@@ -13954,7 +14133,7 @@ var logoCommand = defineCommand107({
13954
14133
  });
13955
14134
 
13956
14135
  // src/commands/images/normalize.ts
13957
- import { defineCommand as defineCommand108 } from "citty";
14136
+ import { defineCommand as defineCommand110 } from "citty";
13958
14137
 
13959
14138
  // src/lib/image/color-changer.ts
13960
14139
  import quantize from "quantize";
@@ -14686,7 +14865,7 @@ function coerceRawArgs(args) {
14686
14865
  "dry-run": bool(args["dry-run"])
14687
14866
  };
14688
14867
  }
14689
- var normalizeCommand = defineCommand108({
14868
+ var normalizeCommand = defineCommand110({
14690
14869
  meta: {
14691
14870
  name: "normalize",
14692
14871
  description: `Normalize logos / images: declarative recolor + bg removal + trim + resize. Operates on local files; writes in-place by default.
@@ -14741,7 +14920,7 @@ Examples:
14741
14920
  });
14742
14921
 
14743
14922
  // src/commands/images/pinterest.ts
14744
- import { defineCommand as defineCommand109 } from "citty";
14923
+ import { defineCommand as defineCommand111 } from "citty";
14745
14924
  registerSchema({
14746
14925
  command: "images.pinterest",
14747
14926
  description: "Pinterest image search via ScrapeCreators. Reference-grade real-world photography, product styling, interiors, fashion, food, and aesthetic mood boards. Inspect before placing \u2014 Pinterest is unverified, trademark-bearing web content.",
@@ -14761,7 +14940,7 @@ registerSchema({
14761
14940
  }
14762
14941
  }
14763
14942
  });
14764
- var pinterestCommand = defineCommand109({
14943
+ var pinterestCommand = defineCommand111({
14765
14944
  meta: {
14766
14945
  name: "pinterest",
14767
14946
  description: "Pinterest image search via ScrapeCreators ($0.00188/request). Best for photo-realistic reference imagery \u2014 lifestyle, interiors, fashion, food, product styling, and mood boards to brief AI generation against. \u26A0 Unverified, trademark-bearing web content \u2014 inspect and respect rights before placing on a customer page. Browse first; auto-ingest only the pins you commit to.\n\nExamples:\n baker images pinterest 'scandinavian living room'\n baker images pinterest 'minimalist skincare product photography' --limit 20\n baker images pinterest 'cozy coffee shop interior' --auto-ingest 2 --context 'Mood reference for hero photography'"
@@ -14801,7 +14980,7 @@ var pinterestCommand = defineCommand109({
14801
14980
  });
14802
14981
 
14803
14982
  // src/commands/images/screenshot.ts
14804
- import { defineCommand as defineCommand110 } from "citty";
14983
+ import { defineCommand as defineCommand112 } from "citty";
14805
14984
  registerSchema({
14806
14985
  command: "images.screenshot",
14807
14986
  description: "Capture a website screenshot via ScreenshotOne. Auto-ingests on success.",
@@ -14817,7 +14996,7 @@ registerSchema({
14817
14996
  }
14818
14997
  }
14819
14998
  });
14820
- var screenshotCommand = defineCommand110({
14999
+ var screenshotCommand = defineCommand112({
14821
15000
  meta: {
14822
15001
  name: "screenshot",
14823
15002
  description: "Screenshot a URL via ScreenshotOne. $0.009/capture. Auto-ingests to library.\n\nExample: baker images screenshot https://stripe.com --full-page"
@@ -14867,7 +15046,7 @@ var screenshotCommand = defineCommand110({
14867
15046
  });
14868
15047
 
14869
15048
  // src/commands/images/search.ts
14870
- import { defineCommand as defineCommand111 } from "citty";
15049
+ import { defineCommand as defineCommand113 } from "citty";
14871
15050
  registerSchema({
14872
15051
  command: "images.search",
14873
15052
  description: "Search images by text query. Only returns ready images.",
@@ -14883,7 +15062,7 @@ registerSchema({
14883
15062
  tags: { type: "string", description: "Comma-separated tags to filter by", required: false }
14884
15063
  }
14885
15064
  });
14886
- var searchCommand = defineCommand111({
15065
+ var searchCommand = defineCommand113({
14887
15066
  meta: {
14888
15067
  name: "search",
14889
15068
  description: "Semantic search images by text query. Uses hybrid BM25 + vector + reranking. Example: baker images search 'hero banner' --aspect-ratio 16:9 --tags logo"
@@ -14943,7 +15122,7 @@ var searchCommand = defineCommand111({
14943
15122
  });
14944
15123
 
14945
15124
  // src/commands/images/sticker.ts
14946
- import { defineCommand as defineCommand112 } from "citty";
15125
+ import { defineCommand as defineCommand114 } from "citty";
14947
15126
  registerSchema({
14948
15127
  command: "images.sticker",
14949
15128
  description: "Search Giphy stickers \u2014 transparent-background overlays for ad creative.",
@@ -14975,7 +15154,7 @@ registerSchema({
14975
15154
  }
14976
15155
  }
14977
15156
  });
14978
- var stickerCommand = defineCommand112({
15157
+ var stickerCommand = defineCommand114({
14979
15158
  meta: {
14980
15159
  name: "sticker",
14981
15160
  description: "Search Giphy's sticker corpus \u2014 transparent-background WebPs / GIFs ideal for overlaying on ad creative (Meta, TikTok, Stories). Same Giphy free API as `baker images gif`; results carry WebP + GIF + MP4 URLs in providerMeta.\n\nExample: baker images sticker 'thumbs up' --limit 10\nExample: baker images sticker celebration --rating g --auto-ingest 3\nExample: baker images sticker --trending --limit 25"
@@ -15022,7 +15201,7 @@ var stickerCommand = defineCommand112({
15022
15201
  });
15023
15202
 
15024
15203
  // src/commands/images/stock.ts
15025
- import { defineCommand as defineCommand113 } from "citty";
15204
+ import { defineCommand as defineCommand115 } from "citty";
15026
15205
  registerSchema({
15027
15206
  command: "images.stock",
15028
15207
  description: "Stock photo, vector illustration, icon-set, and PSD search via Magnific (Freepik's developer API).",
@@ -15080,7 +15259,7 @@ registerSchema({
15080
15259
  }
15081
15260
  }
15082
15261
  });
15083
- var stockCommand = defineCommand113({
15262
+ var stockCommand = defineCommand115({
15084
15263
  meta: {
15085
15264
  name: "stock",
15086
15265
  description: "Stock search via Magnific \u2014 Freepik's developer API (~250M assets: photos, vectors, illustrations, icons, PSDs). $0.002/req. With --auto-ingest, ingested hits return Baker-owned URLs.\n\nExamples:\n baker images stock 'minimalist office'\n baker images stock 'flat office workers' --type vector\n baker images stock 'hero photo of a kitchen' --type photo --orientation landscape --ai exclude\n baker images stock 'brand pattern' --color '#0a0a0a' --license freemium --auto-ingest 2"
@@ -15136,7 +15315,7 @@ var stockCommand = defineCommand113({
15136
15315
  });
15137
15316
 
15138
15317
  // src/lib/tags-command.ts
15139
- import { defineCommand as defineCommand114 } from "citty";
15318
+ import { defineCommand as defineCommand116 } from "citty";
15140
15319
  function makeTagsCommand(command, label, endpoint) {
15141
15320
  registerSchema({
15142
15321
  command: `${command}.tags`,
@@ -15145,7 +15324,7 @@ function makeTagsCommand(command, label, endpoint) {
15145
15324
  output: { type: "string", description: "Output format: md|json", required: false, default: "md" }
15146
15325
  }
15147
15326
  });
15148
- return defineCommand114({
15327
+ return defineCommand116({
15149
15328
  meta: {
15150
15329
  name: "tags",
15151
15330
  description: `List the available ${label} tag names (defaults + company custom tags). Use before filtering with --tags. Example: baker ${command} tags`
@@ -15181,18 +15360,7 @@ function makeTagsCommand(command, label, endpoint) {
15181
15360
  var tagsCommand2 = makeTagsCommand("images", "image", "/api/images/tags");
15182
15361
 
15183
15362
  // src/commands/images/upload.ts
15184
- import { readFile as readFile12 } from "fs/promises";
15185
- import { extname as extname2 } from "path";
15186
- import { defineCommand as defineCommand115 } from "citty";
15187
- var MIME_MAP = {
15188
- ".png": "image/png",
15189
- ".jpg": "image/jpeg",
15190
- ".jpeg": "image/jpeg",
15191
- ".gif": "image/gif",
15192
- ".webp": "image/webp",
15193
- ".svg": "image/svg+xml",
15194
- ".avif": "image/avif"
15195
- };
15363
+ import { defineCommand as defineCommand117 } from "citty";
15196
15364
  registerSchema({
15197
15365
  command: "images.upload",
15198
15366
  description: "Upload an image to the library \u2014 local file path or remote http(s) URL.",
@@ -15230,15 +15398,7 @@ registerSchema({
15230
15398
  function isRemoteUrl2(value) {
15231
15399
  return /^https?:\/\//i.test(value);
15232
15400
  }
15233
- function detectContentType(filePath) {
15234
- const ext = extname2(filePath).toLowerCase();
15235
- const mime = MIME_MAP[ext];
15236
- if (!mime) {
15237
- throw new ApiError("VALIDATION_ERROR", `Cannot detect content type for extension "${ext}". Use --content-type.`);
15238
- }
15239
- return mime;
15240
- }
15241
- var uploadCommand = defineCommand115({
15401
+ var uploadCommand = defineCommand117({
15242
15402
  meta: {
15243
15403
  name: "upload",
15244
15404
  description: "Upload an image to the library \u2014 accepts a local file path OR a remote http(s) URL.\n\nLocal: reads bytes, sends to /api/images/upload, content-type auto-detected from extension.\nRemote: dispatches to /api/images/ingest with hash-dedup on bytes + externalId.\n\nExamples:\n baker images upload ./logo.png --source uploaded\n baker images upload ./cert.png --context 'ISO 27001 badge \u2014 enterprise tier'\n baker images upload https://acme.com/hero.png --source firecrawl --context 'Acme competitor pricing hero'"
@@ -15306,7 +15466,7 @@ async function uploadRemote(target, args) {
15306
15466
  writeJson({ ok: true, data });
15307
15467
  }
15308
15468
  async function uploadLocal(target, args) {
15309
- const contentType = args["content-type"] || detectContentType(target);
15469
+ const contentType = args["content-type"] || detectImageContentType(target);
15310
15470
  if (args["dry-run"]) {
15311
15471
  writeJson({
15312
15472
  ok: true,
@@ -15321,17 +15481,17 @@ async function uploadLocal(target, args) {
15321
15481
  });
15322
15482
  return;
15323
15483
  }
15324
- const fileBuffer = await readFile12(target);
15325
- const base64 = fileBuffer.toString("base64");
15326
- const body = { base64, contentType };
15327
- if (args.source) body.source = args.source;
15328
- if (args.context) body.descriptionContext = args.context;
15329
- const data = await apiPost("/api/images/upload", body);
15484
+ const data = await uploadLocalImage({
15485
+ file: target,
15486
+ contentType,
15487
+ source: args.source,
15488
+ descriptionContext: args.context
15489
+ });
15330
15490
  writeJson({ ok: true, data });
15331
15491
  }
15332
15492
 
15333
15493
  // src/commands/images/upscale.ts
15334
- import { defineCommand as defineCommand116 } from "citty";
15494
+ import { defineCommand as defineCommand118 } from "citty";
15335
15495
  registerSchema({
15336
15496
  command: "images.upscale",
15337
15497
  description: "Upscale a library image via the backend (Replicate, cost-tracked). Waits for completion by default. The image must be status 'ready' and raster (not SVG/AVIF).",
@@ -15346,7 +15506,7 @@ registerSchema({
15346
15506
  }
15347
15507
  });
15348
15508
  var POLL_INTERVAL_MS3 = 1500;
15349
- var upscaleCommand = defineCommand116({
15509
+ var upscaleCommand = defineCommand118({
15350
15510
  meta: {
15351
15511
  name: "upscale",
15352
15512
  description: "Upscale a library image via the Convex backend (Replicate, cost-tracked at $0.05/image). Waits for completion by default.\n\nExample: baker images upscale j571abc123def\nExample: baker images upscale j571abc123def --max-wait 0 # fire-and-forget"
@@ -15401,7 +15561,7 @@ var upscaleCommand = defineCommand116({
15401
15561
  });
15402
15562
 
15403
15563
  // src/commands/images/use.ts
15404
- import { defineCommand as defineCommand117 } from "citty";
15564
+ import { defineCommand as defineCommand119 } from "citty";
15405
15565
  registerSchema({
15406
15566
  command: "images.use",
15407
15567
  description: "Ingest a URL and wait for the library record to be ready.",
@@ -15417,7 +15577,7 @@ registerSchema({
15417
15577
  }
15418
15578
  });
15419
15579
  var POLL_INTERVAL_MS4 = 1500;
15420
- var useCommand = defineCommand117({
15580
+ var useCommand = defineCommand119({
15421
15581
  meta: {
15422
15582
  name: "use",
15423
15583
  description: "Sugar over `ingest`: download \u2192 store \u2192 wait until describe + embed complete \u2192 return ready library record.\n\nExample: baker images use https://cdn.example.com/hero.png --source uploaded"
@@ -15463,7 +15623,7 @@ var useCommand = defineCommand117({
15463
15623
  });
15464
15624
 
15465
15625
  // src/commands/images/index.ts
15466
- var imagesCommand = defineCommand118({
15626
+ var imagesCommand = defineCommand120({
15467
15627
  meta: {
15468
15628
  name: "images",
15469
15629
  description: `Find, source, and normalize images. Subcommands route by provider so cost + license are explicit.
@@ -15533,10 +15693,10 @@ Paid transforms (run on the Convex backend, cost-tracked):
15533
15693
  });
15534
15694
 
15535
15695
  // src/commands/research/index.ts
15536
- import { defineCommand as defineCommand129 } from "citty";
15696
+ import { defineCommand as defineCommand131 } from "citty";
15537
15697
 
15538
15698
  // src/commands/research/advertisers.ts
15539
- import { defineCommand as defineCommand119 } from "citty";
15699
+ import { defineCommand as defineCommand121 } from "citty";
15540
15700
 
15541
15701
  // src/commands/research/output.ts
15542
15702
  var RESEARCH_DATA_NOTE = "Estimates based on third-party SERP data \u2014 not exact figures. Use for directional insights, not precise measurement.";
@@ -15649,7 +15809,7 @@ var FIELDS3 = {
15649
15809
  etv: "Estimated traffic value (USD)",
15650
15810
  visibility: "SERP visibility score (0-1)"
15651
15811
  };
15652
- var advertisersCommand = defineCommand119({
15812
+ var advertisersCommand = defineCommand121({
15653
15813
  meta: {
15654
15814
  name: "advertisers",
15655
15815
  description: `Find domains competing for a keyword in Google SERPs.
@@ -15696,7 +15856,7 @@ Examples:
15696
15856
  });
15697
15857
 
15698
15858
  // src/commands/research/autocomplete.ts
15699
- import { defineCommand as defineCommand120 } from "citty";
15859
+ import { defineCommand as defineCommand122 } from "citty";
15700
15860
  registerSchema({
15701
15861
  command: "research.autocomplete",
15702
15862
  description: "Get Google Autocomplete suggestions for a seed keyword. Useful for keyword expansion and discovering what people actually search for. IMPORTANT: If --location and --language are omitted, defaults to United States (us) and English (en).",
@@ -15719,7 +15879,7 @@ registerSchema({
15719
15879
  var FIELDS4 = {
15720
15880
  suggestion: "Autocomplete suggestion from Google"
15721
15881
  };
15722
- var autocompleteCommand = defineCommand120({
15882
+ var autocompleteCommand = defineCommand122({
15723
15883
  meta: {
15724
15884
  name: "autocomplete",
15725
15885
  description: `Get Google Autocomplete suggestions for keyword expansion.
@@ -15765,7 +15925,7 @@ Examples:
15765
15925
  });
15766
15926
 
15767
15927
  // src/commands/research/countries.ts
15768
- import { defineCommand as defineCommand121 } from "citty";
15928
+ import { defineCommand as defineCommand123 } from "citty";
15769
15929
  registerSchema({
15770
15930
  command: "research.countries",
15771
15931
  description: "List all supported country codes for --location flag in research commands.",
@@ -15822,7 +15982,7 @@ var FIELDS5 = {
15822
15982
  code: "Country code to pass as --location",
15823
15983
  name: "Country name"
15824
15984
  };
15825
- var countriesCommand = defineCommand121({
15985
+ var countriesCommand = defineCommand123({
15826
15986
  meta: {
15827
15987
  name: "countries",
15828
15988
  description: "List all supported country codes for --location flag."
@@ -15833,7 +15993,7 @@ var countriesCommand = defineCommand121({
15833
15993
  });
15834
15994
 
15835
15995
  // src/commands/research/intent.ts
15836
- import { defineCommand as defineCommand122 } from "citty";
15996
+ import { defineCommand as defineCommand124 } from "citty";
15837
15997
  registerSchema({
15838
15998
  command: "research.intent",
15839
15999
  description: "Classify Google Search intent for keywords. Determines if someone searching is looking to buy, research, or navigate. IMPORTANT: If --language is omitted, defaults to English (en). The response includes a query_context object showing which language was used.",
@@ -15856,7 +16016,7 @@ var FIELDS6 = {
15856
16016
  intent: "Primary Google Search intent: informational, navigational, commercial, transactional",
15857
16017
  probability: "Confidence score 0.0-1.0"
15858
16018
  };
15859
- var intentCommand = defineCommand122({
16019
+ var intentCommand = defineCommand124({
15860
16020
  meta: {
15861
16021
  name: "intent",
15862
16022
  description: `Classify Google Search intent for keywords. Returns intent type and confidence.
@@ -15904,7 +16064,7 @@ Examples:
15904
16064
  });
15905
16065
 
15906
16066
  // src/commands/research/keyword-gap.ts
15907
- import { defineCommand as defineCommand123 } from "citty";
16067
+ import { defineCommand as defineCommand125 } from "citty";
15908
16068
  registerSchema({
15909
16069
  command: "research.keyword-gap",
15910
16070
  description: "Find keywords a competitor ranks for (organic or paid) that you don't. Discovers expansion opportunities. IMPORTANT: If --location and --language are omitted, defaults to United States (us) and English (en). The response includes a query_context object showing which location/language were used.",
@@ -15933,7 +16093,7 @@ var FIELDS7 = {
15933
16093
  cpc: "Cost per click USD",
15934
16094
  their_position: "Competitor's ranking position"
15935
16095
  };
15936
- var keywordGapCommand = defineCommand123({
16096
+ var keywordGapCommand = defineCommand125({
15937
16097
  meta: {
15938
16098
  name: "keyword-gap",
15939
16099
  description: `Find keywords a competitor has that you don't. Supports pagination via --offset.
@@ -16007,7 +16167,7 @@ Examples:
16007
16167
  });
16008
16168
 
16009
16169
  // src/commands/research/keywords-for-site.ts
16010
- import { defineCommand as defineCommand124 } from "citty";
16170
+ import { defineCommand as defineCommand126 } from "citty";
16011
16171
  registerSchema({
16012
16172
  command: "research.keywords-for-site",
16013
16173
  description: "Get keywords a competitor targets in Google. Use --type paid to see only paid keywords, --type organic for organic only. IMPORTANT: If --location and --language are omitted, defaults to United States (us) and English (en). The response includes a query_context object showing which location/language were used.",
@@ -16040,7 +16200,7 @@ var FIELDS8 = {
16040
16200
  competition: "LOW, MEDIUM, or HIGH",
16041
16201
  competition_index: "Competition score 0-100"
16042
16202
  };
16043
- var keywordsForSiteCommand = defineCommand124({
16203
+ var keywordsForSiteCommand = defineCommand126({
16044
16204
  meta: {
16045
16205
  name: "keywords-for-site",
16046
16206
  description: `Get keywords a competitor targets in Google. Use --type to filter paid/organic.
@@ -16093,7 +16253,7 @@ Examples:
16093
16253
  });
16094
16254
 
16095
16255
  // src/commands/research/languages.ts
16096
- import { defineCommand as defineCommand125 } from "citty";
16256
+ import { defineCommand as defineCommand127 } from "citty";
16097
16257
  registerSchema({
16098
16258
  command: "research.languages",
16099
16259
  description: "List all supported language codes for --language flag in research commands.",
@@ -16123,7 +16283,7 @@ var FIELDS9 = {
16123
16283
  code: "Language code to pass as --language",
16124
16284
  name: "Language name (also accepted by --language)"
16125
16285
  };
16126
- var languagesCommand2 = defineCommand125({
16286
+ var languagesCommand2 = defineCommand127({
16127
16287
  meta: {
16128
16288
  name: "languages",
16129
16289
  description: "List all supported language codes for --language flag."
@@ -16134,7 +16294,7 @@ var languagesCommand2 = defineCommand125({
16134
16294
  });
16135
16295
 
16136
16296
  // src/commands/research/lighthouse.ts
16137
- import { defineCommand as defineCommand126 } from "citty";
16297
+ import { defineCommand as defineCommand128 } from "citty";
16138
16298
  registerSchema({
16139
16299
  command: "research.lighthouse",
16140
16300
  description: "Landing page performance audit. Returns metrics that affect Google Ads Quality Score and CPC.",
@@ -16153,7 +16313,7 @@ var FIELDS10 = {
16153
16313
  speed_index_ms: "Speed Index in ms (good: < 3400)",
16154
16314
  interactive_ms: "Time to Interactive in ms (good: < 3800)"
16155
16315
  };
16156
- var lighthouseCommand = defineCommand126({
16316
+ var lighthouseCommand = defineCommand128({
16157
16317
  meta: {
16158
16318
  name: "lighthouse",
16159
16319
  description: `Landing page performance audit. Metrics affecting Google Ads Quality Score.
@@ -16191,7 +16351,7 @@ Examples:
16191
16351
  });
16192
16352
 
16193
16353
  // src/commands/research/relevant-pages.ts
16194
- import { defineCommand as defineCommand127 } from "citty";
16354
+ import { defineCommand as defineCommand129 } from "citty";
16195
16355
  registerSchema({
16196
16356
  command: "research.relevant-pages",
16197
16357
  description: "Get the top pages of a competitor domain with organic traffic and ranking data. Shows which pages drive the most traffic. IMPORTANT: If --location and --language are omitted, defaults to United States (us) and English (en).",
@@ -16217,7 +16377,7 @@ var FIELDS11 = {
16217
16377
  keywords: "Total organic keywords the page ranks for",
16218
16378
  top_10: "Keywords in positions 1-10"
16219
16379
  };
16220
- var relevantPagesCommand = defineCommand127({
16380
+ var relevantPagesCommand = defineCommand129({
16221
16381
  meta: {
16222
16382
  name: "relevant-pages",
16223
16383
  description: `Get the top pages of a competitor domain with traffic data.
@@ -16263,7 +16423,7 @@ Examples:
16263
16423
  });
16264
16424
 
16265
16425
  // src/commands/research/web.ts
16266
- import { defineCommand as defineCommand128 } from "citty";
16426
+ import { defineCommand as defineCommand130 } from "citty";
16267
16427
  registerSchema({
16268
16428
  command: "research.web",
16269
16429
  description: "Search the web with AI to answer marketing questions \u2014 competitors, ICP, pricing, pain points, market trends. Three depth levels: medium (quick, default), high (thorough), xhigh (exhaustive deep research).",
@@ -16314,7 +16474,7 @@ async function runDeepResearch(question) {
16314
16474
  }
16315
16475
  throw new Error("Deep research timed out");
16316
16476
  }
16317
- var webCommand = defineCommand128({
16477
+ var webCommand = defineCommand130({
16318
16478
  meta: {
16319
16479
  name: "web",
16320
16480
  description: `Search the web with AI to answer any open-ended marketing question. Uses live internet data via Google Search.
@@ -16374,7 +16534,7 @@ Examples:
16374
16534
  });
16375
16535
 
16376
16536
  // src/commands/research/index.ts
16377
- var researchCommand = defineCommand129({
16537
+ var researchCommand = defineCommand131({
16378
16538
  meta: {
16379
16539
  name: "research",
16380
16540
  description: `Competitive intelligence and AI-powered research commands.
@@ -16414,10 +16574,10 @@ Examples:
16414
16574
  });
16415
16575
 
16416
16576
  // src/commands/scheduled-actions/index.ts
16417
- import { defineCommand as defineCommand136 } from "citty";
16577
+ import { defineCommand as defineCommand138 } from "citty";
16418
16578
 
16419
16579
  // src/commands/scheduled-actions/create.ts
16420
- import { defineCommand as defineCommand130 } from "citty";
16580
+ import { defineCommand as defineCommand132 } from "citty";
16421
16581
 
16422
16582
  // src/commands/scheduled-actions/shared.ts
16423
16583
  var TEMP_SCHEDULED_ACTION_PREFIX = "temp_sched_";
@@ -16458,6 +16618,16 @@ function validateScheduledActionRef(ref) {
16458
16618
  function isNoSpawnAgentFlagSet(args) {
16459
16619
  return args["no-spawn-agent"] === true || args.noSpawnAgent === true || args.spawnAgent === false;
16460
16620
  }
16621
+ function isPromptWithoutAgent(args, agentDisabled) {
16622
+ return agentDisabled && typeof args.prompt === "string";
16623
+ }
16624
+ function failIfPromptWithoutAgent(args, agentDisabled) {
16625
+ if (isPromptWithoutAgent(args, agentDisabled)) {
16626
+ failValidation2(
16627
+ "--prompt only applies when an agent is spawned; it has no effect with --no-spawn-agent or --spawn-agent false."
16628
+ );
16629
+ }
16630
+ }
16461
16631
  function parseBooleanFlag(raw, flagName) {
16462
16632
  if (raw === void 0) {
16463
16633
  return void 0;
@@ -16522,7 +16692,7 @@ registerSchema({
16522
16692
  prompt: { type: "string", description: "Additional prompt instructions for the spawned agent", required: false }
16523
16693
  }
16524
16694
  });
16525
- var createCommand2 = defineCommand130({
16695
+ var createCommand2 = defineCommand132({
16526
16696
  meta: {
16527
16697
  name: "create",
16528
16698
  description: 'Stage a scheduled action. Example: baker scheduled-actions create --name "Weekly report" --description "..." --cron "0 9 * * MON"'
@@ -16550,6 +16720,7 @@ var createCommand2 = defineCommand130({
16550
16720
  const schedule = buildScheduleBody(args, { required: true });
16551
16721
  const chatId = requireChatId();
16552
16722
  const noSpawnAgent = isNoSpawnAgentFlagSet(args);
16723
+ failIfPromptWithoutAgent(args, noSpawnAgent);
16553
16724
  const body = {
16554
16725
  chatId,
16555
16726
  name,
@@ -16570,7 +16741,7 @@ var createCommand2 = defineCommand130({
16570
16741
  });
16571
16742
 
16572
16743
  // src/commands/scheduled-actions/delete.ts
16573
- import { defineCommand as defineCommand131 } from "citty";
16744
+ import { defineCommand as defineCommand133 } from "citty";
16574
16745
  registerSchema({
16575
16746
  command: "scheduled-actions.delete",
16576
16747
  description: "Stage deletion of a published scheduled action or cancellation of a temp_sched_* draft creation.",
@@ -16578,7 +16749,7 @@ registerSchema({
16578
16749
  id: { type: "string", description: "Published scheduled action ID or temp_sched_* draft ID", required: true }
16579
16750
  }
16580
16751
  });
16581
- var deleteCommand2 = defineCommand131({
16752
+ var deleteCommand2 = defineCommand133({
16582
16753
  meta: {
16583
16754
  name: "delete",
16584
16755
  description: "Stage scheduled action deletion. Example: baker scheduled-actions delete <id-or-temp_sched_id>"
@@ -16607,7 +16778,7 @@ var deleteCommand2 = defineCommand131({
16607
16778
  });
16608
16779
 
16609
16780
  // src/commands/scheduled-actions/get.ts
16610
- import { defineCommand as defineCommand132 } from "citty";
16781
+ import { defineCommand as defineCommand134 } from "citty";
16611
16782
  registerSchema({
16612
16783
  command: "scheduled-actions.get",
16613
16784
  description: "Get a published scheduled action or a temp_sched_* draft-created scheduled action.",
@@ -16615,7 +16786,7 @@ registerSchema({
16615
16786
  id: { type: "string", description: "Published scheduled action ID or temp_sched_* draft ID", required: true }
16616
16787
  }
16617
16788
  });
16618
- var getCommand3 = defineCommand132({
16789
+ var getCommand3 = defineCommand134({
16619
16790
  meta: {
16620
16791
  name: "get",
16621
16792
  description: "Get a scheduled action. Example: baker scheduled-actions get <id-or-temp_sched_id>"
@@ -16652,13 +16823,13 @@ var getCommand3 = defineCommand132({
16652
16823
  });
16653
16824
 
16654
16825
  // src/commands/scheduled-actions/list.ts
16655
- import { defineCommand as defineCommand133 } from "citty";
16826
+ import { defineCommand as defineCommand135 } from "citty";
16656
16827
  registerSchema({
16657
16828
  command: "scheduled-actions.list",
16658
16829
  description: "List published scheduled actions. Includes draft state when BAKER_CHAT_ID is set.",
16659
16830
  args: {}
16660
16831
  });
16661
- var listCommand3 = defineCommand133({
16832
+ var listCommand3 = defineCommand135({
16662
16833
  meta: {
16663
16834
  name: "list",
16664
16835
  description: "List scheduled actions. Includes staged draft ops when BAKER_CHAT_ID is set."
@@ -16679,7 +16850,7 @@ var listCommand3 = defineCommand133({
16679
16850
  });
16680
16851
 
16681
16852
  // src/commands/scheduled-actions/trigger.ts
16682
- import { defineCommand as defineCommand134 } from "citty";
16853
+ import { defineCommand as defineCommand136 } from "citty";
16683
16854
  registerSchema({
16684
16855
  command: "scheduled-actions.trigger",
16685
16856
  description: "Immediately trigger a published scheduled action. Does not require BAKER_CHAT_ID and rejects temp_sched_* IDs.",
@@ -16687,7 +16858,7 @@ registerSchema({
16687
16858
  id: { type: "string", description: "Published scheduled action ID", required: true }
16688
16859
  }
16689
16860
  });
16690
- var triggerCommand = defineCommand134({
16861
+ var triggerCommand = defineCommand136({
16691
16862
  meta: {
16692
16863
  name: "trigger",
16693
16864
  description: "Immediately trigger a published scheduled action. Example: baker scheduled-actions trigger <id>"
@@ -16724,7 +16895,7 @@ var triggerCommand = defineCommand134({
16724
16895
  });
16725
16896
 
16726
16897
  // src/commands/scheduled-actions/update.ts
16727
- import { defineCommand as defineCommand135 } from "citty";
16898
+ import { defineCommand as defineCommand137 } from "citty";
16728
16899
  registerSchema({
16729
16900
  command: "scheduled-actions.update",
16730
16901
  description: "Stage an update to a published scheduled action or temp_sched_* draft-created scheduled action.",
@@ -16749,7 +16920,7 @@ registerSchema({
16749
16920
  prompt: { type: "string", description: "Replacement additional spawned-agent instructions", required: false }
16750
16921
  }
16751
16922
  });
16752
- var updateCommand2 = defineCommand135({
16923
+ var updateCommand2 = defineCommand137({
16753
16924
  meta: {
16754
16925
  name: "update",
16755
16926
  description: "Stage a scheduled action update. Example: baker scheduled-actions update <id> --enabled false"
@@ -16800,6 +16971,7 @@ var updateCommand2 = defineCommand135({
16800
16971
  body.spawnAgent = spawnAgent;
16801
16972
  hasPatch = true;
16802
16973
  }
16974
+ failIfPromptWithoutAgent(args, spawnAgent === false);
16803
16975
  if (typeof args.prompt === "string") {
16804
16976
  body.agentPrompt = args.prompt;
16805
16977
  hasPatch = true;
@@ -16819,7 +16991,7 @@ var updateCommand2 = defineCommand135({
16819
16991
  });
16820
16992
 
16821
16993
  // src/commands/scheduled-actions/index.ts
16822
- var scheduledActionsCommand = defineCommand136({
16994
+ var scheduledActionsCommand = defineCommand138({
16823
16995
  meta: {
16824
16996
  name: "scheduled-actions",
16825
16997
  description: `Manage Scheduled Actions. Subcommands: list, get, create, update, delete, trigger.
@@ -16845,8 +17017,8 @@ Examples:
16845
17017
  });
16846
17018
 
16847
17019
  // src/commands/schema.ts
16848
- import { defineCommand as defineCommand137 } from "citty";
16849
- var schemaCommand = defineCommand137({
17020
+ import { defineCommand as defineCommand139 } from "citty";
17021
+ var schemaCommand = defineCommand139({
16850
17022
  meta: {
16851
17023
  name: "schema",
16852
17024
  description: "Inspect command argument schemas (for AI agent introspection). Lists all commands if no argument given. Example: baker schema images.search"
@@ -16882,10 +17054,10 @@ var schemaCommand = defineCommand137({
16882
17054
  });
16883
17055
 
16884
17056
  // src/commands/testimonials/index.ts
16885
- import { defineCommand as defineCommand141 } from "citty";
17057
+ import { defineCommand as defineCommand143 } from "citty";
16886
17058
 
16887
17059
  // src/commands/testimonials/get.ts
16888
- import { defineCommand as defineCommand138 } from "citty";
17060
+ import { defineCommand as defineCommand140 } from "citty";
16889
17061
  registerSchema({
16890
17062
  command: "testimonials.get",
16891
17063
  description: "Get a single testimonial by ID",
@@ -16893,7 +17065,7 @@ registerSchema({
16893
17065
  id: { type: "string", description: "Testimonial ID", required: true }
16894
17066
  }
16895
17067
  });
16896
- var getCommand4 = defineCommand138({
17068
+ var getCommand4 = defineCommand140({
16897
17069
  meta: { name: "get", description: "Get a single testimonial by ID. Example: baker testimonials get j571abc123" },
16898
17070
  args: {
16899
17071
  id: { type: "positional", description: "Testimonial ID", required: false },
@@ -16930,7 +17102,7 @@ var getCommand4 = defineCommand138({
16930
17102
  });
16931
17103
 
16932
17104
  // src/commands/testimonials/list.ts
16933
- import { defineCommand as defineCommand139 } from "citty";
17105
+ import { defineCommand as defineCommand141 } from "citty";
16934
17106
  registerSchema({
16935
17107
  command: "testimonials.list",
16936
17108
  description: "List testimonials with optional filters.",
@@ -16960,7 +17132,7 @@ registerSchema({
16960
17132
  limit: { type: "number", description: "Max results (default 50)", required: false, default: 50 }
16961
17133
  }
16962
17134
  });
16963
- var listCommand4 = defineCommand139({
17135
+ var listCommand4 = defineCommand141({
16964
17136
  meta: {
16965
17137
  name: "list",
16966
17138
  description: "List testimonials with optional filters. Example: baker testimonials list --source google --sentiment positive"
@@ -17009,7 +17181,7 @@ var listCommand4 = defineCommand139({
17009
17181
  });
17010
17182
 
17011
17183
  // src/commands/testimonials/search.ts
17012
- import { defineCommand as defineCommand140 } from "citty";
17184
+ import { defineCommand as defineCommand142 } from "citty";
17013
17185
  registerSchema({
17014
17186
  command: "testimonials.search",
17015
17187
  description: "Search testimonials by text query. Uses hybrid BM25 + vector + reranking.",
@@ -17040,7 +17212,7 @@ registerSchema({
17040
17212
  tags: { type: "string", description: "Comma-separated tags to filter by", required: false }
17041
17213
  }
17042
17214
  });
17043
- var searchCommand2 = defineCommand140({
17215
+ var searchCommand2 = defineCommand142({
17044
17216
  meta: {
17045
17217
  name: "search",
17046
17218
  description: "Semantic search testimonials by text query. Uses hybrid BM25 + vector + reranking. Example: baker testimonials search 'great service' --rating-min 4"
@@ -17114,7 +17286,7 @@ var searchCommand2 = defineCommand140({
17114
17286
  var tagsCommand3 = makeTagsCommand("testimonials", "testimonial", "/api/testimonials/tags");
17115
17287
 
17116
17288
  // src/commands/testimonials/index.ts
17117
- var testimonialsCommand = defineCommand141({
17289
+ var testimonialsCommand = defineCommand143({
17118
17290
  meta: {
17119
17291
  name: "testimonials",
17120
17292
  description: `Find and browse testimonials in Baker. Subcommands: search, get, list, tags.
@@ -17135,10 +17307,10 @@ Examples:
17135
17307
  });
17136
17308
 
17137
17309
  // src/commands/videos/index.ts
17138
- import { defineCommand as defineCommand146 } from "citty";
17310
+ import { defineCommand as defineCommand148 } from "citty";
17139
17311
 
17140
17312
  // src/commands/videos/delete.ts
17141
- import { defineCommand as defineCommand142 } from "citty";
17313
+ import { defineCommand as defineCommand144 } from "citty";
17142
17314
  registerSchema({
17143
17315
  command: "videos.delete",
17144
17316
  description: "Delete a video by ID",
@@ -17152,7 +17324,7 @@ registerSchema({
17152
17324
  }
17153
17325
  }
17154
17326
  });
17155
- var deleteCommand3 = defineCommand142({
17327
+ var deleteCommand3 = defineCommand144({
17156
17328
  meta: {
17157
17329
  name: "delete",
17158
17330
  description: "Delete a video by ID. Use --dry-run to preview. Example: baker videos delete j571abc123 --dry-run"
@@ -17193,7 +17365,7 @@ var deleteCommand3 = defineCommand142({
17193
17365
  });
17194
17366
 
17195
17367
  // src/commands/videos/get.ts
17196
- import { defineCommand as defineCommand143 } from "citty";
17368
+ import { defineCommand as defineCommand145 } from "citty";
17197
17369
  registerSchema({
17198
17370
  command: "videos.get",
17199
17371
  description: "Get a single video by ID",
@@ -17201,7 +17373,7 @@ registerSchema({
17201
17373
  id: { type: "string", description: "Video ID", required: true }
17202
17374
  }
17203
17375
  });
17204
- var getCommand5 = defineCommand143({
17376
+ var getCommand5 = defineCommand145({
17205
17377
  meta: { name: "get", description: "Get a single video by ID. Example: baker videos get j571abc123" },
17206
17378
  args: {
17207
17379
  id: { type: "positional", description: "Video ID", required: false },
@@ -17238,7 +17410,7 @@ var getCommand5 = defineCommand143({
17238
17410
  });
17239
17411
 
17240
17412
  // src/commands/videos/search.ts
17241
- import { defineCommand as defineCommand144 } from "citty";
17413
+ import { defineCommand as defineCommand146 } from "citty";
17242
17414
  registerSchema({
17243
17415
  command: "videos.search",
17244
17416
  description: "Search videos by text query. Only returns ready videos.",
@@ -17248,7 +17420,7 @@ registerSchema({
17248
17420
  tags: { type: "string", description: "Comma-separated tags to filter by", required: false }
17249
17421
  }
17250
17422
  });
17251
- var searchCommand3 = defineCommand144({
17423
+ var searchCommand3 = defineCommand146({
17252
17424
  meta: {
17253
17425
  name: "search",
17254
17426
  description: "Semantic search videos by text query. Uses hybrid BM25 + vector + reranking. Example: baker videos search 'product demo' --tags tutorial"
@@ -17298,10 +17470,10 @@ var searchCommand3 = defineCommand144({
17298
17470
  var tagsCommand4 = makeTagsCommand("videos", "video", "/api/videos/tags");
17299
17471
 
17300
17472
  // src/commands/videos/upload.ts
17301
- import { readFile as readFile13, stat as stat3 } from "fs/promises";
17302
- import { extname as extname3 } from "path";
17303
- import { defineCommand as defineCommand145 } from "citty";
17304
- var MIME_MAP2 = {
17473
+ import { readFile as readFile12, stat as stat3 } from "fs/promises";
17474
+ import { extname as extname4 } from "path";
17475
+ import { defineCommand as defineCommand147 } from "citty";
17476
+ var MIME_MAP = {
17305
17477
  ".mp4": "video/mp4",
17306
17478
  ".mov": "video/quicktime",
17307
17479
  ".webm": "video/webm",
@@ -17326,15 +17498,15 @@ registerSchema({
17326
17498
  }
17327
17499
  }
17328
17500
  });
17329
- function detectContentType2(filePath) {
17330
- const ext = extname3(filePath).toLowerCase();
17331
- const mime = MIME_MAP2[ext];
17501
+ function detectContentType(filePath) {
17502
+ const ext = extname4(filePath).toLowerCase();
17503
+ const mime = MIME_MAP[ext];
17332
17504
  if (!mime) {
17333
17505
  throw new ApiError("VALIDATION_ERROR", `Cannot detect content type for extension "${ext}". Use --content-type.`);
17334
17506
  }
17335
17507
  return mime;
17336
17508
  }
17337
- var uploadCommand2 = defineCommand145({
17509
+ var uploadCommand2 = defineCommand147({
17338
17510
  meta: {
17339
17511
  name: "upload",
17340
17512
  description: "Upload a video file to Baker via Mux direct upload. Auto-detects content type. Example: baker videos upload ./demo.mp4"
@@ -17351,7 +17523,7 @@ var uploadCommand2 = defineCommand145({
17351
17523
  writeJson({ ok: false, error: { code: "VALIDATION_ERROR", message: "File path is required" } });
17352
17524
  process.exit(1);
17353
17525
  }
17354
- const contentType = args["content-type"] || detectContentType2(filePath);
17526
+ const contentType = args["content-type"] || detectContentType(filePath);
17355
17527
  if (args["dry-run"]) {
17356
17528
  const fileStats = await stat3(filePath);
17357
17529
  writeJson({
@@ -17363,7 +17535,7 @@ var uploadCommand2 = defineCommand145({
17363
17535
  return;
17364
17536
  }
17365
17537
  const { uploadUrl, videoId } = await apiPost("/api/videos/upload", {});
17366
- const fileBuffer = await readFile13(filePath);
17538
+ const fileBuffer = await readFile12(filePath);
17367
17539
  const uploadResponse = await fetch(uploadUrl, {
17368
17540
  method: "PUT",
17369
17541
  headers: { "Content-Type": contentType },
@@ -17388,7 +17560,7 @@ var uploadCommand2 = defineCommand145({
17388
17560
  });
17389
17561
 
17390
17562
  // src/commands/videos/index.ts
17391
- var videosCommand = defineCommand146({
17563
+ var videosCommand = defineCommand148({
17392
17564
  meta: {
17393
17565
  name: "videos",
17394
17566
  description: `Find and manage videos in Baker. Subcommands: search, get, upload, delete, tags.
@@ -17411,10 +17583,10 @@ Examples:
17411
17583
  });
17412
17584
 
17413
17585
  // src/commands/winning-ads/index.ts
17414
- import { defineCommand as defineCommand149 } from "citty";
17586
+ import { defineCommand as defineCommand151 } from "citty";
17415
17587
 
17416
17588
  // src/commands/winning-ads/advertisers.ts
17417
- import { defineCommand as defineCommand147 } from "citty";
17589
+ import { defineCommand as defineCommand149 } from "citty";
17418
17590
  registerSchema({
17419
17591
  command: "winning-ads.advertisers",
17420
17592
  description: "Resolve a brand name to advertiser_id(s) in the ad-dna corpus \u2014 to find your OWN advertiser (to --exclude-advertiser) or a competitor (to --advertiser-id).",
@@ -17427,7 +17599,7 @@ registerSchema({
17427
17599
  function identity(record) {
17428
17600
  return record;
17429
17601
  }
17430
- var advertisersCommand2 = defineCommand147({
17602
+ var advertisersCommand2 = defineCommand149({
17431
17603
  meta: {
17432
17604
  name: "advertisers",
17433
17605
  description: 'Resolve a brand name to advertiser_id(s). Use it to find your own advertiser for --exclude-advertiser, or a competitor for --advertiser-id. Example: baker winning-ads advertisers "Deel" --output md'
@@ -17478,7 +17650,8 @@ var advertisersCommand2 = defineCommand147({
17478
17650
  });
17479
17651
 
17480
17652
  // src/commands/winning-ads/search.ts
17481
- import { defineCommand as defineCommand148 } from "citty";
17653
+ import { defineCommand as defineCommand150 } from "citty";
17654
+ import { z as z4 } from "zod";
17482
17655
  registerSchema({
17483
17656
  command: "winning-ads.search",
17484
17657
  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.",
@@ -17586,7 +17759,39 @@ function buildSearchBody(args) {
17586
17759
  }
17587
17760
  return body;
17588
17761
  }
17589
- var searchCommand4 = defineCommand148({
17762
+ function toOutputRecord(value) {
17763
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
17764
+ return {};
17765
+ }
17766
+ return Object.fromEntries(Object.entries(value));
17767
+ }
17768
+ var winningAdsSearchResponseSchema = z4.object({
17769
+ results: z4.array(z4.unknown()).optional(),
17770
+ pool_size: z4.number().nullable().optional(),
17771
+ match_confidence: z4.string().nullable().optional(),
17772
+ ownAdvertiserExclusion: z4.object({
17773
+ status: z4.enum(["applied", "unresolved", "skipped"]),
17774
+ excludedIds: z4.array(z4.string())
17775
+ }).nullable().optional()
17776
+ });
17777
+ function parseWinningAdsSearchResponse(data) {
17778
+ const parsed = winningAdsSearchResponseSchema.safeParse(data);
17779
+ if (!parsed.success) {
17780
+ throw new ApiError("INTERNAL_ERROR", "Invalid winning ads search response");
17781
+ }
17782
+ return parsed.data;
17783
+ }
17784
+ function buildSearchOutputData(data, options) {
17785
+ const rawResults = Array.isArray(data?.results) ? data.results : [];
17786
+ const results = rawResults.map((r) => winningAdNormalizer(toOutputRecord(r), options.full));
17787
+ return {
17788
+ results,
17789
+ pool_size: data?.pool_size ?? null,
17790
+ match_confidence: data?.match_confidence ?? null,
17791
+ ownAdvertiserExclusion: data?.ownAdvertiserExclusion ?? null
17792
+ };
17793
+ }
17794
+ var searchCommand4 = defineCommand150({
17590
17795
  meta: {
17591
17796
  name: "search",
17592
17797
  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"
@@ -17664,19 +17869,12 @@ var searchCommand4 = defineCommand148({
17664
17869
  });
17665
17870
  process.exit(1);
17666
17871
  }
17667
- const data = await apiPost(
17668
- "/api/winning-ads/search",
17669
- body
17670
- );
17872
+ const data = parseWinningAdsSearchResponse(await apiPost("/api/winning-ads/search", body));
17671
17873
  const output = args.output || "json";
17672
17874
  const full = args.full;
17673
17875
  const rawResults = Array.isArray(data?.results) ? data.results : [];
17674
17876
  if (output === "json") {
17675
- const results = rawResults.map((r) => winningAdNormalizer(r, full));
17676
- writeJson({
17677
- ok: true,
17678
- data: { results, pool_size: data?.pool_size ?? null, match_confidence: data?.match_confidence ?? null }
17679
- });
17877
+ writeJson({ ok: true, data: buildSearchOutputData(data, { full }) });
17680
17878
  return;
17681
17879
  }
17682
17880
  writeOutput(
@@ -17698,7 +17896,7 @@ var searchCommand4 = defineCommand148({
17698
17896
  });
17699
17897
 
17700
17898
  // src/commands/winning-ads/index.ts
17701
- var winningAdsCommand = defineCommand149({
17899
+ var winningAdsCommand = defineCommand151({
17702
17900
  meta: {
17703
17901
  name: "winning-ads",
17704
17902
  description: `Search the ad-dna corpus of scored "winning" ads for reference creatives to reproduce. Proxied through the Baker backend (BAKER_API_KEY) \u2014 no separate token needed.
@@ -17738,7 +17936,7 @@ function getCliVersion() {
17738
17936
  }
17739
17937
 
17740
17938
  // src/cli.ts
17741
- var main = defineCommand150({
17939
+ var main = defineCommand152({
17742
17940
  meta: {
17743
17941
  name: "baker",
17744
17942
  version: getCliVersion(),
@@ -17757,6 +17955,7 @@ Introspection: Run 'baker schema <command>' to inspect argument schemas.`
17757
17955
  ga4: ga4Command,
17758
17956
  gsc: gscCommand,
17759
17957
  research: researchCommand,
17958
+ creatives: creativesCommand3,
17760
17959
  images: imagesCommand,
17761
17960
  videos: videosCommand,
17762
17961
  testimonials: testimonialsCommand,