@koda-sl/baker-cli 0.104.0 → 0.106.0-dev.a605c0acb

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
@@ -9,10 +9,10 @@ import {
9
9
  defaultRegistry,
10
10
  generateCatalog,
11
11
  validateCanvasDeep
12
- } from "./chunk-3JVYU72O.js";
12
+ } from "./chunk-26K7V346.js";
13
13
 
14
14
  // src/cli.ts
15
- import { defineCommand as defineCommand152, runMain } from "citty";
15
+ import { defineCommand as defineCommand154, runMain } from "citty";
16
16
 
17
17
  // src/commands/actions/index.ts
18
18
  import { defineCommand as defineCommand18 } from "citty";
@@ -935,6 +935,990 @@ var actionsTagsCreateResponseSchema = okResponse(z2.object({ tagId: z2.string()
935
935
  var actionsTagsDeleteRequestSchema = z2.object({ name: z2.string() });
936
936
  var actionsTagsDeleteResponseSchema = okOnlySchema();
937
937
 
938
+ // ../api/src/ads-linkedin/limits.ts
939
+ var LINKEDIN_LIMITS = {
940
+ campaignGroup: {
941
+ nameMax: 100
942
+ },
943
+ campaign: {
944
+ nameMax: 255
945
+ },
946
+ creative: {
947
+ commentarySoftMax: 600,
948
+ // feed truncates with "…see more" beyond this
949
+ commentaryHardMax: 3e3,
950
+ headlineMax: 70,
951
+ descriptionMax: 300,
952
+ textAdHeadlineMax: 25,
953
+ textAdDescriptionMax: 75,
954
+ spotlightHeadlineMax: 70,
955
+ spotlightDescriptionMax: 70,
956
+ spotlightCtaMax: 18,
957
+ carouselCardHeadlineMax: 45,
958
+ carouselCardsMin: 2,
959
+ carouselCardsMax: 10,
960
+ conversationButtonLabelMax: 25
961
+ },
962
+ audience: {
963
+ nameMax: 100,
964
+ minRowsToServe: 300,
965
+ // LinkedIn refuses to serve smaller matched audiences
966
+ inlineRowsMax: 1e4,
967
+ // above this the list must come as a stored file ref
968
+ rowsMax: 3e5
969
+ },
970
+ conversion: {
971
+ nameMax: 255,
972
+ postClickWindows: [1, 7, 30, 90],
973
+ viewThroughWindows: [1, 7, 30]
974
+ },
975
+ leadForm: {
976
+ nameMax: 256,
977
+ headlineMax: 60,
978
+ descriptionMax: 160,
979
+ questionsMax: 12,
980
+ questionsSoftMax: 4,
981
+ // playbook: >4 fields tanks completion rate
982
+ customQuestionsMax: 3,
983
+ thankYouMessageMax: 300
984
+ }
985
+ };
986
+ var CAMPAIGN_TYPES = ["SPONSORED_UPDATES", "TEXT_AD", "SPONSORED_INMAILS", "DYNAMIC"];
987
+ var COST_TYPES = ["CPC", "CPM", "CPV"];
988
+ var OBJECTIVE_TYPES = [
989
+ "BRAND_AWARENESS",
990
+ "ENGAGEMENT",
991
+ "JOB_APPLICANT",
992
+ "LEAD_GENERATION",
993
+ "WEBSITE_CONVERSION",
994
+ "WEBSITE_VISIT",
995
+ "VIDEO_VIEW",
996
+ "TALENT_LEAD"
997
+ ];
998
+ var OBJECTIVE_CAMPAIGN_TYPES = {
999
+ VIDEO_VIEW: ["SPONSORED_UPDATES"],
1000
+ LEAD_GENERATION: ["SPONSORED_UPDATES", "SPONSORED_INMAILS"],
1001
+ ENGAGEMENT: ["SPONSORED_UPDATES", "DYNAMIC"]
1002
+ };
1003
+ var STAGEABLE_CREATE_STATUSES = ["DRAFT", "ACTIVE", "PAUSED"];
1004
+ var CREATIVE_INTENDED_STATUSES = ["ACTIVE", "PAUSED", "DRAFT"];
1005
+ var CTA_TYPES = [
1006
+ "APPLY",
1007
+ "DOWNLOAD",
1008
+ "VIEW_QUOTE",
1009
+ "LEARN_MORE",
1010
+ "SIGN_UP",
1011
+ "SUBSCRIBE",
1012
+ "REGISTER",
1013
+ "JOIN",
1014
+ "ATTEND",
1015
+ "REQUEST_DEMO",
1016
+ "SEE_MORE"
1017
+ ];
1018
+ var CREATIVE_SELECTIONS = ["OPTIMIZED", "ROUND_ROBIN"];
1019
+ var AUDIENCE_TYPES = ["company-list", "user-list", "retargeting", "engagement"];
1020
+ var CONVERSION_TYPES = [
1021
+ "ADD_TO_CART",
1022
+ "DOWNLOAD",
1023
+ "INSTALL",
1024
+ "KEY_PAGE_VIEW",
1025
+ "LEAD",
1026
+ "PURCHASE",
1027
+ "SIGN_UP",
1028
+ "OTHER"
1029
+ ];
1030
+ var CONVERSION_METHODS = ["INSIGHT_TAG", "CONVERSIONS_API"];
1031
+ var ATTRIBUTION_TYPES = ["LAST_TOUCH_BY_CAMPAIGN", "LAST_TOUCH_BY_CONVERSION"];
1032
+ var CURRENCY_MINIMUMS = {
1033
+ USD: { dailyBudgetMin: 10, unitCostMin: 2 },
1034
+ EUR: { dailyBudgetMin: 10, unitCostMin: 2 },
1035
+ GBP: { dailyBudgetMin: 8, unitCostMin: 2 }
1036
+ };
1037
+ var DEFAULT_CURRENCY_MINIMUM = { dailyBudgetMin: 10, unitCostMin: 2 };
1038
+ var TEMP_REF_REGEX = /^li_temp_[A-Za-z0-9_-]{4,}$/;
1039
+ var NUMERIC_ID_REGEX = /^\d+$/;
1040
+ var URN_REGEX = /^urn:li:[a-zA-Z]+:.+$/;
1041
+ var IMAGE_URN_REGEX = /^urn:li:image:.+$/;
1042
+ var VIDEO_URN_REGEX = /^urn:li:video:.+$/;
1043
+ var DOCUMENT_URN_REGEX = /^urn:li:document:.+$/;
1044
+ var POST_URN_REGEX = /^urn:li:(share|ugcPost):\d+$/;
1045
+ var ORGANIZATION_URN_REGEX = /^urn:li:organization:\d+$/;
1046
+ var TARGETING_FACET_URN_REGEX = /^urn:li:adTargetingFacet:[a-zA-Z]+$/;
1047
+
1048
+ // ../api/src/ads-linkedin/ops.ts
1049
+ import { z as z3 } from "zod";
1050
+ var tempRefSchema = z3.string().regex(TEMP_REF_REGEX, "expected a li_temp_* reference");
1051
+ var parentRefSchema = z3.union([
1052
+ z3.string().regex(URN_REGEX, "expected a urn:li:* value"),
1053
+ z3.string().regex(NUMERIC_ID_REGEX, "expected a numeric id"),
1054
+ tempRefSchema
1055
+ ]);
1056
+ var moneySchema = z3.object({
1057
+ amount: z3.string().regex(/^\d+(\.\d{1,2})?$/, "expected a decimal amount like 50 or 50.00").refine((val) => Number(val) > 0, "amount must be greater than zero"),
1058
+ currencyCode: z3.string().length(3)
1059
+ });
1060
+ var runScheduleSchema = z3.object({
1061
+ start: z3.number().int().positive(),
1062
+ end: z3.number().int().positive().optional()
1063
+ }).refine((s) => s.end === void 0 || s.end > s.start, {
1064
+ message: "runSchedule.end must be after runSchedule.start",
1065
+ path: ["end"]
1066
+ });
1067
+ var facetValuesSchema = z3.array(z3.union([z3.string().regex(URN_REGEX), tempRefSchema])).min(1);
1068
+ var targetingCriteriaSchema = z3.object({
1069
+ include: z3.object({
1070
+ and: z3.array(
1071
+ z3.object({
1072
+ or: z3.record(z3.string().regex(TARGETING_FACET_URN_REGEX, "invalid targeting facet URN"), facetValuesSchema)
1073
+ })
1074
+ ).min(1, "targeting needs at least one include facet group")
1075
+ }),
1076
+ exclude: z3.object({
1077
+ or: z3.record(z3.string().regex(TARGETING_FACET_URN_REGEX, "invalid targeting facet URN"), facetValuesSchema)
1078
+ }).optional()
1079
+ });
1080
+ var stageableStatusSchema = z3.enum(STAGEABLE_CREATE_STATUSES);
1081
+ var entityStatusSchema = z3.enum(["ACTIVE", "PAUSED", "ARCHIVED", "CANCELED", "DRAFT", "COMPLETED"]);
1082
+ function currencyMinimums(currencyCode) {
1083
+ return CURRENCY_MINIMUMS[currencyCode] ?? DEFAULT_CURRENCY_MINIMUM;
1084
+ }
1085
+ var campaignGroupCreateSchema = z3.object({
1086
+ name: z3.string().min(1).max(LINKEDIN_LIMITS.campaignGroup.nameMax),
1087
+ runSchedule: runScheduleSchema,
1088
+ totalBudget: moneySchema.optional(),
1089
+ status: stageableStatusSchema.default("DRAFT")
1090
+ });
1091
+ var campaignGroupUpdateSchema = z3.object({
1092
+ name: z3.string().min(1).max(LINKEDIN_LIMITS.campaignGroup.nameMax).optional(),
1093
+ runSchedule: runScheduleSchema.optional(),
1094
+ totalBudget: moneySchema.optional(),
1095
+ status: entityStatusSchema.optional()
1096
+ }).refine((p) => Object.values(p).some((val) => val !== void 0), "update needs at least one field");
1097
+ var campaignFields = {
1098
+ name: z3.string().min(1).max(LINKEDIN_LIMITS.campaign.nameMax),
1099
+ campaignGroup: parentRefSchema,
1100
+ type: z3.enum(CAMPAIGN_TYPES),
1101
+ objectiveType: z3.enum(OBJECTIVE_TYPES).optional(),
1102
+ costType: z3.enum(COST_TYPES),
1103
+ unitCost: moneySchema.optional(),
1104
+ dailyBudget: moneySchema.optional(),
1105
+ totalBudget: moneySchema.optional(),
1106
+ runSchedule: runScheduleSchema.optional(),
1107
+ locale: z3.object({ country: z3.string().length(2), language: z3.string().length(2) }),
1108
+ targetingCriteria: targetingCriteriaSchema,
1109
+ audienceExpansionEnabled: z3.boolean().default(false),
1110
+ offsiteDeliveryEnabled: z3.boolean().default(false),
1111
+ connectedTelevisionOnly: z3.boolean().default(false),
1112
+ creativeSelection: z3.enum(CREATIVE_SELECTIONS).optional(),
1113
+ associatedEntity: z3.string().regex(ORGANIZATION_URN_REGEX).optional(),
1114
+ status: stageableStatusSchema.default("DRAFT")
1115
+ };
1116
+ function validateCampaignBudgets(p, ctx, { requireBudget }) {
1117
+ if (requireBudget && !p.dailyBudget && !p.totalBudget) {
1118
+ ctx.addIssue({ code: "custom", path: ["dailyBudget"], message: "set dailyBudget and/or totalBudget" });
1119
+ }
1120
+ if (p.totalBudget && !p.dailyBudget && !p.runSchedule?.end) {
1121
+ ctx.addIssue({
1122
+ code: "custom",
1123
+ path: ["runSchedule", "end"],
1124
+ message: "totalBudget-only campaigns need runSchedule.end"
1125
+ });
1126
+ }
1127
+ if (p.dailyBudget) {
1128
+ const min = currencyMinimums(p.dailyBudget.currencyCode).dailyBudgetMin;
1129
+ if (Number(p.dailyBudget.amount) < min) {
1130
+ ctx.addIssue({
1131
+ code: "custom",
1132
+ path: ["dailyBudget", "amount"],
1133
+ message: `dailyBudget below the ${min} ${p.dailyBudget.currencyCode} minimum`
1134
+ });
1135
+ }
1136
+ }
1137
+ if (p.unitCost) {
1138
+ const min = currencyMinimums(p.unitCost.currencyCode).unitCostMin;
1139
+ if (Number(p.unitCost.amount) < min) {
1140
+ ctx.addIssue({
1141
+ code: "custom",
1142
+ path: ["unitCost", "amount"],
1143
+ message: `unitCost below the ${min} ${p.unitCost.currencyCode} minimum bid`
1144
+ });
1145
+ }
1146
+ }
1147
+ }
1148
+ var campaignCreateSchema = z3.object(campaignFields).superRefine((p, ctx) => {
1149
+ validateCampaignBudgets(p, ctx, { requireBudget: true });
1150
+ const allowedTypes = p.objectiveType ? OBJECTIVE_CAMPAIGN_TYPES[p.objectiveType] : void 0;
1151
+ if (allowedTypes && !allowedTypes.includes(p.type)) {
1152
+ ctx.addIssue({
1153
+ code: "custom",
1154
+ path: ["type"],
1155
+ message: `objective ${p.objectiveType} only supports campaign types ${allowedTypes.join(", ")}`
1156
+ });
1157
+ }
1158
+ });
1159
+ var campaignUpdateSchema = z3.object({
1160
+ name: campaignFields.name.optional(),
1161
+ objectiveType: campaignFields.objectiveType,
1162
+ unitCost: moneySchema.optional(),
1163
+ dailyBudget: moneySchema.optional(),
1164
+ totalBudget: moneySchema.optional(),
1165
+ runSchedule: runScheduleSchema.optional(),
1166
+ targetingCriteria: targetingCriteriaSchema.optional(),
1167
+ audienceExpansionEnabled: z3.boolean().optional(),
1168
+ offsiteDeliveryEnabled: z3.boolean().optional(),
1169
+ connectedTelevisionOnly: z3.boolean().optional(),
1170
+ creativeSelection: z3.enum(CREATIVE_SELECTIONS).optional(),
1171
+ status: entityStatusSchema.optional()
1172
+ }).superRefine((p, ctx) => {
1173
+ if (!Object.values(p).some((val) => val !== void 0)) {
1174
+ ctx.addIssue({ code: "custom", message: "update needs at least one field" });
1175
+ }
1176
+ validateCampaignBudgets(p, ctx, { requireBudget: false });
1177
+ });
1178
+ var commentarySchema = z3.string().min(1).max(LINKEDIN_LIMITS.creative.commentaryHardMax);
1179
+ var headlineSchema = z3.string().min(1).max(LINKEDIN_LIMITS.creative.headlineMax);
1180
+ var httpsUrlSchema = z3.string().url().refine((u) => u.startsWith("https://"), "landing pages must be https");
1181
+ var bakerMediaIdSchema = z3.string().min(1);
1182
+ var imageCreativeSchema = z3.object({
1183
+ format: z3.literal("image"),
1184
+ commentary: commentarySchema,
1185
+ imageId: bakerMediaIdSchema.optional(),
1186
+ imageUrn: z3.string().regex(IMAGE_URN_REGEX).optional(),
1187
+ headline: headlineSchema,
1188
+ landingUrl: httpsUrlSchema,
1189
+ cta: z3.enum(CTA_TYPES).optional(),
1190
+ mediaTitle: z3.string().max(200).optional()
1191
+ }).refine((p) => p.imageId ? !p.imageUrn : Boolean(p.imageUrn), {
1192
+ message: "provide exactly one of imageId (Baker library) or imageUrn (already on LinkedIn)",
1193
+ path: ["imageId"]
1194
+ });
1195
+ var videoCreativeSchema = z3.object({
1196
+ format: z3.literal("video"),
1197
+ commentary: commentarySchema,
1198
+ videoId: bakerMediaIdSchema.optional(),
1199
+ videoUrn: z3.string().regex(VIDEO_URN_REGEX).optional(),
1200
+ headline: headlineSchema,
1201
+ landingUrl: httpsUrlSchema.optional(),
1202
+ cta: z3.enum(CTA_TYPES).optional(),
1203
+ mediaTitle: z3.string().max(200).optional()
1204
+ }).refine((p) => p.videoId ? !p.videoUrn : Boolean(p.videoUrn), {
1205
+ message: "provide exactly one of videoId (Baker library) or videoUrn (already on LinkedIn)",
1206
+ path: ["videoId"]
1207
+ });
1208
+ var textCreativeSchema = z3.object({
1209
+ format: z3.literal("text"),
1210
+ headline: z3.string().min(1).max(LINKEDIN_LIMITS.creative.textAdHeadlineMax),
1211
+ description: z3.string().min(1).max(LINKEDIN_LIMITS.creative.textAdDescriptionMax),
1212
+ landingUrl: httpsUrlSchema,
1213
+ imageId: bakerMediaIdSchema.optional(),
1214
+ imageUrn: z3.string().regex(IMAGE_URN_REGEX).optional()
1215
+ });
1216
+ var spotlightCreativeSchema = z3.object({
1217
+ format: z3.literal("spotlight"),
1218
+ headline: z3.string().min(1).max(LINKEDIN_LIMITS.creative.spotlightHeadlineMax),
1219
+ description: z3.string().min(1).max(LINKEDIN_LIMITS.creative.spotlightDescriptionMax),
1220
+ landingUrl: httpsUrlSchema,
1221
+ ctaLabel: z3.string().min(1).max(LINKEDIN_LIMITS.creative.spotlightCtaMax),
1222
+ logoImageId: bakerMediaIdSchema.optional(),
1223
+ logoUrn: z3.string().regex(IMAGE_URN_REGEX).optional(),
1224
+ showMemberProfilePhoto: z3.boolean().default(true)
1225
+ });
1226
+ var followerCreativeSchema = z3.object({
1227
+ format: z3.literal("follower"),
1228
+ headline: z3.string().max(LINKEDIN_LIMITS.creative.spotlightHeadlineMax).optional(),
1229
+ description: z3.string().max(LINKEDIN_LIMITS.creative.spotlightDescriptionMax).optional(),
1230
+ logoImageId: bakerMediaIdSchema.optional(),
1231
+ logoUrn: z3.string().regex(IMAGE_URN_REGEX).optional()
1232
+ });
1233
+ var documentCreativeSchema = z3.object({
1234
+ format: z3.literal("document"),
1235
+ commentary: commentarySchema,
1236
+ documentTitle: z3.string().min(1).max(200),
1237
+ documentUrn: z3.string().regex(DOCUMENT_URN_REGEX).optional(),
1238
+ documentId: bakerMediaIdSchema.optional()
1239
+ }).refine((p) => p.documentId ? !p.documentUrn : Boolean(p.documentUrn), {
1240
+ message: "provide exactly one of documentId or documentUrn",
1241
+ path: ["documentId"]
1242
+ });
1243
+ var carouselCreativeSchema = z3.object({
1244
+ format: z3.literal("carousel"),
1245
+ commentary: commentarySchema,
1246
+ cards: z3.array(
1247
+ z3.object({
1248
+ imageId: bakerMediaIdSchema.optional(),
1249
+ imageUrn: z3.string().regex(IMAGE_URN_REGEX).optional(),
1250
+ headline: z3.string().min(1).max(LINKEDIN_LIMITS.creative.carouselCardHeadlineMax),
1251
+ landingUrl: httpsUrlSchema
1252
+ }).refine((card) => card.imageId ? !card.imageUrn : Boolean(card.imageUrn), {
1253
+ message: "each card needs exactly one of imageId or imageUrn",
1254
+ path: ["imageId"]
1255
+ })
1256
+ ).min(LINKEDIN_LIMITS.creative.carouselCardsMin).max(LINKEDIN_LIMITS.creative.carouselCardsMax)
1257
+ });
1258
+ var conversationCreativeSchema = z3.object({
1259
+ format: z3.literal("conversation"),
1260
+ name: z3.string().max(255).optional(),
1261
+ raw: z3.record(z3.string(), z3.unknown())
1262
+ });
1263
+ var tlaCreativeSchema = z3.object({
1264
+ format: z3.literal("tla"),
1265
+ postUrn: z3.string().regex(POST_URN_REGEX, "expected urn:li:share:* or urn:li:ugcPost:*")
1266
+ });
1267
+ var jobsCreativeSchema = z3.object({
1268
+ format: z3.literal("jobs"),
1269
+ organizationName: z3.string().min(1).max(200),
1270
+ logoImageId: bakerMediaIdSchema.optional(),
1271
+ logoUrn: z3.string().regex(IMAGE_URN_REGEX).optional(),
1272
+ headlinePreset: z3.string().min(1),
1273
+ buttonLabelPreset: z3.string().min(1)
1274
+ });
1275
+ var creativeContentSchema = z3.discriminatedUnion("format", [
1276
+ imageCreativeSchema,
1277
+ videoCreativeSchema,
1278
+ textCreativeSchema,
1279
+ spotlightCreativeSchema,
1280
+ followerCreativeSchema,
1281
+ documentCreativeSchema,
1282
+ carouselCreativeSchema,
1283
+ conversationCreativeSchema,
1284
+ tlaCreativeSchema,
1285
+ jobsCreativeSchema
1286
+ ]);
1287
+ var creativeCreateSchema = z3.object({
1288
+ campaign: parentRefSchema,
1289
+ intendedStatus: z3.enum(CREATIVE_INTENDED_STATUSES).default("DRAFT"),
1290
+ content: creativeContentSchema
1291
+ });
1292
+ var creativeUpdateSchema = z3.object({
1293
+ intendedStatus: z3.enum(["ACTIVE", "PAUSED", "ARCHIVED", "DRAFT"]).optional(),
1294
+ name: z3.string().max(255).optional(),
1295
+ headline: headlineSchema.optional(),
1296
+ landingUrl: httpsUrlSchema.optional(),
1297
+ cta: z3.enum(CTA_TYPES).optional()
1298
+ }).refine((p) => Object.values(p).some((val) => val !== void 0), "update needs at least one field");
1299
+ var listRowSchema = z3.record(z3.string(), z3.string());
1300
+ var audienceCreateSchema = z3.object({
1301
+ name: z3.string().min(1).max(LINKEDIN_LIMITS.audience.nameMax),
1302
+ audienceType: z3.enum(AUDIENCE_TYPES),
1303
+ listColumns: z3.array(z3.string().min(1)).optional(),
1304
+ listRows: z3.array(listRowSchema).max(LINKEDIN_LIMITS.audience.inlineRowsMax).optional(),
1305
+ /** Baker file reference (R2) for lists too large to inline. */
1306
+ sourceFileRef: z3.string().optional(),
1307
+ /** Retargeting/engagement source config — structural passthrough, file-first. */
1308
+ raw: z3.record(z3.string(), z3.unknown()).optional()
1309
+ }).superRefine((p, ctx) => {
1310
+ const isList = p.audienceType === "company-list" || p.audienceType === "user-list";
1311
+ if (isList && !p.listRows?.length && !p.sourceFileRef) {
1312
+ ctx.addIssue({
1313
+ code: "custom",
1314
+ path: ["listRows"],
1315
+ message: "list audiences need listRows (or sourceFileRef for large files)"
1316
+ });
1317
+ }
1318
+ if (!isList && !p.raw) {
1319
+ ctx.addIssue({
1320
+ code: "custom",
1321
+ path: ["raw"],
1322
+ message: `${p.audienceType} audiences need a raw source config (use --file)`
1323
+ });
1324
+ }
1325
+ });
1326
+ var audienceUploadListSchema = z3.object({
1327
+ segment: parentRefSchema,
1328
+ listColumns: z3.array(z3.string().min(1)).optional(),
1329
+ listRows: z3.array(listRowSchema).max(LINKEDIN_LIMITS.audience.inlineRowsMax).optional(),
1330
+ sourceFileRef: z3.string().optional()
1331
+ }).refine((p) => Boolean(p.listRows?.length) || Boolean(p.sourceFileRef), {
1332
+ message: "upload needs listRows (or sourceFileRef for large files)",
1333
+ path: ["listRows"]
1334
+ });
1335
+ var postClickWindowSchema = z3.number().refine(
1336
+ (val) => LINKEDIN_LIMITS.conversion.postClickWindows.includes(val),
1337
+ `postClickAttributionWindowSize must be one of ${LINKEDIN_LIMITS.conversion.postClickWindows.join(", ")}`
1338
+ );
1339
+ var viewThroughWindowSchema = z3.number().refine(
1340
+ (val) => LINKEDIN_LIMITS.conversion.viewThroughWindows.includes(val),
1341
+ `viewThroughAttributionWindowSize must be one of ${LINKEDIN_LIMITS.conversion.viewThroughWindows.join(", ")}`
1342
+ );
1343
+ var conversionCreateSchema = z3.object({
1344
+ name: z3.string().min(1).max(LINKEDIN_LIMITS.conversion.nameMax),
1345
+ type: z3.enum(CONVERSION_TYPES),
1346
+ conversionMethod: z3.enum(CONVERSION_METHODS),
1347
+ postClickAttributionWindowSize: postClickWindowSchema.optional(),
1348
+ viewThroughAttributionWindowSize: viewThroughWindowSchema.optional(),
1349
+ attributionType: z3.enum(ATTRIBUTION_TYPES).optional(),
1350
+ /** Insight-tag URL match rules — passthrough validated by LinkedIn. */
1351
+ urlMatchRuleExpression: z3.array(z3.array(z3.record(z3.string(), z3.string()))).optional(),
1352
+ associateAllCampaigns: z3.boolean().optional()
1353
+ });
1354
+ var conversionUpdateSchema = z3.object({
1355
+ name: conversionCreateSchema.shape.name.optional(),
1356
+ postClickAttributionWindowSize: postClickWindowSchema.optional(),
1357
+ viewThroughAttributionWindowSize: viewThroughWindowSchema.optional(),
1358
+ attributionType: z3.enum(ATTRIBUTION_TYPES).optional(),
1359
+ enabled: z3.boolean().optional()
1360
+ }).refine((p) => Object.values(p).some((val) => val !== void 0), "update needs at least one field");
1361
+ var leadFormCreateSchema = z3.object({
1362
+ name: z3.string().min(1).max(LINKEDIN_LIMITS.leadForm.nameMax),
1363
+ headline: z3.string().min(1).max(LINKEDIN_LIMITS.leadForm.headlineMax),
1364
+ description: z3.string().max(LINKEDIN_LIMITS.leadForm.descriptionMax).optional(),
1365
+ privacyPolicyUrl: httpsUrlSchema,
1366
+ questions: z3.array(z3.object({ name: z3.string().min(1), predefinedField: z3.string().optional() })).min(1).max(LINKEDIN_LIMITS.leadForm.questionsMax),
1367
+ thankYou: z3.object({
1368
+ message: z3.string().max(LINKEDIN_LIMITS.leadForm.thankYouMessageMax).optional(),
1369
+ landingUrl: httpsUrlSchema.optional()
1370
+ }).optional(),
1371
+ legalDisclaimer: z3.string().optional(),
1372
+ raw: z3.record(z3.string(), z3.unknown()).optional()
1373
+ });
1374
+ var leadFormUpdateSchema = z3.object({
1375
+ name: leadFormCreateSchema.shape.name.optional(),
1376
+ raw: z3.record(z3.string(), z3.unknown()).optional()
1377
+ }).refine((p) => Object.values(p).some((val) => val !== void 0), "update needs at least one field");
1378
+ var LINKEDIN_DRAFT_OP_KINDS = [
1379
+ "campaignGroup.create",
1380
+ "campaignGroup.update",
1381
+ "campaign.create",
1382
+ "campaign.update",
1383
+ "creative.create",
1384
+ "creative.update",
1385
+ "audience.create",
1386
+ "audience.uploadList",
1387
+ "conversion.create",
1388
+ "conversion.update",
1389
+ "leadForm.create",
1390
+ "leadForm.update"
1391
+ ];
1392
+ var linkedinDraftOpKindSchema = z3.enum(LINKEDIN_DRAFT_OP_KINDS);
1393
+ var accountIdSchema = z3.string().regex(NUMERIC_ID_REGEX, "accountId must be the bare numeric ad account id");
1394
+ var updateTargetSchema = z3.union([z3.string().regex(URN_REGEX), z3.string().regex(NUMERIC_ID_REGEX)]);
1395
+ function createOp(kind, payload) {
1396
+ return z3.object({ kind: z3.literal(kind), accountId: accountIdSchema, payload });
1397
+ }
1398
+ function updateOp(kind, payload) {
1399
+ return z3.object({ kind: z3.literal(kind), accountId: accountIdSchema, target: updateTargetSchema, payload });
1400
+ }
1401
+ var linkedinDraftOpInputSchema = z3.discriminatedUnion("kind", [
1402
+ createOp("campaignGroup.create", campaignGroupCreateSchema),
1403
+ updateOp("campaignGroup.update", campaignGroupUpdateSchema),
1404
+ createOp("campaign.create", campaignCreateSchema),
1405
+ updateOp("campaign.update", campaignUpdateSchema),
1406
+ createOp("creative.create", creativeCreateSchema),
1407
+ updateOp("creative.update", creativeUpdateSchema),
1408
+ createOp("audience.create", audienceCreateSchema),
1409
+ createOp("audience.uploadList", audienceUploadListSchema),
1410
+ createOp("conversion.create", conversionCreateSchema),
1411
+ updateOp("conversion.update", conversionUpdateSchema),
1412
+ createOp("leadForm.create", leadFormCreateSchema),
1413
+ updateOp("leadForm.update", leadFormUpdateSchema)
1414
+ ]);
1415
+
1416
+ // ../api/src/ads-linkedin/wire.ts
1417
+ import { z as z4 } from "zod";
1418
+ var linkedinWriteModeSchema = z4.enum(["live", "simulated"]);
1419
+ var linkedinDraftOpResultSchema = z4.object({
1420
+ status: z4.enum(["applied", "simulated", "failed", "skipped"]),
1421
+ urn: z4.string().optional(),
1422
+ error: z4.string().optional(),
1423
+ skippedBecause: z4.string().optional(),
1424
+ executedAt: z4.number().optional()
1425
+ });
1426
+ var linkedinDraftStageRequestSchema = z4.object({
1427
+ chatId: z4.string(),
1428
+ op: linkedinDraftOpInputSchema
1429
+ });
1430
+ var linkedinDraftStageResponseSchema = z4.object({
1431
+ staged: z4.literal(true),
1432
+ ref: z4.string(),
1433
+ kind: linkedinDraftOpKindSchema,
1434
+ mode: linkedinWriteModeSchema,
1435
+ dependsOn: z4.array(z4.string()),
1436
+ summary: z4.string(),
1437
+ warnings: z4.array(z4.string())
1438
+ });
1439
+ var linkedinDraftOpViewSchema = z4.object({
1440
+ ref: z4.string(),
1441
+ kind: linkedinDraftOpKindSchema,
1442
+ accountId: z4.string(),
1443
+ target: z4.string().optional(),
1444
+ dependsOn: z4.array(z4.string()),
1445
+ summary: z4.string(),
1446
+ stagedAt: z4.number(),
1447
+ result: linkedinDraftOpResultSchema.optional()
1448
+ });
1449
+ var linkedinDraftListRequestSchema = z4.object({
1450
+ chatId: z4.string()
1451
+ });
1452
+ var linkedinDraftListResponseSchema = z4.object({
1453
+ status: z4.enum(["active", "publishing", "applied", "discarded", "none"]),
1454
+ mode: linkedinWriteModeSchema,
1455
+ count: z4.number(),
1456
+ ops: z4.array(linkedinDraftOpViewSchema)
1457
+ });
1458
+ var linkedinDraftRemoveRequestSchema = z4.object({
1459
+ chatId: z4.string(),
1460
+ ref: z4.string()
1461
+ });
1462
+ var linkedinDraftRemoveResponseSchema = z4.object({
1463
+ /** The requested ref plus any dependents removed by cascade. */
1464
+ removed: z4.array(z4.string())
1465
+ });
1466
+ var linkedinDraftClearRequestSchema = z4.object({
1467
+ chatId: z4.string()
1468
+ });
1469
+ var linkedinDraftClearResponseSchema = z4.object({
1470
+ cleared: z4.number()
1471
+ });
1472
+ var linkedinFieldErrorSchema = z4.object({
1473
+ path: z4.string(),
1474
+ message: z4.string()
1475
+ });
1476
+ var linkedinDraftErrorResponseSchema = z4.object({
1477
+ code: z4.string(),
1478
+ error: z4.string(),
1479
+ fields: z4.array(linkedinFieldErrorSchema).optional()
1480
+ });
1481
+
1482
+ // ../api/src/images.ts
1483
+ import { z as z5 } from "zod";
1484
+ var imageSourceSchema = z5.enum([
1485
+ "uploaded",
1486
+ "website",
1487
+ "google_testimonial",
1488
+ "trustpilot_testimonial",
1489
+ "instagram",
1490
+ "magnific",
1491
+ "brandfetch",
1492
+ "google_images",
1493
+ "firecrawl",
1494
+ "screenshotone",
1495
+ "iconify",
1496
+ "giphy",
1497
+ "pinterest",
1498
+ "ai_generated"
1499
+ ]);
1500
+ var imageStatusSchema = z5.enum(["uploading", "processing", "ready", "error"]);
1501
+ var upscaleConfigSchema = z5.object({
1502
+ model: z5.literal("philz1337x/crystal-upscaler"),
1503
+ input: z5.object({
1504
+ scale_factor: z5.number(),
1505
+ creativity: z5.number(),
1506
+ output_format: z5.string()
1507
+ }),
1508
+ status: z5.enum(["pending", "completed"])
1509
+ });
1510
+ var imageDocSchema = z5.object({
1511
+ _id: z5.string(),
1512
+ _creationTime: z5.number(),
1513
+ companyId: z5.string(),
1514
+ storageKey: z5.string(),
1515
+ upscaleConfig: upscaleConfigSchema.optional(),
1516
+ upscaledStorageKey: z5.string().optional(),
1517
+ name: z5.string(),
1518
+ description: z5.string(),
1519
+ tags: z5.array(z5.string()),
1520
+ source: z5.string(),
1521
+ externalId: z5.string().optional(),
1522
+ externalUrl: z5.string().optional(),
1523
+ contentHash: z5.string().optional(),
1524
+ sourceId: z5.string().optional(),
1525
+ descriptionContext: z5.string().optional(),
1526
+ width: z5.number().optional(),
1527
+ height: z5.number().optional(),
1528
+ aspectRatio: z5.number().optional(),
1529
+ dominantColor: z5.string().optional(),
1530
+ imagePalette: z5.array(z5.string()).optional(),
1531
+ thumbhashDataUri: z5.string().optional(),
1532
+ isLightImage: z5.boolean().optional(),
1533
+ descriptionEmbedding: z5.array(z5.number()).optional(),
1534
+ imageEmbedding: z5.array(z5.number()).optional(),
1535
+ searchText: z5.string().optional(),
1536
+ status: imageStatusSchema,
1537
+ errorMessage: z5.string().optional(),
1538
+ createdAt: z5.number(),
1539
+ updatedAt: z5.number(),
1540
+ imageUrl: z5.string()
1541
+ });
1542
+ var imageHitSourceSchema = z5.enum([
1543
+ "library",
1544
+ "magnific",
1545
+ "google_images",
1546
+ "brandfetch",
1547
+ "firecrawl",
1548
+ "screenshotone",
1549
+ "iconify",
1550
+ "giphy",
1551
+ "pinterest"
1552
+ ]);
1553
+ var imageHitSchema = z5.object({
1554
+ source: imageHitSourceSchema,
1555
+ url: z5.string(),
1556
+ thumbnailUrl: z5.string().optional(),
1557
+ width: z5.number().optional(),
1558
+ height: z5.number().optional(),
1559
+ aspectRatio: z5.number().optional(),
1560
+ dominantColor: z5.string().optional(),
1561
+ externalId: z5.string().optional(),
1562
+ externalUrl: z5.string().optional(),
1563
+ providerMeta: z5.record(z5.string(), z5.unknown()).optional(),
1564
+ descriptionContext: z5.string().optional(),
1565
+ _id: z5.string().optional(),
1566
+ name: z5.string().optional(),
1567
+ description: z5.string().optional(),
1568
+ tags: z5.array(z5.string()).optional(),
1569
+ score: z5.number().optional(),
1570
+ alsoInLibrary: z5.string().optional(),
1571
+ prefetchedBytes: z5.string().optional(),
1572
+ prefetchedContentType: z5.string().optional()
1573
+ });
1574
+ var ingestedImageHitSchema = imageHitSchema.extend({
1575
+ imageId: z5.string().optional(),
1576
+ imageUrl: z5.string().optional(),
1577
+ deduped: z5.boolean().optional(),
1578
+ sourceUrl: z5.string().optional()
1579
+ });
1580
+ var autoIngestItemSchema = z5.object({
1581
+ imageId: z5.string(),
1582
+ deduped: z5.boolean(),
1583
+ imageUrl: z5.string(),
1584
+ sourceUrl: z5.string(),
1585
+ externalUrl: z5.string().optional()
1586
+ });
1587
+ function providerHitsResponseSchema() {
1588
+ return z5.object({
1589
+ hits: z5.array(ingestedImageHitSchema),
1590
+ ingested: z5.array(autoIngestItemSchema)
1591
+ });
1592
+ }
1593
+ var imagesGetRequestSchema = z5.object({ id: z5.string().min(1, "Missing id parameter") });
1594
+ var imagesSearchRequestSchema = z5.object({
1595
+ query: z5.string().min(1),
1596
+ limit: z5.coerce.number().int().positive().max(100).optional(),
1597
+ aspectRatio: z5.string().optional(),
1598
+ tags: z5.array(z5.string()).optional(),
1599
+ source: z5.string().optional(),
1600
+ externalUrlHost: z5.string().optional()
1601
+ });
1602
+ var imageSearchResultSchema = z5.object({
1603
+ _id: z5.string(),
1604
+ imageUrl: z5.string(),
1605
+ name: z5.string(),
1606
+ description: z5.string(),
1607
+ tags: z5.array(z5.string()),
1608
+ width: z5.number().optional(),
1609
+ height: z5.number().optional(),
1610
+ aspectRatio: z5.number().optional(),
1611
+ dominantColor: z5.string().optional(),
1612
+ imagePalette: z5.array(z5.string()).optional(),
1613
+ source: z5.string(),
1614
+ externalUrl: z5.string().optional(),
1615
+ score: z5.number()
1616
+ });
1617
+ var imagesSearchResponseSchema = z5.array(imageSearchResultSchema);
1618
+ var imagesUploadRequestSchema = z5.object({
1619
+ base64: z5.string().min(1),
1620
+ contentType: z5.string().min(1),
1621
+ source: z5.string().optional(),
1622
+ descriptionContext: z5.string().optional()
1623
+ });
1624
+ var imagesUploadResponseSchema = z5.object({ imageId: z5.string() });
1625
+ var imagesDeleteRequestSchema = z5.object({ id: z5.string().min(1, "Missing image ID") });
1626
+ var imagesDeleteResponseSchema = z5.object({ ok: z5.literal(true) });
1627
+ var imagesUpscaleRequestSchema = z5.object({ imageId: z5.string().min(1, "Missing image ID") });
1628
+ var imagesUpscaleResponseSchema = z5.object({
1629
+ imageId: z5.string(),
1630
+ status: z5.literal("processing")
1631
+ });
1632
+ var IMAGES_FIND_SOURCES = ["library", "magnific", "google", "iconify", "giphy", "pinterest"];
1633
+ var imagesFindRequestSchema = z5.object({
1634
+ query: z5.string().min(1),
1635
+ sources: z5.array(z5.enum(IMAGES_FIND_SOURCES)).optional(),
1636
+ limit: z5.coerce.number().int().positive().max(50).optional(),
1637
+ fallback: z5.boolean().optional(),
1638
+ threshold: z5.number().min(0).max(1).optional(),
1639
+ autoIngest: z5.coerce.number().int().min(0).max(20).optional(),
1640
+ descriptionContext: z5.string().optional()
1641
+ });
1642
+ var imagesFindResponseSchema = z5.object({
1643
+ groups: z5.object({
1644
+ library: z5.array(imageHitSchema),
1645
+ external: z5.array(ingestedImageHitSchema)
1646
+ }),
1647
+ meta: z5.object({
1648
+ counts: z5.record(z5.string(), z5.number()),
1649
+ errors: z5.array(z5.object({ source: imageHitSourceSchema, message: z5.string() }))
1650
+ }),
1651
+ ingested: z5.array(autoIngestItemSchema)
1652
+ });
1653
+ var imagesStockRequestSchema = z5.object({
1654
+ query: z5.string().min(1),
1655
+ orientation: z5.enum(["landscape", "portrait", "square", "panoramic"]).optional(),
1656
+ contentType: z5.enum(["photo", "vector", "psd"]).optional(),
1657
+ license: z5.enum(["freemium", "premium"]).optional(),
1658
+ color: z5.string().optional(),
1659
+ aiGenerated: z5.enum(["exclude", "only"]).optional(),
1660
+ people: z5.enum(["include", "exclude", "only"]).optional(),
1661
+ order: z5.enum(["relevance", "recent"]).optional(),
1662
+ limit: z5.coerce.number().int().positive().max(50).optional(),
1663
+ page: z5.coerce.number().int().positive().optional(),
1664
+ autoIngest: z5.coerce.number().int().min(0).max(20).optional(),
1665
+ descriptionContext: z5.string().optional()
1666
+ });
1667
+ var imagesStockResponseSchema = providerHitsResponseSchema();
1668
+ var imagesGoogleRequestSchema = z5.object({
1669
+ query: z5.string().min(1),
1670
+ type: z5.string().optional(),
1671
+ size: z5.string().optional(),
1672
+ color: z5.string().optional(),
1673
+ safe: z5.enum(["off", "active"]).optional(),
1674
+ limit: z5.coerce.number().int().positive().max(50).optional(),
1675
+ autoIngest: z5.coerce.number().int().min(0).max(20).optional(),
1676
+ descriptionContext: z5.string().optional()
1677
+ });
1678
+ var imagesGoogleResponseSchema = providerHitsResponseSchema();
1679
+ var imagesPinterestRequestSchema = z5.object({
1680
+ query: z5.string().min(1),
1681
+ limit: z5.coerce.number().int().positive().max(20).optional(),
1682
+ autoIngest: z5.coerce.number().int().min(0).max(20).optional(),
1683
+ descriptionContext: z5.string().optional()
1684
+ });
1685
+ var imagesPinterestResponseSchema = providerHitsResponseSchema();
1686
+ var rgbTriple = z5.tuple([
1687
+ z5.number().int().min(0).max(255),
1688
+ z5.number().int().min(0).max(255),
1689
+ z5.number().int().min(0).max(255)
1690
+ ]);
1691
+ var imageGenerateModelSchema = z5.enum([
1692
+ "openai/gpt-5.4-image-2",
1693
+ "google/gemini-3.5-flash",
1694
+ "google/gemini-3.1-flash-image-preview",
1695
+ "google/gemini-3-pro-image-preview",
1696
+ "recraft/recraft-v4.1-pro-vector"
1697
+ ]);
1698
+ var imagesGenerateRequestSchema = z5.object({
1699
+ prompt: z5.string().min(1),
1700
+ model: imageGenerateModelSchema.optional(),
1701
+ // Aspect ratio + size are validated loosely as strings; the per-model enum
1702
+ // lives in canvas-contract and OpenRouter rejects unsupported combinations.
1703
+ aspectRatio: z5.string().optional(),
1704
+ imageSize: z5.string().optional(),
1705
+ // Recraft v4.1 Pro Vector levers (ignored by other models).
1706
+ strength: z5.coerce.number().min(0).max(1).optional(),
1707
+ rgbColors: z5.array(rgbTriple).optional(),
1708
+ backgroundRgbColor: rgbTriple.optional(),
1709
+ // Public image URLs used as visual references (multi-reference, in order).
1710
+ referenceUrls: z5.array(z5.string().url()).optional(),
1711
+ descriptionContext: z5.string().optional()
1712
+ });
1713
+ var generatedImageSchema = z5.object({
1714
+ imageId: z5.string(),
1715
+ deduped: z5.boolean(),
1716
+ imageUrl: z5.string(),
1717
+ width: z5.number().optional(),
1718
+ height: z5.number().optional()
1719
+ });
1720
+ var imagesGenerateResponseSchema = z5.object({
1721
+ model: z5.string(),
1722
+ costUsd: z5.number(),
1723
+ images: z5.array(generatedImageSchema)
1724
+ });
1725
+ var imagesLogoRequestSchema = z5.object({
1726
+ domain: z5.string().min(1),
1727
+ variant: z5.enum(["icon", "logo", "symbol"]).optional(),
1728
+ autoIngest: z5.coerce.number().int().min(0).max(5).optional(),
1729
+ descriptionContext: z5.string().optional()
1730
+ });
1731
+ var imagesLogoResponseSchema = providerHitsResponseSchema();
1732
+ var imagesIconRequestSchema = z5.object({
1733
+ name: z5.string().min(1),
1734
+ set: z5.string().optional(),
1735
+ color: z5.string().optional(),
1736
+ width: z5.coerce.number().int().positive().optional(),
1737
+ autoIngest: z5.coerce.number().int().min(0).max(20).optional(),
1738
+ descriptionContext: z5.string().optional()
1739
+ });
1740
+ var imagesIconResponseSchema = providerHitsResponseSchema();
1741
+ var imagesExtractRequestSchema = z5.object({
1742
+ url: z5.string().url(),
1743
+ waitFor: z5.coerce.number().int().min(0).max(3e4).optional(),
1744
+ limit: z5.coerce.number().int().positive().max(50).optional(),
1745
+ autoIngest: z5.coerce.number().int().min(0).max(20).optional(),
1746
+ descriptionContext: z5.string().optional()
1747
+ });
1748
+ var imagesExtractResponseSchema = providerHitsResponseSchema();
1749
+ var imagesScreenshotRequestSchema = z5.object({
1750
+ url: z5.string().url(),
1751
+ fullPage: z5.boolean().optional(),
1752
+ viewportWidth: z5.coerce.number().int().positive().max(3840).optional(),
1753
+ viewportHeight: z5.coerce.number().int().positive().max(2160).optional(),
1754
+ format: z5.enum(["webp", "png", "jpg"]).optional(),
1755
+ descriptionContext: z5.string().optional()
1756
+ });
1757
+ var imagesScreenshotResponseSchema = providerHitsResponseSchema();
1758
+ var imagesGiphyRequestSchema = z5.object({
1759
+ query: z5.string().min(1).optional(),
1760
+ trending: z5.coerce.boolean().optional(),
1761
+ limit: z5.coerce.number().int().positive().max(50).optional(),
1762
+ rating: z5.enum(["g", "pg", "pg-13", "r"]).optional(),
1763
+ lang: z5.string().optional(),
1764
+ autoIngest: z5.coerce.number().int().min(0).max(20).optional(),
1765
+ descriptionContext: z5.string().optional()
1766
+ });
1767
+ var imagesGifResponseSchema = providerHitsResponseSchema();
1768
+ var imagesStickerResponseSchema = providerHitsResponseSchema();
1769
+ var imagesIngestRequestSchema = z5.object({
1770
+ url: z5.string().url(),
1771
+ // Validate source at the HTTP boundary so unknown values produce the standard
1772
+ // `{ code: "BAD_REQUEST", message }` shape instead of a plain Error from
1773
+ // `assertImageSource` inside the action.
1774
+ source: imageSourceSchema,
1775
+ externalId: z5.string().optional(),
1776
+ externalUrl: z5.string().optional(),
1777
+ descriptionContext: z5.string().optional()
1778
+ });
1779
+ var imagesIngestResponseSchema = z5.object({
1780
+ imageId: z5.string(),
1781
+ deduped: z5.boolean(),
1782
+ contentHash: z5.string()
1783
+ });
1784
+
1785
+ // ../api/src/testimonials.ts
1786
+ import { z as z6 } from "zod";
1787
+ var testimonialSourceTypeSchema = z6.enum(["google", "trustpilot"]);
1788
+ var testimonialStatusSchema = z6.enum(["pending", "processing", "ready", "error"]);
1789
+ var testimonialSentimentSchema = z6.enum(["positive", "neutral", "negative"]);
1790
+ var testimonialDocSchema = z6.object({
1791
+ _id: z6.string(),
1792
+ _creationTime: z6.number(),
1793
+ companyId: z6.string(),
1794
+ sourceId: z6.string(),
1795
+ sourceType: testimonialSourceTypeSchema,
1796
+ reviewText: z6.string(),
1797
+ reviewTitle: z6.string().optional(),
1798
+ searchText: z6.string().optional(),
1799
+ reviewerName: z6.string().optional(),
1800
+ reviewerImageUrl: z6.string().optional(),
1801
+ reviewerImageId: z6.string().optional(),
1802
+ reviewerLocation: z6.string().optional(),
1803
+ rating: z6.number().optional(),
1804
+ reviewDate: z6.number().optional(),
1805
+ ownerAnswer: z6.string().optional(),
1806
+ mediaUrls: z6.array(z6.string()).optional(),
1807
+ imageIds: z6.array(z6.string()).optional(),
1808
+ videoIds: z6.array(z6.string()).optional(),
1809
+ sourceUrl: z6.string().optional(),
1810
+ rawData: z6.unknown().optional(),
1811
+ tags: z6.array(z6.string()),
1812
+ highlight: z6.string().optional(),
1813
+ language: z6.string().optional(),
1814
+ summary: z6.string().optional(),
1815
+ sentiment: testimonialSentimentSchema.optional(),
1816
+ textEmbedding: z6.array(z6.number()).optional(),
1817
+ externalId: z6.string().optional(),
1818
+ contentHash: z6.string().optional(),
1819
+ status: testimonialStatusSchema,
1820
+ errorMessage: z6.string().optional(),
1821
+ createdAt: z6.number(),
1822
+ updatedAt: z6.number()
1823
+ });
1824
+ var testimonialsListRequestSchema = z6.object({
1825
+ source: testimonialSourceTypeSchema.optional(),
1826
+ rating_min: z6.coerce.number().int().min(1).max(5).optional(),
1827
+ rating_max: z6.coerce.number().int().min(1).max(5).optional(),
1828
+ tags: z6.string().transform((s) => s.split(",").filter(Boolean)).optional(),
1829
+ status: testimonialStatusSchema.optional(),
1830
+ sentiment: testimonialSentimentSchema.optional(),
1831
+ language: z6.string().min(2).max(5).optional(),
1832
+ limit: z6.coerce.number().int().positive().max(200).optional()
1833
+ });
1834
+ var testimonialsListResponseSchema = z6.array(testimonialDocSchema);
1835
+ var testimonialsGetRequestSchema = z6.object({ id: z6.string().min(1, "Missing id parameter") });
1836
+ var testimonialsSearchRequestSchema = z6.object({
1837
+ query: z6.string().min(1),
1838
+ limit: z6.coerce.number().int().positive().max(100).optional(),
1839
+ source: testimonialSourceTypeSchema.optional(),
1840
+ rating_min: z6.coerce.number().int().min(1).max(5).optional(),
1841
+ rating_max: z6.coerce.number().int().min(1).max(5).optional(),
1842
+ tags: z6.array(z6.string()).optional(),
1843
+ status: testimonialStatusSchema.optional(),
1844
+ sentiment: testimonialSentimentSchema.optional(),
1845
+ language: z6.string().min(2).max(5).optional()
1846
+ }).refine(
1847
+ (data) => data.rating_min === void 0 || data.rating_max === void 0 || data.rating_min <= data.rating_max,
1848
+ { message: "rating_min must be less than or equal to rating_max" }
1849
+ );
1850
+ var testimonialsSearchResponseSchema = z6.array(testimonialDocSchema);
1851
+ var testimonialsOutscraperWebhookResponseSchema = z6.object({
1852
+ ok: z6.literal(true),
1853
+ note: z6.string().optional()
1854
+ });
1855
+
1856
+ // ../api/src/videos.ts
1857
+ import { z as z7 } from "zod";
1858
+ var videoStatusSchema = z7.enum(["uploading", "uploaded", "processing", "ready", "error"]);
1859
+ var videoTranscriptSegmentSchema = z7.object({
1860
+ text: z7.string(),
1861
+ startSecond: z7.number(),
1862
+ endSecond: z7.number()
1863
+ });
1864
+ var videoSceneSchema = z7.object({
1865
+ title: z7.string(),
1866
+ description: z7.string(),
1867
+ startSecond: z7.number(),
1868
+ endSecond: z7.number(),
1869
+ thumbnailTime: z7.number()
1870
+ });
1871
+ var videoDocSchema = z7.object({
1872
+ _id: z7.string(),
1873
+ _creationTime: z7.number(),
1874
+ companyId: z7.string(),
1875
+ muxAssetId: z7.string(),
1876
+ muxPlaybackId: z7.string(),
1877
+ muxUploadId: z7.string(),
1878
+ name: z7.string(),
1879
+ description: z7.string(),
1880
+ tags: z7.array(z7.string()),
1881
+ source: z7.string(),
1882
+ externalId: z7.string().optional(),
1883
+ sourceId: z7.string().optional(),
1884
+ width: z7.number().optional(),
1885
+ height: z7.number().optional(),
1886
+ aspectRatio: z7.number().optional(),
1887
+ duration: z7.number().optional(),
1888
+ transcript: z7.string().optional(),
1889
+ transcriptSegments: z7.array(videoTranscriptSegmentSchema).optional(),
1890
+ scenes: z7.array(videoSceneSchema).optional(),
1891
+ descriptionEmbedding: z7.array(z7.number()).optional(),
1892
+ searchText: z7.string().optional(),
1893
+ status: videoStatusSchema,
1894
+ errorMessage: z7.string().optional(),
1895
+ createdAt: z7.number(),
1896
+ updatedAt: z7.number(),
1897
+ thumbnailUrl: z7.string()
1898
+ });
1899
+ var videosWebhookResponseSchema = z7.object({ ok: z7.literal(true) });
1900
+ var videosGetRequestSchema = z7.object({ id: z7.string().min(1, "Missing id parameter") });
1901
+ var videosSearchRequestSchema = z7.object({
1902
+ query: z7.string().min(1),
1903
+ limit: z7.coerce.number().int().positive().max(100).optional(),
1904
+ tags: z7.array(z7.string()).optional()
1905
+ });
1906
+ var videoSearchResultSchema = z7.object({
1907
+ _id: z7.string(),
1908
+ thumbnailUrl: z7.string(),
1909
+ name: z7.string(),
1910
+ description: z7.string(),
1911
+ tags: z7.array(z7.string()),
1912
+ status: z7.string(),
1913
+ duration: z7.number().optional(),
1914
+ muxPlaybackId: z7.string(),
1915
+ createdAt: z7.number()
1916
+ });
1917
+ var videosSearchResponseSchema = z7.array(videoSearchResultSchema);
1918
+ var videosUploadResponseSchema = z7.object({ uploadUrl: z7.string(), videoId: z7.string() });
1919
+ var videosDeleteRequestSchema = z7.object({ id: z7.string().min(1, "Missing video ID") });
1920
+ var videosDeleteResponseSchema = z7.object({ ok: z7.literal(true) });
1921
+
938
1922
  // src/commands/actions/complete.ts
939
1923
  import { defineCommand as defineCommand2 } from "citty";
940
1924
  registerSchema({
@@ -1762,7 +2746,7 @@ Examples:
1762
2746
  });
1763
2747
 
1764
2748
  // src/commands/ads/index.ts
1765
- import { defineCommand as defineCommand78 } from "citty";
2749
+ import { defineCommand as defineCommand80 } from "citty";
1766
2750
 
1767
2751
  // src/commands/ads/google/index.ts
1768
2752
  import { defineCommand as defineCommand29 } from "citty";
@@ -4086,7 +5070,7 @@ Examples:
4086
5070
  });
4087
5071
 
4088
5072
  // src/commands/ads/linkedin/index.ts
4089
- import { defineCommand as defineCommand47 } from "citty";
5073
+ import { defineCommand as defineCommand49 } from "citty";
4090
5074
 
4091
5075
  // src/commands/ads/linkedin/schemas.ts
4092
5076
  registerSchema({
@@ -4384,6 +5368,293 @@ registerSchema({
4384
5368
  "skip-cache": { type: "boolean", description: "Bypass cache", required: false }
4385
5369
  }
4386
5370
  });
5371
+ var writeAccountArgs = {
5372
+ "account-id": { type: "string", description: "Numeric account ID or urn:li:sponsoredAccount:N", required: false },
5373
+ "account-urn": { type: "string", description: "Alias for --account-id", required: false }
5374
+ };
5375
+ registerSchema({
5376
+ command: "ads.linkedin.campaign-groups.create",
5377
+ description: "Stage a new campaign group (staged until publish; review with `baker ads linkedin draft`). Returns a li_temp_* ref that later `campaigns create --group` calls can reference.",
5378
+ args: {
5379
+ ...writeAccountArgs,
5380
+ name: { type: "string", description: "Name (\u2264100 chars)", required: true },
5381
+ start: { type: "string", description: "Run schedule start (ISO date or epoch ms)", required: true },
5382
+ end: { type: "string", description: "Run schedule end", required: false },
5383
+ "total-budget": { type: "string", description: "Lifetime budget amount", required: false },
5384
+ currency: { type: "string", description: "3-letter currency code (required with money flags)", required: false },
5385
+ status: { type: "string", description: "DRAFT|ACTIVE|PAUSED (default DRAFT)", required: false },
5386
+ file: { type: "string", description: "JSON payload file; flags override file keys", required: false }
5387
+ }
5388
+ });
5389
+ registerSchema({
5390
+ command: "ads.linkedin.campaign-groups.update",
5391
+ description: "Stage changes to an existing campaign group (name, schedule, budget, status). Captures a before-snapshot for the dashboard diff. Applies on chat publish.",
5392
+ args: {
5393
+ id: { type: "positional", description: "Campaign group id or URN", required: true },
5394
+ ...writeAccountArgs,
5395
+ name: { type: "string", description: "New name", required: false },
5396
+ start: { type: "string", description: "New schedule start", required: false },
5397
+ end: { type: "string", description: "New schedule end", required: false },
5398
+ "total-budget": { type: "string", description: "New lifetime budget", required: false },
5399
+ currency: { type: "string", description: "3-letter currency code", required: false },
5400
+ status: { type: "string", description: "ACTIVE|PAUSED|ARCHIVED", required: false },
5401
+ file: { type: "string", description: "JSON file with fields to change", required: false }
5402
+ }
5403
+ });
5404
+ registerSchema({
5405
+ command: "ads.linkedin.campaigns.create",
5406
+ description: "Stage a new campaign. --group accepts a real id/URN or a li_temp_* ref staged earlier in this chat. Targeting goes in --targeting-file as LinkedIn criteria: {include:{and:[{or:{'urn:li:adTargetingFacet:locations':['urn:li:geo:\u2026']}}]}} \u2014 find facet values with `baker ads linkedin facets values`. Playbook defaults: manual bid (--bid), audience expansion off, LAN off. Staged until publish.",
5407
+ args: {
5408
+ ...writeAccountArgs,
5409
+ name: { type: "string", description: "Name (\u2264255 chars)", required: true },
5410
+ group: { type: "string", description: "Campaign group id/URN or li_temp_* ref", required: true },
5411
+ type: { type: "string", description: "SPONSORED_UPDATES|TEXT_AD|SPONSORED_INMAILS|DYNAMIC", required: true },
5412
+ objective: { type: "string", description: "LEAD_GENERATION|WEBSITE_VISIT|BRAND_AWARENESS|\u2026", required: false },
5413
+ "cost-type": { type: "string", description: "CPC|CPM|CPV", required: true },
5414
+ bid: { type: "string", description: "Manual bid amount (unitCost)", required: false },
5415
+ "daily-budget": { type: "string", description: "Daily budget amount", required: false },
5416
+ "total-budget": { type: "string", description: "Lifetime budget amount", required: false },
5417
+ currency: { type: "string", description: "3-letter currency code (required with money flags)", required: false },
5418
+ start: { type: "string", description: "Run schedule start", required: false },
5419
+ end: { type: "string", description: "Run schedule end", required: false },
5420
+ locale: { type: "string", description: "Profile locale like en_US (default en_US)", required: false },
5421
+ "targeting-file": { type: "string", description: "JSON file with targetingCriteria", required: true },
5422
+ "audience-expansion": { type: "string", description: "on|off (playbook: off; default off)", required: false },
5423
+ lan: {
5424
+ type: "string",
5425
+ description: "LinkedIn Audience Network on|off (playbook: off; default off)",
5426
+ required: false
5427
+ },
5428
+ ctv: { type: "string", description: "Connected TV on|off (default off)", required: false },
5429
+ "creative-selection": { type: "string", description: "OPTIMIZED|ROUND_ROBIN", required: false },
5430
+ "associated-entity": { type: "string", description: "Organization URN", required: false },
5431
+ status: { type: "string", description: "DRAFT|ACTIVE|PAUSED (default DRAFT)", required: false },
5432
+ file: { type: "string", description: "JSON payload file; flags override file keys", required: false }
5433
+ }
5434
+ });
5435
+ registerSchema({
5436
+ command: "ads.linkedin.campaigns.update",
5437
+ description: "Stage changes to an existing campaign \u2014 budget, bid, targeting, schedule, flags, status. Only pass what changes; a before-snapshot powers the dashboard diff. Applies on chat publish.",
5438
+ args: {
5439
+ id: { type: "positional", description: "Campaign id or URN", required: true },
5440
+ ...writeAccountArgs,
5441
+ name: { type: "string", description: "New name", required: false },
5442
+ objective: { type: "string", description: "New objective", required: false },
5443
+ bid: { type: "string", description: "New manual bid", required: false },
5444
+ "daily-budget": { type: "string", description: "New daily budget", required: false },
5445
+ "total-budget": { type: "string", description: "New lifetime budget", required: false },
5446
+ currency: { type: "string", description: "3-letter currency code", required: false },
5447
+ start: { type: "string", description: "New schedule start", required: false },
5448
+ end: { type: "string", description: "New schedule end", required: false },
5449
+ "targeting-file": { type: "string", description: "JSON file with replacement targetingCriteria", required: false },
5450
+ "audience-expansion": { type: "string", description: "on|off", required: false },
5451
+ lan: { type: "string", description: "on|off", required: false },
5452
+ ctv: { type: "string", description: "on|off", required: false },
5453
+ "creative-selection": { type: "string", description: "OPTIMIZED|ROUND_ROBIN", required: false },
5454
+ status: { type: "string", description: "ACTIVE|PAUSED|ARCHIVED", required: false },
5455
+ file: { type: "string", description: "JSON file with fields to change", required: false }
5456
+ }
5457
+ });
5458
+ var statusSugarArgs = {
5459
+ id: { type: "positional", description: "Entity id or URN", required: true },
5460
+ ...writeAccountArgs
5461
+ };
5462
+ registerSchema({
5463
+ command: "ads.linkedin.campaigns.pause",
5464
+ description: "Stage pausing a campaign (status \u2192 PAUSED on publish).",
5465
+ args: statusSugarArgs
5466
+ });
5467
+ registerSchema({
5468
+ command: "ads.linkedin.campaigns.resume",
5469
+ description: "Stage resuming a paused campaign (status \u2192 ACTIVE on publish).",
5470
+ args: statusSugarArgs
5471
+ });
5472
+ registerSchema({
5473
+ command: "ads.linkedin.campaigns.archive",
5474
+ description: "Stage archiving a campaign (status \u2192 ARCHIVED on publish).",
5475
+ args: statusSugarArgs
5476
+ });
5477
+ registerSchema({
5478
+ command: "ads.linkedin.campaign-groups.pause",
5479
+ description: "Stage pausing a campaign group.",
5480
+ args: statusSugarArgs
5481
+ });
5482
+ registerSchema({
5483
+ command: "ads.linkedin.campaign-groups.resume",
5484
+ description: "Stage resuming a campaign group.",
5485
+ args: statusSugarArgs
5486
+ });
5487
+ registerSchema({
5488
+ command: "ads.linkedin.creatives.pause",
5489
+ description: "Stage pausing a creative (intendedStatus \u2192 PAUSED on publish). Playbook early-kill rule: >30 impressions with 0 clicks.",
5490
+ args: statusSugarArgs
5491
+ });
5492
+ registerSchema({
5493
+ command: "ads.linkedin.creatives.resume",
5494
+ description: "Stage resuming a paused creative.",
5495
+ args: statusSugarArgs
5496
+ });
5497
+ var duplicateArgs = {
5498
+ id: { type: "positional", description: "Source id or URN", required: true },
5499
+ ...writeAccountArgs,
5500
+ name: { type: "string", description: "Name for the copy", required: false },
5501
+ file: { type: "string", description: "JSON file with field overrides for the copy", required: false }
5502
+ };
5503
+ registerSchema({
5504
+ command: "ads.linkedin.campaigns.duplicate",
5505
+ description: "Stage a copy of an existing campaign as a new DRAFT create (playbook: duplicate, never link-to-original). Reads the source at stage time; override fields via --name/--file.",
5506
+ args: duplicateArgs
5507
+ });
5508
+ registerSchema({
5509
+ command: "ads.linkedin.campaign-groups.duplicate",
5510
+ description: "Stage a copy of an existing campaign group as a new DRAFT create.",
5511
+ args: duplicateArgs
5512
+ });
5513
+ registerSchema({
5514
+ command: "ads.linkedin.creatives.duplicate",
5515
+ description: "Stage a copy of an existing creative by re-sponsoring its underlying post into a new DRAFT creative (--file can override the target campaign).",
5516
+ args: duplicateArgs
5517
+ });
5518
+ registerSchema({
5519
+ command: "ads.linkedin.creatives.create",
5520
+ description: "Stage a new creative (ad). Formats: image|video|text|spotlight|follower|document|carousel|conversation|tla|jobs. Media: --image-id/--video-id reference the Baker library (`baker images`/`baker videos`) and upload to LinkedIn at publish; --image-urn/--video-urn reference media already on LinkedIn. Limits: headline \u226470 (text ads \u226425), intro soft-truncates at 600 chars, landing URL must be https. Complex formats take --file with the full content object. Staged until publish.",
5521
+ args: {
5522
+ ...writeAccountArgs,
5523
+ campaign: { type: "string", description: "Campaign id/URN or li_temp_* ref", required: true },
5524
+ format: {
5525
+ type: "string",
5526
+ description: "image|video|text|spotlight|follower|document|carousel|conversation|tla|jobs",
5527
+ required: true
5528
+ },
5529
+ "image-id": { type: "string", description: "Baker image library id", required: false },
5530
+ "image-urn": { type: "string", description: "Existing urn:li:image:*", required: false },
5531
+ "video-id": { type: "string", description: "Baker video library id", required: false },
5532
+ "video-urn": { type: "string", description: "Existing urn:li:video:*", required: false },
5533
+ "logo-image-id": { type: "string", description: "Baker image id for the logo", required: false },
5534
+ intro: { type: "string", description: "Commentary / intro text", required: false },
5535
+ headline: { type: "string", description: "Headline", required: false },
5536
+ description: { type: "string", description: "Description (text/spotlight)", required: false },
5537
+ "landing-url": { type: "string", description: "https destination URL", required: false },
5538
+ cta: { type: "string", description: "LEARN_MORE|REQUEST_DEMO|SIGN_UP|REGISTER|DOWNLOAD|\u2026", required: false },
5539
+ "cta-label": { type: "string", description: "Spotlight CTA label (\u226418 chars)", required: false },
5540
+ "post-urn": { type: "string", description: "TLA: post to sponsor (urn:li:share|ugcPost:*)", required: false },
5541
+ "intended-status": { type: "string", description: "DRAFT|ACTIVE|PAUSED (default DRAFT)", required: false },
5542
+ title: { type: "string", description: "Media title", required: false },
5543
+ file: { type: "string", description: "JSON file with the full content object", required: false }
5544
+ }
5545
+ });
5546
+ registerSchema({
5547
+ command: "ads.linkedin.creatives.update",
5548
+ description: "Stage changes to an existing creative (intendedStatus, name). Applies on chat publish.",
5549
+ args: {
5550
+ id: { type: "positional", description: "Creative id or URN", required: true },
5551
+ ...writeAccountArgs,
5552
+ "intended-status": { type: "string", description: "ACTIVE|PAUSED|ARCHIVED|DRAFT", required: false },
5553
+ name: { type: "string", description: "New creative name", required: false },
5554
+ file: { type: "string", description: "JSON file with fields to change", required: false }
5555
+ }
5556
+ });
5557
+ registerSchema({
5558
+ command: "ads.linkedin.audiences.create",
5559
+ description: "Stage a new matched audience. company-list/user-list take --list-file (CSV with header; companyName,domain for companies; email column is SHA-256 hashed locally). LinkedIn needs 300+ MATCHED members to serve \u2014 upload well above that. retargeting/engagement configs go in --file. Staged until publish.",
5560
+ args: {
5561
+ ...writeAccountArgs,
5562
+ name: { type: "string", description: "Audience name (\u2264100 chars)", required: true },
5563
+ type: { type: "string", description: "company-list|user-list|retargeting|engagement", required: true },
5564
+ "list-file": { type: "string", description: "CSV file with the list rows", required: false },
5565
+ file: { type: "string", description: "JSON raw source config for retargeting/engagement", required: false }
5566
+ }
5567
+ });
5568
+ registerSchema({
5569
+ command: "ads.linkedin.audiences.upload",
5570
+ description: "Stage adding rows to an existing audience segment (or a li_temp_* segment staged in this chat). Same CSV rules as audiences.create.",
5571
+ args: {
5572
+ id: { type: "positional", description: "Segment URN/id or li_temp_* ref", required: true },
5573
+ ...writeAccountArgs,
5574
+ "list-file": { type: "string", description: "CSV file with the rows to add", required: true }
5575
+ }
5576
+ });
5577
+ registerSchema({
5578
+ command: "ads.linkedin.conversions.create",
5579
+ description: "Stage a new conversion rule. Playbook defaults: --post-click-window 30 --view-window 7, counting ONE_TIME_EACH_MEMBER for leads. Staged until publish.",
5580
+ args: {
5581
+ ...writeAccountArgs,
5582
+ name: { type: "string", description: "Rule name", required: true },
5583
+ type: {
5584
+ type: "string",
5585
+ description: "LEAD|SIGN_UP|DOWNLOAD|PURCHASE|KEY_PAGE_VIEW|ADD_TO_CART|INSTALL|OTHER",
5586
+ required: true
5587
+ },
5588
+ method: { type: "string", description: "INSIGHT_TAG|CONVERSIONS_API", required: true },
5589
+ "post-click-window": { type: "string", description: "1|7|30|90 days", required: false },
5590
+ "view-window": { type: "string", description: "1|7|30 days", required: false },
5591
+ "attribution-type": {
5592
+ type: "string",
5593
+ description: "LAST_TOUCH_BY_CAMPAIGN|LAST_TOUCH_BY_CONVERSION",
5594
+ required: false
5595
+ },
5596
+ "associate-all-campaigns": {
5597
+ type: "boolean",
5598
+ description: "Auto-associate with active campaigns",
5599
+ required: false
5600
+ },
5601
+ file: { type: "string", description: "JSON payload file (e.g. urlMatchRuleExpression)", required: false }
5602
+ }
5603
+ });
5604
+ registerSchema({
5605
+ command: "ads.linkedin.conversions.update",
5606
+ description: "Stage changes to a conversion rule (windows, attribution, enabled, name). Applies on chat publish.",
5607
+ args: {
5608
+ id: { type: "positional", description: "Conversion rule id or URN", required: true },
5609
+ ...writeAccountArgs,
5610
+ name: { type: "string", description: "New name", required: false },
5611
+ "post-click-window": { type: "string", description: "1|7|30|90 days", required: false },
5612
+ "view-window": { type: "string", description: "1|7|30 days", required: false },
5613
+ "attribution-type": { type: "string", description: "New attribution type", required: false },
5614
+ enabled: { type: "string", description: "on|off", required: false },
5615
+ file: { type: "string", description: "JSON file with fields to change", required: false }
5616
+ }
5617
+ });
5618
+ registerSchema({
5619
+ command: "ads.linkedin.lead-forms.create",
5620
+ description: "Stage a new Lead Gen Form from a JSON file: {name, headline \u226460, description? \u2264160, privacyPolicyUrl, questions[] \u226412 (playbook: \u22644 for completion), thankYou?, legalDisclaimer?}. Staged until publish.",
5621
+ args: {
5622
+ ...writeAccountArgs,
5623
+ file: { type: "string", description: "JSON form definition file", required: true },
5624
+ name: { type: "string", description: "Override the form name", required: false }
5625
+ }
5626
+ });
5627
+ registerSchema({
5628
+ command: "ads.linkedin.lead-forms.update",
5629
+ description: "Stage changes to a Lead Gen Form from a JSON file. Applies on chat publish.",
5630
+ args: {
5631
+ id: { type: "positional", description: "Lead form id or URN", required: true },
5632
+ ...writeAccountArgs,
5633
+ file: { type: "string", description: "JSON file with the fields to change", required: true },
5634
+ name: { type: "string", description: "New form name", required: false }
5635
+ }
5636
+ });
5637
+ registerSchema({
5638
+ command: "ads.linkedin.draft.list",
5639
+ description: "Show every LinkedIn write op staged in this chat: ref, summary, dependencies, warnings, mode (simulated = publish will NOT hit LinkedIn), and post-publish results (applied/simulated/failed/skipped). Ops are invisible to the read commands until publish \u2014 check here before finishing.",
5640
+ args: {}
5641
+ });
5642
+ registerSchema({
5643
+ command: "ads.linkedin.draft.remove",
5644
+ description: "Drop one staged LinkedIn op from this chat's draft. Removing a create cascades to every op that depends on it (a campaign removal drops its staged creatives).",
5645
+ args: {
5646
+ ref: {
5647
+ type: "positional",
5648
+ description: "Op ref (li_temp_* for creates, target id/URN for updates)",
5649
+ required: true
5650
+ }
5651
+ }
5652
+ });
5653
+ registerSchema({
5654
+ command: "ads.linkedin.draft.clear",
5655
+ description: "Discard ALL staged LinkedIn ops in this chat's draft. Nothing applies on publish.",
5656
+ args: {}
5657
+ });
4387
5658
 
4388
5659
  // src/commands/ads/linkedin/account.ts
4389
5660
  import { defineCommand as defineCommand30 } from "citty";
@@ -5238,18 +6509,742 @@ Examples:
5238
6509
  });
