@koda-sl/baker-cli 0.147.0 → 0.148.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -492,6 +492,8 @@ Command groups: `budgets`, `campaigns`, `ad-groups`, `keywords` (add/update/remo
492
492
 
493
493
  **Amending a staged op** — `draft amend <ref> (--file patch.json | --patch '<json>')` merges a JSON patch into an already-staged op's payload (objects deep-merge, `null` deletes a key, arrays/scalars replace) and re-validates it in full. Always prefer this over removing and re-staging — it preserves the op's ref (so dependents don't break) and its position in the draft. `draft show <ref>` prints the full staged payload (including warnings/annotations) so you can verify a change looks right before publish.
494
494
 
495
+ **Already-satisfied writes aren't staged** — an update or pause/resume whose every field already holds the requested value on the live account is dropped instead of staged: the envelope returns `"staged": false, "noop": true` with a `reason` ("this ad is already Paused on Google Ads"), plus a hint. This is a success, not an error — there is nothing to publish. Only a positively-read live snapshot can trigger it; a failed or empty read always stages. Batch responses list the dropped ops under `skipped`, and `count` reflects what actually entered the draft.
496
+
495
497
  **Batch keyword adds** — `keywords add`, `negative-keywords add`, and `keyword-lists add` take a whole batch in one command: comma-separate `--text` entries and/or pass `--file <list.txt>` (one keyword per line). A `:EXACT`/`:PHRASE`/`:BROAD` suffix per entry overrides the `--match-type` default. Batches stage all-or-nothing as one request (limit 500); each keyword still lands as its own draft op, so it stays individually removable/amendable.
496
498
 
497
499
  ---
package/dist/cli.js CHANGED
@@ -5395,17 +5395,27 @@ var googleDraftStageRequestSchema = z13.object({
5395
5395
  chatId: z13.string(),
5396
5396
  op: googleDraftOpInputSchema
5397
5397
  });
5398
- var googleDraftStageResponseSchema = z13.object({
5399
- staged: z13.literal(true),
5400
- ref: z13.string(),
5401
- kind: googleDraftOpKindSchema,
5402
- mode: googleWriteModeSchema,
5403
- dependsOn: z13.array(z13.string()),
5404
- summary: z13.string(),
5405
- warnings: z13.array(z13.string()),
5406
- /** True when the op amended an already-staged op in place instead of appending a new one. */
5407
- amended: z13.boolean().optional()
5408
- });
5398
+ var googleDraftStageResponseSchema = z13.discriminatedUnion("staged", [
5399
+ z13.object({
5400
+ staged: z13.literal(true),
5401
+ ref: z13.string(),
5402
+ kind: googleDraftOpKindSchema,
5403
+ mode: googleWriteModeSchema,
5404
+ dependsOn: z13.array(z13.string()),
5405
+ summary: z13.string(),
5406
+ warnings: z13.array(z13.string()),
5407
+ /** True when the op amended an already-staged op in place instead of appending a new one. */
5408
+ amended: z13.boolean().optional()
5409
+ }),
5410
+ z13.object({
5411
+ staged: z13.literal(false),
5412
+ noop: z13.literal(true),
5413
+ kind: googleDraftOpKindSchema,
5414
+ mode: googleWriteModeSchema,
5415
+ summary: z13.string(),
5416
+ reason: z13.string()
5417
+ })
5418
+ ]);
5409
5419
  var googleDraftAmendRequestSchema = z13.object({
5410
5420
  chatId: z13.string(),
5411
5421
  ref: z13.string(),
@@ -5432,7 +5442,8 @@ var googleDraftStageBatchResponseSchema = z13.object({
5432
5442
  summary: z13.string(),
5433
5443
  warnings: z13.array(z13.string())
5434
5444
  })
5435
- )
5445
+ ),
5446
+ skipped: z13.array(z13.object({ kind: googleDraftOpKindSchema, summary: z13.string(), reason: z13.string() })).optional()
5436
5447
  });
5437
5448
  var googleDraftOpViewSchema = z13.object({
5438
5449
  ref: z13.string(),
@@ -5691,7 +5702,7 @@ function keywordEntries(args) {
5691
5702
  const seen = /* @__PURE__ */ new Set();
5692
5703
  for (const item of raw) {
5693
5704
  const entry = parseKeywordEntry(item, defaultMatch);
5694
- const key = `${entry.text}\0${entry.matchType}`;
5705
+ const key = JSON.stringify([entry.text, entry.matchType]);
5695
5706
  if (!seen.has(key)) {
5696
5707
  seen.add(key);
5697
5708
  entries.push(entry);
@@ -5758,6 +5769,9 @@ function handleGoogleError(err) {
5758
5769
  });
5759
5770
  process.exit(1);
5760
5771
  }
5772
+ function noopHints(data) {
5773
+ return data.staged ? [] : [`NOT STAGED \u2014 ${data.reason}. Nothing to publish for this change; say so rather than reporting it as done.`];
5774
+ }
5761
5775
  async function stageGoogleOp(raw, hints) {
5762
5776
  const preflight = googleDraftOpInputSchema.safeParse(raw);
5763
5777
  if (!preflight.success) {
@@ -5775,7 +5789,8 @@ async function stageGoogleOp(raw, hints) {
5775
5789
  try {
5776
5790
  const chatId = requireChatId();
5777
5791
  const response = await apiPost("/api/ads/google/draft/stage", { chatId, op: preflight.data });
5778
- writeJsonEnvelope(hints && hints.length > 0 ? { ...response, hints } : response);
5792
+ const allHints = [...noopHints(response.data), ...hints ?? []];
5793
+ writeJsonEnvelope(allHints.length > 0 ? { ...response, hints: allHints } : response);
5779
5794
  } catch (err) {
5780
5795
  handleGoogleError(err);
5781
5796
  }
@@ -5811,7 +5826,14 @@ async function stageGoogleOps(rawOps, hints) {
5811
5826
  try {
5812
5827
  const chatId = requireChatId();
5813
5828
  const response = await apiPost("/api/ads/google/draft/stage-batch", { chatId, ops });
5814
- writeJsonEnvelope(hints && hints.length > 0 ? { ...response, hints } : response);
5829
+ const skipped = response.data.skipped ?? [];
5830
+ const allHints = [
5831
+ ...skipped.length > 0 ? [
5832
+ `${skipped.length} of ${ops.length} op(s) NOT STAGED \u2014 already in the requested state: ${skipped.map((s) => s.reason).join("; ")}`
5833
+ ] : [],
5834
+ ...hints ?? []
5835
+ ];
5836
+ writeJsonEnvelope(allHints.length > 0 ? { ...response, hints: allHints } : response);
5815
5837
  } catch (err) {
5816
5838
  handleGoogleError(err);
5817
5839
  }