@koda-sl/baker-cli 0.150.0-dev.04484bc24 → 0.150.0-dev.57528fa3f

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
@@ -75,7 +75,7 @@ import {
75
75
  } from "./chunk-RK67WL4O.js";
76
76
 
77
77
  // src/cli.ts
78
- import { defineCommand as defineCommand178, runMain } from "citty";
78
+ import { defineCommand as defineCommand182, runMain } from "citty";
79
79
 
80
80
  // src/commands/actions/index.ts
81
81
  import { defineCommand as defineCommand18 } from "citty";
@@ -1297,15 +1297,6 @@ var LINKEDIN_LIMITS = {
1297
1297
  campaign: {
1298
1298
  nameMax: 255
1299
1299
  },
1300
- /**
1301
- * LinkedIn documents no ceiling on URL tracking parameters; these are our own
1302
- * guards against a payload that would build an unusable landing URL.
1303
- */
1304
- trackingParams: {
1305
- keyMax: 100,
1306
- valueMax: 500,
1307
- parametersMax: 20
1308
- },
1309
1300
  creative: {
1310
1301
  commentarySoftMax: 600,
1311
1302
  // feed truncates with "…see more" beyond this
@@ -1444,17 +1435,6 @@ var CONVERSION_TYPES = [
1444
1435
  ];
1445
1436
  var CONVERSION_METHODS = ["INSIGHT_TAG", "CONVERSIONS_API"];
1446
1437
  var ATTRIBUTION_TYPES = ["LAST_TOUCH_BY_CAMPAIGN", "LAST_TOUCH_BY_CONVERSION"];
1447
- var TRACKING_PARAM_DYNAMIC_VALUES = [
1448
- "ACCOUNT_ID",
1449
- "ACCOUNT_NAME",
1450
- "CAMPAIGN_GROUP_ID",
1451
- "CAMPAIGN_GROUP_NAME",
1452
- "CAMPAIGN_ID",
1453
- "CAMPAIGN_NAME",
1454
- "CREATIVE_ID",
1455
- "CREATIVE_NAME"
1456
- ];
1457
- var TRACKING_PARAM_KEY_REGEX = /^[A-Za-z0-9_.-]+$/;
1458
1438
  var CURRENCY_MINIMUMS = {
1459
1439
  USD: { dailyBudgetMin: 10, unitCostMin: 2 },
1460
1440
  EUR: { dailyBudgetMin: 10, unitCostMin: 2 },
@@ -1612,35 +1592,6 @@ var campaignUpdateSchema = z3.object({
1612
1592
  }
1613
1593
  validateCampaignBudgets(p, ctx, { requireBudget: false });
1614
1594
  });
1615
- var trackingParamKeySchema = z3.string().min(1).max(LINKEDIN_LIMITS.trackingParams.keyMax).regex(TRACKING_PARAM_KEY_REGEX, "tracking parameter keys may only use letters, digits, _ . and -");
1616
- var trackingParamsSetSchema = z3.object({
1617
- dynamicValueParameters: z3.record(trackingParamKeySchema, z3.enum(TRACKING_PARAM_DYNAMIC_VALUES)).optional(),
1618
- customValueParameters: z3.record(trackingParamKeySchema, z3.string().min(1).max(LINKEDIN_LIMITS.trackingParams.valueMax)).optional()
1619
- }).superRefine((p, ctx) => {
1620
- if (!p.dynamicValueParameters && !p.customValueParameters) {
1621
- ctx.addIssue({
1622
- code: "custom",
1623
- message: "set at least one parameter \u2014 pass both maps as {} to clear the ad set's tracking parameters instead"
1624
- });
1625
- }
1626
- const both = Object.keys(p.dynamicValueParameters ?? {}).filter(
1627
- (key) => p.customValueParameters?.[key] !== void 0
1628
- );
1629
- if (both.length > 0) {
1630
- ctx.addIssue({
1631
- code: "custom",
1632
- path: ["customValueParameters"],
1633
- message: `${both.join(", ")} is set as both a dynamic and a fixed parameter \u2014 LinkedIn appends each key once, so keep only one`
1634
- });
1635
- }
1636
- const count = Object.keys(p.dynamicValueParameters ?? {}).length + Object.keys(p.customValueParameters ?? {}).length;
1637
- if (count > LINKEDIN_LIMITS.trackingParams.parametersMax) {
1638
- ctx.addIssue({
1639
- code: "custom",
1640
- message: `${count} parameters \u2014 keep it under ${LINKEDIN_LIMITS.trackingParams.parametersMax}`
1641
- });
1642
- }
1643
- });
1644
1595
  var commentarySchema = z3.string().min(1).max(LINKEDIN_LIMITS.creative.commentaryHardMax);
1645
1596
  var headlineSchema = z3.string().min(1).max(LINKEDIN_LIMITS.creative.headlineMax);
1646
1597
  var httpsUrlSchema = z3.string().url().refine((u) => u.startsWith("https://"), "landing pages must be https");
@@ -2019,7 +1970,6 @@ var LINKEDIN_DRAFT_OP_KINDS = [
2019
1970
  "campaignGroup.update",
2020
1971
  "campaign.create",
2021
1972
  "campaign.update",
2022
- "trackingParams.set",
2023
1973
  "creative.create",
2024
1974
  "creative.update",
2025
1975
  "audience.create",
@@ -2043,7 +1993,6 @@ var linkedinDraftOpInputSchema = z3.discriminatedUnion("kind", [
2043
1993
  updateOp("campaignGroup.update", campaignGroupUpdateSchema),
2044
1994
  createOp("campaign.create", campaignCreateSchema),
2045
1995
  updateOp("campaign.update", campaignUpdateSchema),
2046
- updateOp("trackingParams.set", trackingParamsSetSchema),
2047
1996
  createOp("creative.create", creativeCreateSchema),
2048
1997
  updateOp("creative.update", creativeUpdateSchema),
2049
1998
  createOp("audience.create", audienceCreateSchema),
@@ -2184,6 +2133,7 @@ var chatChangeTypeSchema = z5.enum([
2184
2133
  "linkedin-ads",
2185
2134
  "google-ads",
2186
2135
  "meta-ads",
2136
+ "tag-manager",
2187
2137
  // Immediate (already-applied) library effects — see lib/chatChanges.ts.
2188
2138
  "image",
2189
2139
  "video",
@@ -8356,26 +8306,6 @@ registerSchema({
8356
8306
  file: { type: "string", description: "JSON file with fields to change", required: false }
8357
8307
  }
8358
8308
  });
8359
- registerSchema({
8360
- command: "ads.linkedin.campaigns.url-params",
8361
- description: "Start here for UTMs: stage the URL tracking parameters on an ad set. LinkedIn appends them to the link of EVERY ad in the ad set, including ads already running, so this is the level to set campaign-wide UTMs at \u2014 editing each ad's landing URL instead risks the same key landing twice. --param takes fixed values (repeatable or &-joined); --dynamic takes a value LinkedIn fills in per ad (ACCOUNT_ID, ACCOUNT_NAME, CAMPAIGN_GROUP_ID, CAMPAIGN_GROUP_NAME, CAMPAIGN_ID, CAMPAIGN_NAME, CREATIVE_ID, CREATIVE_NAME). Applies on chat publish; message and conversation ads are unaffected.",
8362
- args: {
8363
- id: { type: "positional", description: "Ad set (campaign) id or URN", required: true },
8364
- ...writeAccountArgs,
8365
- param: {
8366
- type: "string",
8367
- description: 'Fixed key=value, repeatable or &-joined \u2014 e.g. "utm_source=linkedin&utm_medium=paid-social"',
8368
- required: false
8369
- },
8370
- dynamic: {
8371
- type: "string",
8372
- description: "key=PLACEHOLDER filled in per ad, repeatable \u2014 e.g. utm_campaign=CAMPAIGN_NAME",
8373
- required: false
8374
- },
8375
- clear: { type: "boolean", description: "Remove every tracking parameter from this ad set", required: false },
8376
- file: { type: "string", description: "JSON payload file; flags override file keys", required: false }
8377
- }
8378
- });
8379
8309
  var statusSugarArgs = {
8380
8310
  id: { type: "positional", description: "Entity id or URN", required: true },
8381
8311
  ...writeAccountArgs
@@ -9900,88 +9830,6 @@ Example: baker ads linkedin ${entity} ${name} 123456`
9900
9830
  }
9901
9831
  });
9902
9832
  }
9903
- function trackingPairs(value, flag) {
9904
- const raw = value === void 0 ? [] : Array.isArray(value) ? value : [value];
9905
- return raw.flatMap(
9906
- (entry) => String(entry).split("&").filter((pair) => pair.length > 0).map((pair) => {
9907
- const at = pair.indexOf("=");
9908
- if (at < 1 || at === pair.length - 1) {
9909
- failWriteValidation2(`${flag} expects key=value pairs \u2014 got "${pair}"`);
9910
- }
9911
- return [pair.slice(0, at).trim(), pair.slice(at + 1).trim()];
9912
- })
9913
- );
9914
- }
9915
- function asDynamicValue(value) {
9916
- const bare = value.replace(/^\{+|\}+$/g, "").toUpperCase();
9917
- return TRACKING_PARAM_DYNAMIC_VALUES.includes(bare) ? bare : void 0;
9918
- }
9919
- function trackingParamsPayload(args) {
9920
- if (args.clear) {
9921
- return { dynamicValueParameters: {}, customValueParameters: {} };
9922
- }
9923
- const customValueParameters = {};
9924
- for (const [key, value] of trackingPairs(args.param, "--param")) {
9925
- if (asDynamicValue(value)) {
9926
- failWriteValidation2(
9927
- `--param ${key}=${value} names a value LinkedIn fills in per ad \u2014 pass it as --dynamic ${key}=${value.replace(/^\{+|\}+$/g, "").toUpperCase()}`
9928
- );
9929
- }
9930
- customValueParameters[key] = value;
9931
- }
9932
- const dynamicValueParameters = {};
9933
- for (const [key, value] of trackingPairs(args.dynamic, "--dynamic")) {
9934
- const resolved = asDynamicValue(value);
9935
- if (!resolved) {
9936
- failWriteValidation2(
9937
- `--dynamic ${key}=${value} is not a value LinkedIn can fill in \u2014 use one of ${TRACKING_PARAM_DYNAMIC_VALUES.join(", ")}, or pass a fixed value with --param`
9938
- );
9939
- }
9940
- dynamicValueParameters[key] = resolved;
9941
- }
9942
- return mergePayload2(loadJsonFileArg2(args.file), {
9943
- customValueParameters: Object.keys(customValueParameters).length > 0 ? customValueParameters : void 0,
9944
- dynamicValueParameters: Object.keys(dynamicValueParameters).length > 0 ? dynamicValueParameters : void 0
9945
- });
9946
- }
9947
- var campaignsUrlParamsCommand = defineCommand37({
9948
- meta: {
9949
- name: "url-params",
9950
- description: `Stage the URL tracking parameters on an ad set \u2014 LinkedIn appends them to the link of EVERY ad in it, including ads already running. ${STAGED_NOTE}
9951
-
9952
- Start here: set UTMs at this level, not per ad. A per-ad landing URL is only for a genuinely different destination \u2014 LinkedIn appends the ad set's parameters on top of whatever the ad's own URL carries, so the same key set in both places lands twice.
9953
-
9954
- Values LinkedIn fills in per ad (--dynamic): ACCOUNT_ID, ACCOUNT_NAME, CAMPAIGN_GROUP_ID, CAMPAIGN_GROUP_NAME, CAMPAIGN_ID, CAMPAIGN_NAME, CREATIVE_ID, CREATIVE_NAME.
9955
-
9956
- Examples:
9957
- baker ads linkedin campaigns url-params 123456 --param "utm_source=linkedin&utm_medium=paid-social" --dynamic utm_campaign=CAMPAIGN_NAME --dynamic utm_content=CREATIVE_ID
9958
- baker ads linkedin campaigns url-params 123456 --param utm_agency=baker
9959
- baker ads linkedin campaigns url-params 123456 --clear`
9960
- },
9961
- args: {
9962
- id: { type: "positional", description: "Ad set (campaign) id or URN", required: true },
9963
- ...accountArgs,
9964
- param: {
9965
- type: "string",
9966
- description: 'Fixed key=value, repeatable or &-joined (e.g. --param "utm_source=linkedin&utm_medium=paid")'
9967
- },
9968
- dynamic: {
9969
- type: "string",
9970
- description: "key=PLACEHOLDER LinkedIn fills in per ad, repeatable (e.g. --dynamic utm_campaign=CAMPAIGN_NAME)"
9971
- },
9972
- clear: { type: "boolean", description: "Remove every tracking parameter from this ad set" },
9973
- file: { type: "string", description: "JSON file with the full payload; flags override file keys" }
9974
- },
9975
- run: async ({ args }) => {
9976
- const accountId = bareAccountId(args);
9977
- await stageOp({
9978
- kind: "trackingParams.set",
9979
- accountId,
9980
- target: requireTarget2(args, "ad set"),
9981
- payload: trackingParamsPayload(args)
9982
- });
9983
- }
9984
- });
9985
9833
  var campaignsPauseCommand = statusSugarCommand("campaigns", "campaign.update", "pause");
9986
9834
  var campaignsResumeCommand = statusSugarCommand("campaigns", "campaign.update", "resume");
9987
9835
  var campaignsArchiveCommand = statusSugarCommand("campaigns", "campaign.update", "archive");
@@ -10514,8 +10362,7 @@ Examples:
10514
10362
  baker ads linkedin campaigns --account-id 503001492
10515
10363
  baker ads linkedin campaigns --account-id 503001492 --all-statuses --output csv
10516
10364
  baker ads linkedin campaigns update 123456 --daily-budget 100 --currency EUR
10517
- baker ads linkedin campaigns pause 123456
10518
- baker ads linkedin campaigns url-params 123456 --param utm_source=linkedin \u2014 UTMs for every ad in the ad set`
10365
+ baker ads linkedin campaigns pause 123456`
10519
10366
  },
10520
10367
  subCommands: {
10521
10368
  create: campaignsCreateCommand,
@@ -10523,8 +10370,7 @@ Examples:
10523
10370
  pause: campaignsPauseCommand,
10524
10371
  resume: campaignsResumeCommand,
10525
10372
  archive: campaignsArchiveCommand,
10526
- duplicate: campaignsDuplicateCommand,
10527
- "url-params": campaignsUrlParamsCommand
10373
+ duplicate: campaignsDuplicateCommand
10528
10374
  },
10529
10375
  args: {
10530
10376
  "account-id": { type: "string", description: "Numeric account ID or urn:li:sponsoredAccount:N" },
@@ -29173,9 +29019,505 @@ var schemaCommand = defineCommand154({
29173
29019
  }
29174
29020
  });
29175
29021
 
29176
- // src/commands/tags/index.ts
29022
+ // src/commands/tag-manager/index.ts
29023
+ import { defineCommand as defineCommand158 } from "citty";
29024
+
29025
+ // src/commands/tag-manager/draft.ts
29177
29026
  import { defineCommand as defineCommand155 } from "citty";
29178
29027
 
29028
+ // src/commands/tag-manager/shared.ts
29029
+ import { readFileSync as readFileSync12 } from "fs";
29030
+ function failValidation3(message) {
29031
+ writeJsonEnvelope({ ok: false, error: { code: "VALIDATION_ERROR", message } });
29032
+ process.exit(1);
29033
+ }
29034
+ function requireTarget4(args, entity) {
29035
+ const positional = Array.isArray(args._) ? args._[0] : void 0;
29036
+ const target = args.id ?? args.target ?? positional;
29037
+ if (typeof target !== "string" || target.length === 0) {
29038
+ failValidation3(`pass the ${entity} id or path as the positional argument`);
29039
+ }
29040
+ return target;
29041
+ }
29042
+ function loadJsonArg(args, flag = "json") {
29043
+ const inline = args[flag];
29044
+ const file = args.file;
29045
+ const raw = typeof file === "string" && file.length > 0 ? readFileSync12(file, "utf8") : typeof inline === "string" && inline.length > 0 ? inline : void 0;
29046
+ if (raw === void 0) {
29047
+ failValidation3(`pass --${flag} with inline JSON or --file with a path to a JSON file`);
29048
+ }
29049
+ try {
29050
+ const parsed = JSON.parse(raw);
29051
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
29052
+ failValidation3(`--${flag} must be a JSON object`);
29053
+ }
29054
+ return parsed;
29055
+ } catch {
29056
+ return failValidation3(`--${flag} is not valid JSON`);
29057
+ }
29058
+ }
29059
+ function handleError3(err) {
29060
+ if (err instanceof ApiError) {
29061
+ const notConnected = err.code === "NOT_FOUND" || err.code === "UNAUTHORIZED";
29062
+ writeJsonEnvelope({
29063
+ ok: false,
29064
+ error: {
29065
+ code: err.code,
29066
+ message: err.message,
29067
+ ...notConnected ? {
29068
+ fix: {
29069
+ action: "ask_user",
29070
+ explanation: "Tag Manager is not connected for this company yet. Ask the user to connect Tag Manager in Settings \u2192 Connections and pick the container to manage, then retry. Continue with the rest of the task in the meantime."
29071
+ }
29072
+ } : {},
29073
+ retryable: false
29074
+ }
29075
+ });
29076
+ process.exit(1);
29077
+ }
29078
+ writeJsonEnvelope({ ok: false, error: { code: "NETWORK_ERROR", message: "Unexpected error" } });
29079
+ process.exit(1);
29080
+ }
29081
+ var STAGE_HINTS = [
29082
+ "Staged only \u2014 nothing has changed in Tag Manager yet. These apply when the chat completes, in dependency order, and close as a Tag Manager version. Whether that version goes live is a per-company setting, so never promise the user their tracking is live; say the changes are ready and will apply when you finish."
29083
+ ];
29084
+ async function stageOp3(op) {
29085
+ const chatId = requireChatId();
29086
+ try {
29087
+ const data = await apiPost("/api/tag-manager/draft/stage", { chatId, op });
29088
+ writeJsonEnvelope({ ok: true, data, hints: STAGE_HINTS });
29089
+ } catch (err) {
29090
+ handleError3(err);
29091
+ }
29092
+ }
29093
+ async function draftAction2(path27, body) {
29094
+ const chatId = requireChatId();
29095
+ try {
29096
+ const data = await apiPost(path27, { chatId, ...body });
29097
+ writeJsonEnvelope({ ok: true, data });
29098
+ return data;
29099
+ } catch (err) {
29100
+ handleError3(err);
29101
+ }
29102
+ }
29103
+ function renderDraft(response) {
29104
+ if (response.count === 0) {
29105
+ return "No Tag Manager changes staged on this chat.";
29106
+ }
29107
+ const lines = [
29108
+ `${response.count} staged Tag Manager change(s)`,
29109
+ ...response.ops.map((op) => {
29110
+ const status = op.result ? ` [${op.result.status}]` : "";
29111
+ return ` ${op.ref} ${op.summary}${status}`;
29112
+ })
29113
+ ];
29114
+ if (response.versionId) {
29115
+ lines.push(
29116
+ "",
29117
+ response.published ? `Applied and published as Tag Manager version ${response.versionId}. These changes are live on the site.` : `Applied into Tag Manager version ${response.versionId}. It is NOT published \u2014 publish it in Tag Manager to go live.`
29118
+ );
29119
+ }
29120
+ if (response.compilerError) {
29121
+ lines.push("", `Tag Manager reported a problem building the version: ${response.compilerError}`);
29122
+ }
29123
+ return lines.join("\n");
29124
+ }
29125
+ async function draftList(json) {
29126
+ const chatId = requireChatId();
29127
+ try {
29128
+ const data = await apiPost("/api/tag-manager/draft", { chatId });
29129
+ if (json) {
29130
+ writeJsonEnvelope({ ok: true, data });
29131
+ return;
29132
+ }
29133
+ process.stdout.write(`${renderDraft(data)}
29134
+ `);
29135
+ } catch (err) {
29136
+ handleError3(err);
29137
+ }
29138
+ }
29139
+
29140
+ // src/commands/tag-manager/draft.ts
29141
+ registerSchema({
29142
+ command: "tagManager.draft",
29143
+ description: "Review and undo the Tag Manager changes staged on this chat. Use `list` to see everything staged, `show` to inspect one change in full before publish, `amend` to correct one in place, and `remove`/`clear` to drop them.",
29144
+ args: {
29145
+ json: { type: "boolean", description: "Print the raw JSON envelope instead of the readable list", required: false }
29146
+ }
29147
+ });
29148
+ var draftCommand3 = defineCommand155({
29149
+ meta: {
29150
+ name: "draft",
29151
+ description: "List, show, amend, remove, or clear staged Tag Manager changes for this chat"
29152
+ },
29153
+ subCommands: {
29154
+ list: defineCommand155({
29155
+ meta: {
29156
+ name: "list",
29157
+ description: "Review everything staged on this chat (--json for the raw envelope)"
29158
+ },
29159
+ args: { json: { type: "boolean", description: "Print the raw JSON envelope", required: false } },
29160
+ run: async ({ args }) => {
29161
+ await draftList(args.json === true);
29162
+ }
29163
+ }),
29164
+ show: defineCommand155({
29165
+ meta: {
29166
+ name: "show",
29167
+ description: "Print the full staged payload for one change \u2014 the receipt to verify it looks right before publish (never truncated)."
29168
+ },
29169
+ args: { ref: { type: "positional", description: "Staged ref (gtm_temp_*) or target", required: false } },
29170
+ run: async ({ args }) => {
29171
+ await draftAction2("/api/tag-manager/draft/show", {
29172
+ ref: requireTarget4(args, "change")
29173
+ });
29174
+ }
29175
+ }),
29176
+ amend: defineCommand155({
29177
+ meta: {
29178
+ name: "amend",
29179
+ description: "Update a staged change in place \u2014 merges a JSON patch into its payload (objects deep-merge, null deletes a key, arrays/scalars replace) and re-validates. Use this instead of remove + re-create."
29180
+ },
29181
+ args: {
29182
+ ref: { type: "positional", description: "Staged ref (gtm_temp_*) or target", required: false },
29183
+ patch: { type: "string", description: "Inline JSON patch object", required: false },
29184
+ file: { type: "string", description: "JSON file with the patch object", required: false }
29185
+ },
29186
+ run: async ({ args }) => {
29187
+ await draftAction2("/api/tag-manager/draft/amend", {
29188
+ ref: requireTarget4(args, "change"),
29189
+ patch: loadJsonArg(args, "patch")
29190
+ });
29191
+ }
29192
+ }),
29193
+ remove: defineCommand155({
29194
+ meta: { name: "remove", description: "Remove one staged change (cascades to anything depending on it)" },
29195
+ args: { ref: { type: "positional", description: "Staged ref (gtm_temp_*) or target", required: false } },
29196
+ run: async ({ args }) => {
29197
+ await draftAction2("/api/tag-manager/draft/remove", {
29198
+ ref: requireTarget4(args, "change")
29199
+ });
29200
+ }
29201
+ }),
29202
+ clear: defineCommand155({
29203
+ meta: { name: "clear", description: "Discard all Tag Manager changes staged on this chat" },
29204
+ run: async () => {
29205
+ await draftAction2("/api/tag-manager/draft/clear", {});
29206
+ }
29207
+ })
29208
+ }
29209
+ });
29210
+
29211
+ // src/commands/tag-manager/read.ts
29212
+ import { defineCommand as defineCommand156 } from "citty";
29213
+ registerSchema({
29214
+ command: "tagManager.containers",
29215
+ description: "List the Google Tag Manager containers this company's connection can reach. The connected container is flagged. Start here to confirm which container you are managing.",
29216
+ args: {}
29217
+ });
29218
+ registerSchema({
29219
+ command: "tagManager.read",
29220
+ description: "Read what is currently inside a Tag Manager container: tags, triggers, variables, folders and enabled built-in variables. Start here before proposing any change, so you edit what exists instead of duplicating it. Compact by default; pass --full for raw Tag Manager objects.",
29221
+ args: {
29222
+ container: { type: "string", description: "Numeric container id (defaults to the connected one)", required: false },
29223
+ workspace: { type: "string", description: "Workspace id (defaults to the default workspace)", required: false },
29224
+ entities: {
29225
+ type: "string",
29226
+ description: "Comma-separated subset: tag,trigger,variable,folder,builtInVariable",
29227
+ required: false
29228
+ },
29229
+ full: { type: "boolean", description: "Include the raw Tag Manager object for each entity", required: false }
29230
+ }
29231
+ });
29232
+ function fail6(err) {
29233
+ if (err instanceof ApiError) {
29234
+ const notConnected = err.code === "NOT_FOUND" || err.code === "UNAUTHORIZED";
29235
+ writeJsonEnvelope({
29236
+ ok: false,
29237
+ error: {
29238
+ code: err.code,
29239
+ message: err.message,
29240
+ ...notConnected ? {
29241
+ fix: {
29242
+ action: "ask_user",
29243
+ explanation: "Tag Manager is not connected for this company yet. Ask the user to connect Tag Manager in Settings \u2192 Connections and choose the container to manage, then retry. Continue with the rest of the task in the meantime."
29244
+ }
29245
+ } : {},
29246
+ retryable: false
29247
+ }
29248
+ });
29249
+ process.exit(1);
29250
+ }
29251
+ writeJsonEnvelope({ ok: false, error: { code: "NETWORK_ERROR", message: "Unexpected error" } });
29252
+ process.exit(1);
29253
+ }
29254
+ var containersCommand = defineCommand156({
29255
+ meta: {
29256
+ name: "containers",
29257
+ description: `List Tag Manager containers reachable by this company's connection.
29258
+
29259
+ Start here:
29260
+ baker tag-manager containers`
29261
+ },
29262
+ run: async () => {
29263
+ try {
29264
+ const data = await apiGet("/api/tag-manager/containers");
29265
+ writeJsonEnvelope({ ok: true, data });
29266
+ } catch (err) {
29267
+ fail6(err);
29268
+ }
29269
+ }
29270
+ });
29271
+ var readCommand = defineCommand156({
29272
+ meta: {
29273
+ name: "read",
29274
+ description: `Read the current contents of the Tag Manager container \u2014 always do this before staging changes.
29275
+
29276
+ Examples:
29277
+ baker tag-manager read
29278
+ baker tag-manager read --entities tag,trigger
29279
+ baker tag-manager read --full`
29280
+ },
29281
+ args: {
29282
+ container: { type: "string", description: "Numeric container id", required: false },
29283
+ workspace: { type: "string", description: "Workspace id", required: false },
29284
+ entities: { type: "string", description: "Comma-separated entity subset", required: false },
29285
+ full: { type: "boolean", description: "Include raw Tag Manager objects", required: false }
29286
+ },
29287
+ run: async ({ args }) => {
29288
+ const entities = typeof args.entities === "string" && args.entities.length > 0 ? args.entities.split(",").map((entry) => entry.trim()) : void 0;
29289
+ try {
29290
+ const data = await apiPost("/api/tag-manager/read", {
29291
+ containerId: args.container,
29292
+ workspaceId: args.workspace,
29293
+ entities,
29294
+ full: args.full === true
29295
+ });
29296
+ const hints = [];
29297
+ if (data.installedContainerMismatch) {
29298
+ hints.push(data.installedContainerMismatch);
29299
+ }
29300
+ hints.push(
29301
+ "Reference an existing trigger or variable by its numeric id or {{Name}}. To reference something you are staging in this same chat, use its gtm_temp_* ref and the executor resolves it at publish."
29302
+ );
29303
+ writeJsonEnvelope({ ok: true, data, hints });
29304
+ } catch (err) {
29305
+ fail6(err);
29306
+ }
29307
+ }
29308
+ });
29309
+
29310
+ // src/commands/tag-manager/write-commands.ts
29311
+ import { defineCommand as defineCommand157 } from "citty";
29312
+ var ENTITIES = [
29313
+ {
29314
+ entity: "tag",
29315
+ noun: "tag",
29316
+ createHint: 'Payload needs name + type (e.g. "gaawe" for a GA4 event) and usually parameter[] and firingTriggerId[]. Reference a trigger by its numeric id, or by a gtm_temp_* ref if you are staging it in this same chat.'
29317
+ },
29318
+ {
29319
+ entity: "trigger",
29320
+ noun: "trigger",
29321
+ createHint: 'Payload needs name + type (e.g. "pageview", "click", "customEvent"). Stage the trigger BEFORE the tag that fires on it, then reference it by its gtm_temp_* ref.'
29322
+ },
29323
+ {
29324
+ entity: "variable",
29325
+ noun: "variable",
29326
+ createHint: 'Payload needs name + type (e.g. "v" for a data layer variable, "c" for a constant). Other entities reference a variable by NAME as {{Variable Name}}.'
29327
+ },
29328
+ {
29329
+ entity: "folder",
29330
+ noun: "folder",
29331
+ createHint: "Payload needs name. Use folders to keep a large container legible."
29332
+ }
29333
+ ];
29334
+ for (const { entity, noun, createHint } of ENTITIES) {
29335
+ registerSchema({
29336
+ command: `tagManager.${entity}.create`,
29337
+ description: `Stage the creation of a Tag Manager ${noun}. ${createHint} Staged only \u2014 applies on publish into an unpublished container version.`,
29338
+ args: {
29339
+ json: { type: "string", description: `Inline JSON ${noun} definition`, required: false },
29340
+ file: { type: "string", description: `Path to a JSON file with the ${noun} definition`, required: false },
29341
+ container: { type: "string", description: "Numeric container id", required: false }
29342
+ }
29343
+ });
29344
+ registerSchema({
29345
+ command: `tagManager.${entity}.update`,
29346
+ description: `Stage an update to an existing Tag Manager ${noun}. Pass the ${noun} id or path as the positional argument and the changed fields as JSON. Read the container first so you send the fields you mean to change.`,
29347
+ args: {
29348
+ json: { type: "string", description: "Inline JSON with the changed fields", required: false },
29349
+ file: { type: "string", description: "Path to a JSON file with the changed fields", required: false }
29350
+ }
29351
+ });
29352
+ registerSchema({
29353
+ command: `tagManager.${entity}.delete`,
29354
+ description: `Stage the deletion of a Tag Manager ${noun}. Pass its id or path as the positional argument. Deleting is irreversible once the resulting version is published \u2014 confirm with the user first.`,
29355
+ args: {}
29356
+ });
29357
+ }
29358
+ function entityCommand(entity, noun) {
29359
+ return defineCommand157({
29360
+ meta: { name: entity, description: `Stage ${noun} changes on this chat's Tag Manager draft` },
29361
+ subCommands: {
29362
+ create: defineCommand157({
29363
+ meta: {
29364
+ name: "create",
29365
+ description: `Stage a new ${noun}
29366
+
29367
+ Examples:
29368
+ baker tag-manager ${entity} create --json '{"name":"GA4 Config","type":"googtag"}'
29369
+ baker tag-manager ${entity} create --file ./${entity}.json`
29370
+ },
29371
+ args: {
29372
+ json: { type: "string", description: "Inline JSON definition", required: false },
29373
+ file: { type: "string", description: "Path to a JSON file", required: false },
29374
+ container: { type: "string", description: "Numeric container id", required: false }
29375
+ },
29376
+ run: async ({ args }) => {
29377
+ await stageOp3({
29378
+ kind: `tagManager.${entity}.create`,
29379
+ payload: loadJsonArg(args),
29380
+ ...typeof args.container === "string" ? { containerId: args.container } : {}
29381
+ });
29382
+ }
29383
+ }),
29384
+ update: defineCommand157({
29385
+ meta: {
29386
+ name: "update",
29387
+ description: `Stage an update to an existing ${noun} (pass its id or path)`
29388
+ },
29389
+ args: {
29390
+ id: { type: "positional", description: `${noun} id or path`, required: false },
29391
+ json: { type: "string", description: "Inline JSON with changed fields", required: false },
29392
+ file: { type: "string", description: "Path to a JSON file", required: false },
29393
+ container: { type: "string", description: "Numeric container id", required: false }
29394
+ },
29395
+ run: async ({ args }) => {
29396
+ await stageOp3({
29397
+ kind: `tagManager.${entity}.update`,
29398
+ target: requireTarget4(args, noun),
29399
+ payload: loadJsonArg(args),
29400
+ ...typeof args.container === "string" ? { containerId: args.container } : {}
29401
+ });
29402
+ }
29403
+ }),
29404
+ delete: defineCommand157({
29405
+ meta: { name: "delete", description: `Stage the deletion of a ${noun} (pass its id or path)` },
29406
+ args: {
29407
+ id: { type: "positional", description: `${noun} id or path`, required: false },
29408
+ container: { type: "string", description: "Numeric container id", required: false }
29409
+ },
29410
+ run: async ({ args }) => {
29411
+ await stageOp3({
29412
+ kind: `tagManager.${entity}.delete`,
29413
+ target: requireTarget4(args, noun),
29414
+ ...typeof args.container === "string" ? { containerId: args.container } : {}
29415
+ });
29416
+ }
29417
+ })
29418
+ }
29419
+ });
29420
+ }
29421
+ var tagCommand = entityCommand("tag", "tag");
29422
+ var triggerCommand2 = entityCommand("trigger", "trigger");
29423
+ var variableCommand = entityCommand("variable", "variable");
29424
+ var folderCommand = entityCommand("folder", "folder");
29425
+ registerSchema({
29426
+ command: "tagManager.builtin",
29427
+ description: "Enable or disable Tag Manager built-in variables by type (e.g. clickUrl, pageUrl, formId). Built-in variables must be enabled before a trigger or tag can reference them as {{Click URL}}.",
29428
+ args: {
29429
+ types: { type: "string", description: "Comma-separated built-in variable types", required: true }
29430
+ }
29431
+ });
29432
+ function builtinTypes(args) {
29433
+ const raw = args.types;
29434
+ if (typeof raw !== "string" || raw.length === 0) {
29435
+ process.stdout.write(
29436
+ `${JSON.stringify({ ok: false, error: { code: "VALIDATION_ERROR", message: "pass --types with comma-separated built-in variable types" } }, null, 2)}
29437
+ `
29438
+ );
29439
+ process.exit(1);
29440
+ }
29441
+ return raw.split(",").map((entry) => entry.trim());
29442
+ }
29443
+ var builtinCommand = defineCommand157({
29444
+ meta: {
29445
+ name: "builtin",
29446
+ description: `Enable or disable built-in variables
29447
+
29448
+ Examples:
29449
+ baker tag-manager builtin enable --types clickUrl,clickText
29450
+ baker tag-manager builtin disable --types formId`
29451
+ },
29452
+ subCommands: {
29453
+ enable: defineCommand157({
29454
+ meta: { name: "enable", description: "Stage enabling built-in variables" },
29455
+ args: {
29456
+ types: { type: "string", description: "Comma-separated types", required: false },
29457
+ container: { type: "string", description: "Numeric container id", required: false }
29458
+ },
29459
+ run: async ({ args }) => {
29460
+ await stageOp3({
29461
+ kind: "tagManager.builtInVariable.enable",
29462
+ payload: { type: builtinTypes(args) },
29463
+ ...typeof args.container === "string" ? { containerId: args.container } : {}
29464
+ });
29465
+ }
29466
+ }),
29467
+ disable: defineCommand157({
29468
+ meta: { name: "disable", description: "Stage disabling built-in variables" },
29469
+ args: {
29470
+ types: { type: "string", description: "Comma-separated types", required: false },
29471
+ container: { type: "string", description: "Numeric container id", required: false }
29472
+ },
29473
+ run: async ({ args }) => {
29474
+ await stageOp3({
29475
+ kind: "tagManager.builtInVariable.disable",
29476
+ payload: { type: builtinTypes(args) },
29477
+ ...typeof args.container === "string" ? { containerId: args.container } : {}
29478
+ });
29479
+ }
29480
+ })
29481
+ }
29482
+ });
29483
+
29484
+ // src/commands/tag-manager/index.ts
29485
+ var tagManagerCommand = defineCommand158({
29486
+ meta: {
29487
+ name: "tag-manager",
29488
+ description: `Read and change what lives inside the client's Google Tag Manager container \u2014 tags, triggers, variables, folders and built-in variables.
29489
+
29490
+ Start here:
29491
+ baker tag-manager read \u2014 see what the container holds today
29492
+
29493
+ Then stage changes (nothing is sent to Tag Manager until publish):
29494
+ baker tag-manager trigger create --json '{"name":"Quote Submit","type":"customEvent"}'
29495
+ baker tag-manager tag create --json '{"name":"GA4 Quote","type":"gaawe","firingTriggerId":["gtm_temp_trigger_..."]}'
29496
+ baker tag-manager draft list \u2014 review everything staged
29497
+
29498
+ On publish, staged changes are written into a new Tag Manager version that is NOT published.
29499
+ Tell the user to publish that version in Tag Manager to make it live.
29500
+
29501
+ This owns the CONTENTS of the container. Installing the container snippet on the site is a
29502
+ different job \u2014 that is \`baker tags\` with the googleTagManager tag.
29503
+
29504
+ Full guide: __tooling__/docs/tools/baker/tag-manager.md`
29505
+ },
29506
+ subCommands: {
29507
+ containers: containersCommand,
29508
+ read: readCommand,
29509
+ tag: tagCommand,
29510
+ trigger: triggerCommand2,
29511
+ variable: variableCommand,
29512
+ folder: folderCommand,
29513
+ builtin: builtinCommand,
29514
+ draft: draftCommand3
29515
+ }
29516
+ });
29517
+
29518
+ // src/commands/tags/index.ts
29519
+ import { defineCommand as defineCommand159 } from "citty";
29520
+
29179
29521
  // src/commands/tags/shared.ts
29180
29522
  function failApi3(err) {
29181
29523
  if (err instanceof ApiError) {
@@ -29240,7 +29582,7 @@ async function listTags(json) {
29240
29582
  failApi3(err);
29241
29583
  }
29242
29584
  }
29243
- var listCommand10 = defineCommand155({
29585
+ var listCommand10 = defineCommand159({
29244
29586
  meta: {
29245
29587
  name: "list",
29246
29588
  description: "Effective tags for this chat (production + staged), with each tag's full readable config (secrets excluded) \u2014 reuse a stored value to pre-fill a change rather than asking the user. Refs printed here are what flow side-effect tagIds should use. Example: baker tags list"
@@ -29259,7 +29601,7 @@ async function listDraft3() {
29259
29601
  failApi3(err);
29260
29602
  }
29261
29603
  }
29262
- var draftCommand3 = defineCommand155({
29604
+ var draftCommand4 = defineCommand159({
29263
29605
  meta: {
29264
29606
  name: "draft",
29265
29607
  description: "Review the tag changes staged in this chat (read-only). Staged changes were approved via request_tag_input and apply when the chat is published; to amend or drop one, propose a follow-up change through the same tool (a delete on a tag_temp_* ref drops the staged create)."
@@ -29268,7 +29610,7 @@ var draftCommand3 = defineCommand155({
29268
29610
  await listDraft3();
29269
29611
  }
29270
29612
  });
29271
- var tagsCommand3 = defineCommand155({
29613
+ var tagsCommand3 = defineCommand159({
29272
29614
  meta: {
29273
29615
  name: "tags",
29274
29616
  description: `Read the client's marketing/analytics tags (Meta pixel, GA4, Google Ads, GTM, Clarity, Hotjar, \u2026) \u2014 production tags plus the changes staged in this chat.
@@ -29286,7 +29628,7 @@ Full guide: __tooling__/docs/tools/baker/tags.md`
29286
29628
  },
29287
29629
  subCommands: {
29288
29630
  list: listCommand10,
29289
- draft: draftCommand3
29631
+ draft: draftCommand4
29290
29632
  },
29291
29633
  run: async () => {
29292
29634
  await listTags(false);
@@ -29294,10 +29636,10 @@ Full guide: __tooling__/docs/tools/baker/tags.md`
29294
29636
  });
29295
29637
 
29296
29638
  // src/commands/testimonials/index.ts
29297
- import { defineCommand as defineCommand159 } from "citty";
29639
+ import { defineCommand as defineCommand163 } from "citty";
29298
29640
 
29299
29641
  // src/commands/testimonials/get.ts
29300
- import { defineCommand as defineCommand156 } from "citty";
29642
+ import { defineCommand as defineCommand160 } from "citty";
29301
29643
  registerSchema({
29302
29644
  command: "testimonials.get",
29303
29645
  description: "Get a single testimonial by ID",
@@ -29305,7 +29647,7 @@ registerSchema({
29305
29647
  id: { type: "string", description: "Testimonial ID", required: true }
29306
29648
  }
29307
29649
  });
29308
- var getCommand4 = defineCommand156({
29650
+ var getCommand4 = defineCommand160({
29309
29651
  meta: { name: "get", description: "Get a single testimonial by ID. Example: baker testimonials get j571abc123" },
29310
29652
  args: {
29311
29653
  id: { type: "positional", description: "Testimonial ID", required: false },
@@ -29342,7 +29684,7 @@ var getCommand4 = defineCommand156({
29342
29684
  });
29343
29685
 
29344
29686
  // src/commands/testimonials/list.ts
29345
- import { defineCommand as defineCommand157 } from "citty";
29687
+ import { defineCommand as defineCommand161 } from "citty";
29346
29688
  registerSchema({
29347
29689
  command: "testimonials.list",
29348
29690
  description: "List testimonials with optional filters.",
@@ -29372,7 +29714,7 @@ registerSchema({
29372
29714
  limit: { type: "number", description: "Max results (default 50)", required: false, default: 50 }
29373
29715
  }
29374
29716
  });
29375
- var listCommand11 = defineCommand157({
29717
+ var listCommand11 = defineCommand161({
29376
29718
  meta: {
29377
29719
  name: "list",
29378
29720
  description: "List testimonials with optional filters. Example: baker testimonials list --source google --sentiment positive"
@@ -29421,7 +29763,7 @@ var listCommand11 = defineCommand157({
29421
29763
  });
29422
29764
 
29423
29765
  // src/commands/testimonials/search.ts
29424
- import { defineCommand as defineCommand158 } from "citty";
29766
+ import { defineCommand as defineCommand162 } from "citty";
29425
29767
  function languageBiasHint(results, requestedLanguage) {
29426
29768
  if (requestedLanguage) {
29427
29769
  return null;
@@ -29499,7 +29841,7 @@ function buildSearchRequest(query, args) {
29499
29841
  }
29500
29842
  return body;
29501
29843
  }
29502
- var searchCommand2 = defineCommand158({
29844
+ var searchCommand2 = defineCommand162({
29503
29845
  meta: {
29504
29846
  name: "search",
29505
29847
  description: "Semantic search testimonials by text query. Uses hybrid BM25 + vector + reranking. Example: baker testimonials search 'great service' --rating-min 4"
@@ -29555,7 +29897,7 @@ var searchCommand2 = defineCommand158({
29555
29897
  var tagsCommand4 = makeTagsCommand("testimonials", "testimonial", "/api/testimonials/tags");
29556
29898
 
29557
29899
  // src/commands/testimonials/index.ts
29558
- var testimonialsCommand = defineCommand159({
29900
+ var testimonialsCommand = defineCommand163({
29559
29901
  meta: {
29560
29902
  name: "testimonials",
29561
29903
  description: `Find and browse testimonials in Baker. Subcommands: search, get, list, tags.
@@ -29577,10 +29919,10 @@ Full guide: __tooling__/docs/tools/baker/testimonials.md`
29577
29919
  });
29578
29920
 
29579
29921
  // src/commands/videos/index.ts
29580
- import { defineCommand as defineCommand164 } from "citty";
29922
+ import { defineCommand as defineCommand168 } from "citty";
29581
29923
 
29582
29924
  // src/commands/videos/delete.ts
29583
- import { defineCommand as defineCommand160 } from "citty";
29925
+ import { defineCommand as defineCommand164 } from "citty";
29584
29926
  registerSchema({
29585
29927
  command: "videos.delete",
29586
29928
  description: "Delete a video by ID",
@@ -29594,7 +29936,7 @@ registerSchema({
29594
29936
  }
29595
29937
  }
29596
29938
  });
29597
- var deleteCommand3 = defineCommand160({
29939
+ var deleteCommand3 = defineCommand164({
29598
29940
  meta: {
29599
29941
  name: "delete",
29600
29942
  description: "Delete a video by ID. Use --dry-run to preview. Example: baker videos delete j571abc123 --dry-run"
@@ -29635,7 +29977,7 @@ var deleteCommand3 = defineCommand160({
29635
29977
  });
29636
29978
 
29637
29979
  // src/commands/videos/get.ts
29638
- import { defineCommand as defineCommand161 } from "citty";
29980
+ import { defineCommand as defineCommand165 } from "citty";
29639
29981
  registerSchema({
29640
29982
  command: "videos.get",
29641
29983
  description: "Get a single video by ID",
@@ -29643,7 +29985,7 @@ registerSchema({
29643
29985
  id: { type: "string", description: "Video ID", required: true }
29644
29986
  }
29645
29987
  });
29646
- var getCommand5 = defineCommand161({
29988
+ var getCommand5 = defineCommand165({
29647
29989
  meta: { name: "get", description: "Get a single video by ID. Example: baker videos get j571abc123" },
29648
29990
  args: {
29649
29991
  id: { type: "positional", description: "Video ID", required: false },
@@ -29680,7 +30022,7 @@ var getCommand5 = defineCommand161({
29680
30022
  });
29681
30023
 
29682
30024
  // src/commands/videos/search.ts
29683
- import { defineCommand as defineCommand162 } from "citty";
30025
+ import { defineCommand as defineCommand166 } from "citty";
29684
30026
  registerSchema({
29685
30027
  command: "videos.search",
29686
30028
  description: "Search videos by text query. Only returns ready videos.",
@@ -29690,7 +30032,7 @@ registerSchema({
29690
30032
  tags: { type: "string", description: "Comma-separated tags to filter by", required: false }
29691
30033
  }
29692
30034
  });
29693
- var searchCommand3 = defineCommand162({
30035
+ var searchCommand3 = defineCommand166({
29694
30036
  meta: {
29695
30037
  name: "search",
29696
30038
  description: "Semantic search videos by text query. Uses hybrid BM25 + vector + reranking. Example: baker videos search 'product demo' --tags tutorial"
@@ -29742,7 +30084,7 @@ var tagsCommand5 = makeTagsCommand("videos", "video", "/api/videos/tags");
29742
30084
  // src/commands/videos/upload.ts
29743
30085
  import { readFile as readFile22, stat as stat7 } from "fs/promises";
29744
30086
  import { extname as extname3 } from "path";
29745
- import { defineCommand as defineCommand163 } from "citty";
30087
+ import { defineCommand as defineCommand167 } from "citty";
29746
30088
  var MIME_MAP = {
29747
30089
  ".mp4": "video/mp4",
29748
30090
  ".mov": "video/quicktime",
@@ -29776,7 +30118,7 @@ function detectContentType(filePath) {
29776
30118
  }
29777
30119
  return mime;
29778
30120
  }
29779
- var uploadCommand2 = defineCommand163({
30121
+ var uploadCommand2 = defineCommand167({
29780
30122
  meta: {
29781
30123
  name: "upload",
29782
30124
  description: "Upload a video file to Baker via Mux direct upload. Auto-detects content type. Example: baker videos upload ./demo.mp4"
@@ -29830,7 +30172,7 @@ var uploadCommand2 = defineCommand163({
29830
30172
  });
29831
30173
 
29832
30174
  // src/commands/videos/index.ts
29833
- var videosCommand = defineCommand164({
30175
+ var videosCommand = defineCommand168({
29834
30176
  meta: {
29835
30177
  name: "videos",
29836
30178
  description: `Find and manage videos in Baker. Subcommands: search, get, upload, delete, tags.
@@ -29854,10 +30196,10 @@ Full guide: __tooling__/docs/tools/baker/videos.md`
29854
30196
  });
29855
30197
 
29856
30198
  // src/commands/winning-ads/index.ts
29857
- import { defineCommand as defineCommand177 } from "citty";
30199
+ import { defineCommand as defineCommand181 } from "citty";
29858
30200
 
29859
30201
  // src/commands/winning-ads/advertisers.ts
29860
- import { defineCommand as defineCommand165 } from "citty";
30202
+ import { defineCommand as defineCommand169 } from "citty";
29861
30203
 
29862
30204
  // src/commands/winning-ads/shared.ts
29863
30205
  function splitList(value) {
@@ -29910,7 +30252,7 @@ function advertiserNormalizer(record, full) {
29910
30252
  last_synced_at: record.last_synced_at ?? null
29911
30253
  };
29912
30254
  }
29913
- var advertisersCommand2 = defineCommand165({
30255
+ var advertisersCommand2 = defineCommand169({
29914
30256
  meta: {
29915
30257
  name: "advertisers",
29916
30258
  description: 'List corpus advertisers by name or domain. Find your own advertiser for --exclude-advertiser, or a competitor for --advertiser-id / winners. Example: baker winning-ads advertisers "Deel" --output md'
@@ -29968,7 +30310,7 @@ var advertisersCommand2 = defineCommand165({
29968
30310
  });
29969
30311
 
29970
30312
  // src/commands/winning-ads/brief.ts
29971
- import { defineCommand as defineCommand166 } from "citty";
30313
+ import { defineCommand as defineCommand170 } from "citty";
29972
30314
  registerSchema({
29973
30315
  command: "winning-ads.brief",
29974
30316
  description: "Generate a creative brief grounded in strategically-similar winning ads. Optionally describe the target creative with --dna (JSON) and steer with --notes.",
@@ -30014,7 +30356,7 @@ function parseDna(raw) {
30014
30356
  }
30015
30357
  return parsed;
30016
30358
  }
30017
- var briefCommand = defineCommand166({
30359
+ var briefCommand = defineCommand170({
30018
30360
  meta: {
30019
30361
  name: "brief",
30020
30362
  description: `Generate a creative brief from winning references. Example: baker winning-ads brief --dna '{"angle":"cost savings"}' --notes "B2B, LinkedIn video" --k 8`
@@ -30050,7 +30392,7 @@ var briefCommand = defineCommand166({
30050
30392
  });
30051
30393
 
30052
30394
  // src/commands/winning-ads/content.ts
30053
- import { defineCommand as defineCommand167 } from "citty";
30395
+ import { defineCommand as defineCommand171 } from "citty";
30054
30396
  registerSchema({
30055
30397
  command: "winning-ads.content",
30056
30398
  description: "Read what's INSIDE one winning ad: the spoken transcript, the on-screen text, and the ad copy. Use this after `search`/`winners`/`feed` return a shortlist \u2014 pass an ad_id to understand a reference before reproducing it. Add --full for speech, pacing, and soundtrack detail. Video ads carry the transcript/on-screen text; static ads carry only the copy.",
@@ -30063,7 +30405,7 @@ registerSchema({
30063
30405
  }
30064
30406
  }
30065
30407
  });
30066
- var contentCommand = defineCommand167({
30408
+ var contentCommand = defineCommand171({
30067
30409
  meta: {
30068
30410
  name: "content",
30069
30411
  description: "Read the transcript + on-screen text + copy of one winning ad. Example: baker winning-ads content adg_123 --platform meta --full --output md"
@@ -30112,7 +30454,7 @@ var contentCommand = defineCommand167({
30112
30454
  });
30113
30455
 
30114
30456
  // src/commands/winning-ads/feed.ts
30115
- import { defineCommand as defineCommand168 } from "citty";
30457
+ import { defineCommand as defineCommand172 } from "citty";
30116
30458
  function buildFeedParams(input) {
30117
30459
  const params = {};
30118
30460
  const advertiser = splitList(input.advertiser);
@@ -30164,7 +30506,7 @@ registerSchema({
30164
30506
  format: { type: "string", description: "Comma-separated formats to include (e.g. static,video)", required: false }
30165
30507
  }
30166
30508
  });
30167
- var feedCommand = defineCommand168({
30509
+ var feedCommand = defineCommand172({
30168
30510
  meta: {
30169
30511
  name: "feed",
30170
30512
  description: "Winners across every brand you follow (browse, then trim per advertiser). Example: baker winning-ads feed --per-advertiser 5 --output md"
@@ -30249,7 +30591,7 @@ var feedCommand = defineCommand168({
30249
30591
  });
30250
30592
 
30251
30593
  // src/commands/winning-ads/follow.ts
30252
- import { defineCommand as defineCommand169 } from "citty";
30594
+ import { defineCommand as defineCommand173 } from "citty";
30253
30595
  var PLATFORMS = ["meta", "linkedin"];
30254
30596
  registerSchema({
30255
30597
  command: "winning-ads.follow",
@@ -30264,7 +30606,7 @@ registerSchema({
30264
30606
  label: { type: "string", description: "Optional display label (defaults to the resolved name)", required: false }
30265
30607
  }
30266
30608
  });
30267
- var followCommand = defineCommand169({
30609
+ var followCommand = defineCommand173({
30268
30610
  meta: {
30269
30611
  name: "follow",
30270
30612
  description: 'Follow a brand to track ALL its ads \u2014 every platform and country. --platform is how we read your input, not a limit. A domain tracks both Meta + LinkedIn. Example: baker winning-ads follow "deel.com" --platform meta'
@@ -30311,7 +30653,7 @@ var followCommand = defineCommand169({
30311
30653
  });
30312
30654
 
30313
30655
  // src/commands/winning-ads/follow-competitors.ts
30314
- import { defineCommand as defineCommand170 } from "citty";
30656
+ import { defineCommand as defineCommand174 } from "citty";
30315
30657
  var PLATFORMS2 = ["meta", "linkedin"];
30316
30658
  var BATCH_TIMEOUT_MS = 3e5;
30317
30659
  function buildFollowBatchBody(input) {
@@ -30344,7 +30686,7 @@ registerSchema({
30344
30686
  }
30345
30687
  }
30346
30688
  });
30347
- var followCompetitorsCommand = defineCommand170({
30689
+ var followCompetitorsCommand = defineCommand174({
30348
30690
  meta: {
30349
30691
  name: "follow-competitors",
30350
30692
  description: 'Follow many brands at once by domain \u2014 add every competitor in one call. Example: baker winning-ads follow-competitors "deel.com,notion.so,hubspot.com"'
@@ -30419,7 +30761,7 @@ var followCompetitorsCommand = defineCommand170({
30419
30761
  });
30420
30762
 
30421
30763
  // src/commands/winning-ads/following.ts
30422
- import { defineCommand as defineCommand171 } from "citty";
30764
+ import { defineCommand as defineCommand175 } from "citty";
30423
30765
  registerSchema({
30424
30766
  command: "winning-ads.following",
30425
30767
  description: "List the brands you follow in your ad-dna library, with each one's status (ready vs still adding) and cached ad counts.",
@@ -30452,7 +30794,7 @@ function followingNormalizer(record, full) {
30452
30794
  platforms: Array.isArray(record.platforms) ? record.platforms : []
30453
30795
  };
30454
30796
  }
30455
- var followingCommand = defineCommand171({
30797
+ var followingCommand = defineCommand175({
30456
30798
  meta: {
30457
30799
  name: "following",
30458
30800
  description: "List brands you follow, with status (ready / adding\u2026) and cached counts. Example: baker winning-ads following --output md"
@@ -30487,7 +30829,7 @@ var followingCommand = defineCommand171({
30487
30829
  });
30488
30830
 
30489
30831
  // src/commands/winning-ads/patterns.ts
30490
- import { defineCommand as defineCommand172 } from "citty";
30832
+ import { defineCommand as defineCommand176 } from "citty";
30491
30833
  registerSchema({
30492
30834
  command: "winning-ads.patterns",
30493
30835
  description: "Mine what separates two cohorts of ads: pass a comma-list of winning ad ids (--winners) and a comma-list of weaker ad ids (--duds). Returns the discriminating DNA fields.",
@@ -30526,7 +30868,7 @@ function discriminatorRow(record) {
30526
30868
  top_values_duds: Array.isArray(record.top_values_b) ? record.top_values_b.join(", ") : ""
30527
30869
  };
30528
30870
  }
30529
- var patternsCommand = defineCommand172({
30871
+ var patternsCommand = defineCommand176({
30530
30872
  meta: {
30531
30873
  name: "patterns",
30532
30874
  description: "Discover what separates winning ads from weak ones. Example: baker winning-ads patterns --winners a_1,a_2,a_3 --duds a_9,a_8 --output md"
@@ -30582,7 +30924,7 @@ var patternsCommand = defineCommand172({
30582
30924
  });
30583
30925
 
30584
30926
  // src/commands/winning-ads/search.ts
30585
- import { defineCommand as defineCommand173 } from "citty";
30927
+ import { defineCommand as defineCommand177 } from "citty";
30586
30928
  registerSchema({
30587
30929
  command: "winning-ads.search",
30588
30930
  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.",
@@ -30690,7 +31032,7 @@ function buildSearchBody(args) {
30690
31032
  }
30691
31033
  return body;
30692
31034
  }
30693
- var searchCommand4 = defineCommand173({
31035
+ var searchCommand4 = defineCommand177({
30694
31036
  meta: {
30695
31037
  name: "search",
30696
31038
  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"
@@ -30805,7 +31147,7 @@ var searchCommand4 = defineCommand173({
30805
31147
  });
30806
31148
 
30807
31149
  // src/commands/winning-ads/seeds.ts
30808
- import { defineCommand as defineCommand174 } from "citty";
31150
+ import { defineCommand as defineCommand178 } from "citty";
30809
31151
  function leanRow(r) {
30810
31152
  return {
30811
31153
  key: r.key,
@@ -30833,7 +31175,7 @@ function makeSeedCommand(opts) {
30833
31175
  limit: { type: "number", description: "Max keys 1-100 (default 20)", required: false, default: 20 }
30834
31176
  }
30835
31177
  });
30836
- return defineCommand174({
31178
+ return defineCommand178({
30837
31179
  meta: { name: opts.name, description: opts.description },
30838
31180
  args: {
30839
31181
  platform: { type: "string", description: "Single platform to segment on", required: false },
@@ -30882,7 +31224,7 @@ var formatsCommand = makeSeedCommand({
30882
31224
  });
30883
31225
 
30884
31226
  // src/commands/winning-ads/unfollow.ts
30885
- import { defineCommand as defineCommand175 } from "citty";
31227
+ import { defineCommand as defineCommand179 } from "citty";
30886
31228
  registerSchema({
30887
31229
  command: "winning-ads.unfollow",
30888
31230
  description: "Stop following a brand \u2014 removes it from your ad-dna library by advertiser id.",
@@ -30890,7 +31232,7 @@ registerSchema({
30890
31232
  advertiser: { type: "string", description: "Advertiser id to unfollow", required: true }
30891
31233
  }
30892
31234
  });
30893
- var unfollowCommand = defineCommand175({
31235
+ var unfollowCommand = defineCommand179({
30894
31236
  meta: {
30895
31237
  name: "unfollow",
30896
31238
  description: "Stop following a brand by advertiser id. Example: baker winning-ads unfollow adv_123"
@@ -30911,7 +31253,7 @@ var unfollowCommand = defineCommand175({
30911
31253
  });
30912
31254
 
30913
31255
  // src/commands/winning-ads/winners.ts
30914
- import { defineCommand as defineCommand176 } from "citty";
31256
+ import { defineCommand as defineCommand180 } from "citty";
30915
31257
  registerSchema({
30916
31258
  command: "winning-ads.winners",
30917
31259
  description: "Top winning ads for one advertiser id (from `advertisers` or `following`). Returns lean winner cards; add --full for DNA + longevity.",
@@ -30921,7 +31263,7 @@ registerSchema({
30921
31263
  platform: { type: "string", description: "Filter to a single platform: meta|linkedin", required: false }
30922
31264
  }
30923
31265
  });
30924
- var winnersCommand = defineCommand176({
31266
+ var winnersCommand = defineCommand180({
30925
31267
  meta: {
30926
31268
  name: "winners",
30927
31269
  description: "Top winning ads for a specific advertiser id. Example: baker winning-ads winners adv_123 --top 15 --output md"
@@ -30971,7 +31313,7 @@ var winnersCommand = defineCommand176({
30971
31313
  });
30972
31314
 
30973
31315
  // src/commands/winning-ads/index.ts
30974
- var winningAdsCommand = defineCommand177({
31316
+ var winningAdsCommand = defineCommand181({
30975
31317
  meta: {
30976
31318
  name: "winning-ads",
30977
31319
  description: `Search the ad-dna corpus of scored "winning" ads for reference creatives to reproduce, and manage the brands your library tracks. Proxied through the Baker backend (BAKER_API_KEY) \u2014 no separate token needed.
@@ -31027,7 +31369,7 @@ Full guide: __tooling__/docs/tools/baker/winning-ads.md`
31027
31369
  });
31028
31370
 
31029
31371
  // src/version.ts
31030
- import { readFileSync as readFileSync12 } from "fs";
31372
+ import { readFileSync as readFileSync13 } from "fs";
31031
31373
  function packageJsonUrl() {
31032
31374
  return new URL("../package.json", import.meta.url);
31033
31375
  }
@@ -31039,15 +31381,15 @@ function parsePackageVersion(raw) {
31039
31381
  throw new Error("Invalid CLI package.json: missing version");
31040
31382
  }
31041
31383
  function getCliVersion() {
31042
- return parsePackageVersion(readFileSync12(packageJsonUrl(), "utf8"));
31384
+ return parsePackageVersion(readFileSync13(packageJsonUrl(), "utf8"));
31043
31385
  }
31044
31386
 
31045
31387
  // src/cli.ts
31046
- var main = defineCommand178({
31388
+ var main = defineCommand182({
31047
31389
  meta: {
31048
31390
  name: "baker",
31049
31391
  version: getCliVersion(),
31050
- description: `AI-agent CLI for finding and managing images, videos, testimonials, action items, scheduled actions, marketing tags, account history, and ad platform data in Baker.
31392
+ description: `AI-agent CLI for finding and managing images, videos, testimonials, action items, scheduled actions, marketing tags, Tag Manager containers, account history, and ad platform data in Baker.
31051
31393
 
31052
31394
  Auth: Set BAKER_API_KEY (starts with bk_) and BAKER_API_URL environment variables.
31053
31395
  Chat: Set BAKER_CHAT_ID for action and scheduled-action commands that stage changes against a chat.
@@ -31070,6 +31412,7 @@ Introspection: Run 'baker schema <command>' to inspect argument schemas.`
31070
31412
  testimonials: testimonialsCommand,
31071
31413
  canvas: canvasCommand,
31072
31414
  tags: tagsCommand3,
31415
+ "tag-manager": tagManagerCommand,
31073
31416
  chats: chatsCommand,
31074
31417
  history: historyCommand,
31075
31418
  "winning-ads": winningAdsCommand,