5239
6510
 
5240
6511
  // src/commands/ads/linkedin/campaign-groups.ts
6512
+ import { defineCommand as defineCommand37 } from "citty";
6513
+
6514
+ // src/commands/ads/linkedin/write-commands.ts
5241
6515
  import { defineCommand as defineCommand36 } from "citty";
5242
- var campaignGroupsCommand = defineCommand36({
6516
+
6517
+ // src/commands/ads/linkedin/write-shared.ts
6518
+ import { createHash as createHash2 } from "crypto";
6519
+ import { readFileSync as readFileSync6 } from "fs";
6520
+ function bareAccountId(args) {
6521
+ const raw = resolveAccountIdArg(args);
6522
+ return raw.replace(/^urn:li:sponsoredAccount:/, "").replace(/^sponsoredAccount:/, "");
6523
+ }
6524
+ function failWriteValidation(message) {
6525
+ writeJsonEnvelope({ ok: false, error: { code: "VALIDATION_ERROR", message } });
6526
+ process.exit(1);
6527
+ }
6528
+ function loadJsonFileArg(path11) {
6529
+ if (typeof path11 !== "string" || path11.length === 0) {
6530
+ return {};
6531
+ }
6532
+ try {
6533
+ const parsed = JSON.parse(readFileSync6(path11, "utf8"));
6534
+ if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
6535
+ failWriteValidation(`${path11} must contain a JSON object`);
6536
+ }
6537
+ return parsed;
6538
+ } catch (err) {
6539
+ if (err instanceof SyntaxError) {
6540
+ failWriteValidation(`${path11} is not valid JSON: ${err.message}`);
6541
+ }
6542
+ throw err;
6543
+ }
6544
+ }
6545
+ function mergePayload(file, flags) {
6546
+ const merged = { ...file };
6547
+ for (const [key, value] of Object.entries(flags)) {
6548
+ if (value !== void 0) {
6549
+ merged[key] = value;
6550
+ }
6551
+ }
6552
+ return merged;
6553
+ }
6554
+ function parseMoneyFlag(amount, currency, flag) {
6555
+ if (amount === void 0 || amount === null || amount === "") {
6556
+ return void 0;
6557
+ }
6558
+ if (typeof currency !== "string" || currency.length !== 3) {
6559
+ failWriteValidation(`${flag} needs --currency (3-letter code matching the ad account, e.g. EUR)`);
6560
+ }
6561
+ return { amount: String(amount), currencyCode: currency.toUpperCase() };
6562
+ }
6563
+ function parseDateFlag(value, flag) {
6564
+ if (value === void 0 || value === null || value === "") {
6565
+ return void 0;
6566
+ }
6567
+ const raw = String(value);
6568
+ if (/^\d{10,}$/.test(raw)) {
6569
+ return Number(raw);
6570
+ }
6571
+ const parsed = Date.parse(raw);
6572
+ if (Number.isNaN(parsed)) {
6573
+ failWriteValidation(`${flag} must be an ISO date (2026-07-10) or epoch milliseconds`);
6574
+ }
6575
+ return parsed;
6576
+ }
6577
+ function parseOnOffFlag(value, flag) {
6578
+ if (value === void 0 || value === null || value === "") {
6579
+ return void 0;
6580
+ }
6581
+ const raw = String(value).toLowerCase();
6582
+ if (raw === "on" || raw === "true") {
6583
+ return true;
6584
+ }
6585
+ if (raw === "off" || raw === "false") {
6586
+ return false;
6587
+ }
6588
+ failWriteValidation(`${flag} must be on|off`);
6589
+ }
6590
+ function parseLocaleFlag(value) {
6591
+ if (value === void 0 || value === null || value === "") {
6592
+ return void 0;
6593
+ }
6594
+ const match = /^([a-z]{2})[_-]([A-Za-z]{2})$/.exec(String(value));
6595
+ if (!match) {
6596
+ failWriteValidation("--locale must look like en_US (language_COUNTRY)");
6597
+ }
6598
+ return { language: match[1], country: match[2].toUpperCase() };
6599
+ }
6600
+ function loadTargetingFileArg(path11) {
6601
+ if (typeof path11 !== "string" || path11.length === 0) {
6602
+ return void 0;
6603
+ }
6604
+ const parsed = loadJsonFileArg(path11);
6605
+ const criteria = parsed.targetingCriteria ?? parsed;
6606
+ if (!criteria.include) {
6607
+ failWriteValidation(
6608
+ `${path11} must contain targeting criteria with an "include" block (see baker schema ads.linkedin.campaigns.create)`
6609
+ );
6610
+ }
6611
+ return criteria;
6612
+ }
6613
+ function sha256Lower(value) {
6614
+ return createHash2("sha256").update(value.trim().toLowerCase()).digest("hex");
6615
+ }
6616
+ function parseCsvLine(line) {
6617
+ const cells = [];
6618
+ let current = "";
6619
+ let inQuotes = false;
6620
+ for (let i = 0; i < line.length; i++) {
6621
+ const char = line[i];
6622
+ if (inQuotes) {
6623
+ if (char === '"' && line[i + 1] === '"') {
6624
+ current += '"';
6625
+ i++;
6626
+ } else if (char === '"') {
6627
+ inQuotes = false;
6628
+ } else {
6629
+ current += char;
6630
+ }
6631
+ } else if (char === '"') {
6632
+ inQuotes = true;
6633
+ } else if (char === ",") {
6634
+ cells.push(current);
6635
+ current = "";
6636
+ } else {
6637
+ current += char;
6638
+ }
6639
+ }
6640
+ cells.push(current);
6641
+ return cells.map((cell) => cell.trim());
6642
+ }
6643
+ function parseListFileArg(path11, maxRows) {
6644
+ if (typeof path11 !== "string" || path11.length === 0) {
6645
+ return void 0;
6646
+ }
6647
+ const raw = readFileSync6(path11, "utf8");
6648
+ const lines = raw.split(/\r?\n/).filter((line) => line.trim().length > 0);
6649
+ if (lines.length < 2) {
6650
+ failWriteValidation(`${path11} needs a header row and at least one data row`);
6651
+ }
6652
+ const columns = parseCsvLine(lines[0]).map((column) => column.trim());
6653
+ const rows = [];
6654
+ for (const line of lines.slice(1)) {
6655
+ const cells = parseCsvLine(line);
6656
+ const row = {};
6657
+ columns.forEach((column, index) => {
6658
+ const value = cells[index] ?? "";
6659
+ if (!value) {
6660
+ return;
6661
+ }
6662
+ row[column] = column.toLowerCase() === "email" ? sha256Lower(value) : value;
6663
+ });
6664
+ if (Object.keys(row).length > 0) {
6665
+ rows.push(row);
6666
+ }
6667
+ }
6668
+ if (rows.length > maxRows) {
6669
+ failWriteValidation(`${path11} has ${rows.length} rows \u2014 the inline limit is ${maxRows}. Split the list.`);
6670
+ }
6671
+ return { columns, rows };
6672
+ }
6673
+ async function stageOp(raw) {
6674
+ const preflight = linkedinDraftOpInputSchema.safeParse(raw);
6675
+ if (!preflight.success) {
6676
+ const fields = preflight.error.issues.map((issue) => ({
6677
+ path: issue.path.join("."),
6678
+ message: issue.message
6679
+ }));
6680
+ writeJsonEnvelope({
6681
+ ok: false,
6682
+ error: {
6683
+ code: "VALIDATION_ERROR",
6684
+ message: fields.map((field) => field.path ? `${field.path}: ${field.message}` : field.message).join("; "),
6685
+ fields
6686
+ }
6687
+ });
6688
+ process.exit(1);
6689
+ }
6690
+ try {
6691
+ const chatId = requireChatId();
6692
+ const response = await apiPost("/api/ads/linkedin/draft/stage", {
6693
+ chatId,
6694
+ op: preflight.data
6695
+ });
6696
+ writeJsonEnvelope(response);
6697
+ } catch (err) {
6698
+ handleLinkedinError(err);
6699
+ }
6700
+ }
6701
+ async function stageStatusChange(input) {
6702
+ const payload = input.kind === "creative.update" ? { intendedStatus: input.status } : { status: input.status };
6703
+ await stageOp({ kind: input.kind, accountId: input.accountId, target: input.target, payload });
6704
+ }
6705
+ function requireTarget(args, entity) {
6706
+ const target = args.id ?? args.target;
6707
+ if (typeof target !== "string" || target.length === 0) {
6708
+ failWriteValidation(`pass the ${entity} id or URN as the positional argument`);
6709
+ }
6710
+ return target;
6711
+ }
6712
+
6713
+ // src/commands/ads/linkedin/write-commands.ts
6714
+ var STAGED_NOTE = "Staged until publish \u2014 review with `baker ads linkedin draft`.";
6715
+ var accountArgs = {
6716
+ "account-id": { type: "string", description: "Numeric account ID or urn:li:sponsoredAccount:N" },
6717
+ "account-urn": { type: "string", description: "Alias for --account-id" }
6718
+ };
6719
+ function runScheduleFromFlags(args) {
6720
+ const start = parseDateFlag(args.start, "--start");
6721
+ const end = parseDateFlag(args.end, "--end");
6722
+ if (start === void 0 && end === void 0) {
6723
+ return void 0;
6724
+ }
6725
+ return { ...start !== void 0 ? { start } : {}, ...end !== void 0 ? { end } : {} };
6726
+ }
6727
+ var campaignGroupsCreateCommand = defineCommand36({
6728
+ meta: {
6729
+ name: "create",
6730
+ description: `Stage a new campaign group. ${STAGED_NOTE}
6731
+ Example: baker ads linkedin campaign-groups create --name "Q3 ABM" --start 2026-07-10 --total-budget 5000 --currency EUR`
6732
+ },
6733
+ args: {
6734
+ ...accountArgs,
6735
+ name: { type: "string", description: "Campaign group name (\u2264100 chars)" },
6736
+ start: { type: "string", description: "Run schedule start (ISO date or epoch ms)" },
6737
+ end: { type: "string", description: "Run schedule end" },
6738
+ "total-budget": { type: "string", description: "Lifetime budget amount" },
6739
+ currency: { type: "string", description: "3-letter currency code (required with money flags)" },
6740
+ status: { type: "string", description: "DRAFT|ACTIVE|PAUSED (default DRAFT)" },
6741
+ file: { type: "string", description: "JSON file with the full payload; flags override file keys" }
6742
+ },
6743
+ run: async ({ args }) => {
6744
+ const accountId = bareAccountId(args);
6745
+ const payload = mergePayload(loadJsonFileArg(args.file), {
6746
+ name: args.name,
6747
+ runSchedule: runScheduleFromFlags(args),
6748
+ totalBudget: parseMoneyFlag(args["total-budget"], args.currency, "--total-budget"),
6749
+ status: args.status ? String(args.status).toUpperCase() : void 0
6750
+ });
6751
+ await stageOp({ kind: "campaignGroup.create", accountId, payload });
6752
+ }
6753
+ });
6754
+ var campaignGroupsUpdateCommand = defineCommand36({
6755
+ meta: {
6756
+ name: "update",
6757
+ description: `Stage changes to an existing campaign group. ${STAGED_NOTE}
6758
+ Example: baker ads linkedin campaign-groups update 635137195 --total-budget 8000 --currency EUR`
6759
+ },
6760
+ args: {
6761
+ id: { type: "positional", description: "Campaign group id or URN", required: true },
6762
+ ...accountArgs,
6763
+ name: { type: "string", description: "New name" },
6764
+ start: { type: "string", description: "New run schedule start" },
6765
+ end: { type: "string", description: "New run schedule end" },
6766
+ "total-budget": { type: "string", description: "New lifetime budget" },
6767
+ currency: { type: "string", description: "3-letter currency code" },
6768
+ status: { type: "string", description: "ACTIVE|PAUSED|ARCHIVED|\u2026" },
6769
+ file: { type: "string", description: "JSON file with fields to change" }
6770
+ },
6771
+ run: async ({ args }) => {
6772
+ const accountId = bareAccountId(args);
6773
+ const payload = mergePayload(loadJsonFileArg(args.file), {
6774
+ name: args.name,
6775
+ runSchedule: runScheduleFromFlags(args),
6776
+ totalBudget: parseMoneyFlag(args["total-budget"], args.currency, "--total-budget"),
6777
+ status: args.status ? String(args.status).toUpperCase() : void 0
6778
+ });
6779
+ await stageOp({
6780
+ kind: "campaignGroup.update",
6781
+ accountId,
6782
+ target: requireTarget(args, "campaign group"),
6783
+ payload
6784
+ });
6785
+ }
6786
+ });
6787
+ function campaignPayloadFromFlags(args) {
6788
+ return {
6789
+ name: args.name,
6790
+ campaignGroup: args.group,
6791
+ type: args.type ? String(args.type).toUpperCase() : void 0,
6792
+ objectiveType: args.objective ? String(args.objective).toUpperCase() : void 0,
6793
+ costType: args["cost-type"] ? String(args["cost-type"]).toUpperCase() : void 0,
6794
+ unitCost: parseMoneyFlag(args.bid, args.currency, "--bid"),
6795
+ dailyBudget: parseMoneyFlag(args["daily-budget"], args.currency, "--daily-budget"),
6796
+ totalBudget: parseMoneyFlag(args["total-budget"], args.currency, "--total-budget"),
6797
+ runSchedule: runScheduleFromFlags(args),
6798
+ locale: parseLocaleFlag(args.locale),
6799
+ targetingCriteria: loadTargetingFileArg(args["targeting-file"]),
6800
+ audienceExpansionEnabled: parseOnOffFlag(args["audience-expansion"], "--audience-expansion"),
6801
+ offsiteDeliveryEnabled: parseOnOffFlag(args.lan, "--lan"),
6802
+ connectedTelevisionOnly: parseOnOffFlag(args.ctv, "--ctv"),
6803
+ creativeSelection: args["creative-selection"] ? String(args["creative-selection"]).toUpperCase() : void 0,
6804
+ associatedEntity: args["associated-entity"],
6805
+ status: args.status ? String(args.status).toUpperCase() : void 0
6806
+ };
6807
+ }
6808
+ var campaignFlagArgs = {
6809
+ ...accountArgs,
6810
+ name: { type: "string", description: "Campaign name (\u2264255 chars)" },
6811
+ group: { type: "string", description: "Campaign group id/URN, or li_temp_* ref staged in this chat" },
6812
+ type: { type: "string", description: "SPONSORED_UPDATES|TEXT_AD|SPONSORED_INMAILS|DYNAMIC" },
6813
+ objective: { type: "string", description: "e.g. LEAD_GENERATION, WEBSITE_VISIT, BRAND_AWARENESS" },
6814
+ "cost-type": { type: "string", description: "CPC|CPM|CPV" },
6815
+ bid: { type: "string", description: "Manual bid (unitCost) \u2014 playbook mandates manual bidding" },
6816
+ "daily-budget": { type: "string", description: "Daily budget amount" },
6817
+ "total-budget": { type: "string", description: "Lifetime budget amount" },
6818
+ currency: { type: "string", description: "3-letter currency code (required with money flags)" },
6819
+ start: { type: "string", description: "Run schedule start (ISO date or epoch ms)" },
6820
+ end: { type: "string", description: "Run schedule end" },
6821
+ locale: { type: "string", description: "Profile locale like en_US" },
6822
+ "targeting-file": { type: "string", description: "JSON file with targetingCriteria (include.and[].or facets)" },
6823
+ "audience-expansion": { type: "string", description: "on|off (playbook: off)" },
6824
+ lan: { type: "string", description: "LinkedIn Audience Network on|off (playbook: off)" },
6825
+ ctv: { type: "string", description: "Connected TV on|off" },
6826
+ "creative-selection": { type: "string", description: "OPTIMIZED|ROUND_ROBIN" },
6827
+ "associated-entity": { type: "string", description: "Organization URN" },
6828
+ status: { type: "string", description: "DRAFT|ACTIVE|PAUSED" },
6829
+ file: { type: "string", description: "JSON file with the full payload; flags override file keys" }
6830
+ };
6831
+ var campaignsCreateCommand = defineCommand36({
6832
+ meta: {
6833
+ name: "create",
6834
+ description: `Stage a new campaign. ${STAGED_NOTE}
6835
+ Targeting goes in --targeting-file (facet URNs). --group accepts a li_temp_* ref from a campaign group staged in this chat.
6836
+ Example: baker ads linkedin campaigns create --name "ABM Tier-1" --group li_temp_x1 --objective WEBSITE_VISIT --type SPONSORED_UPDATES --cost-type CPC --bid 8.50 --daily-budget 75 --currency EUR --locale en_US --targeting-file targeting.json`
6837
+ },
6838
+ args: campaignFlagArgs,
6839
+ run: async ({ args }) => {
6840
+ const accountId = bareAccountId(args);
6841
+ const payload = mergePayload(loadJsonFileArg(args.file), campaignPayloadFromFlags(args));
6842
+ if (payload.locale === void 0) {
6843
+ payload.locale = { country: "US", language: "en" };
6844
+ }
6845
+ await stageOp({ kind: "campaign.create", accountId, payload });
6846
+ }
6847
+ });
6848
+ var campaignsUpdateCommand = defineCommand36({
6849
+ meta: {
6850
+ name: "update",
6851
+ description: `Stage changes to an existing campaign (budget, bid, targeting, flags, schedule, status). ${STAGED_NOTE}
6852
+ Example: baker ads linkedin campaigns update 123456 --daily-budget 100 --currency EUR --audience-expansion off`
6853
+ },
6854
+ args: {
6855
+ id: { type: "positional", description: "Campaign id or URN", required: true },
6856
+ ...campaignFlagArgs
6857
+ },
6858
+ run: async ({ args }) => {
6859
+ const accountId = bareAccountId(args);
6860
+ const flags = campaignPayloadFromFlags(args);
6861
+ flags.campaignGroup = void 0;
6862
+ flags.type = void 0;
6863
+ flags.locale = void 0;
6864
+ flags.associatedEntity = void 0;
6865
+ const payload = mergePayload(loadJsonFileArg(args.file), flags);
6866
+ await stageOp({
6867
+ kind: "campaign.update",
6868
+ accountId,
6869
+ target: requireTarget(args, "campaign"),
6870
+ payload
6871
+ });
6872
+ }
6873
+ });
6874
+ function statusSugarCommand(entity, kind, name) {
6875
+ const status = name === "pause" ? "PAUSED" : name === "resume" ? "ACTIVE" : "ARCHIVED";
6876
+ return defineCommand36({
6877
+ meta: {
6878
+ name,
6879
+ description: `Stage a status change to ${status}. ${STAGED_NOTE}
6880
+ Example: baker ads linkedin ${entity} ${name} 123456`
6881
+ },
6882
+ args: {
6883
+ id: { type: "positional", description: "Entity id or URN", required: true },
6884
+ ...accountArgs
6885
+ },
6886
+ run: async ({ args }) => {
6887
+ await stageStatusChange({
6888
+ kind,
6889
+ accountId: bareAccountId(args),
6890
+ target: requireTarget(args, entity.replace(/-/g, " ").replace(/s$/, "")),
6891
+ status
6892
+ });
6893
+ }
6894
+ });
6895
+ }
6896
+ var campaignsPauseCommand = statusSugarCommand("campaigns", "campaign.update", "pause");
6897
+ var campaignsResumeCommand = statusSugarCommand("campaigns", "campaign.update", "resume");
6898
+ var campaignsArchiveCommand = statusSugarCommand("campaigns", "campaign.update", "archive");
6899
+ var campaignGroupsPauseCommand = statusSugarCommand("campaign-groups", "campaignGroup.update", "pause");
6900
+ var campaignGroupsResumeCommand = statusSugarCommand("campaign-groups", "campaignGroup.update", "resume");
6901
+ var creativesPauseCommand = statusSugarCommand("creatives", "creative.update", "pause");
6902
+ var creativesResumeCommand = statusSugarCommand("creatives", "creative.update", "resume");
6903
+ function duplicateCommand(entity, cliGroup) {
6904
+ return defineCommand36({
6905
+ meta: {
6906
+ name: "duplicate",
6907
+ description: `Stage a copy of an existing ${entity} as a new DRAFT create (playbook: duplicate, never link-to-original). ${STAGED_NOTE}
6908
+ Example: baker ads linkedin ${cliGroup} duplicate 123456 --name "New variant"`
6909
+ },
6910
+ args: {
6911
+ id: { type: "positional", description: "Source id or URN", required: true },
6912
+ ...accountArgs,
6913
+ name: { type: "string", description: "Name for the copy" },
6914
+ file: { type: "string", description: "JSON file with field overrides for the copy" }
6915
+ },
6916
+ run: async ({ args }) => {
6917
+ try {
6918
+ const chatId = requireChatId();
6919
+ const overrides = mergePayload(loadJsonFileArg(args.file), { name: args.name });
6920
+ const response = await apiPost(
6921
+ "/api/ads/linkedin/draft/duplicate",
6922
+ {
6923
+ chatId,
6924
+ accountId: bareAccountId(args),
6925
+ entity,
6926
+ sourceId: requireTarget(args, entity),
6927
+ ...Object.keys(overrides).length > 0 ? { overrides } : {}
6928
+ }
6929
+ );
6930
+ writeJsonEnvelope(response);
6931
+ } catch (err) {
6932
+ handleLinkedinError(err);
6933
+ }
6934
+ }
6935
+ });
6936
+ }
6937
+ var campaignsDuplicateCommand = duplicateCommand("campaign", "campaigns");
6938
+ var campaignGroupsDuplicateCommand = duplicateCommand("campaignGroup", "campaign-groups");
6939
+ var creativesDuplicateCommand = duplicateCommand("creative", "creatives");
6940
+ var CREATIVE_FORMATS = [
6941
+ "image",
6942
+ "video",
6943
+ "text",
6944
+ "spotlight",
6945
+ "follower",
6946
+ "document",
6947
+ "carousel",
6948
+ "conversation",
6949
+ "tla",
6950
+ "jobs"
6951
+ ];
6952
+ var creativesCreateCommand = defineCommand36({
6953
+ meta: {
6954
+ name: "create",
6955
+ description: `Stage a new creative (ad). ${STAGED_NOTE}
6956
+ Formats: ${CREATIVE_FORMATS.join("|")}. Media comes from the Baker library (--image-id / --video-id from \`baker images\`/\`baker videos\`) or as LinkedIn URNs. Complex formats (carousel, conversation, jobs) take --file with the full content object.
6957
+ Examples:
6958
+ baker ads linkedin creatives create --campaign li_temp_x2 --format image --image-id <bakerImageId> --intro "Meet the platform\u2026" --headline "Book a demo" --landing-url https://example.com/demo --cta REQUEST_DEMO
6959
+ baker ads linkedin creatives create --campaign 123456 --format tla --post-urn urn:li:ugcPost:789`
6960
+ },
6961
+ args: {
6962
+ ...accountArgs,
6963
+ campaign: { type: "string", description: "Campaign id/URN, or li_temp_* ref staged in this chat" },
6964
+ format: { type: "string", description: CREATIVE_FORMATS.join("|") },
6965
+ "image-id": { type: "string", description: "Baker image library id (uploaded to LinkedIn at publish)" },
6966
+ "image-urn": { type: "string", description: "Existing urn:li:image:*" },
6967
+ "video-id": { type: "string", description: "Baker video library id" },
6968
+ "video-urn": { type: "string", description: "Existing urn:li:video:*" },
6969
+ "logo-image-id": { type: "string", description: "Baker image id for logo (text/spotlight/follower/jobs)" },
6970
+ intro: { type: "string", description: "Commentary / intro text (\u2264600 chars before feed truncation)" },
6971
+ headline: { type: "string", description: "Headline (\u226470; text ads \u226425)" },
6972
+ description: { type: "string", description: "Description (text ads \u226475, spotlight \u226470)" },
6973
+ "landing-url": { type: "string", description: "https destination URL" },
6974
+ cta: { type: "string", description: "LEARN_MORE|REQUEST_DEMO|SIGN_UP|REGISTER|DOWNLOAD|\u2026" },
6975
+ "cta-label": { type: "string", description: "Spotlight custom CTA label (\u226418 chars)" },
6976
+ "post-urn": { type: "string", description: "TLA: existing post to sponsor (urn:li:share|ugcPost:*)" },
6977
+ "intended-status": { type: "string", description: "DRAFT|ACTIVE|PAUSED (default DRAFT)" },
6978
+ title: { type: "string", description: "Media title" },
6979
+ file: { type: "string", description: "JSON file with the full content object; flags override" }
6980
+ },
6981
+ run: async ({ args }) => {
6982
+ const accountId = bareAccountId(args);
6983
+ if (!args.campaign) {
6984
+ failWriteValidation("--campaign is required (id, URN, or li_temp_* ref)");
6985
+ }
6986
+ const format = args.format ? String(args.format) : void 0;
6987
+ if (!format || !CREATIVE_FORMATS.includes(format)) {
6988
+ failWriteValidation(`--format must be one of ${CREATIVE_FORMATS.join("|")}`);
6989
+ }
6990
+ const content = mergePayload(loadJsonFileArg(args.file), {
6991
+ format,
6992
+ commentary: args.intro,
6993
+ imageId: args["image-id"],
6994
+ imageUrn: args["image-urn"],
6995
+ videoId: args["video-id"],
6996
+ videoUrn: args["video-urn"],
6997
+ logoImageId: args["logo-image-id"],
6998
+ headline: args.headline,
6999
+ description: args.description,
7000
+ landingUrl: args["landing-url"],
7001
+ cta: args.cta ? String(args.cta).toUpperCase() : void 0,
7002
+ ctaLabel: args["cta-label"],
7003
+ postUrn: args["post-urn"],
7004
+ mediaTitle: args.title
7005
+ });
7006
+ await stageOp({
7007
+ kind: "creative.create",
7008
+ accountId,
7009
+ payload: {
7010
+ campaign: args.campaign,
7011
+ intendedStatus: args["intended-status"] ? String(args["intended-status"]).toUpperCase() : void 0,
7012
+ content
7013
+ }
7014
+ });
7015
+ }
7016
+ });
7017
+ var creativesUpdateCommand = defineCommand36({
7018
+ meta: {
7019
+ name: "update",
7020
+ description: `Stage changes to an existing creative. ${STAGED_NOTE}
7021
+ Example: baker ads linkedin creatives update urn:li:sponsoredCreative:789 --intended-status PAUSED`
7022
+ },
7023
+ args: {
7024
+ id: { type: "positional", description: "Creative id or URN", required: true },
7025
+ ...accountArgs,
7026
+ "intended-status": { type: "string", description: "ACTIVE|PAUSED|ARCHIVED|DRAFT" },
7027
+ name: { type: "string", description: "New creative name" },
7028
+ file: { type: "string", description: "JSON file with fields to change" }
7029
+ },
7030
+ run: async ({ args }) => {
7031
+ const accountId = bareAccountId(args);
7032
+ const payload = mergePayload(loadJsonFileArg(args.file), {
7033
+ intendedStatus: args["intended-status"] ? String(args["intended-status"]).toUpperCase() : void 0,
7034
+ name: args.name
7035
+ });
7036
+ await stageOp({
7037
+ kind: "creative.update",
7038
+ accountId,
7039
+ target: requireTarget(args, "creative"),
7040
+ payload
7041
+ });
7042
+ }
7043
+ });
7044
+ var AUDIENCE_TYPES2 = ["company-list", "user-list", "retargeting", "engagement"];
7045
+ var INLINE_ROWS_MAX = 1e4;
7046
+ var audiencesCreateCommand = defineCommand36({
7047
+ meta: {
7048
+ name: "create",
7049
+ description: `Stage a new matched audience. ${STAGED_NOTE}
7050
+ List audiences take --list-file (CSV: companyName,domain for companies; email,\u2026 for contacts \u2014 emails are SHA-256 hashed locally). LinkedIn needs 300+ MATCHED members to serve.
7051
+ Example: baker ads linkedin audiences create --name "ABM Tier-1" --type company-list --list-file accounts.csv`
7052
+ },
7053
+ args: {
7054
+ ...accountArgs,
7055
+ name: { type: "string", description: "Audience name (\u2264100 chars)" },
7056
+ type: { type: "string", description: AUDIENCE_TYPES2.join("|") },
7057
+ "list-file": { type: "string", description: "CSV file with the list rows (header row required)" },
7058
+ file: { type: "string", description: "JSON file with a raw source config (retargeting/engagement)" }
7059
+ },
7060
+ run: async ({ args }) => {
7061
+ const accountId = bareAccountId(args);
7062
+ const list = parseListFileArg(args["list-file"], INLINE_ROWS_MAX);
7063
+ const raw = args.file ? loadJsonFileArg(args.file) : void 0;
7064
+ await stageOp({
7065
+ kind: "audience.create",
7066
+ accountId,
7067
+ payload: {
7068
+ name: args.name,
7069
+ audienceType: args.type,
7070
+ ...list ? { listColumns: list.columns, listRows: list.rows } : {},
7071
+ ...raw ? { raw } : {}
7072
+ }
7073
+ });
7074
+ }
7075
+ });
7076
+ var audiencesUploadCommand = defineCommand36({
7077
+ meta: {
7078
+ name: "upload",
7079
+ description: `Stage a list upload into an existing audience segment. ${STAGED_NOTE}
7080
+ Example: baker ads linkedin audiences upload urn:li:dmpSegment:42 --list-file more-accounts.csv`
7081
+ },
7082
+ args: {
7083
+ id: { type: "positional", description: "Segment URN/id, or li_temp_* ref staged in this chat", required: true },
7084
+ ...accountArgs,
7085
+ "list-file": { type: "string", description: "CSV file with the rows to add", required: true }
7086
+ },
7087
+ run: async ({ args }) => {
7088
+ const accountId = bareAccountId(args);
7089
+ const list = parseListFileArg(args["list-file"], INLINE_ROWS_MAX);
7090
+ if (!list) {
7091
+ failWriteValidation("--list-file is required");
7092
+ }
7093
+ await stageOp({
7094
+ kind: "audience.uploadList",
7095
+ accountId,
7096
+ payload: {
7097
+ segment: requireTarget(args, "audience segment"),
7098
+ listColumns: list.columns,
7099
+ listRows: list.rows
7100
+ }
7101
+ });
7102
+ }
7103
+ });
7104
+ var audiencesCommand = defineCommand36({
7105
+ meta: {
7106
+ name: "audiences",
7107
+ description: "Stage LinkedIn matched-audience changes (create lists, upload rows). Staged until publish \u2014 never hits the API directly."
7108
+ },
7109
+ subCommands: {
7110
+ create: audiencesCreateCommand,
7111
+ upload: audiencesUploadCommand
7112
+ }
7113
+ });
7114
+ var conversionsCreateCommand = defineCommand36({
7115
+ meta: {
7116
+ name: "create",
7117
+ description: `Stage a new conversion rule. ${STAGED_NOTE}
7118
+ Example: baker ads linkedin conversions create --name "Demo booked" --type LEAD --method INSIGHT_TAG --post-click-window 30 --view-window 7`
7119
+ },
7120
+ args: {
7121
+ ...accountArgs,
7122
+ name: { type: "string", description: "Conversion rule name" },
7123
+ type: { type: "string", description: "LEAD|SIGN_UP|DOWNLOAD|PURCHASE|KEY_PAGE_VIEW|ADD_TO_CART|INSTALL|OTHER" },
7124
+ method: { type: "string", description: "INSIGHT_TAG|CONVERSIONS_API" },
7125
+ "post-click-window": { type: "string", description: "1|7|30|90 days (playbook default 30)" },
7126
+ "view-window": { type: "string", description: "1|7|30 days (playbook default 7)" },
7127
+ "attribution-type": { type: "string", description: "LAST_TOUCH_BY_CAMPAIGN|LAST_TOUCH_BY_CONVERSION" },
7128
+ "associate-all-campaigns": { type: "boolean", description: "Auto-associate with active campaigns" },
7129
+ file: { type: "string", description: "JSON file with the full payload (e.g. urlMatchRuleExpression)" }
7130
+ },
7131
+ run: async ({ args }) => {
7132
+ const accountId = bareAccountId(args);
7133
+ const payload = mergePayload(loadJsonFileArg(args.file), {
7134
+ name: args.name,
7135
+ type: args.type ? String(args.type).toUpperCase() : void 0,
7136
+ conversionMethod: args.method ? String(args.method).toUpperCase() : void 0,
7137
+ postClickAttributionWindowSize: args["post-click-window"] ? Number(args["post-click-window"]) : void 0,
7138
+ viewThroughAttributionWindowSize: args["view-window"] ? Number(args["view-window"]) : void 0,
7139
+ attributionType: args["attribution-type"] ? String(args["attribution-type"]).toUpperCase() : void 0,
7140
+ associateAllCampaigns: args["associate-all-campaigns"] === true ? true : void 0
7141
+ });
7142
+ await stageOp({ kind: "conversion.create", accountId, payload });
7143
+ }
7144
+ });
7145
+ var conversionsUpdateCommand = defineCommand36({
7146
+ meta: {
7147
+ name: "update",
7148
+ description: `Stage changes to a conversion rule (windows, name, enabled). ${STAGED_NOTE}
7149
+ Example: baker ads linkedin conversions update 104988516 --post-click-window 30 --view-window 7`
7150
+ },
7151
+ args: {
7152
+ id: { type: "positional", description: "Conversion rule id or URN", required: true },
7153
+ ...accountArgs,
7154
+ name: { type: "string", description: "New name" },
7155
+ "post-click-window": { type: "string", description: "1|7|30|90 days" },
7156
+ "view-window": { type: "string", description: "1|7|30 days" },
7157
+ "attribution-type": { type: "string", description: "LAST_TOUCH_BY_CAMPAIGN|LAST_TOUCH_BY_CONVERSION" },
7158
+ enabled: { type: "string", description: "on|off" },
7159
+ file: { type: "string", description: "JSON file with fields to change" }
7160
+ },
7161
+ run: async ({ args }) => {
7162
+ const accountId = bareAccountId(args);
7163
+ const payload = mergePayload(loadJsonFileArg(args.file), {
7164
+ name: args.name,
7165
+ postClickAttributionWindowSize: args["post-click-window"] ? Number(args["post-click-window"]) : void 0,
7166
+ viewThroughAttributionWindowSize: args["view-window"] ? Number(args["view-window"]) : void 0,
7167
+ attributionType: args["attribution-type"] ? String(args["attribution-type"]).toUpperCase() : void 0,
7168
+ enabled: parseOnOffFlag(args.enabled, "--enabled")
7169
+ });
7170
+ await stageOp({
7171
+ kind: "conversion.update",
7172
+ accountId,
7173
+ target: requireTarget(args, "conversion rule"),
7174
+ payload
7175
+ });
7176
+ }
7177
+ });
7178
+ var leadFormsCreateCommand = defineCommand36({
7179
+ meta: {
7180
+ name: "create",
7181
+ description: `Stage a new Lead Gen Form from a JSON file. ${STAGED_NOTE}
7182
+ The file needs: name, headline (\u226460), privacyPolicyUrl, questions[] (\u226412; playbook: \u22644 for completion). Example: baker ads linkedin lead-forms create --file form.json`
7183
+ },
7184
+ args: {
7185
+ ...accountArgs,
7186
+ file: { type: "string", description: "JSON file with the form definition", required: true },
7187
+ name: { type: "string", description: "Override the form name" }
7188
+ },
7189
+ run: async ({ args }) => {
7190
+ const accountId = bareAccountId(args);
7191
+ const payload = mergePayload(loadJsonFileArg(args.file), { name: args.name });
7192
+ await stageOp({ kind: "leadForm.create", accountId, payload });
7193
+ }
7194
+ });
7195
+ var leadFormsUpdateCommand = defineCommand36({
7196
+ meta: {
7197
+ name: "update",
7198
+ description: `Stage changes to a Lead Gen Form from a JSON file. ${STAGED_NOTE}`
7199
+ },
7200
+ args: {
7201
+ id: { type: "positional", description: "Lead form id or URN", required: true },
7202
+ ...accountArgs,
7203
+ file: { type: "string", description: "JSON file with the fields to change", required: true },
7204
+ name: { type: "string", description: "New form name" }
7205
+ },
7206
+ run: async ({ args }) => {
7207
+ const accountId = bareAccountId(args);
7208
+ const payload = mergePayload({ raw: loadJsonFileArg(args.file) }, { name: args.name });
7209
+ await stageOp({
7210
+ kind: "leadForm.update",
7211
+ accountId,
7212
+ target: requireTarget(args, "lead form"),
7213
+ payload
7214
+ });
7215
+ }
7216
+ });
7217
+ var leadFormsCommand = defineCommand36({
7218
+ meta: {
7219
+ name: "lead-forms",
7220
+ description: "Stage LinkedIn Lead Gen Form changes (file-first). Staged until publish \u2014 never hits the API directly."
7221
+ },
7222
+ subCommands: {
7223
+ create: leadFormsCreateCommand,
7224
+ update: leadFormsUpdateCommand
7225
+ }
7226
+ });
7227
+
7228
+ // src/commands/ads/linkedin/campaign-groups.ts
7229
+ var campaignGroupsCommand = defineCommand37({
5243
7230
  meta: {
5244
7231
  name: "campaign-groups",
5245
- description: `List LinkedIn campaign groups.
7232
+ description: `List LinkedIn campaign groups, or stage writes (create/update/pause/resume/duplicate).
5246
7233
 
5247
7234
  Default: ACTIVE only. Pass --all-statuses to widen, or --statuses ACTIVE,PAUSED to override.
7235
+ Write subcommands stage ops on this chat's draft and apply only when the chat is published.
5248
7236
 
5249
7237
  Examples:
5250
7238
  baker ads linkedin campaign-groups --account-id 503001492
5251
7239
  baker ads linkedin campaign-groups --account-id 503001492 --all-statuses
5252
- baker ads linkedin campaign-groups --account-id 503001492 --statuses ACTIVE,PAUSED --output csv`
7240
+ baker ads linkedin campaign-groups create --name "Q3 ABM" --start 2026-07-10 --total-budget 5000 --currency EUR`
7241
+ },
7242
+ subCommands: {
7243
+ create: campaignGroupsCreateCommand,
7244
+ update: campaignGroupsUpdateCommand,
7245
+ pause: campaignGroupsPauseCommand,
7246
+ resume: campaignGroupsResumeCommand,
7247
+ duplicate: campaignGroupsDuplicateCommand
5253
7248
  },
5254
7249
  args: {
5255
7250
  "account-id": { type: "string", description: "Numeric account ID or urn:li:sponsoredAccount:N" },
@@ -5282,11 +7277,11 @@ Examples:
5282
7277
  });
5283
7278
 
5284
7279
  // src/commands/ads/linkedin/campaigns.ts
5285
- import { defineCommand as defineCommand37 } from "citty";
5286
- var campaignsCommand = defineCommand37({
7280
+ import { defineCommand as defineCommand38 } from "citty";
7281
+ var campaignsCommand = defineCommand38({
5287
7282
  meta: {
5288
7283
  name: "campaigns",
5289
- description: `List LinkedIn campaigns.
7284
+ description: `List LinkedIn campaigns, or stage writes (create/update/pause/resume/archive/duplicate).
5290
7285
 
5291
7286
  Default: ACTIVE only. The schema includes the audit-relevant settings:
5292
7287
  audienceExpansionEnabled \u2014 playbook \xA702 silent budget drain (should be false)
@@ -5294,10 +7289,22 @@ Default: ACTIVE only. The schema includes the audit-relevant settings:
5294
7289
  optimizationTargetType \u2014 driver of the bidding strategy
5295
7290
  costType \u2014 CPC | CPM | CPV | CPS
5296
7291
 
7292
+ Write subcommands stage ops on this chat's draft \u2014 they never hit the LinkedIn
7293
+ API directly and apply only when the chat is published.
7294
+
5297
7295
  Examples:
5298
7296
  baker ads linkedin campaigns --account-id 503001492
5299
- baker ads linkedin campaigns --account-id 503001492 --campaign-group-id 12345
5300
- baker ads linkedin campaigns --account-id 503001492 --all-statuses --output csv`
7297
+ baker ads linkedin campaigns --account-id 503001492 --all-statuses --output csv
7298
+ baker ads linkedin campaigns update 123456 --daily-budget 100 --currency EUR
7299
+ baker ads linkedin campaigns pause 123456`
7300
+ },
7301
+ subCommands: {
7302
+ create: campaignsCreateCommand,
7303
+ update: campaignsUpdateCommand,
7304
+ pause: campaignsPauseCommand,
7305
+ resume: campaignsResumeCommand,
7306
+ archive: campaignsArchiveCommand,
7307
+ duplicate: campaignsDuplicateCommand
5301
7308
  },
5302
7309
  args: {
5303
7310
  "account-id": { type: "string", description: "Numeric account ID or urn:li:sponsoredAccount:N" },
@@ -5332,8 +7339,8 @@ Examples:
5332
7339
  });
5333
7340
 
5334
7341
  // src/commands/ads/linkedin/conversation.ts
5335
- import { defineCommand as defineCommand38 } from "citty";
5336
- var conversationCommand = defineCommand38({
7342
+ import { defineCommand as defineCommand39 } from "citty";
7343
+ var conversationCommand = defineCommand39({
5337
7344
  meta: {
5338
7345
  name: "conversation",
5339
7346
  description: `Per-button click rates inside Sponsored Messaging / Conversation Ads.
@@ -5396,7 +7403,7 @@ Examples:
5396
7403
  });
5397
7404
 
5398
7405
  // src/commands/ads/linkedin/conversions.ts
5399
- import { defineCommand as defineCommand39 } from "citty";
7406
+ import { defineCommand as defineCommand40 } from "citty";
5400
7407
  var DAY_MS3 = 864e5;
5401
7408
  function healthOf(rules) {
5402
7409
  const enabled = rules.filter((r) => r.enabled !== false);
@@ -5422,7 +7429,7 @@ function healthOf(rules) {
5422
7429
  wrongLeadDedup: wrongDedup
5423
7430
  };
5424
7431
  }
5425
- var listCmd = defineCommand39({
7432
+ var listCmd = defineCommand40({
5426
7433
  meta: {
5427
7434
  name: "list",
5428
7435
  description: `List conversion rules on the account.`
@@ -5450,7 +7457,7 @@ var listCmd = defineCommand39({
5450
7457
  }
5451
7458
  }
5452
7459
  });
5453
- var healthCmd = defineCommand39({
7460
+ var healthCmd = defineCommand40({
5454
7461
  meta: {
5455
7462
  name: "health",
5456
7463
  description: `5-point Insight Tag / CAPI health check (playbook \xA707).
@@ -5480,27 +7487,30 @@ Surfaces:
5480
7487
  }
5481
7488
  }
5482
7489
  });
5483
- var conversionsCommand = defineCommand39({
7490
+ var conversionsCommand = defineCommand40({
5484
7491
  meta: {
5485
7492
  name: "conversions",
5486
7493
  description: `Conversion rules \u2014 Insight Tag and Conversions API.
5487
7494
 
5488
7495
  Subcommands:
5489
7496
  list \u2014 every rule on the account
5490
- health \u2014 playbook \xA707 5-point health check`
7497
+ health \u2014 playbook \xA707 5-point health check
7498
+ create / update \u2014 stage a conversion-rule write (applies on chat publish)`
5491
7499
  },
5492
7500
  subCommands: {
5493
7501
  list: listCmd,
5494
- health: healthCmd
7502
+ health: healthCmd,
7503
+ create: conversionsCreateCommand,
7504
+ update: conversionsUpdateCommand
5495
7505
  }
5496
7506
  });
5497
7507
 
5498
7508
  // src/commands/ads/linkedin/creatives.ts
5499
- import { defineCommand as defineCommand40 } from "citty";
5500
- var creativesCommand = defineCommand40({
7509
+ import { defineCommand as defineCommand41 } from "citty";
7510
+ var creativesCommand = defineCommand41({
5501
7511
  meta: {
5502
7512
  name: "creatives",
5503
- description: `List LinkedIn creatives (ads).
7513
+ description: `List LinkedIn creatives (ads), or stage writes (create/update/pause/resume/duplicate).
5504
7514
 
5505
7515
  Defaults to ACTIVE intended-status. Pass --all-statuses or --statuses ACTIVE,PAUSED to widen.
5506
7516
 
@@ -5508,10 +7518,20 @@ The 'content' block is opaque (varies by creative type \u2014 image, video, docu
5508
7518
  spotlight, conversation, dynamic). Use 'reviewStatus' to spot rejected/pending
5509
7519
  creatives during an audit.
5510
7520
 
7521
+ Write subcommands stage ops on this chat's draft \u2014 media comes from the Baker
7522
+ library and is uploaded to LinkedIn only at publish.
7523
+
5511
7524
  Examples:
5512
7525
  baker ads linkedin creatives --account-id 503001492
5513
- baker ads linkedin creatives --account-id 503001492 --campaign-id 1234
5514
- baker ads linkedin creatives --account-id 503001492 --all-statuses --output csv`
7526
+ baker ads linkedin creatives create --campaign 1234 --format image --image-id <bakerImageId> --intro "\u2026" --headline "Book a demo" --landing-url https://example.com --cta REQUEST_DEMO
7527
+ baker ads linkedin creatives pause urn:li:sponsoredCreative:789`
7528
+ },
7529
+ subCommands: {
7530
+ create: creativesCreateCommand,
7531
+ update: creativesUpdateCommand,
7532
+ pause: creativesPauseCommand,
7533
+ resume: creativesResumeCommand,
7534
+ duplicate: creativesDuplicateCommand
5515
7535
  },
5516
7536
  args: {
5517
7537
  "account-id": { type: "string", description: "Numeric account ID or urn:li:sponsoredAccount:N" },
@@ -5546,7 +7566,7 @@ Examples:
5546
7566
  });
5547
7567
 
5548
7568
  // src/commands/ads/linkedin/demographics.ts
5549
- import { defineCommand as defineCommand41 } from "citty";
7569
+ import { defineCommand as defineCommand42 } from "citty";
5550
7570
  var DEFAULT_PIVOTS = ["job-title", "company", "industry", "seniority", "job-function", "company-size"];
5551
7571
  function numberOf2(v) {
5552
7572
  if (typeof v === "number") return Number.isFinite(v) ? v : 0;
@@ -5559,7 +7579,7 @@ function numberOf2(v) {
5559
7579
  function topByImpressions(rows, limit) {
5560
7580
  return [...rows].sort((a, b) => numberOf2(b.impressions) - numberOf2(a.impressions)).slice(0, limit);
5561
7581
  }
5562
- var demographicsCommand = defineCommand41({
7582
+ var demographicsCommand = defineCommand42({
5563
7583
  meta: {
5564
7584
  name: "demographics",
5565
7585
  description: `Sweep all firmographic pivots in one command \u2014 LinkedIn's superpower.
@@ -5642,22 +7662,97 @@ Examples:
5642
7662
  }
5643
7663
  }
5644
7664
  });
5645
- function resolveRange(args) {
5646
- const start = args.start;
5647
- const end = args.end;
5648
- if (start) {
5649
- return { start, end };
5650
- }
5651
- const days = args["last-days"] ? Number(args["last-days"]) : 30;
5652
- if (!Number.isFinite(days) || days < 1) {
5653
- handleLinkedinError(new Error("Invalid --last-days; must be a positive integer."));
5654
- }
5655
- return { start: daysAgoIso(days), end: todayIso() };
5656
- }
7665
+ function resolveRange(args) {
7666
+ const start = args.start;
7667
+ const end = args.end;
7668
+ if (start) {
7669
+ return { start, end };
7670
+ }
7671
+ const days = args["last-days"] ? Number(args["last-days"]) : 30;
7672
+ if (!Number.isFinite(days) || days < 1) {
7673
+ handleLinkedinError(new Error("Invalid --last-days; must be a positive integer."));
7674
+ }
7675
+ return { start: daysAgoIso(days), end: todayIso() };
7676
+ }
7677
+
7678
+ // src/commands/ads/linkedin/draft.ts
7679
+ import { defineCommand as defineCommand43 } from "citty";
7680
+ async function listDraft() {
7681
+ try {
7682
+ const chatId = requireChatId();
7683
+ const response = await apiPost("/api/ads/linkedin/draft", {
7684
+ chatId
7685
+ });
7686
+ writeJsonEnvelope(response);
7687
+ } catch (err) {
7688
+ handleLinkedinError(err);
7689
+ }
7690
+ }
7691
+ var listCommand3 = defineCommand43({
7692
+ meta: {
7693
+ name: "list",
7694
+ description: "Show every LinkedIn write op staged in this chat, its mode (simulated = publish will NOT hit LinkedIn), dependencies, and post-publish results. Example: baker ads linkedin draft"
7695
+ },
7696
+ run: listDraft
7697
+ });
7698
+ var removeCommand2 = defineCommand43({
7699
+ meta: {
7700
+ name: "remove",
7701
+ description: "Drop one staged LinkedIn op. Removing a create cascades to everything that depends on it. Example: baker ads linkedin draft remove li_temp_abc123"
7702
+ },
7703
+ args: {
7704
+ ref: {
7705
+ type: "positional",
7706
+ description: "Op ref (li_temp_* for creates, target id/URN for updates)",
7707
+ required: true
7708
+ }
7709
+ },
7710
+ run: async ({ args }) => {
7711
+ try {
7712
+ const chatId = requireChatId();
7713
+ const response = await apiPost(
7714
+ "/api/ads/linkedin/draft/remove",
7715
+ { chatId, ref: args.ref }
7716
+ );
7717
+ writeJsonEnvelope(response);
7718
+ } catch (err) {
7719
+ handleLinkedinError(err);
7720
+ }
7721
+ }
7722
+ });
7723
+ var clearCommand2 = defineCommand43({
7724
+ meta: {
7725
+ name: "clear",
7726
+ description: "Discard ALL staged LinkedIn ops in this chat's draft \u2014 nothing will apply on publish. Example: baker ads linkedin draft clear"
7727
+ },
7728
+ run: async () => {
7729
+ try {
7730
+ const chatId = requireChatId();
7731
+ const response = await apiPost("/api/ads/linkedin/draft/clear", {
7732
+ chatId
7733
+ });
7734
+ writeJsonEnvelope(response);
7735
+ } catch (err) {
7736
+ handleLinkedinError(err);
7737
+ }
7738
+ }
7739
+ });
7740
+ var linkedinDraftCommand = defineCommand43({
7741
+ meta: {
7742
+ name: "draft",
7743
+ description: "Review and edit the LinkedIn write ops staged in this chat BEFORE publish. Subcommands: list (default), remove, clear. Ops never hit LinkedIn until the chat is published."
7744
+ },
7745
+ subCommands: {
7746
+ list: listCommand3,
7747
+ remove: removeCommand2,
7748
+ clear: clearCommand2
7749
+ },
7750
+ run: listDraft
7751
+ });
5657
7752
 
5658
7753
  // src/commands/ads/linkedin/facets.ts
5659
- import { defineCommand as defineCommand42 } from "citty";
5660
- var listCmd2 = defineCommand42({
7754
+ import { defineCommand as defineCommand44 } from "citty";
7755
+ var listCmd2 = defineCommand44({
5661
7756
  meta: {
5662
7757
  name: "list",
5663
7758
  description: `List every targeting facet LinkedIn supports.
@@ -5685,7 +7780,7 @@ seniorities, titles, employers, growthRate, companyCategory, skills, etc.).`
5685
7780
  }
5686
7781
  }
5687
7782
  });
5688
- var valuesCmd = defineCommand42({
7783
+ var valuesCmd = defineCommand44({
5689
7784
  meta: {
5690
7785
  name: "values",
5691
7786
  description: `Look up entity values for a single facet \u2014 full list or typeahead search.
@@ -5726,7 +7821,7 @@ or the full URN (urn:li:adTargetingFacet:industries).`
5726
7821
  }
5727
7822
  }
5728
7823
  });
5729
- var facetsCommand = defineCommand42({
7824
+ var facetsCommand = defineCommand44({
5730
7825
  meta: {
5731
7826
  name: "facets",
5732
7827
  description: `LinkedIn targeting facets and entity lookup.
@@ -5743,8 +7838,8 @@ Subcommands:
5743
7838
  });
5744
7839
 
5745
7840
  // src/commands/ads/linkedin/forecast.ts
5746
- import { readFileSync as readFileSync6 } from "fs";
5747
- import { defineCommand as defineCommand43 } from "citty";
7841
+ import { readFileSync as readFileSync7 } from "fs";
7842
+ import { defineCommand as defineCommand45 } from "citty";
5748
7843
  function loadTargeting3(args) {
5749
7844
  const inline = args.targeting;
5750
7845
  if (inline) {
@@ -5757,7 +7852,7 @@ function loadTargeting3(args) {
5757
7852
  const file = args["targeting-file"];
5758
7853
  if (file) {
5759
7854
  try {
5760
- return JSON.parse(readFileSync6(file, "utf-8"));
7855
+ return JSON.parse(readFileSync7(file, "utf-8"));
5761
7856
  } catch (e) {
5762
7857
  handleLinkedinError(
5763
7858
  new Error(`Failed to read --targeting-file: ${e instanceof Error ? e.message : "I/O error"}`)
@@ -5774,7 +7869,7 @@ function parseMoney(raw) {
5774
7869
  }
5775
7870
  return { amount: m[1] ?? "0", currencyCode: m[2] ?? "USD" };
5776
7871
  }
5777
- var forecastCommand = defineCommand43({
7872
+ var forecastCommand = defineCommand45({
5778
7873
  meta: {
5779
7874
  name: "forecast",
5780
7875
  description: `Forecast reach + impressions + clicks + spend for a hypothetical campaign.
@@ -5821,9 +7916,9 @@ Examples:
5821
7916
  });
5822
7917
 
5823
7918
  // src/commands/ads/linkedin/leads.ts
5824
- import { defineCommand as defineCommand44 } from "citty";
7919
+ import { defineCommand as defineCommand46 } from "citty";
5825
7920
  var DAY_MS4 = 864e5;
5826
- var leadsCommand = defineCommand44({
7921
+ var leadsCommand = defineCommand46({
5827
7922
  meta: {
5828
7923
  name: "leads",
5829
7924
  description: `List Lead Gen Form responses (playbook \xA707).
@@ -5883,7 +7978,7 @@ Examples:
5883
7978
  });
5884
7979
 
5885
7980
  // src/commands/ads/linkedin/resolve.ts
5886
- import { defineCommand as defineCommand45 } from "citty";
7981
+ import { defineCommand as defineCommand47 } from "citty";
5887
7982
  function toOrgUrn(raw) {
5888
7983
  const trimmed = raw.trim();
5889
7984
  if (trimmed.length === 0) {
@@ -5894,7 +7989,7 @@ function toOrgUrn(raw) {
5894
7989
  }
5895
7990
  return /^\d+$/.test(trimmed) ? `urn:li:organization:${trimmed}` : null;
5896
7991
  }
5897
- var resolveCommand = defineCommand45({
7992
+ var resolveCommand = defineCommand47({
5898
7993
  meta: {
5899
7994
  name: "resolve",
5900
7995
  description: `Resolve organization URNs to company names.
@@ -5936,8 +8031,8 @@ couldn't be resolved \u2014 typically an org outside LinkedIn's targetable set.`
5936
8031
  });
5937
8032
 
5938
8033
  // src/commands/ads/linkedin/top-companies.ts
5939
- import { defineCommand as defineCommand46 } from "citty";
5940
- var topCompaniesCommand = defineCommand46({
8034
+ import { defineCommand as defineCommand48 } from "citty";
8035
+ var topCompaniesCommand = defineCommand48({
5941
8036
  meta: {
5942
8037
  name: "top-companies",
5943
8038
  description: `Top companies whose employees saw / clicked / converted on a campaign.
@@ -6008,7 +8103,7 @@ Examples:
6008
8103
  });
6009
8104
 
6010
8105
  // src/commands/ads/linkedin/index.ts
6011
- var linkedinCommand = defineCommand47({
8106
+ var linkedinCommand = defineCommand49({
6012
8107
  meta: {
6013
8108
  name: "linkedin",
6014
8109
  description: `LinkedIn Marketing API \u2014 AI-first command surface for B2B ad insights.
@@ -6034,6 +8129,17 @@ Performance \u2014 the workhorse (3 axes):
6034
8129
  baker ads linkedin analytics --list-intents
6035
8130
  baker ads linkedin analytics --list-pivots
6036
8131
 
8132
+ Staged writes (NEVER hit LinkedIn directly \u2014 ops stage on this chat's draft,
8133
+ show in the dashboard as pending LinkedIn changes, and apply when the chat is
8134
+ published; if the company's LinkedIn-write flag is off the publish is simulated):
8135
+ baker ads linkedin campaign-groups create|update|pause|resume|duplicate
8136
+ baker ads linkedin campaigns create|update|pause|resume|archive|duplicate
8137
+ baker ads linkedin creatives create|update|pause|resume|duplicate
8138
+ baker ads linkedin audiences create|upload
8139
+ baker ads linkedin conversions create|update
8140
+ baker ads linkedin lead-forms create|update
8141
+ baker ads linkedin draft [list|remove <ref>|clear] \u2014 review/undo staged ops
8142
+
6037
8143
  Account ID format:
6038
8144
  --account-id accepts numeric (503001492) or URN (urn:li:sponsoredAccount:503001492).
6039
8145
  Set BAKER_LINKEDIN_AD_ACCOUNT_ID env to skip --account-id on every call.`
@@ -6044,6 +8150,9 @@ Account ID format:
6044
8150
  "campaign-groups": campaignGroupsCommand,
6045
8151
  campaigns: campaignsCommand,
6046
8152
  creatives: creativesCommand,
8153
+ audiences: audiencesCommand,
8154
+ "lead-forms": leadFormsCommand,
8155
+ draft: linkedinDraftCommand,
6047
8156
  analytics: analyticsCommand,
6048
8157
  demographics: demographicsCommand,
6049
8158
  "top-companies": topCompaniesCommand,
@@ -6060,10 +8169,10 @@ Account ID format:
6060
8169
  });
6061
8170
 
6062
8171
  // src/commands/ads/meta/index.ts
6063
- import { defineCommand as defineCommand60 } from "citty";
8172
+ import { defineCommand as defineCommand62 } from "citty";
6064
8173
 
6065
8174
  // src/commands/ads/meta/account.ts
6066
- import { defineCommand as defineCommand48 } from "citty";
8175
+ import { defineCommand as defineCommand50 } from "citty";
6067
8176
 
6068
8177
  // src/commands/ads/meta/shared.ts
6069
8178
  var DAY_MS5 = 864e5;
@@ -6142,7 +8251,7 @@ function resolveEffectiveStatus(args) {
6142
8251
  }
6143
8252
 
6144
8253
  // src/commands/ads/meta/account.ts
6145
- var accountCommand2 = defineCommand48({
8254
+ var accountCommand2 = defineCommand50({
6146
8255
  meta: {
6147
8256
  name: "account",
6148
8257
  description: `Show single Meta ad account detail (currency, timezone, balance, business).
@@ -6169,8 +8278,8 @@ Examples:
6169
8278
  });
6170
8279
 
6171
8280
  // src/commands/ads/meta/accounts.ts
6172
- import { defineCommand as defineCommand49 } from "citty";
6173
- var accountsCommand3 = defineCommand49({
8281
+ import { defineCommand as defineCommand51 } from "citty";
8282
+ var accountsCommand3 = defineCommand51({
6174
8283
  meta: {
6175
8284
  name: "accounts",
6176
8285
  description: `List Meta ad accounts in this company's connected scope.
@@ -6218,8 +8327,8 @@ Examples:
6218
8327
  });
6219
8328
 
6220
8329
  // src/commands/ads/meta/activities.ts
6221
- import { defineCommand as defineCommand50 } from "citty";
6222
- var activitiesCommand = defineCommand50({
8330
+ import { defineCommand as defineCommand52 } from "citty";
8331
+ var activitiesCommand = defineCommand52({
6223
8332
  meta: {
6224
8333
  name: "activities",
6225
8334
  description: `Audit log of recent ad-account changes (created, paused, edited). Default lookback 7 days,
@@ -6256,8 +8365,8 @@ Examples:
6256
8365
  });
6257
8366
 
6258
8367
  // src/commands/ads/meta/ads.ts
6259
- import { defineCommand as defineCommand51 } from "citty";
6260
- var adsListCommand = defineCommand51({
8368
+ import { defineCommand as defineCommand53 } from "citty";
8369
+ var adsListCommand = defineCommand53({
6261
8370
  meta: {
6262
8371
  name: "ads",
6263
8372
  description: `List ads in a Meta ad account. Defaults to ACTIVE only \u2014 pass --all-statuses to widen.
@@ -6305,8 +8414,8 @@ Examples:
6305
8414
  });
6306
8415
 
6307
8416
  // src/commands/ads/meta/adsets.ts
6308
- import { defineCommand as defineCommand52 } from "citty";
6309
- var adsetsCommand = defineCommand52({
8417
+ import { defineCommand as defineCommand54 } from "citty";
8418
+ var adsetsCommand = defineCommand54({
6310
8419
  meta: {
6311
8420
  name: "adsets",
6312
8421
  description: `List ad sets in a Meta ad account, optionally scoped to one campaign. Defaults to ACTIVE only.
@@ -6348,8 +8457,8 @@ Examples:
6348
8457
  });
6349
8458
 
6350
8459
  // src/commands/ads/meta/audiences.ts
6351
- import { defineCommand as defineCommand53 } from "citty";
6352
- var audiencesCommand = defineCommand53({
8460
+ import { defineCommand as defineCommand55 } from "citty";
8461
+ var audiencesCommand2 = defineCommand55({
6353
8462
  meta: {
6354
8463
  name: "audiences",
6355
8464
  description: `List custom audiences for a Meta ad account. Includes lookalikes, website-pixel audiences,
@@ -6384,8 +8493,8 @@ Examples:
6384
8493
  });
6385
8494
 
6386
8495
  // src/commands/ads/meta/businesses.ts
6387
- import { defineCommand as defineCommand54 } from "citty";
6388
- var businessesCommand = defineCommand54({
8496
+ import { defineCommand as defineCommand56 } from "citty";
8497
+ var businessesCommand = defineCommand56({
6389
8498
  meta: {
6390
8499
  name: "businesses",
6391
8500
  description: `List Meta Business Manager accounts the connected user has access to. Required for ad-studies and product-catalogs commands.
@@ -6415,8 +8524,8 @@ Examples:
6415
8524
  });
6416
8525
 
6417
8526
  // src/commands/ads/meta/campaigns.ts
6418
- import { defineCommand as defineCommand55 } from "citty";
6419
- var campaignsCommand2 = defineCommand55({
8527
+ import { defineCommand as defineCommand57 } from "citty";
8528
+ var campaignsCommand2 = defineCommand57({
6420
8529
  meta: {
6421
8530
  name: "campaigns",
6422
8531
  description: `List campaigns for a Meta ad account. Defaults to ACTIVE only \u2014 pass --all-statuses to widen.
@@ -6460,8 +8569,8 @@ Examples:
6460
8569
  });
6461
8570
 
6462
8571
  // src/commands/ads/meta/creatives.ts
6463
- import { defineCommand as defineCommand56 } from "citty";
6464
- var creativesCommand2 = defineCommand56({
8572
+ import { defineCommand as defineCommand58 } from "citty";
8573
+ var creativesCommand2 = defineCommand58({
6465
8574
  meta: {
6466
8575
  name: "creatives",
6467
8576
  description: `List ad creatives in an account, or fetch a single creative by ID.
@@ -6505,7 +8614,7 @@ Examples:
6505
8614
  });
6506
8615
 
6507
8616
  // src/commands/ads/meta/insights.ts
6508
- import { defineCommand as defineCommand57 } from "citty";
8617
+ import { defineCommand as defineCommand59 } from "citty";
6509
8618
 
6510
8619
  // src/commands/ads/meta/presets.ts
6511
8620
  var INSIGHTS_INTENTS = {
@@ -6710,7 +8819,7 @@ function sortRowsBySpendDesc(rows) {
6710
8819
  return sb - sa;
6711
8820
  });
6712
8821
  }
6713
- var insightsCommand = defineCommand57({
8822
+ var insightsCommand = defineCommand59({
6714
8823
  meta: {
6715
8824
  name: "insights",
6716
8825
  description: `Performance reporting \u2014 the main Meta tool for AI agents.
@@ -6811,8 +8920,8 @@ Async is automatic for heavy queries; pass --async to force it, or --no-async to
6811
8920
  });
6812
8921
 
6813
8922
  // src/commands/ads/meta/pixels.ts
6814
- import { defineCommand as defineCommand58 } from "citty";
6815
- var pixelsCommand = defineCommand58({
8923
+ import { defineCommand as defineCommand60 } from "citty";
8924
+ var pixelsCommand = defineCommand60({
6816
8925
  meta: {
6817
8926
  name: "pixels",
6818
8927
  description: `List Meta Pixels for an ad account, or fetch firing stats for one pixel.
@@ -6883,7 +8992,7 @@ function emit(data, args) {
6883
8992
 
6884
8993
  // src/commands/ads/meta/preview.ts
6885
8994
  import { writeFileSync as writeFileSync3 } from "fs";
6886
- import { defineCommand as defineCommand59 } from "citty";
8995
+ import { defineCommand as defineCommand61 } from "citty";
6887
8996
  var VALID_AD_FORMATS = [
6888
8997
  "DESKTOP_FEED_STANDARD",
6889
8998
  "MOBILE_FEED_STANDARD",
@@ -6917,7 +9026,7 @@ var VALID_AD_FORMATS = [
6917
9026
  "MARKETPLACE_MOBILE",
6918
9027
  "BIZ_DISCO_FEED_MOBILE"
6919
9028
  ];
6920
- var previewCommand = defineCommand59({
9029
+ var previewCommand = defineCommand61({
6921
9030
  meta: {
6922
9031
  name: "preview",
6923
9032
  description: `Generate a Meta-hosted preview iframe for a creative or ad. Returns iframe HTML which you
@@ -6964,7 +9073,7 @@ Examples:
6964
9073
  });
6965
9074
 
6966
9075
  // src/commands/ads/meta/index.ts
6967
- var metaCommand = defineCommand60({
9076
+ var metaCommand = defineCommand62({
6968
9077
  meta: {
6969
9078
  name: "meta",
6970
9079
  description: `Meta Marketing API \u2014 AI-first command surface (Facebook + Instagram ads).
@@ -7007,7 +9116,7 @@ Audit & review:
7007
9116
  adsets: adsetsCommand,
7008
9117
  ads: adsListCommand,
7009
9118
  creatives: creativesCommand2,
7010
- audiences: audiencesCommand,
9119
+ audiences: audiencesCommand2,
7011
9120
  pixels: pixelsCommand,
7012
9121
  activities: activitiesCommand,
7013
9122
  insights: insightsCommand,
@@ -7016,10 +9125,10 @@ Audit & review:
7016
9125
  });
7017
9126
 
7018
9127
  // src/commands/ads/x/index.ts
7019
- import { defineCommand as defineCommand77 } from "citty";
9128
+ import { defineCommand as defineCommand79 } from "citty";
7020
9129
 
7021
9130
  // src/commands/ads/x/accounts.ts
7022
- import { defineCommand as defineCommand61 } from "citty";
9131
+ import { defineCommand as defineCommand63 } from "citty";
7023
9132
  registerSchema({
7024
9133
  command: "ads.x.accounts",
7025
9134
  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.",
@@ -7047,7 +9156,7 @@ function handleAccountsError2(err) {
7047
9156
  writeAdsJson({ ok: false, error: { code: "NETWORK_ERROR", message: "Unexpected error" } });
7048
9157
  process.exit(1);
7049
9158
  }
7050
- var accountsCommand4 = defineCommand61({
9159
+ var accountsCommand4 = defineCommand63({
7051
9160
  meta: {
7052
9161
  name: "accounts",
7053
9162
  description: `List accessible X Ads accounts. Returns account IDs needed for all other commands.
@@ -7087,7 +9196,7 @@ Examples:
7087
9196
  });
7088
9197
 
7089
9198
  // src/commands/ads/x/active-entities.ts
7090
- import { defineCommand as defineCommand62 } from "citty";
9199
+ import { defineCommand as defineCommand64 } from "citty";
7091
9200
 
7092
9201
  // src/commands/ads/x/error-parser.ts
7093
9202
  function mapXErrorCode(message) {
@@ -7238,7 +9347,7 @@ function parseCsv(v) {
7238
9347
  const parts = v.split(",").map((s) => s.trim()).filter(Boolean);
7239
9348
  return parts.length > 0 ? parts : void 0;
7240
9349
  }
7241
- var activeEntitiesCommand = defineCommand62({
9350
+ var activeEntitiesCommand = defineCommand64({
7242
9351
  meta: {
7243
9352
  name: "active-entities",
7244
9353
  description: `List entities with metric activity in a time range.
@@ -7296,7 +9405,7 @@ Examples:
7296
9405
  });
7297
9406
 
7298
9407
  // src/commands/ads/x/audiences.ts
7299
- import { defineCommand as defineCommand63 } from "citty";
9408
+ import { defineCommand as defineCommand65 } from "citty";
7300
9409
  registerSchema({
7301
9410
  command: "ads.x.audiences",
7302
9411
  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.",
@@ -7305,7 +9414,7 @@ registerSchema({
7305
9414
  "no-cache": { type: "boolean", description: "Skip cache", required: false }
7306
9415
  }
7307
9416
  });
7308
- var audiencesCommand2 = defineCommand63({
9417
+ var audiencesCommand3 = defineCommand65({
7309
9418
  meta: {
7310
9419
  name: "audiences",
7311
9420
  description: `List X Ads custom audiences.
@@ -7354,7 +9463,7 @@ Examples:
7354
9463
  });
7355
9464
 
7356
9465
  // src/commands/ads/x/campaigns.ts
7357
- import { defineCommand as defineCommand64 } from "citty";
9466
+ import { defineCommand as defineCommand66 } from "citty";
7358
9467
 
7359
9468
  // src/commands/ads/x/run-list.ts
7360
9469
  function buildCleanParams(opts) {
@@ -7417,7 +9526,7 @@ registerSchema({
7417
9526
  "no-cache": { type: "boolean", description: "Skip cache", required: false }
7418
9527
  }
7419
9528
  });
7420
- var campaignsCommand3 = defineCommand64({
9529
+ var campaignsCommand3 = defineCommand66({
7421
9530
  meta: {
7422
9531
  name: "campaigns",
7423
9532
  description: `List X Ads campaigns. Returns budget, schedule, funding instrument, status.
@@ -7459,7 +9568,7 @@ Examples:
7459
9568
  });
7460
9569
 
7461
9570
  // src/commands/ads/x/cards.ts
7462
- import { defineCommand as defineCommand65 } from "citty";
9571
+ import { defineCommand as defineCommand67 } from "citty";
7463
9572
  registerSchema({
7464
9573
  command: "ads.x.cards",
7465
9574
  description: "List website cards, video cards, and carousels for an X Ads account.",
@@ -7468,7 +9577,7 @@ registerSchema({
7468
9577
  "no-cache": { type: "boolean", description: "Skip cache", required: false }
7469
9578
  }
7470
9579
  });
7471
- var cardsCommand = defineCommand65({
9580
+ var cardsCommand = defineCommand67({
7472
9581
  meta: {
7473
9582
  name: "cards",
7474
9583
  description: `List X Ads cards (rich creatives).
@@ -7517,7 +9626,7 @@ Examples:
7517
9626
  });
7518
9627
 
7519
9628
  // src/commands/ads/x/funding.ts
7520
- import { defineCommand as defineCommand66 } from "citty";
9629
+ import { defineCommand as defineCommand68 } from "citty";
7521
9630
  registerSchema({
7522
9631
  command: "ads.x.funding",
7523
9632
  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.",
@@ -7526,7 +9635,7 @@ registerSchema({
7526
9635
  "no-cache": { type: "boolean", description: "Skip cache", required: false }
7527
9636
  }
7528
9637
  });
7529
- var fundingCommand = defineCommand66({
9638
+ var fundingCommand = defineCommand68({
7530
9639
  meta: {
7531
9640
  name: "funding",
7532
9641
  description: `List funding instruments for an X Ads account.
@@ -7575,7 +9684,7 @@ Examples:
7575
9684
  });
7576
9685
 
7577
9686
  // src/commands/ads/x/line-items.ts
7578
- import { defineCommand as defineCommand67 } from "citty";
9687
+ import { defineCommand as defineCommand69 } from "citty";
7579
9688
  registerSchema({
7580
9689
  command: "ads.x.lineItems",
7581
9690
  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).",
@@ -7587,7 +9696,7 @@ registerSchema({
7587
9696
  "no-cache": { type: "boolean", description: "Skip cache", required: false }
7588
9697
  }
7589
9698
  });
7590
- var lineItemsCommand = defineCommand67({
9699
+ var lineItemsCommand = defineCommand69({
7591
9700
  meta: {
7592
9701
  name: "line-items",
7593
9702
  description: `List X Ads line items (ad groups).
@@ -7628,7 +9737,7 @@ Examples:
7628
9737
  });
7629
9738
 
7630
9739
  // src/commands/ads/x/media.ts
7631
- import { defineCommand as defineCommand68 } from "citty";
9740
+ import { defineCommand as defineCommand70 } from "citty";
7632
9741
  registerSchema({
7633
9742
  command: "ads.x.media",
7634
9743
  description: "List media assets in the X Ads media library (images, GIFs, videos). Filter by media-type (IMAGE, GIF, VIDEO).",
@@ -7638,7 +9747,7 @@ registerSchema({
7638
9747
  "no-cache": { type: "boolean", description: "Skip cache", required: false }
7639
9748
  }
7640
9749
  });
7641
- var mediaCommand = defineCommand68({
9750
+ var mediaCommand = defineCommand70({
7642
9751
  meta: {
7643
9752
  name: "media",
7644
9753
  description: `List media assets in the X Ads media library.
@@ -7690,7 +9799,7 @@ Examples:
7690
9799
  });
7691
9800
 
7692
9801
  // src/commands/ads/x/promoted-tweets.ts
7693
- import { defineCommand as defineCommand69 } from "citty";
9802
+ import { defineCommand as defineCommand71 } from "citty";
7694
9803
  registerSchema({
7695
9804
  command: "ads.x.promotedTweets",
7696
9805
  description: "List promoted tweets for an X Ads account. Returns id, line_item_id, tweet_id, approval_status. Filter by line-item-ids (CSV).",
@@ -7701,7 +9810,7 @@ registerSchema({
7701
9810
  "no-cache": { type: "boolean", description: "Skip cache", required: false }
7702
9811
  }
7703
9812
  });
7704
- var promotedTweetsCommand = defineCommand69({
9813
+ var promotedTweetsCommand = defineCommand71({
7705
9814
  meta: {
7706
9815
  name: "promoted-tweets",
7707
9816
  description: `List X Ads promoted tweets.
@@ -7755,11 +9864,11 @@ Examples:
7755
9864
  });
7756
9865
 
7757
9866
  // src/commands/ads/x/stats/index.ts
7758
- import { defineCommand as defineCommand74 } from "citty";
9867
+ import { defineCommand as defineCommand76 } from "citty";
7759
9868
 
7760
9869
  // src/commands/ads/x/stats/job.ts
7761
9870
  import { gunzipSync } from "zlib";
7762
- import { defineCommand as defineCommand70 } from "citty";
9871
+ import { defineCommand as defineCommand72 } from "citty";
7763
9872
  var POLL_INTERVAL_MS2 = 1e4;
7764
9873
  var DEADLINE_MS = 12 * 60 * 1e3;
7765
9874
  var RESULT_CACHE_TTL_MS = 6 * 60 * 60 * 1e3;
@@ -7829,7 +9938,7 @@ async function pollUntilDone(accountId, jobId) {
7829
9938
  function buildCacheKey(body) {
7830
9939
  return `stats-job:${JSON.stringify(body)}`;
7831
9940
  }
7832
- var statsJobCommand = defineCommand70({
9941
+ var statsJobCommand = defineCommand72({
7833
9942
  meta: {
7834
9943
  name: "job",
7835
9944
  description: `Async X Ads stats job, sync from the CLI's perspective. Creates \u2192 polls \u2192 downloads \u2192 returns.
@@ -7934,7 +10043,7 @@ For fine-grained control (don't wait, poll yourself), use:
7934
10043
  });
7935
10044
 
7936
10045
  // src/commands/ads/x/stats/job-create.ts
7937
- import { defineCommand as defineCommand71 } from "citty";
10046
+ import { defineCommand as defineCommand73 } from "citty";
7938
10047
  registerSchema({
7939
10048
  command: "ads.x.statsJobCreate",
7940
10049
  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.",
@@ -7957,7 +10066,7 @@ function parseCsv3(v) {
7957
10066
  const parts = v.split(",").map((s) => s.trim()).filter(Boolean);
7958
10067
  return parts.length > 0 ? parts : void 0;
7959
10068
  }
7960
- var statsJobCreateCommand = defineCommand71({
10069
+ var statsJobCreateCommand = defineCommand73({
7961
10070
  meta: {
7962
10071
  name: "job-create",
7963
10072
  description: `Create an async X Ads stats job (up to 90 days, supports segmentation).
@@ -8020,7 +10129,7 @@ Examples:
8020
10129
  });
8021
10130
 
8022
10131
  // src/commands/ads/x/stats/job-status.ts
8023
- import { defineCommand as defineCommand72 } from "citty";
10132
+ import { defineCommand as defineCommand74 } from "citty";
8024
10133
  registerSchema({
8025
10134
  command: "ads.x.statsJobStatus",
8026
10135
  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).",
@@ -8030,7 +10139,7 @@ registerSchema({
8030
10139
  "job-ids": { type: "string", description: "CSV of job IDs", required: false }
8031
10140
  }
8032
10141
  });
8033
- var statsJobStatusCommand = defineCommand72({
10142
+ var statsJobStatusCommand = defineCommand74({
8034
10143
  meta: {
8035
10144
  name: "job-status",
8036
10145
  description: `Poll the status of an async X Ads stats job.
@@ -8071,7 +10180,7 @@ Examples:
8071
10180
  });
8072
10181
 
8073
10182
  // src/commands/ads/x/stats/sync.ts
8074
- import { defineCommand as defineCommand73 } from "citty";
10183
+ import { defineCommand as defineCommand75 } from "citty";
8075
10184
 
8076
10185
  // src/commands/ads/x/presets.ts
8077
10186
  var X_STATS_PRESETS = [
@@ -8230,7 +10339,7 @@ async function runSync(args, q) {
8230
10339
  process.exit(1);
8231
10340
  }
8232
10341
  }
8233
- var statsSyncCommand = defineCommand73({
10342
+ var statsSyncCommand = defineCommand75({
8234
10343
  meta: {
8235
10344
  name: "sync",
8236
10345
  description: `Synchronous X Ads analytics (max 7-day window).
@@ -8273,7 +10382,7 @@ Examples:
8273
10382
  });
8274
10383
 
8275
10384
  // src/commands/ads/x/stats/index.ts
8276
- var statsCommand = defineCommand74({
10385
+ var statsCommand = defineCommand76({
8277
10386
  meta: {
8278
10387
  name: "stats",
8279
10388
  description: `X Ads analytics. Sync (\u22647 days, no segmentation) or async jobs (\u226490 days, segmentable).
@@ -8301,7 +10410,7 @@ Examples:
8301
10410
  });
8302
10411
 
8303
10412
  // src/commands/ads/x/targeting-constants.ts
8304
- import { defineCommand as defineCommand75 } from "citty";
10413
+ import { defineCommand as defineCommand77 } from "citty";
8305
10414
  var ALLOWED_CONSTANTS = [
8306
10415
  "locations",
8307
10416
  "interests",
@@ -8329,7 +10438,7 @@ registerSchema({
8329
10438
  "no-cache": { type: "boolean", description: "Skip cache", required: false }
8330
10439
  }
8331
10440
  });
8332
- var targetingConstantsCommand = defineCommand75({
10441
+ var targetingConstantsCommand = defineCommand77({
8333
10442
  meta: {
8334
10443
  name: "targeting-constants",
8335
10444
  description: `Lookup X Ads targeting constants.
@@ -8379,7 +10488,7 @@ Examples:
8379
10488
  });
8380
10489
 
8381
10490
  // src/commands/ads/x/targeting-criteria.ts
8382
- import { defineCommand as defineCommand76 } from "citty";
10491
+ import { defineCommand as defineCommand78 } from "citty";
8383
10492
  registerSchema({
8384
10493
  command: "ads.x.targetingCriteria",
8385
10494
  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.",
@@ -8389,7 +10498,7 @@ registerSchema({
8389
10498
  "no-cache": { type: "boolean", description: "Skip cache", required: false }
8390
10499
  }
8391
10500
  });
8392
- var targetingCriteriaCommand = defineCommand76({
10501
+ var targetingCriteriaCommand = defineCommand78({
8393
10502
  meta: {
8394
10503
  name: "targeting-criteria",
8395
10504
  description: `List targeting criteria attached to line items.
@@ -8440,7 +10549,7 @@ Examples:
8440
10549
  });
8441
10550
 
8442
10551
  // src/commands/ads/x/index.ts
8443
- var xCommand = defineCommand77({
10552
+ var xCommand = defineCommand79({
8444
10553
  meta: {
8445
10554
  name: "x",
8446
10555
  description: `X (Twitter) Ads commands. Read campaigns, line items, promoted tweets, creatives, audiences, and analytics.
@@ -8469,7 +10578,7 @@ The CLI auto-detects --account-id when exactly one X Ads account is connected, o
8469
10578
  "promoted-tweets": promotedTweetsCommand,
8470
10579
  cards: cardsCommand,
8471
10580
  media: mediaCommand,
8472
- audiences: audiencesCommand2,
10581
+ audiences: audiencesCommand3,
8473
10582
  "targeting-criteria": targetingCriteriaCommand,
8474
10583
  "targeting-constants": targetingConstantsCommand,
8475
10584
  "active-entities": activeEntitiesCommand,
@@ -8478,7 +10587,7 @@ The CLI auto-detects --account-id when exactly one X Ads account is connected, o
8478
10587
  });
8479
10588
 
8480
10589
  // src/commands/ads/index.ts
8481
- var adsCommand = defineCommand78({
10590
+ var adsCommand = defineCommand80({
8482
10591
  meta: {
8483
10592
  name: "ads",
8484
10593
  description: `Ad platform commands. Each platform exposes its own native command surface \u2014 no forced parity.
@@ -8508,11 +10617,11 @@ Examples:
8508
10617
  });
8509
10618
 
8510
10619
  // src/commands/canvas/index.ts
8511
- import { defineCommand as defineCommand86 } from "citty";
10620
+ import { defineCommand as defineCommand88 } from "citty";
8512
10621
 
8513
10622
  // src/commands/canvas/catalog.ts
8514
- import { defineCommand as defineCommand79 } from "citty";
8515
- var catalogCommand = defineCommand79({
10623
+ import { defineCommand as defineCommand81 } from "citty";
10624
+ var catalogCommand = defineCommand81({
8516
10625
  meta: {
8517
10626
  name: "catalog",
8518
10627
  description: "Print the agent-facing node catalog (JSON Schema). Includes every registered node grouped by category."
@@ -8529,9 +10638,9 @@ import { execFile } from "child_process";
8529
10638
  import { readdir, readFile, stat } from "fs/promises";
8530
10639
  import path from "path";
8531
10640
  import { promisify } from "util";
8532
- import { defineCommand as defineCommand80 } from "citty";
10641
+ import { defineCommand as defineCommand82 } from "citty";
8533
10642
  var execFileAsync = promisify(execFile);
8534
- var inspectCommand = defineCommand80({
10643
+ var inspectCommand = defineCommand82({
8535
10644
  meta: {
8536
10645
  name: "inspect",
8537
10646
  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."
@@ -8640,7 +10749,7 @@ async function probeDuration(filePath) {
8640
10749
  // src/commands/canvas/run.ts
8641
10750
  import { readFile as readFile2 } from "fs/promises";
8642
10751
  import path4 from "path";
8643
- import { defineCommand as defineCommand81 } from "citty";
10752
+ import { defineCommand as defineCommand83 } from "citty";
8644
10753
 
8645
10754
  // src/commands/canvas/placeholders.ts
8646
10755
  function unsuppliedPlaceholderAssets(canvas) {
@@ -8710,7 +10819,7 @@ async function pruneOldRuns(outputsDir, keep, currentRunId, log) {
8710
10819
  }
8711
10820
 
8712
10821
  // src/commands/canvas/run.ts
8713
- var runCommand = defineCommand81({
10822
+ var runCommand = defineCommand83({
8714
10823
  meta: { name: "run", description: "Validate and execute a canvas JSON file." },
8715
10824
  args: {
8716
10825
  file: { type: "positional", required: true, description: "Path to canvas JSON" },
@@ -8806,30 +10915,30 @@ var runCommand = defineCommand81({
8806
10915
  // src/commands/canvas/scaffold-static-ad.ts
8807
10916
  import { readFile as readFile3, writeFile } from "fs/promises";
8808
10917
  import path5 from "path";
8809
- import { defineCommand as defineCommand82 } from "citty";
10918
+ import { defineCommand as defineCommand84 } from "citty";
8810
10919
 
8811
10920
  // src/engine/scaffold/staticAd.ts
8812
- import { z as z3 } from "zod";
10921
+ import { z as z8 } from "zod";
8813
10922
  var GEN_ASPECT_RATIOS = /* @__PURE__ */ new Set(["1:1", "4:5", "9:16", "16:9", "4:3", "3:4", "2:3", "3:2", "21:9"]);
8814
10923
  var DEFAULT_ASPECT_RATIO = "9:16";
8815
- var Blueprint = z3.object({
8816
- meta: z3.object({ estimated_aspect_ratio: z3.string().optional() }).loose().optional(),
8817
- text_content: z3.array(z3.object({ text: z3.string().optional() }).loose()).optional()
10924
+ var Blueprint = z8.object({
10925
+ meta: z8.object({ estimated_aspect_ratio: z8.string().optional() }).loose().optional(),
10926
+ text_content: z8.array(z8.object({ text: z8.string().optional() }).loose()).optional()
8818
10927
  }).loose();
8819
- var ElementLocator = z3.object({
8820
- collection: z3.enum(["subjects", "people", "brands_logos"]),
8821
- index: z3.number().int().nonnegative()
10928
+ var ElementLocator = z8.object({
10929
+ collection: z8.enum(["subjects", "people", "brands_logos"]),
10930
+ index: z8.number().int().nonnegative()
8822
10931
  }).loose();
8823
- var MainElement = z3.object({
10932
+ var MainElement = z8.object({
8824
10933
  // logo | product | person | animal | badge | other
8825
- type: z3.string(),
8826
- label: z3.string().optional(),
8827
- description: z3.string().optional(),
8828
- expression: z3.string().nullable().optional(),
8829
- reason: z3.string().optional(),
10934
+ type: z8.string(),
10935
+ label: z8.string().optional(),
10936
+ description: z8.string().optional(),
10937
+ expression: z8.string().nullable().optional(),
10938
+ reason: z8.string().optional(),
8830
10939
  locator: ElementLocator.optional()
8831
10940
  }).loose();
8832
- var MainElements = z3.array(MainElement);
10941
+ var MainElements = z8.array(MainElement);
8833
10942
  function sanitizeId(raw, fallback) {
8834
10943
  const id = raw.toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "");
8835
10944
  return /^[a-z]/.test(id) ? id : `${fallback}_${id}`.replace(/_+$/g, "") || fallback;
@@ -9125,7 +11234,7 @@ async function runVisionPasses(canvas) {
9125
11234
  return fail("read_outputs", e instanceof Error ? e.message : String(e));
9126
11235
  }
9127
11236
  }
9128
- var scaffoldStaticAdCommand = defineCommand82({
11237
+ var scaffoldStaticAdCommand = defineCommand84({
9129
11238
  meta: {
9130
11239
  name: "scaffold-static-ad",
9131
11240
  description: "Turn a source/inspiration image into a runnable static-ad canvas. Runs billed passes \u2014 image_describe (the blueprint, baked to prompt.json as the editable 'prompt'), an AI selection of the image's MAIN identity elements, and a structured global-layout pass (the column/row grid with per-region bounds and text sizes) \u2014 then scaffolds a canvas that wires one [TODO] ingest slot per element (logo/product/subject/badge + brand font) into image_generate. Edit prompt.json and drop the real assets, then `baker canvas run` it."
@@ -9219,7 +11328,7 @@ var scaffoldStaticAdCommand = defineCommand82({
9219
11328
  // src/commands/canvas/scaffold-video.ts
9220
11329
  import { cp, mkdir, readFile as readFile6, writeFile as writeFile2 } from "fs/promises";
9221
11330
  import path8 from "path";
9222
- import { defineCommand as defineCommand83 } from "citty";
11331
+ import { defineCommand as defineCommand85 } from "citty";
9223
11332
 
9224
11333
  // src/engine/nodes/local/lib/sceneDetect.ts
9225
11334
  import { execFile as execFile2 } from "child_process";
@@ -9324,7 +11433,7 @@ import { toCardinal as nwKo } from "n2words/ko-KR";
9324
11433
  import { toCardinal as nwNl } from "n2words/nl-NL";
9325
11434
  import { toCardinal as nwPl } from "n2words/pl-PL";
9326
11435
  import { toCardinal as nwPt } from "n2words/pt-PT";
9327
- import { z as z4 } from "zod";
11436
+ import { z as z9 } from "zod";
9328
11437
 
9329
11438
  // src/engine/scaffold/lib/shoot-modes.ts
9330
11439
  var SHOOT_MODES = [
@@ -9581,49 +11690,49 @@ function trimArgs(durationS, offsetS = 0) {
9581
11690
  "{{out.video}}"
9582
11691
  ];
9583
11692
  }
9584
- var FrameAsset = z4.object({ url: z4.string().optional() }).loose().optional();
9585
- var DialogueLine = z4.object({
9586
- speaker: z4.string().optional(),
9587
- line: z4.string().optional(),
11693
+ var FrameAsset = z9.object({ url: z9.string().optional() }).loose().optional();
11694
+ var DialogueLine = z9.object({
11695
+ speaker: z9.string().optional(),
11696
+ line: z9.string().optional(),
9588
11697
  // Absolute seconds on the source timeline (the deconstruct emits both).
9589
- start_s: z4.number().optional(),
9590
- end_s: z4.number().optional(),
9591
- delivery: z4.string().optional(),
9592
- voice_description: z4.string().optional()
11698
+ start_s: z9.number().optional(),
11699
+ end_s: z9.number().optional(),
11700
+ delivery: z9.string().optional(),
11701
+ voice_description: z9.string().optional()
9593
11702
  }).loose();
9594
- var Sfx = z4.object({
9595
- at_s: z4.number().optional(),
9596
- duration_s: z4.number().optional(),
9597
- sound_effect_prompt: z4.string().optional(),
9598
- description: z4.string().optional()
11703
+ var Sfx = z9.object({
11704
+ at_s: z9.number().optional(),
11705
+ duration_s: z9.number().optional(),
11706
+ sound_effect_prompt: z9.string().optional(),
11707
+ description: z9.string().optional()
9599
11708
  }).loose();
9600
- var CompositionRegion = z4.object({
11709
+ var CompositionRegion = z9.object({
9601
11710
  // full | top | bottom | left | right | inset
9602
- panel: z4.string().optional(),
11711
+ panel: z9.string().optional(),
9603
11712
  // 9-grid anchor for an `inset` presenter box.
9604
- position: z4.string().optional(),
9605
- is_presenter: z4.boolean().optional(),
11713
+ position: z9.string().optional(),
11714
+ is_presenter: z9.boolean().optional(),
9606
11715
  // The cast id shown/speaking in this region (routes lip-sync + element refs).
9607
- cast_ref: z4.string().optional(),
9608
- summary: z4.string().optional(),
9609
- frame_prompt: z4.string().optional(),
9610
- motion_prompt: z4.string().optional()
11716
+ cast_ref: z9.string().optional(),
11717
+ summary: z9.string().optional(),
11718
+ frame_prompt: z9.string().optional(),
11719
+ motion_prompt: z9.string().optional()
9611
11720
  }).loose();
9612
- var SceneComposition = z4.object({
11721
+ var SceneComposition = z9.object({
9613
11722
  // full_frame (default) | split_screen | pip | keyed_overlay
9614
- layout: z4.string().optional(),
11723
+ layout: z9.string().optional(),
9615
11724
  // split_screen only: vertical (top/bottom) | horizontal (left/right).
9616
- split_axis: z4.string().optional(),
9617
- regions: z4.array(CompositionRegion).optional()
11725
+ split_axis: z9.string().optional(),
11726
+ regions: z9.array(CompositionRegion).optional()
9618
11727
  }).loose();
9619
- var CameraMotion = z4.object({ movement: z4.string().optional(), detail: z4.string().optional() }).loose();
9620
- var TranscriptWord = z4.object({ text: z4.string().optional() }).loose();
9621
- var Scene = z4.object({
9622
- start_s: z4.number().optional(),
9623
- end_s: z4.number().optional(),
9624
- duration_s: z4.number().optional(),
9625
- summary: z4.string().optional(),
9626
- action_detail: z4.string().optional(),
11728
+ var CameraMotion = z9.object({ movement: z9.string().optional(), detail: z9.string().optional() }).loose();
11729
+ var TranscriptWord = z9.object({ text: z9.string().optional() }).loose();
11730
+ var Scene = z9.object({
11731
+ start_s: z9.number().optional(),
11732
+ end_s: z9.number().optional(),
11733
+ duration_s: z9.number().optional(),
11734
+ summary: z9.string().optional(),
11735
+ action_detail: z9.string().optional(),
9627
11736
  // The scene's spatial layout. Absent/full_frame ⇒ one uncut shot (default path).
9628
11737
  // A layered layout (split_screen/pip/keyed_overlay) with regions ⇒ the scaffold
9629
11738
  // builds one clip per region and stacks/overlays them into the scene picture.
@@ -9631,77 +11740,77 @@ var Scene = z4.object({
9631
11740
  // The capture "look" for this scene — selected from the ad-native shoot-mode
9632
11741
  // grammar (see lib/shoot-modes.ts). When absent the scaffold auto-derives a
9633
11742
  // UGC/product mode; a human can override per scene by setting this.
9634
- shoot_mode: z4.string().optional(),
11743
+ shoot_mode: z9.string().optional(),
9635
11744
  // Diegetic ambient the clip's native audio should carry (no music). When
9636
11745
  // absent the scene falls back to its shoot mode's default ambience.
9637
- ambient: z4.string().optional(),
11746
+ ambient: z9.string().optional(),
9638
11747
  camera_motion: CameraMotion.optional(),
9639
- start_frame_prompt: z4.string().optional(),
9640
- end_frame_prompt: z4.string().optional(),
9641
- motion_prompt: z4.string().optional(),
11748
+ start_frame_prompt: z9.string().optional(),
11749
+ end_frame_prompt: z9.string().optional(),
11750
+ motion_prompt: z9.string().optional(),
9642
11751
  // The scene's role in the ad's persuasion arc (DECON-supplied); drives the
9643
11752
  // script re-craft checklist. Inferred from position when absent.
9644
- narrative_role: z4.string().optional(),
11753
+ narrative_role: z9.string().optional(),
9645
11754
  // DECON-supplied on the HOOK scene: the engineered physical/emotional state that
9646
11755
  // makes the first frame stop the scroll (sweaty/breathless/urgent …). Injected
9647
11756
  // into the hook's start-frame description so the generator renders that state,
9648
11757
  // not a calm influencer (CCA-11).
9649
- hook_mechanic: z4.object({ mechanic: z4.string().optional(), why_it_stops_scroll: z4.string().optional() }).loose().optional(),
11758
+ hook_mechanic: z9.object({ mechanic: z9.string().optional(), why_it_stops_scroll: z9.string().optional() }).loose().optional(),
9650
11759
  // DECON-supplied per-scene location (so a gym hook isn't flattened to "home").
9651
- scene_setting: z4.string().optional(),
11760
+ scene_setting: z9.string().optional(),
9652
11761
  // How this scene cuts to the next (DECON-supplied). A recognized non-cut type
9653
11762
  // (fade/whip/zoom/dissolve/swipe) is reproduced as an ffmpeg xfade at the
9654
11763
  // boundary; cut/match_cut/none/other stay hard cuts. The last scene's value is
9655
11764
  // ignored (nothing follows it).
9656
- transition_out: z4.object({ type: z4.string().optional(), description: z4.string().optional() }).loose().optional(),
9657
- dialogue: z4.array(DialogueLine).optional(),
9658
- sfx: z4.array(Sfx).optional(),
9659
- overlays: z4.array(z4.unknown()).optional(),
9660
- floating_elements: z4.array(z4.unknown()).optional(),
9661
- transcript_slice: z4.array(TranscriptWord).optional(),
11765
+ transition_out: z9.object({ type: z9.string().optional(), description: z9.string().optional() }).loose().optional(),
11766
+ dialogue: z9.array(DialogueLine).optional(),
11767
+ sfx: z9.array(Sfx).optional(),
11768
+ overlays: z9.array(z9.unknown()).optional(),
11769
+ floating_elements: z9.array(z9.unknown()).optional(),
11770
+ transcript_slice: z9.array(TranscriptWord).optional(),
9662
11771
  start_frame_asset: FrameAsset,
9663
11772
  end_frame_asset: FrameAsset,
9664
11773
  // DECON-supplied: true when this scene is a length-split CONTINUATION of the
9665
11774
  // previous one (the SAME physical shot, broken up only because it exceeded the
9666
11775
  // clip ceiling). The scaffold then shares the splice keyframe — this scene's
9667
11776
  // start frame IS the previous scene's end frame — so the join is seamless.
9668
- continues_previous: z4.boolean().optional()
11777
+ continues_previous: z9.boolean().optional()
9669
11778
  }).loose();
9670
- var VideoBlueprint = z4.object({
9671
- source: z4.object({ aspect_ratio: z4.string().optional(), duration_s: z4.number().optional() }).loose().optional(),
9672
- global: z4.object({
9673
- music: z4.object({
9674
- present: z4.boolean().optional(),
9675
- music_prompt: z4.string().optional(),
11779
+ var VideoBlueprint = z9.object({
11780
+ source: z9.object({ aspect_ratio: z9.string().optional(), duration_s: z9.number().optional() }).loose().optional(),
11781
+ global: z9.object({
11782
+ music: z9.object({
11783
+ present: z9.boolean().optional(),
11784
+ music_prompt: z9.string().optional(),
9676
11785
  // Absolute second the music enters in the reference (the bed often
9677
11786
  // kicks in mid-ad, after the hook). We start the regenerated track here
9678
11787
  // instead of at 0 so the timing matches.
9679
- starts_at_s: z4.number().optional(),
11788
+ starts_at_s: z9.number().optional(),
9680
11789
  // Populated by the deconstruct when AudD (Shazam-style) recognizes the
9681
11790
  // reference track. We never reuse it — only style the regenerated bed.
9682
- identified_track: z4.object({ title: z4.string().optional(), artist: z4.string().optional() }).loose().nullish()
11791
+ identified_track: z9.object({ title: z9.string().optional(), artist: z9.string().optional() }).loose().nullish()
9683
11792
  }).loose().optional(),
9684
- cast: z4.array(
9685
- z4.object({
9686
- id: z4.string().optional(),
9687
- description: z4.string().optional(),
11793
+ cast: z9.array(
11794
+ z9.object({
11795
+ id: z9.string().optional(),
11796
+ description: z9.string().optional(),
9688
11797
  // The deconstruct's note on the target-market localization (e.g. "native
9689
11798
  // French speaker") — read to derive the spoken-track language code.
9690
- market_localization_note: z4.string().optional()
11799
+ market_localization_note: z9.string().optional()
9691
11800
  }).loose()
9692
11801
  ).optional(),
9693
- voiceover: z4.object({
11802
+ voiceover: z9.object({
9694
11803
  // on_camera | mixed → mouths are on screen (lip-sync candidates);
9695
11804
  // voiceover | none → narration over the picture (no lip-sync).
9696
- mode: z4.string().optional(),
9697
- voice_description: z4.string().optional(),
9698
- persona: z4.string().optional()
11805
+ mode: z9.string().optional(),
11806
+ voice_description: z9.string().optional(),
11807
+ persona: z9.string().optional()
9699
11808
  }).loose().optional(),
9700
11809
  // Visual palette — read only to colour a clean brand-card/CTA plate (the
9701
11810
  // first hex is the dominant brand colour); never to drive frame generation.
9702
- style: z4.object({ palette: z4.array(z4.object({ hex: z4.string().optional() }).loose()).optional() }).loose().optional()
11811
+ style: z9.object({ palette: z9.array(z9.object({ hex: z9.string().optional() }).loose()).optional() }).loose().optional()
9703
11812
  }).loose().optional(),
9704
- scenes: z4.array(Scene).min(1)
11813
+ scenes: z9.array(Scene).min(1)
9705
11814
  }).loose();
9706
11815
  function injectHookPhysicality(blueprint) {
9707
11816
  for (const scene of blueprint.scenes) {
@@ -9711,26 +11820,26 @@ function injectHookPhysicality(blueprint) {
9711
11820
  scene.start_frame_prompt = `${prompt} The subject's physical state IS the scroll-stopper \u2014 render it explicitly, not a calm pose: ${why}.`;
9712
11821
  }
9713
11822
  }
9714
- var AppearsItem = z4.union([z4.number(), z4.object({ scene: z4.number(), edge: z4.string().optional() }).loose()]);
9715
- var RecurringElement = z4.object({
11823
+ var AppearsItem = z9.union([z9.number(), z9.object({ scene: z9.number(), edge: z9.string().optional() }).loose()]);
11824
+ var RecurringElement = z9.object({
9716
11825
  // person | animal | product | logo | badge | other
9717
- type: z4.string(),
9718
- label: z4.string().optional(),
9719
- description: z4.string().optional(),
9720
- expression: z4.string().nullable().optional(),
11826
+ type: z9.string(),
11827
+ label: z9.string().optional(),
11828
+ description: z9.string().optional(),
11829
+ expression: z9.string().nullable().optional(),
9721
11830
  // When the element maps to a global cast entry, its stable id (for annotation).
9722
- cast_id: z4.string().nullable().optional(),
11831
+ cast_id: z9.string().nullable().optional(),
9723
11832
  // The label of another element that is the SAME individual as this one, shown
9724
11833
  // in a DIFFERENT wardrobe/persona/state (e.g. one creator playing skeptic in a
9725
11834
  // pink shirt and believer in a white shirt). Each look gets its own reference
9726
11835
  // slot, but the face/identity must stay identical across them.
9727
- same_as: z4.string().nullable().optional(),
11836
+ same_as: z9.string().nullable().optional(),
9728
11837
  // Scenes the element appears in. Either a bare list of scene indices (both
9729
11838
  // edges) or per-{scene,edge} entries. Both forms are accepted and merged.
9730
- scenes: z4.array(z4.number()).optional(),
9731
- appears_in: z4.array(AppearsItem).optional()
11839
+ scenes: z9.array(z9.number()).optional(),
11840
+ appears_in: z9.array(AppearsItem).optional()
9732
11841
  }).loose();
9733
- var RecurringElements = z4.array(RecurringElement);
11842
+ var RecurringElements = z9.array(RecurringElement);
9734
11843
  function sanitizeId2(raw, fallback) {
9735
11844
  const id = raw.toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "");
9736
11845
  return /^[a-z]/.test(id) ? id : `${fallback}_${id}`.replace(/_+$/g, "") || fallback;
@@ -11193,25 +13302,25 @@ function buildSfxMusic(blueprint, nodes) {
11193
13302
  }
11194
13303
  return tracks;
11195
13304
  }
11196
- var OverlayStyle = z4.object({ color_hex: z4.string().optional(), background: z4.string().optional(), size: z4.string().optional() }).loose();
11197
- var Overlay = z4.object({
11198
- text: z4.string().optional(),
11199
- appears_at_s: z4.number().optional(),
11200
- duration_s: z4.number().optional(),
11201
- position: z4.string().optional(),
11202
- role: z4.string().optional(),
11203
- animation: z4.string().optional(),
11204
- animation_detail: z4.string().optional(),
13305
+ var OverlayStyle = z9.object({ color_hex: z9.string().optional(), background: z9.string().optional(), size: z9.string().optional() }).loose();
13306
+ var Overlay = z9.object({
13307
+ text: z9.string().optional(),
13308
+ appears_at_s: z9.number().optional(),
13309
+ duration_s: z9.number().optional(),
13310
+ position: z9.string().optional(),
13311
+ role: z9.string().optional(),
13312
+ animation: z9.string().optional(),
13313
+ animation_detail: z9.string().optional(),
11205
13314
  style: OverlayStyle.optional()
11206
13315
  }).loose();
11207
- var FloatingElement = z4.object({
11208
- kind: z4.string().optional(),
11209
- description: z4.string().optional(),
11210
- brand_name: z4.string().nullish(),
11211
- what_it_represents: z4.string().optional(),
11212
- appears_at_s: z4.number().optional(),
11213
- duration_s: z4.number().optional(),
11214
- position: z4.string().optional()
13316
+ var FloatingElement = z9.object({
13317
+ kind: z9.string().optional(),
13318
+ description: z9.string().optional(),
13319
+ brand_name: z9.string().nullish(),
13320
+ what_it_represents: z9.string().optional(),
13321
+ appears_at_s: z9.number().optional(),
13322
+ duration_s: z9.number().optional(),
13323
+ position: z9.string().optional()
11215
13324
  }).loose();
11216
13325
  function escapeHtml(s) {
11217
13326
  return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
@@ -11243,7 +13352,7 @@ function positionClass(position) {
11243
13352
  function collectCaptions(blueprint) {
11244
13353
  return blueprint.scenes.flatMap((scene) => {
11245
13354
  const sceneStart = scene.start_s ?? 0;
11246
- const overlays = z4.array(Overlay).safeParse(scene.overlays ?? []);
13355
+ const overlays = z9.array(Overlay).safeParse(scene.overlays ?? []);
11247
13356
  return overlays.success ? overlays.data.filter((ov) => Boolean(ov.text?.trim())).map((ov) => {
11248
13357
  const at = ov.appears_at_s ?? sceneStart;
11249
13358
  return { text: ov.text.trim(), at, end: at + (ov.duration_s ?? 2.5), ov };
@@ -11352,7 +13461,7 @@ function buildOverlayHtml(input) {
11352
13461
  if (ovParts.length > 0) blocks.push(ovParts.join("\n"));
11353
13462
  for (const scene of blueprint.scenes) {
11354
13463
  const sceneStart = scene.start_s ?? 0;
11355
- const floats = z4.array(FloatingElement).safeParse(scene.floating_elements ?? []);
13464
+ const floats = z9.array(FloatingElement).safeParse(scene.floating_elements ?? []);
11356
13465
  const parts = (floats.success ? floats.data.map((fe) => floatingStub(fe, sceneStart)) : []).filter(Boolean);
11357
13466
  const pip = uiPipStub(scene);
11358
13467
  if (pip) parts.push(pip);
@@ -11609,8 +13718,8 @@ function buildMotionBoard(blueprint) {
11609
13718
  const end_s = scene.end_s ?? start_s + sceneDurationS(scene);
11610
13719
  cursor = end_s;
11611
13720
  const spoken = sceneSpokenText(scene);
11612
- const overlays = z4.array(Overlay).safeParse(scene.overlays ?? []);
11613
- const floats = z4.array(FloatingElement).safeParse(scene.floating_elements ?? []);
13721
+ const overlays = z9.array(Overlay).safeParse(scene.overlays ?? []);
13722
+ const floats = z9.array(FloatingElement).safeParse(scene.floating_elements ?? []);
11614
13723
  const graphics = [
11615
13724
  ...(overlays.success ? overlays.data : []).filter((ov) => ov.text?.trim()).map((ov) => ({
11616
13725
  kind: "text",
@@ -12051,7 +14160,7 @@ async function runAnalysisPasses(deconstructCanvas, selectModel) {
12051
14160
  return fail2("deconstruct", e instanceof Error ? e.message : String(e));
12052
14161
  }
12053
14162
  }
12054
- var scaffoldVideoCommand = defineCommand83({
14163
+ var scaffoldVideoCommand = defineCommand85({
12055
14164
  meta: {
12056
14165
  name: "scaffold-video",
12057
14166
  description: "Turn a reference video into a runnable reproduction canvas in one command. Runs billed passes \u2014 video_deconstruct (the full scene-by-scene blueprint + transcript, baked to prompt.json as the editable 'prompt') and an AI selection of the video's RECURRING identity elements (person/animal/product/logo) \u2014 then scaffolds a pipeline where every scene boundary is a static-ad-grade frame (the blueprint as target_blueprint, a reference legend, the real frame as anchor) and each recurring element gets ONE shared [TODO] ingest slot wired into every frame it appears in. The clips feed Seedance an ultra-detailed motion brief (action, camera, dialogue, transcript). Edit prompt.json, drop the real source images, then `baker canvas run`."
@@ -12196,7 +14305,7 @@ var scaffoldVideoCommand = defineCommand83({
12196
14305
  // src/commands/canvas/set-prompt.ts
12197
14306
  import { readFile as readFile7, writeFile as writeFile3 } from "fs/promises";
12198
14307
  import path9 from "path";
12199
- import { defineCommand as defineCommand84 } from "citty";
14308
+ import { defineCommand as defineCommand86 } from "citty";
12200
14309
  function setNodePrompt(canvas, nodeId, text) {
12201
14310
  const nodes = canvas?.nodes;
12202
14311
  if (!Array.isArray(nodes)) throw new Error("canvas has no nodes array");
@@ -12211,7 +14320,7 @@ function setNodePrompt(canvas, nodeId, text) {
12211
14320
  newNodes[idx] = newNode;
12212
14321
  return { ...canvas, nodes: newNodes };
12213
14322
  }
12214
- var setPromptCommand = defineCommand84({
14323
+ var setPromptCommand = defineCommand86({
12215
14324
  meta: {
12216
14325
  name: "set-prompt",
12217
14326
  description: "Safely set a node's params.prompt (a frame description, motion prompt, etc.) without hand-editing the JSON. Prefer --text-file for multi-line/accented copy \u2014 it preserves UTF-8 exactly, unlike shell-quoted jq."
@@ -12271,8 +14380,8 @@ var setPromptCommand = defineCommand84({
12271
14380
  // src/commands/canvas/validate.ts
12272
14381
  import { readFile as readFile8 } from "fs/promises";
12273
14382
  import path10 from "path";
12274
- import { defineCommand as defineCommand85 } from "citty";
12275
- var validateCommand = defineCommand85({
14383
+ import { defineCommand as defineCommand87 } from "citty";
14384
+ var validateCommand = defineCommand87({
12276
14385
  meta: {
12277
14386
  name: "validate",
12278
14387
  description: "Validate a canvas JSON file (no execution). Includes a per-node cost preview and runs each node's deep validators (composition meta checks for hyperframe_render/_snapshot)."
@@ -12315,7 +14424,7 @@ var validateCommand = defineCommand85({
12315
14424
  });
12316
14425
 
12317
14426
  // src/commands/canvas/index.ts
12318
- var canvasCommand = defineCommand86({
14427
+ var canvasCommand = defineCommand88({
12319
14428
  meta: {
12320
14429
  name: "canvas",
12321
14430
  description: `Run Baker creative canvas JSON files locally. Local nodes execute in-process; remote nodes POST to the Convex backend gateway.
@@ -12342,10 +14451,10 @@ Subcommands:
12342
14451
  });
12343
14452
 
12344
14453
  // src/commands/creatives/index.ts
12345
- import { defineCommand as defineCommand88 } from "citty";
14454
+ import { defineCommand as defineCommand90 } from "citty";
12346
14455
 
12347
14456
  // src/commands/creatives/publish.ts
12348
- import { defineCommand as defineCommand87 } from "citty";
14457
+ import { defineCommand as defineCommand89 } from "citty";
12349
14458
 
12350
14459
  // src/commands/images/api.ts
12351
14460
  import { readFile as readFile9 } from "fs/promises";
@@ -12401,7 +14510,7 @@ async function waitForReadyImage(deps, imageId, opts = {}) {
12401
14510
  let lastStatus = "unknown";
12402
14511
  while (Date.now() <= deadline) {
12403
14512
  const image = await getImage(deps, imageId);
12404
- lastStatus = image.status ?? "unknown";
14513
+ lastStatus = image.status;
12405
14514
  if (image.status === "ready") {
12406
14515
  return image;
12407
14516
  }
@@ -12468,7 +14577,7 @@ async function publishCreative(args, deps = defaultImageApiDeps) {
12468
14577
  sourceReferenceUrl
12469
14578
  });
12470
14579
  }
12471
- var publishCommand = defineCommand87({
14580
+ var publishCommand = defineCommand89({
12472
14581
  meta: {
12473
14582
  name: "publish",
12474
14583
  description: "Publish a final static creative image to Baker Creatives and print the creative reference JSON."
@@ -12514,7 +14623,7 @@ var publishCommand = defineCommand87({
12514
14623
  });
12515
14624
 
12516
14625
  // src/commands/creatives/index.ts
12517
- var creativesCommand3 = defineCommand88({
14626
+ var creativesCommand3 = defineCommand90({
12518
14627
  meta: {
12519
14628
  name: "creatives",
12520
14629
  description: `Publish static ad creatives as first-class Baker outputs.
@@ -12530,10 +14639,10 @@ Publishing uploads the image to the Company image library, applies the official
12530
14639
  });
12531
14640
 
12532
14641
  // src/commands/ga4/index.ts
12533
- import { defineCommand as defineCommand92 } from "citty";
14642
+ import { defineCommand as defineCommand94 } from "citty";
12534
14643
 
12535
14644
  // src/commands/ga4/audit.ts
12536
- import { defineCommand as defineCommand89 } from "citty";
14645
+ import { defineCommand as defineCommand91 } from "citty";
12537
14646
 
12538
14647
  // src/commands/ga4/resolve.ts
12539
14648
  async function fetchProperties(useCache = true) {
@@ -12596,7 +14705,7 @@ registerSchema({
12596
14705
  "no-cache": { type: "boolean", description: "Skip cache, hit API directly", required: false }
12597
14706
  }
12598
14707
  });
12599
- var auditCommand2 = defineCommand89({
14708
+ var auditCommand2 = defineCommand91({
12600
14709
  meta: {
12601
14710
  name: "audit",
12602
14711
  description: `Run all GA4 admin health checks. Returns property config with playbook warnings.
@@ -12648,7 +14757,7 @@ Examples:
12648
14757
  });
12649
14758
 
12650
14759
  // src/commands/ga4/properties.ts
12651
- import { defineCommand as defineCommand90 } from "citty";
14760
+ import { defineCommand as defineCommand92 } from "citty";
12652
14761
  registerSchema({
12653
14762
  command: "ga4.properties",
12654
14763
  description: "List all accessible GA4 properties. Returns property IDs needed for query and audit commands. Run this first to find property IDs.",
@@ -12656,7 +14765,7 @@ registerSchema({
12656
14765
  "no-cache": { type: "boolean", description: "Skip cache, hit API directly", required: false }
12657
14766
  }
12658
14767
  });
12659
- var propertiesCommand = defineCommand90({
14768
+ var propertiesCommand = defineCommand92({
12660
14769
  meta: {
12661
14770
  name: "properties",
12662
14771
  description: `List accessible GA4 properties.
@@ -12704,9 +14813,9 @@ Examples:
12704
14813
  });
12705
14814
 
12706
14815
  // src/commands/ga4/query.ts
12707
- import { appendFileSync as appendFileSync2, existsSync as existsSync5, readFileSync as readFileSync7, writeFileSync as writeFileSync4 } from "fs";
14816
+ import { appendFileSync as appendFileSync2, existsSync as existsSync5, readFileSync as readFileSync8, writeFileSync as writeFileSync4 } from "fs";
12708
14817
  import { resolve as resolve2 } from "path";
12709
- import { defineCommand as defineCommand91 } from "citty";
14818
+ import { defineCommand as defineCommand93 } from "citty";
12710
14819
 
12711
14820
  // src/commands/ga4/presets.ts
12712
14821
  var GA4_PRESETS = [
@@ -12795,7 +14904,7 @@ function writeRowsToFile2(filePath, rows, append) {
12795
14904
  writeFileSync4(filePath, content, "utf-8");
12796
14905
  }
12797
14906
  } else if (append && existsSync5(filePath)) {
12798
- const existing = JSON.parse(readFileSync7(filePath, "utf-8"));
14907
+ const existing = JSON.parse(readFileSync8(filePath, "utf-8"));
12799
14908
  writeFileSync4(filePath, JSON.stringify([...existing, ...rows], null, 2), "utf-8");
12800
14909
  } else {
12801
14910
  writeFileSync4(filePath, JSON.stringify(rows, null, 2), "utf-8");
@@ -12838,7 +14947,7 @@ function handleError(err) {
12838
14947
  });
12839
14948
  process.exit(1);
12840
14949
  }
12841
- var queryCommand2 = defineCommand91({
14950
+ var queryCommand2 = defineCommand93({
12842
14951
  meta: {
12843
14952
  name: "query",
12844
14953
  description: `Run GA4 Data API reports. Preset-first with free-form escape hatch.
@@ -12909,7 +15018,7 @@ Free-form (escape hatch):
12909
15018
  });
12910
15019
 
12911
15020
  // src/commands/ga4/index.ts
12912
- var ga4Command = defineCommand92({
15021
+ var ga4Command = defineCommand94({
12913
15022
  meta: {
12914
15023
  name: "ga4",
12915
15024
  description: `Google Analytics 4 commands. Audit property config, run playbook-aligned reports.
@@ -12932,12 +15041,12 @@ Examples:
12932
15041
  });
12933
15042
 
12934
15043
  // src/commands/gsc/index.ts
12935
- import { defineCommand as defineCommand96 } from "citty";
15044
+ import { defineCommand as defineCommand98 } from "citty";
12936
15045
 
12937
15046
  // src/commands/gsc/query.ts
12938
- import { appendFileSync as appendFileSync3, existsSync as existsSync6, readFileSync as readFileSync8, writeFileSync as writeFileSync5 } from "fs";
15047
+ import { appendFileSync as appendFileSync3, existsSync as existsSync6, readFileSync as readFileSync9, writeFileSync as writeFileSync5 } from "fs";
12939
15048
  import { resolve as resolve3 } from "path";
12940
- import { defineCommand as defineCommand93 } from "citty";
15049
+ import { defineCommand as defineCommand95 } from "citty";
12941
15050
 
12942
15051
  // src/commands/gsc/presets.ts
12943
15052
  var GSC_PRESETS = [
@@ -13080,7 +15189,7 @@ function writeRowsToFile3(filePath, rows, append) {
13080
15189
  writeFileSync5(filePath, content, "utf-8");
13081
15190
  }
13082
15191
  } else if (append && existsSync6(filePath)) {
13083
- const existing = JSON.parse(readFileSync8(filePath, "utf-8"));
15192
+ const existing = JSON.parse(readFileSync9(filePath, "utf-8"));
13084
15193
  writeFileSync5(filePath, JSON.stringify([...existing, ...rows], null, 2), "utf-8");
13085
15194
  } else {
13086
15195
  writeFileSync5(filePath, JSON.stringify(rows, null, 2), "utf-8");
@@ -13125,7 +15234,7 @@ function handleError2(err) {
13125
15234
  });
13126
15235
  process.exit(1);
13127
15236
  }
13128
- var queryCommand3 = defineCommand93({
15237
+ var queryCommand3 = defineCommand95({
13129
15238
  meta: {
13130
15239
  name: "query",
13131
15240
  description: `Run GSC Search Analytics queries. Preset-first with free-form escape hatch.
@@ -13203,7 +15312,7 @@ Free-form (escape hatch):
13203
15312
  });
13204
15313
 
13205
15314
  // src/commands/gsc/sitemaps.ts
13206
- import { defineCommand as defineCommand94 } from "citty";
15315
+ import { defineCommand as defineCommand96 } from "citty";
13207
15316
  registerSchema({
13208
15317
  command: "gsc.sitemaps",
13209
15318
  description: "List sitemaps for a Search Console site. Check sitemap health and errors.",
@@ -13212,7 +15321,7 @@ registerSchema({
13212
15321
  "no-cache": { type: "boolean", description: "Skip cache, hit API directly", required: false }
13213
15322
  }
13214
15323
  });
13215
- var sitemapsCommand = defineCommand94({
15324
+ var sitemapsCommand = defineCommand96({
13216
15325
  meta: {
13217
15326
  name: "sitemaps",
13218
15327
  description: `List sitemaps for a site. Check health and errors.
@@ -13262,7 +15371,7 @@ Examples:
13262
15371
  });
13263
15372
 
13264
15373
  // src/commands/gsc/sites.ts
13265
- import { defineCommand as defineCommand95 } from "citty";
15374
+ import { defineCommand as defineCommand97 } from "citty";
13266
15375
  registerSchema({
13267
15376
  command: "gsc.sites",
13268
15377
  description: "List all verified Google Search Console sites. Returns site URLs needed for query and sitemaps commands.",
@@ -13270,7 +15379,7 @@ registerSchema({
13270
15379
  "no-cache": { type: "boolean", description: "Skip cache, hit API directly", required: false }
13271
15380
  }
13272
15381
  });
13273
- var sitesCommand = defineCommand95({
15382
+ var sitesCommand = defineCommand97({
13274
15383
  meta: {
13275
15384
  name: "sites",
13276
15385
  description: `List verified Search Console sites.
@@ -13318,7 +15427,7 @@ Examples:
13318
15427
  });
13319
15428
 
13320
15429
  // src/commands/gsc/index.ts
13321
- var gscCommand = defineCommand96({
15430
+ var gscCommand = defineCommand98({
13322
15431
  meta: {
13323
15432
  name: "gsc",
13324
15433
  description: `Google Search Console commands. PPC-SEO arbitrage, brand halo analysis, negative keyword discovery.
@@ -13341,10 +15450,10 @@ Examples:
13341
15450
  });
13342
15451
 
13343
15452
  // src/commands/images/index.ts
13344
- import { defineCommand as defineCommand120 } from "citty";
15453
+ import { defineCommand as defineCommand122 } from "citty";
13345
15454
 
13346
15455
  // src/commands/images/crop.ts
13347
- import { defineCommand as defineCommand97 } from "citty";
15456
+ import { defineCommand as defineCommand99 } from "citty";
13348
15457
 
13349
15458
  // src/lib/image/crop-sprite.ts
13350
15459
  import sharp from "sharp";
@@ -13469,7 +15578,7 @@ function emitError2(err) {
13469
15578
  }
13470
15579
  process.exit(1);
13471
15580
  }
13472
- var cropCommand = defineCommand97({
15581
+ var cropCommand = defineCommand99({
13473
15582
  meta: {
13474
15583
  name: "crop",
13475
15584
  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"
@@ -13505,7 +15614,7 @@ var cropCommand = defineCommand97({
13505
15614
  });
13506
15615
 
13507
15616
  // src/commands/images/delete.ts
13508
- import { defineCommand as defineCommand98 } from "citty";
15617
+ import { defineCommand as defineCommand100 } from "citty";
13509
15618
  registerSchema({
13510
15619
  command: "images.delete",
13511
15620
  description: "Delete an image by ID",
@@ -13519,7 +15628,7 @@ registerSchema({
13519
15628
  }
13520
15629
  }
13521
15630
  });
13522
- var deleteCommand = defineCommand98({
15631
+ var deleteCommand = defineCommand100({
13523
15632
  meta: {
13524
15633
  name: "delete",
13525
15634
  description: "Delete an image by ID. Use --dry-run to preview. Example: baker images delete j571abc123 --dry-run"
@@ -13560,7 +15669,7 @@ var deleteCommand = defineCommand98({
13560
15669
  });
13561
15670
 
13562
15671
  // src/commands/images/dimensions.ts
13563
- import { defineCommand as defineCommand99 } from "citty";
15672
+ import { defineCommand as defineCommand101 } from "citty";
13564
15673
 
13565
15674
  // src/lib/image/dimensions.ts
13566
15675
  import { imageSize } from "image-size";
@@ -13583,7 +15692,7 @@ registerSchema({
13583
15692
  target: { type: "string", description: "Local file path or remote http(s) URL", required: true }
13584
15693
  }
13585
15694
  });
13586
- var dimensionsCommand = defineCommand99({
15695
+ var dimensionsCommand = defineCommand101({
13587
15696
  meta: {
13588
15697
  name: "dimensions",
13589
15698
  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"
@@ -13627,7 +15736,7 @@ var dimensionsCommand = defineCommand99({
13627
15736
  });
13628
15737
 
13629
15738
  // src/commands/images/extract.ts
13630
- import { defineCommand as defineCommand100 } from "citty";
15739
+ import { defineCommand as defineCommand102 } from "citty";
13631
15740
  registerSchema({
13632
15741
  command: "images.extract",
13633
15742
  description: "Extract images from a URL via Firecrawl (formats: images).",
@@ -13643,7 +15752,7 @@ registerSchema({
13643
15752
  }
13644
15753
  }
13645
15754
  });
13646
- var extractCommand = defineCommand100({
15755
+ var extractCommand = defineCommand102({
13647
15756
  meta: {
13648
15757
  name: "extract",
13649
15758
  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"
@@ -13681,7 +15790,7 @@ var extractCommand = defineCommand100({
13681
15790
  });
13682
15791
 
13683
15792
  // src/commands/images/find.ts
13684
- import { defineCommand as defineCommand101 } from "citty";
15793
+ import { defineCommand as defineCommand103 } from "citty";
13685
15794
  registerSchema({
13686
15795
  command: "images.find",
13687
15796
  description: "Fanout image search: library first, then opted-in external providers.",
@@ -13713,7 +15822,7 @@ registerSchema({
13713
15822
  }
13714
15823
  }
13715
15824
  });
13716
- var findCommand = defineCommand101({
15825
+ var findCommand = defineCommand103({
13717
15826
  meta: {
13718
15827
  name: "find",
13719
15828
  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"
@@ -13739,7 +15848,9 @@ var findCommand = defineCommand101({
13739
15848
  process.exit(1);
13740
15849
  }
13741
15850
  const body = { query };
13742
- if (args.sources) body.sources = args.sources.split(",").filter(Boolean);
15851
+ if (args.sources) {
15852
+ body.sources = args.sources.split(",").filter(Boolean);
15853
+ }
13743
15854
  if (args.limit) body.limit = Number(args.limit);
13744
15855
  if (args.fallback) body.fallback = true;
13745
15856
  if (args.threshold) body.threshold = Number(args.threshold);
@@ -13760,7 +15871,7 @@ var findCommand = defineCommand101({
13760
15871
 
13761
15872
  // src/commands/images/generate.ts
13762
15873
  import { readFile as readFile11 } from "fs/promises";
13763
- import { defineCommand as defineCommand102 } from "citty";
15874
+ import { defineCommand as defineCommand104 } from "citty";
13764
15875
  import sharp2 from "sharp";
13765
15876
  var GENERATE_TIMEOUT_MS = 18e4;
13766
15877
  var REFERENCE_MAX_EDGE = 1536;
@@ -13826,8 +15937,15 @@ function buildGenerateBody(args, prompt) {
13826
15937
  if (args["aspect-ratio"]) body.aspectRatio = args["aspect-ratio"];
13827
15938
  if (args["image-size"]) body.imageSize = args["image-size"];
13828
15939
  if (args.strength !== void 0) body.strength = Number(args.strength);
13829
- if (args["rgb-colors"]) body.rgbColors = parseJsonArg(args["rgb-colors"], "rgb-colors");
13830
- if (args["bg-rgb"]) body.backgroundRgbColor = parseJsonArg(args["bg-rgb"], "bg-rgb");
15940
+ if (args["rgb-colors"]) {
15941
+ body.rgbColors = parseJsonArg(args["rgb-colors"], "rgb-colors");
15942
+ }
15943
+ if (args["bg-rgb"]) {
15944
+ body.backgroundRgbColor = parseJsonArg(
15945
+ args["bg-rgb"],
15946
+ "bg-rgb"
15947
+ );
15948
+ }
13831
15949
  if (args.context) body.descriptionContext = args.context;
13832
15950
  return body;
13833
15951
  }
@@ -13856,7 +15974,7 @@ async function resolveReferences(spec) {
13856
15974
  }
13857
15975
  return out;
13858
15976
  }
13859
- var generateCommand = defineCommand102({
15977
+ var generateCommand = defineCommand104({
13860
15978
  meta: {
13861
15979
  name: "generate",
13862
15980
  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]]'"
@@ -13908,7 +16026,7 @@ var generateCommand = defineCommand102({
13908
16026
  });
13909
16027
 
13910
16028
  // src/commands/images/get.ts
13911
- import { defineCommand as defineCommand103 } from "citty";
16029
+ import { defineCommand as defineCommand105 } from "citty";
13912
16030
  registerSchema({
13913
16031
  command: "images.get",
13914
16032
  description: "Get a single image by ID",
@@ -13916,7 +16034,7 @@ registerSchema({
13916
16034
  id: { type: "string", description: "Image ID", required: true }
13917
16035
  }
13918
16036
  });
13919
- var getCommand2 = defineCommand103({
16037
+ var getCommand2 = defineCommand105({
13920
16038
  meta: { name: "get", description: "Get a single image by ID. Example: baker images get j571abc123" },
13921
16039
  args: {
13922
16040
  id: { type: "positional", description: "Image ID", required: false },
@@ -13952,7 +16070,7 @@ var getCommand2 = defineCommand103({
13952
16070
  });
13953
16071
 
13954
16072
  // src/commands/images/gif.ts
13955
- import { defineCommand as defineCommand104 } from "citty";
16073
+ import { defineCommand as defineCommand106 } from "citty";
13956
16074
  registerSchema({
13957
16075
  command: "images.gif",
13958
16076
  description: "Search Giphy for GIFs / reaction memes (paid social creative).",
@@ -13984,7 +16102,7 @@ registerSchema({
13984
16102
  }
13985
16103
  }
13986
16104
  });
13987
- var gifCommand = defineCommand104({
16105
+ var gifCommand = defineCommand106({
13988
16106
  meta: {
13989
16107
  name: "gif",
13990
16108
  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"
@@ -14031,7 +16149,7 @@ var gifCommand = defineCommand104({
14031
16149
  });
14032
16150
 
14033
16151
  // src/commands/images/google.ts
14034
- import { defineCommand as defineCommand105 } from "citty";
16152
+ import { defineCommand as defineCommand107 } from "citty";
14035
16153
  registerSchema({
14036
16154
  command: "images.google",
14037
16155
  description: "Google Images search via the official Custom Search JSON API. Unverified source \u2014 inspect before placing.",
@@ -14067,7 +16185,7 @@ registerSchema({
14067
16185
  }
14068
16186
  }
14069
16187
  });
14070
- var googleCommand2 = defineCommand105({
16188
+ var googleCommand2 = defineCommand107({
14071
16189
  meta: {
14072
16190
  name: "google",
14073
16191
  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"
@@ -14115,7 +16233,7 @@ var googleCommand2 = defineCommand105({
14115
16233
  });
14116
16234
 
14117
16235
  // src/commands/images/icon.ts
14118
- import { defineCommand as defineCommand106 } from "citty";
16236
+ import { defineCommand as defineCommand108 } from "citty";
14119
16237
  registerSchema({
14120
16238
  command: "images.icon",
14121
16239
  description: "Icon lookup via Iconify (200+ icon sets, free CDN).",
@@ -14141,7 +16259,7 @@ registerSchema({
14141
16259
  }
14142
16260
  }
14143
16261
  });
14144
- var iconCommand = defineCommand106({
16262
+ var iconCommand = defineCommand108({
14145
16263
  meta: {
14146
16264
  name: "icon",
14147
16265
  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'"
@@ -14181,7 +16299,7 @@ var iconCommand = defineCommand106({
14181
16299
  });
14182
16300
 
14183
16301
  // src/commands/images/ingest.ts
14184
- import { defineCommand as defineCommand107 } from "citty";
16302
+ import { defineCommand as defineCommand109 } from "citty";
14185
16303
  registerSchema({
14186
16304
  command: "images.ingest",
14187
16305
  description: "Ingest a remote image URL into the library (full describe + embed).",
@@ -14193,7 +16311,7 @@ registerSchema({
14193
16311
  context: { type: "string", description: "Description context hint", required: false }
14194
16312
  }
14195
16313
  });
14196
- var ingestCommand = defineCommand107({
16314
+ var ingestCommand = defineCommand109({
14197
16315
  meta: {
14198
16316
  name: "ingest",
14199
16317
  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"
@@ -14235,7 +16353,7 @@ var ingestCommand = defineCommand107({
14235
16353
  });
14236
16354
 
14237
16355
  // src/commands/images/library.ts
14238
- import { defineCommand as defineCommand108 } from "citty";
16356
+ import { defineCommand as defineCommand110 } from "citty";
14239
16357
  registerSchema({
14240
16358
  command: "images.library",
14241
16359
  description: "Search the company image library. Returns only ready images.",
@@ -14261,7 +16379,7 @@ registerSchema({
14261
16379
  }
14262
16380
  }
14263
16381
  });
14264
- var libraryCommand = defineCommand108({
16382
+ var libraryCommand = defineCommand110({
14265
16383
  meta: {
14266
16384
  name: "library",
14267
16385
  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"
@@ -14318,7 +16436,7 @@ var libraryCommand = defineCommand108({
14318
16436
  });
14319
16437
 
14320
16438
  // src/commands/images/logo.ts
14321
- import { defineCommand as defineCommand109 } from "citty";
16439
+ import { defineCommand as defineCommand111 } from "citty";
14322
16440
  registerSchema({
14323
16441
  command: "images.logo",
14324
16442
  description: "Brand logo lookup via Brandfetch CDN (fallback/404). Auto-ingests by default.",
@@ -14343,7 +16461,7 @@ registerSchema({
14343
16461
  }
14344
16462
  }
14345
16463
  });
14346
- var logoCommand = defineCommand109({
16464
+ var logoCommand = defineCommand111({
14347
16465
  meta: {
14348
16466
  name: "logo",
14349
16467
  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"
@@ -14381,7 +16499,7 @@ var logoCommand = defineCommand109({
14381
16499
  });
14382
16500
 
14383
16501
  // src/commands/images/normalize.ts
14384
- import { defineCommand as defineCommand110 } from "citty";
16502
+ import { defineCommand as defineCommand112 } from "citty";
14385
16503
 
14386
16504
  // src/lib/image/color-changer.ts
14387
16505
  import quantize from "quantize";
@@ -15113,7 +17231,7 @@ function coerceRawArgs(args) {
15113
17231
  "dry-run": bool(args["dry-run"])
15114
17232
  };
15115
17233
  }
15116
- var normalizeCommand = defineCommand110({
17234
+ var normalizeCommand = defineCommand112({
15117
17235
  meta: {
15118
17236
  name: "normalize",
15119
17237
  description: `Normalize logos / images: declarative recolor + bg removal + trim + resize. Operates on local files; writes in-place by default.
@@ -15168,7 +17286,7 @@ Examples:
15168
17286
  });
15169
17287
 
15170
17288
  // src/commands/images/pinterest.ts
15171
- import { defineCommand as defineCommand111 } from "citty";
17289
+ import { defineCommand as defineCommand113 } from "citty";
15172
17290
  registerSchema({
15173
17291
  command: "images.pinterest",
15174
17292
  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.",
@@ -15188,7 +17306,7 @@ registerSchema({
15188
17306
  }
15189
17307
  }
15190
17308
  });
15191
- var pinterestCommand = defineCommand111({
17309
+ var pinterestCommand = defineCommand113({
15192
17310
  meta: {
15193
17311
  name: "pinterest",
15194
17312
  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'"
@@ -15228,7 +17346,7 @@ var pinterestCommand = defineCommand111({
15228
17346
  });
15229
17347
 
15230
17348
  // src/commands/images/screenshot.ts
15231
- import { defineCommand as defineCommand112 } from "citty";
17349
+ import { defineCommand as defineCommand114 } from "citty";
15232
17350
  registerSchema({
15233
17351
  command: "images.screenshot",
15234
17352
  description: "Capture a website screenshot via ScreenshotOne. Auto-ingests on success.",
@@ -15244,7 +17362,7 @@ registerSchema({
15244
17362
  }
15245
17363
  }
15246
17364
  });
15247
- var screenshotCommand = defineCommand112({
17365
+ var screenshotCommand = defineCommand114({
15248
17366
  meta: {
15249
17367
  name: "screenshot",
15250
17368
  description: "Screenshot a URL via ScreenshotOne. $0.009/capture. Auto-ingests to library.\n\nExample: baker images screenshot https://stripe.com --full-page"
@@ -15294,7 +17412,7 @@ var screenshotCommand = defineCommand112({
15294
17412
  });
15295
17413
 
15296
17414
  // src/commands/images/search.ts
15297
- import { defineCommand as defineCommand113 } from "citty";
17415
+ import { defineCommand as defineCommand115 } from "citty";
15298
17416
  registerSchema({
15299
17417
  command: "images.search",
15300
17418
  description: "Search images by text query. Only returns ready images.",
@@ -15310,7 +17428,7 @@ registerSchema({
15310
17428
  tags: { type: "string", description: "Comma-separated tags to filter by", required: false }
15311
17429
  }
15312
17430
  });
15313
- var searchCommand = defineCommand113({
17431
+ var searchCommand = defineCommand115({
15314
17432
  meta: {
15315
17433
  name: "search",
15316
17434
  description: "Semantic search images by text query. Uses hybrid BM25 + vector + reranking. Example: baker images search 'hero banner' --aspect-ratio 16:9 --tags logo"
@@ -15370,7 +17488,7 @@ var searchCommand = defineCommand113({
15370
17488
  });
15371
17489
 
15372
17490
  // src/commands/images/sticker.ts
15373
- import { defineCommand as defineCommand114 } from "citty";
17491
+ import { defineCommand as defineCommand116 } from "citty";
15374
17492
  registerSchema({
15375
17493
  command: "images.sticker",
15376
17494
  description: "Search Giphy stickers \u2014 transparent-background overlays for ad creative.",
@@ -15402,7 +17520,7 @@ registerSchema({
15402
17520
  }
15403
17521
  }
15404
17522
  });
15405
- var stickerCommand = defineCommand114({
17523
+ var stickerCommand = defineCommand116({
15406
17524
  meta: {
15407
17525
  name: "sticker",
15408
17526
  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"
@@ -15449,7 +17567,7 @@ var stickerCommand = defineCommand114({
15449
17567
  });
15450
17568
 
15451
17569
  // src/commands/images/stock.ts
15452
- import { defineCommand as defineCommand115 } from "citty";
17570
+ import { defineCommand as defineCommand117 } from "citty";
15453
17571
  registerSchema({
15454
17572
  command: "images.stock",
15455
17573
  description: "Stock photo, vector illustration, icon-set, and PSD search via Magnific (Freepik's developer API).",
@@ -15507,7 +17625,7 @@ registerSchema({
15507
17625
  }
15508
17626
  }
15509
17627
  });
15510
- var stockCommand = defineCommand115({
17628
+ var stockCommand = defineCommand117({
15511
17629
  meta: {
15512
17630
  name: "stock",
15513
17631
  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"
@@ -15563,7 +17681,7 @@ var stockCommand = defineCommand115({
15563
17681
  });
15564
17682
 
15565
17683
  // src/lib/tags-command.ts
15566
- import { defineCommand as defineCommand116 } from "citty";
17684
+ import { defineCommand as defineCommand118 } from "citty";
15567
17685
  function makeTagsCommand(command, label, endpoint) {
15568
17686
  registerSchema({
15569
17687
  command: `${command}.tags`,
@@ -15572,7 +17690,7 @@ function makeTagsCommand(command, label, endpoint) {
15572
17690
  output: { type: "string", description: "Output format: md|json", required: false, default: "md" }
15573
17691
  }
15574
17692
  });
15575
- return defineCommand116({
17693
+ return defineCommand118({
15576
17694
  meta: {
15577
17695
  name: "tags",
15578
17696
  description: `List the available ${label} tag names (defaults + company custom tags). Use before filtering with --tags. Example: baker ${command} tags`
@@ -15608,7 +17726,7 @@ function makeTagsCommand(command, label, endpoint) {
15608
17726
  var tagsCommand2 = makeTagsCommand("images", "image", "/api/images/tags");
15609
17727
 
15610
17728
  // src/commands/images/upload.ts
15611
- import { defineCommand as defineCommand117 } from "citty";
17729
+ import { defineCommand as defineCommand119 } from "citty";
15612
17730
  registerSchema({
15613
17731
  command: "images.upload",
15614
17732
  description: "Upload an image to the library \u2014 local file path or remote http(s) URL.",
@@ -15646,7 +17764,7 @@ registerSchema({
15646
17764
  function isRemoteUrl2(value) {
15647
17765
  return /^https?:\/\//i.test(value);
15648
17766
  }
15649
- var uploadCommand = defineCommand117({
17767
+ var uploadCommand = defineCommand119({
15650
17768
  meta: {
15651
17769
  name: "upload",
15652
17770
  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'"
@@ -15739,7 +17857,7 @@ async function uploadLocal(target, args) {
15739
17857
  }
15740
17858
 
15741
17859
  // src/commands/images/upscale.ts
15742
- import { defineCommand as defineCommand118 } from "citty";
17860
+ import { defineCommand as defineCommand120 } from "citty";
15743
17861
  registerSchema({
15744
17862
  command: "images.upscale",
15745
17863
  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).",
@@ -15754,7 +17872,7 @@ registerSchema({
15754
17872
  }
15755
17873
  });
15756
17874
  var POLL_INTERVAL_MS3 = 1500;
15757
- var upscaleCommand = defineCommand118({
17875
+ var upscaleCommand = defineCommand120({
15758
17876
  meta: {
15759
17877
  name: "upscale",
15760
17878
  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"
@@ -15783,10 +17901,10 @@ var upscaleCommand = defineCommand118({
15783
17901
  writeJson({ ok: true, data: { imageId, status: "completed", image: doc } });
15784
17902
  return;
15785
17903
  }
15786
- if (doc.upscaleConfig?.status === "error") {
17904
+ if (!doc.upscaleConfig) {
15787
17905
  writeJson({
15788
17906
  ok: false,
15789
- error: { code: "IMAGE_PROCESSING_ERROR", message: doc.upscaleConfig.error ?? "Upscale failed" }
17907
+ error: { code: "IMAGE_PROCESSING_ERROR", message: "Upscale failed" }
15790
17908
  });
15791
17909
  process.exit(1);
15792
17910
  }
@@ -15809,7 +17927,7 @@ var upscaleCommand = defineCommand118({
15809
17927
  });
15810
17928
 
15811
17929
  // src/commands/images/use.ts
15812
- import { defineCommand as defineCommand119 } from "citty";
17930
+ import { defineCommand as defineCommand121 } from "citty";
15813
17931
  registerSchema({
15814
17932
  command: "images.use",
15815
17933
  description: "Ingest a URL and wait for the library record to be ready.",
@@ -15825,7 +17943,7 @@ registerSchema({
15825
17943
  }
15826
17944
  });
15827
17945
  var POLL_INTERVAL_MS4 = 1500;
15828
- var useCommand = defineCommand119({
17946
+ var useCommand = defineCommand121({
15829
17947
  meta: {
15830
17948
  name: "use",
15831
17949
  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"
@@ -15871,7 +17989,7 @@ var useCommand = defineCommand119({
15871
17989
  });
15872
17990
 
15873
17991
  // src/commands/images/index.ts
15874
- var imagesCommand = defineCommand120({
17992
+ var imagesCommand = defineCommand122({
15875
17993
  meta: {
15876
17994
  name: "images",
15877
17995
  description: `Find, source, and normalize images. Subcommands route by provider so cost + license are explicit.
@@ -15941,10 +18059,10 @@ Paid transforms (run on the Convex backend, cost-tracked):
15941
18059
  });
15942
18060
 
15943
18061
  // src/commands/research/index.ts
15944
- import { defineCommand as defineCommand131 } from "citty";
18062
+ import { defineCommand as defineCommand133 } from "citty";
15945
18063
 
15946
18064
  // src/commands/research/advertisers.ts
15947
- import { defineCommand as defineCommand121 } from "citty";
18065
+ import { defineCommand as defineCommand123 } from "citty";
15948
18066
 
15949
18067
  // src/commands/research/output.ts
15950
18068
  var RESEARCH_DATA_NOTE = "Estimates based on third-party SERP data \u2014 not exact figures. Use for directional insights, not precise measurement.";
@@ -16057,7 +18175,7 @@ var FIELDS3 = {
16057
18175
  etv: "Estimated traffic value (USD)",
16058
18176
  visibility: "SERP visibility score (0-1)"
16059
18177
  };
16060
- var advertisersCommand = defineCommand121({
18178
+ var advertisersCommand = defineCommand123({
16061
18179
  meta: {
16062
18180
  name: "advertisers",
16063
18181
  description: `Find domains competing for a keyword in Google SERPs.
@@ -16104,7 +18222,7 @@ Examples:
16104
18222
  });
16105
18223
 
16106
18224
  // src/commands/research/autocomplete.ts
16107
- import { defineCommand as defineCommand122 } from "citty";
18225
+ import { defineCommand as defineCommand124 } from "citty";
16108
18226
  registerSchema({
16109
18227
  command: "research.autocomplete",
16110
18228
  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).",
@@ -16127,7 +18245,7 @@ registerSchema({
16127
18245
  var FIELDS4 = {
16128
18246
  suggestion: "Autocomplete suggestion from Google"
16129
18247
  };
16130
- var autocompleteCommand = defineCommand122({
18248
+ var autocompleteCommand = defineCommand124({
16131
18249
  meta: {
16132
18250
  name: "autocomplete",
16133
18251
  description: `Get Google Autocomplete suggestions for keyword expansion.
@@ -16173,7 +18291,7 @@ Examples:
16173
18291
  });
16174
18292
 
16175
18293
  // src/commands/research/countries.ts
16176
- import { defineCommand as defineCommand123 } from "citty";
18294
+ import { defineCommand as defineCommand125 } from "citty";
16177
18295
  registerSchema({
16178
18296
  command: "research.countries",
16179
18297
  description: "List all supported country codes for --location flag in research commands.",
@@ -16230,7 +18348,7 @@ var FIELDS5 = {
16230
18348
  code: "Country code to pass as --location",
16231
18349
  name: "Country name"
16232
18350
  };
16233
- var countriesCommand = defineCommand123({
18351
+ var countriesCommand = defineCommand125({
16234
18352
  meta: {
16235
18353
  name: "countries",
16236
18354
  description: "List all supported country codes for --location flag."
@@ -16241,7 +18359,7 @@ var countriesCommand = defineCommand123({
16241
18359
  });
16242
18360
 
16243
18361
  // src/commands/research/intent.ts
16244
- import { defineCommand as defineCommand124 } from "citty";
18362
+ import { defineCommand as defineCommand126 } from "citty";
16245
18363
  registerSchema({
16246
18364
  command: "research.intent",
16247
18365
  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.",
@@ -16264,7 +18382,7 @@ var FIELDS6 = {
16264
18382
  intent: "Primary Google Search intent: informational, navigational, commercial, transactional",
16265
18383
  probability: "Confidence score 0.0-1.0"
16266
18384
  };
16267
- var intentCommand = defineCommand124({
18385
+ var intentCommand = defineCommand126({
16268
18386
  meta: {
16269
18387
  name: "intent",
16270
18388
  description: `Classify Google Search intent for keywords. Returns intent type and confidence.
@@ -16312,7 +18430,7 @@ Examples:
16312
18430
  });
16313
18431
 
16314
18432
  // src/commands/research/keyword-gap.ts
16315
- import { defineCommand as defineCommand125 } from "citty";
18433
+ import { defineCommand as defineCommand127 } from "citty";
16316
18434
  registerSchema({
16317
18435
  command: "research.keyword-gap",
16318
18436
  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.",
@@ -16341,7 +18459,7 @@ var FIELDS7 = {
16341
18459
  cpc: "Cost per click USD",
16342
18460
  their_position: "Competitor's ranking position"
16343
18461
  };
16344
- var keywordGapCommand = defineCommand125({
18462
+ var keywordGapCommand = defineCommand127({
16345
18463
  meta: {
16346
18464
  name: "keyword-gap",
16347
18465
  description: `Find keywords a competitor has that you don't. Supports pagination via --offset.
@@ -16415,7 +18533,7 @@ Examples:
16415
18533
  });
16416
18534
 
16417
18535
  // src/commands/research/keywords-for-site.ts
16418
- import { defineCommand as defineCommand126 } from "citty";
18536
+ import { defineCommand as defineCommand128 } from "citty";
16419
18537
  registerSchema({
16420
18538
  command: "research.keywords-for-site",
16421
18539
  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.",
@@ -16448,7 +18566,7 @@ var FIELDS8 = {
16448
18566
  competition: "LOW, MEDIUM, or HIGH",
16449
18567
  competition_index: "Competition score 0-100"
16450
18568
  };
16451
- var keywordsForSiteCommand = defineCommand126({
18569
+ var keywordsForSiteCommand = defineCommand128({
16452
18570
  meta: {
16453
18571
  name: "keywords-for-site",
16454
18572
  description: `Get keywords a competitor targets in Google. Use --type to filter paid/organic.
@@ -16501,7 +18619,7 @@ Examples:
16501
18619
  });
16502
18620
 
16503
18621
  // src/commands/research/languages.ts
16504
- import { defineCommand as defineCommand127 } from "citty";
18622
+ import { defineCommand as defineCommand129 } from "citty";
16505
18623
  registerSchema({
16506
18624
  command: "research.languages",
16507
18625
  description: "List all supported language codes for --language flag in research commands.",
@@ -16531,7 +18649,7 @@ var FIELDS9 = {
16531
18649
  code: "Language code to pass as --language",
16532
18650
  name: "Language name (also accepted by --language)"
16533
18651
  };
16534
- var languagesCommand2 = defineCommand127({
18652
+ var languagesCommand2 = defineCommand129({
16535
18653
  meta: {
16536
18654
  name: "languages",
16537
18655
  description: "List all supported language codes for --language flag."
@@ -16542,7 +18660,7 @@ var languagesCommand2 = defineCommand127({
16542
18660
  });
16543
18661
 
16544
18662
  // src/commands/research/lighthouse.ts
16545
- import { defineCommand as defineCommand128 } from "citty";
18663
+ import { defineCommand as defineCommand130 } from "citty";
16546
18664
  registerSchema({
16547
18665
  command: "research.lighthouse",
16548
18666
  description: "Landing page performance audit. Returns metrics that affect Google Ads Quality Score and CPC.",
@@ -16561,7 +18679,7 @@ var FIELDS10 = {
16561
18679
  speed_index_ms: "Speed Index in ms (good: < 3400)",
16562
18680
  interactive_ms: "Time to Interactive in ms (good: < 3800)"
16563
18681
  };
16564
- var lighthouseCommand = defineCommand128({
18682
+ var lighthouseCommand = defineCommand130({
16565
18683
  meta: {
16566
18684
  name: "lighthouse",
16567
18685
  description: `Landing page performance audit. Metrics affecting Google Ads Quality Score.
@@ -16599,7 +18717,7 @@ Examples:
16599
18717
  });
16600
18718
 
16601
18719
  // src/commands/research/relevant-pages.ts
16602
- import { defineCommand as defineCommand129 } from "citty";
18720
+ import { defineCommand as defineCommand131 } from "citty";
16603
18721
  registerSchema({
16604
18722
  command: "research.relevant-pages",
16605
18723
  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).",
@@ -16625,7 +18743,7 @@ var FIELDS11 = {
16625
18743
  keywords: "Total organic keywords the page ranks for",
16626
18744
  top_10: "Keywords in positions 1-10"
16627
18745
  };
16628
- var relevantPagesCommand = defineCommand129({
18746
+ var relevantPagesCommand = defineCommand131({
16629
18747
  meta: {
16630
18748
  name: "relevant-pages",
16631
18749
  description: `Get the top pages of a competitor domain with traffic data.
@@ -16671,7 +18789,7 @@ Examples:
16671
18789
  });
16672
18790
 
16673
18791
  // src/commands/research/web.ts
16674
- import { defineCommand as defineCommand130 } from "citty";
18792
+ import { defineCommand as defineCommand132 } from "citty";
16675
18793
  registerSchema({
16676
18794
  command: "research.web",
16677
18795
  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).",
@@ -16722,7 +18840,7 @@ async function runDeepResearch(question) {
16722
18840
  }
16723
18841
  throw new Error("Deep research timed out");
16724
18842
  }
16725
- var webCommand = defineCommand130({
18843
+ var webCommand = defineCommand132({
16726
18844
  meta: {
16727
18845
  name: "web",
16728
18846
  description: `Search the web with AI to answer any open-ended marketing question. Uses live internet data via Google Search.
@@ -16782,7 +18900,7 @@ Examples:
16782
18900
  });
16783
18901
 
16784
18902
  // src/commands/research/index.ts
16785
- var researchCommand = defineCommand131({
18903
+ var researchCommand = defineCommand133({
16786
18904
  meta: {
16787
18905
  name: "research",
16788
18906
  description: `Competitive intelligence and AI-powered research commands.
@@ -16822,10 +18940,10 @@ Examples:
16822
18940
  });
16823
18941
 
16824
18942
  // src/commands/scheduled-actions/index.ts
16825
- import { defineCommand as defineCommand138 } from "citty";
18943
+ import { defineCommand as defineCommand140 } from "citty";
16826
18944
 
16827
18945
  // src/commands/scheduled-actions/create.ts
16828
- import { defineCommand as defineCommand132 } from "citty";
18946
+ import { defineCommand as defineCommand134 } from "citty";
16829
18947
 
16830
18948
  // src/commands/scheduled-actions/shared.ts
16831
18949
  var TEMP_SCHEDULED_ACTION_PREFIX = "temp_sched_";
@@ -16940,7 +19058,7 @@ registerSchema({
16940
19058
  prompt: { type: "string", description: "Additional prompt instructions for the spawned agent", required: false }
16941
19059
  }
16942
19060
  });
16943
- var createCommand2 = defineCommand132({
19061
+ var createCommand2 = defineCommand134({
16944
19062
  meta: {
16945
19063
  name: "create",
16946
19064
  description: 'Stage a scheduled action. Example: baker scheduled-actions create --name "Weekly report" --description "..." --cron "0 9 * * MON"'
@@ -16989,7 +19107,7 @@ var createCommand2 = defineCommand132({
16989
19107
  });
16990
19108
 
16991
19109
  // src/commands/scheduled-actions/delete.ts
16992
- import { defineCommand as defineCommand133 } from "citty";
19110
+ import { defineCommand as defineCommand135 } from "citty";
16993
19111
  registerSchema({
16994
19112
  command: "scheduled-actions.delete",
16995
19113
  description: "Stage deletion of a published scheduled action or cancellation of a temp_sched_* draft creation.",
@@ -16997,7 +19115,7 @@ registerSchema({
16997
19115
  id: { type: "string", description: "Published scheduled action ID or temp_sched_* draft ID", required: true }
16998
19116
  }
16999
19117
  });
17000
- var deleteCommand2 = defineCommand133({
19118
+ var deleteCommand2 = defineCommand135({
17001
19119
  meta: {
17002
19120
  name: "delete",
17003
19121
  description: "Stage scheduled action deletion. Example: baker scheduled-actions delete <id-or-temp_sched_id>"
@@ -17026,7 +19144,7 @@ var deleteCommand2 = defineCommand133({
17026
19144
  });
17027
19145
 
17028
19146
  // src/commands/scheduled-actions/get.ts
17029
- import { defineCommand as defineCommand134 } from "citty";
19147
+ import { defineCommand as defineCommand136 } from "citty";
17030
19148
  registerSchema({
17031
19149
  command: "scheduled-actions.get",
17032
19150
  description: "Get a published scheduled action or a temp_sched_* draft-created scheduled action.",
@@ -17034,7 +19152,7 @@ registerSchema({
17034
19152
  id: { type: "string", description: "Published scheduled action ID or temp_sched_* draft ID", required: true }
17035
19153
  }
17036
19154
  });
17037
- var getCommand3 = defineCommand134({
19155
+ var getCommand3 = defineCommand136({
17038
19156
  meta: {
17039
19157
  name: "get",
17040
19158
  description: "Get a scheduled action. Example: baker scheduled-actions get <id-or-temp_sched_id>"
@@ -17071,13 +19189,13 @@ var getCommand3 = defineCommand134({
17071
19189
  });
17072
19190
 
17073
19191
  // src/commands/scheduled-actions/list.ts
17074
- import { defineCommand as defineCommand135 } from "citty";
19192
+ import { defineCommand as defineCommand137 } from "citty";
17075
19193
  registerSchema({
17076
19194
  command: "scheduled-actions.list",
17077
19195
  description: "List published scheduled actions. Includes draft state when BAKER_CHAT_ID is set.",
17078
19196
  args: {}
17079
19197
  });
17080
- var listCommand3 = defineCommand135({
19198
+ var listCommand4 = defineCommand137({
17081
19199
  meta: {
17082
19200
  name: "list",
17083
19201
  description: "List scheduled actions. Includes staged draft ops when BAKER_CHAT_ID is set."
@@ -17098,7 +19216,7 @@ var listCommand3 = defineCommand135({
17098
19216
  });
17099
19217
 
17100
19218
  // src/commands/scheduled-actions/trigger.ts
17101
- import { defineCommand as defineCommand136 } from "citty";
19219
+ import { defineCommand as defineCommand138 } from "citty";
17102
19220
  registerSchema({
17103
19221
  command: "scheduled-actions.trigger",
17104
19222
  description: "Immediately trigger a published scheduled action. Does not require BAKER_CHAT_ID and rejects temp_sched_* IDs.",
@@ -17106,7 +19224,7 @@ registerSchema({
17106
19224
  id: { type: "string", description: "Published scheduled action ID", required: true }
17107
19225
  }
17108
19226
  });
17109
- var triggerCommand = defineCommand136({
19227
+ var triggerCommand = defineCommand138({
17110
19228
  meta: {
17111
19229
  name: "trigger",
17112
19230
  description: "Immediately trigger a published scheduled action. Example: baker scheduled-actions trigger <id>"
@@ -17143,7 +19261,7 @@ var triggerCommand = defineCommand136({
17143
19261
  });
17144
19262
 
17145
19263
  // src/commands/scheduled-actions/update.ts
17146
- import { defineCommand as defineCommand137 } from "citty";
19264
+ import { defineCommand as defineCommand139 } from "citty";
17147
19265
  registerSchema({
17148
19266
  command: "scheduled-actions.update",
17149
19267
  description: "Stage an update to a published scheduled action or temp_sched_* draft-created scheduled action.",
@@ -17168,7 +19286,7 @@ registerSchema({
17168
19286
  prompt: { type: "string", description: "Replacement additional spawned-agent instructions", required: false }
17169
19287
  }
17170
19288
  });
17171
- var updateCommand2 = defineCommand137({
19289
+ var updateCommand2 = defineCommand139({
17172
19290
  meta: {
17173
19291
  name: "update",
17174
19292
  description: "Stage a scheduled action update. Example: baker scheduled-actions update <id> --enabled false"
@@ -17239,7 +19357,7 @@ var updateCommand2 = defineCommand137({
17239
19357
  });
17240
19358
 
17241
19359
  // src/commands/scheduled-actions/index.ts
17242
- var scheduledActionsCommand = defineCommand138({
19360
+ var scheduledActionsCommand = defineCommand140({
17243
19361
  meta: {
17244
19362
  name: "scheduled-actions",
17245
19363
  description: `Manage Scheduled Actions. Subcommands: list, get, create, update, delete, trigger.
@@ -17255,7 +19373,7 @@ Examples:
17255
19373
  baker scheduled-actions trigger <id>`
17256
19374
  },
17257
19375
  subCommands: {
17258
- list: listCommand3,
19376
+ list: listCommand4,
17259
19377
  get: getCommand3,
17260
19378
  create: createCommand2,
17261
19379
  update: updateCommand2,
@@ -17265,8 +19383,8 @@ Examples:
17265
19383
  });
17266
19384
 
17267
19385
  // src/commands/schema.ts
17268
- import { defineCommand as defineCommand139 } from "citty";
17269
- var schemaCommand = defineCommand139({
19386
+ import { defineCommand as defineCommand141 } from "citty";
19387
+ var schemaCommand = defineCommand141({
17270
19388
  meta: {
17271
19389
  name: "schema",
17272
19390
  description: "Inspect command argument schemas (for AI agent introspection). Lists all commands if no argument given. Example: baker schema images.search"
@@ -17302,10 +19420,10 @@ var schemaCommand = defineCommand139({
17302
19420
  });
17303
19421
 
17304
19422
  // src/commands/testimonials/index.ts
17305
- import { defineCommand as defineCommand143 } from "citty";
19423
+ import { defineCommand as defineCommand145 } from "citty";
17306
19424
 
17307
19425
  // src/commands/testimonials/get.ts
17308
- import { defineCommand as defineCommand140 } from "citty";
19426
+ import { defineCommand as defineCommand142 } from "citty";
17309
19427
  registerSchema({
17310
19428
  command: "testimonials.get",
17311
19429
  description: "Get a single testimonial by ID",
@@ -17313,7 +19431,7 @@ registerSchema({
17313
19431
  id: { type: "string", description: "Testimonial ID", required: true }
17314
19432
  }
17315
19433
  });
17316
- var getCommand4 = defineCommand140({
19434
+ var getCommand4 = defineCommand142({
17317
19435
  meta: { name: "get", description: "Get a single testimonial by ID. Example: baker testimonials get j571abc123" },
17318
19436
  args: {
17319
19437
  id: { type: "positional", description: "Testimonial ID", required: false },
@@ -17350,7 +19468,7 @@ var getCommand4 = defineCommand140({
17350
19468
  });
17351
19469
 
17352
19470
  // src/commands/testimonials/list.ts
17353
- import { defineCommand as defineCommand141 } from "citty";
19471
+ import { defineCommand as defineCommand143 } from "citty";
17354
19472
  registerSchema({
17355
19473
  command: "testimonials.list",
17356
19474
  description: "List testimonials with optional filters.",
@@ -17380,7 +19498,7 @@ registerSchema({
17380
19498
  limit: { type: "number", description: "Max results (default 50)", required: false, default: 50 }
17381
19499
  }
17382
19500
  });
17383
- var listCommand4 = defineCommand141({
19501
+ var listCommand5 = defineCommand143({
17384
19502
  meta: {
17385
19503
  name: "list",
17386
19504
  description: "List testimonials with optional filters. Example: baker testimonials list --source google --sentiment positive"
@@ -17429,7 +19547,7 @@ var listCommand4 = defineCommand141({
17429
19547
  });
17430
19548
 
17431
19549
  // src/commands/testimonials/search.ts
17432
- import { defineCommand as defineCommand142 } from "citty";
19550
+ import { defineCommand as defineCommand144 } from "citty";
17433
19551
  registerSchema({
17434
19552
  command: "testimonials.search",
17435
19553
  description: "Search testimonials by text query. Uses hybrid BM25 + vector + reranking.",
@@ -17460,7 +19578,7 @@ registerSchema({
17460
19578
  tags: { type: "string", description: "Comma-separated tags to filter by", required: false }
17461
19579
  }
17462
19580
  });
17463
- var searchCommand2 = defineCommand142({
19581
+ var searchCommand2 = defineCommand144({
17464
19582
  meta: {
17465
19583
  name: "search",
17466
19584
  description: "Semantic search testimonials by text query. Uses hybrid BM25 + vector + reranking. Example: baker testimonials search 'great service' --rating-min 4"
@@ -17534,7 +19652,7 @@ var searchCommand2 = defineCommand142({
17534
19652
  var tagsCommand3 = makeTagsCommand("testimonials", "testimonial", "/api/testimonials/tags");
17535
19653
 
17536
19654
  // src/commands/testimonials/index.ts
17537
- var testimonialsCommand = defineCommand143({
19655
+ var testimonialsCommand = defineCommand145({
17538
19656
  meta: {
17539
19657
  name: "testimonials",
17540
19658
  description: `Find and browse testimonials in Baker. Subcommands: search, get, list, tags.
@@ -17549,16 +19667,16 @@ Examples:
17549
19667
  subCommands: {
17550
19668
  get: getCommand4,
17551
19669
  search: searchCommand2,
17552
- list: listCommand4,
19670
+ list: listCommand5,
17553
19671
  tags: tagsCommand3
17554
19672
  }
17555
19673
  });
17556
19674
 
17557
19675
  // src/commands/videos/index.ts
17558
- import { defineCommand as defineCommand148 } from "citty";
19676
+ import { defineCommand as defineCommand150 } from "citty";
17559
19677
 
17560
19678
  // src/commands/videos/delete.ts
17561
- import { defineCommand as defineCommand144 } from "citty";
19679
+ import { defineCommand as defineCommand146 } from "citty";
17562
19680
  registerSchema({
17563
19681
  command: "videos.delete",
17564
19682
  description: "Delete a video by ID",
@@ -17572,7 +19690,7 @@ registerSchema({
17572
19690
  }
17573
19691
  }
17574
19692
  });
17575
- var deleteCommand3 = defineCommand144({
19693
+ var deleteCommand3 = defineCommand146({
17576
19694
  meta: {
17577
19695
  name: "delete",
17578
19696
  description: "Delete a video by ID. Use --dry-run to preview. Example: baker videos delete j571abc123 --dry-run"
@@ -17613,7 +19731,7 @@ var deleteCommand3 = defineCommand144({
17613
19731
  });
17614
19732
 
17615
19733
  // src/commands/videos/get.ts
17616
- import { defineCommand as defineCommand145 } from "citty";
19734
+ import { defineCommand as defineCommand147 } from "citty";
17617
19735
  registerSchema({
17618
19736
  command: "videos.get",
17619
19737
  description: "Get a single video by ID",
@@ -17621,7 +19739,7 @@ registerSchema({
17621
19739
  id: { type: "string", description: "Video ID", required: true }
17622
19740
  }
17623
19741
  });
17624
- var getCommand5 = defineCommand145({
19742
+ var getCommand5 = defineCommand147({
17625
19743
  meta: { name: "get", description: "Get a single video by ID. Example: baker videos get j571abc123" },
17626
19744
  args: {
17627
19745
  id: { type: "positional", description: "Video ID", required: false },
@@ -17658,7 +19776,7 @@ var getCommand5 = defineCommand145({
17658
19776
  });
17659
19777
 
17660
19778
  // src/commands/videos/search.ts
17661
- import { defineCommand as defineCommand146 } from "citty";
19779
+ import { defineCommand as defineCommand148 } from "citty";
17662
19780
  registerSchema({
17663
19781
  command: "videos.search",
17664
19782
  description: "Search videos by text query. Only returns ready videos.",
@@ -17668,7 +19786,7 @@ registerSchema({
17668
19786
  tags: { type: "string", description: "Comma-separated tags to filter by", required: false }
17669
19787
  }
17670
19788
  });
17671
- var searchCommand3 = defineCommand146({
19789
+ var searchCommand3 = defineCommand148({
17672
19790
  meta: {
17673
19791
  name: "search",
17674
19792
  description: "Semantic search videos by text query. Uses hybrid BM25 + vector + reranking. Example: baker videos search 'product demo' --tags tutorial"
@@ -17720,7 +19838,7 @@ var tagsCommand4 = makeTagsCommand("videos", "video", "/api/videos/tags");
17720
19838
  // src/commands/videos/upload.ts
17721
19839
  import { readFile as readFile12, stat as stat3 } from "fs/promises";
17722
19840
  import { extname as extname3 } from "path";
17723
- import { defineCommand as defineCommand147 } from "citty";
19841
+ import { defineCommand as defineCommand149 } from "citty";
17724
19842
  var MIME_MAP = {
17725
19843
  ".mp4": "video/mp4",
17726
19844
  ".mov": "video/quicktime",
@@ -17754,7 +19872,7 @@ function detectContentType(filePath) {
17754
19872
  }
17755
19873
  return mime;
17756
19874
  }
17757
- var uploadCommand2 = defineCommand147({
19875
+ var uploadCommand2 = defineCommand149({
17758
19876
  meta: {
17759
19877
  name: "upload",
17760
19878
  description: "Upload a video file to Baker via Mux direct upload. Auto-detects content type. Example: baker videos upload ./demo.mp4"
@@ -17808,7 +19926,7 @@ var uploadCommand2 = defineCommand147({
17808
19926
  });
17809
19927
 
17810
19928
  // src/commands/videos/index.ts
17811
- var videosCommand = defineCommand148({
19929
+ var videosCommand = defineCommand150({
17812
19930
  meta: {
17813
19931
  name: "videos",
17814
19932
  description: `Find and manage videos in Baker. Subcommands: search, get, upload, delete, tags.
@@ -17831,10 +19949,10 @@ Examples:
17831
19949
  });
17832
19950
 
17833
19951
  // src/commands/winning-ads/index.ts
17834
- import { defineCommand as defineCommand151 } from "citty";
19952
+ import { defineCommand as defineCommand153 } from "citty";
17835
19953
 
17836
19954
  // src/commands/winning-ads/advertisers.ts
17837
- import { defineCommand as defineCommand149 } from "citty";
19955
+ import { defineCommand as defineCommand151 } from "citty";
17838
19956
  registerSchema({
17839
19957
  command: "winning-ads.advertisers",
17840
19958
  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).",
@@ -17847,7 +19965,7 @@ registerSchema({
17847
19965
  function identity(record) {
17848
19966
  return record;
17849
19967
  }
17850
- var advertisersCommand2 = defineCommand149({
19968
+ var advertisersCommand2 = defineCommand151({
17851
19969
  meta: {
17852
19970
  name: "advertisers",
17853
19971
  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'
@@ -17898,7 +20016,7 @@ var advertisersCommand2 = defineCommand149({
17898
20016
  });
17899
20017
 
17900
20018
  // src/commands/winning-ads/search.ts
17901
- import { defineCommand as defineCommand150 } from "citty";
20019
+ import { defineCommand as defineCommand152 } from "citty";
17902
20020
  registerSchema({
17903
20021
  command: "winning-ads.search",
17904
20022
  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.",
@@ -18006,7 +20124,7 @@ function buildSearchBody(args) {
18006
20124
  }
18007
20125
  return body;
18008
20126
  }
18009
- var searchCommand4 = defineCommand150({
20127
+ var searchCommand4 = defineCommand152({
18010
20128
  meta: {
18011
20129
  name: "search",
18012
20130
  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"
@@ -18118,7 +20236,7 @@ var searchCommand4 = defineCommand150({
18118
20236
  });
18119
20237
 
18120
20238
  // src/commands/winning-ads/index.ts
18121
- var winningAdsCommand = defineCommand151({
20239
+ var winningAdsCommand = defineCommand153({
18122
20240
  meta: {
18123
20241
  name: "winning-ads",
18124
20242
  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.
@@ -18142,7 +20260,7 @@ Examples:
18142
20260
  });
18143
20261
 
18144
20262
  // src/version.ts
18145
- import { readFileSync as readFileSync9 } from "fs";
20263
+ import { readFileSync as readFileSync10 } from "fs";
18146
20264
  function packageJsonUrl() {
18147
20265
  return new URL("../package.json", import.meta.url);
18148
20266
  }
@@ -18154,11 +20272,11 @@ function parsePackageVersion(raw) {
18154
20272
  throw new Error("Invalid CLI package.json: missing version");
18155
20273
  }
18156
20274
  function getCliVersion() {
18157
- return parsePackageVersion(readFileSync9(packageJsonUrl(), "utf8"));
20275
+ return parsePackageVersion(readFileSync10(packageJsonUrl(), "utf8"));
18158
20276
  }
18159
20277
 
18160
20278
  // src/cli.ts
18161
- var main = defineCommand152({
20279
+ var main = defineCommand154({
18162
20280
  meta: {
18163
20281
  name: "baker",
18164
20282
  version: getCliVersion(),