@koda-sl/baker-cli 0.557.0 → 0.558.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.
Files changed (65) hide show
  1. package/dist/{account-reads-T6GTSAXE.js → account-reads-GQWTMLTY.js} +2 -2
  2. package/dist/{accounts-HJAQTVDA.js → accounts-ZFQYZT3J.js} +7 -2
  3. package/dist/accounts-ZFQYZT3J.js.map +1 -0
  4. package/dist/{ads-WWZK7UYG.js → ads-VYMFRGAC.js} +2 -2
  5. package/dist/{advertisers-FME2ABRB.js → advertisers-A3Q53R2S.js} +2 -2
  6. package/dist/{autocomplete-CBOYGT73.js → autocomplete-OOLUPUIJ.js} +2 -2
  7. package/dist/{changes-OIVPZJHE.js → changes-MVCX3GLE.js} +16 -11
  8. package/dist/changes-MVCX3GLE.js.map +1 -0
  9. package/dist/{chunk-JPK34QPS.js → chunk-2L24FULD.js} +3 -3
  10. package/dist/chunk-2L24FULD.js.map +1 -0
  11. package/dist/{chunk-PQ7RLE43.js → chunk-HNELOMY2.js} +26 -433
  12. package/dist/chunk-HNELOMY2.js.map +1 -0
  13. package/dist/chunk-NPGPRZW2.js +1313 -0
  14. package/dist/chunk-NPGPRZW2.js.map +1 -0
  15. package/dist/{chunk-SNWFM2DB.js → chunk-Q2JDS6WL.js} +11 -1
  16. package/dist/chunk-Q2JDS6WL.js.map +1 -0
  17. package/dist/cli.js +2 -2
  18. package/dist/{conversion-upload-command-J2FIO74Y.js → conversion-upload-command-U36QYHUT.js} +2 -2
  19. package/dist/{currency-B5T7TYKD.js → currency-XFY2EX72.js} +3 -3
  20. package/dist/{discover-SS3PO5Y6.js → discover-J5344ATV.js} +2 -2
  21. package/dist/{draft-EB5ZFDVN.js → draft-OIGJUFOH.js} +2 -2
  22. package/dist/{forecast-XDRKCR65.js → forecast-HFCRTFIF.js} +2 -2
  23. package/dist/{google-UJ4YJGTJ.js → google-TOO3FDZ6.js} +22 -22
  24. package/dist/{intent-OMSJPOJZ.js → intent-PU3XUDLL.js} +2 -2
  25. package/dist/{keyword-gap-3P72T4NV.js → keyword-gap-B6T7ZFTZ.js} +2 -2
  26. package/dist/{keyword-metrics-DDY2RDYK.js → keyword-metrics-CXGNLERO.js} +2 -2
  27. package/dist/{keywords-for-site-TFHOM23P.js → keywords-for-site-OYMDRMMX.js} +2 -2
  28. package/dist/{languages-WWKMRZCH.js → languages-ETKG5WM3.js} +2 -2
  29. package/dist/{locations-P3CUBKYZ.js → locations-VPZYTFVK.js} +2 -2
  30. package/dist/{metrics-PSXIV4QS.js → metrics-J3WD2CUD.js} +2 -2
  31. package/dist/{query-PC7V3OFF.js → query-ZYG5RCCW.js} +5 -5
  32. package/dist/{relevant-pages-3CCAQA5Y.js → relevant-pages-7U6I2WPM.js} +2 -2
  33. package/dist/{research-M6LXE3VX.js → research-3BWJCS7N.js} +8 -8
  34. package/dist/{themes-BGBDYQQK.js → themes-C4PPU427.js} +2 -2
  35. package/dist/{url-options-GJ7ZIUPD.js → url-options-C3ZOWN2L.js} +3 -3
  36. package/package.json +1 -1
  37. package/dist/accounts-HJAQTVDA.js.map +0 -1
  38. package/dist/changes-OIVPZJHE.js.map +0 -1
  39. package/dist/chunk-6HPTF5YS.js +0 -857
  40. package/dist/chunk-6HPTF5YS.js.map +0 -1
  41. package/dist/chunk-JPK34QPS.js.map +0 -1
  42. package/dist/chunk-PQ7RLE43.js.map +0 -1
  43. package/dist/chunk-SNWFM2DB.js.map +0 -1
  44. /package/dist/{account-reads-T6GTSAXE.js.map → account-reads-GQWTMLTY.js.map} +0 -0
  45. /package/dist/{ads-WWZK7UYG.js.map → ads-VYMFRGAC.js.map} +0 -0
  46. /package/dist/{advertisers-FME2ABRB.js.map → advertisers-A3Q53R2S.js.map} +0 -0
  47. /package/dist/{autocomplete-CBOYGT73.js.map → autocomplete-OOLUPUIJ.js.map} +0 -0
  48. /package/dist/{conversion-upload-command-J2FIO74Y.js.map → conversion-upload-command-U36QYHUT.js.map} +0 -0
  49. /package/dist/{currency-B5T7TYKD.js.map → currency-XFY2EX72.js.map} +0 -0
  50. /package/dist/{discover-SS3PO5Y6.js.map → discover-J5344ATV.js.map} +0 -0
  51. /package/dist/{draft-EB5ZFDVN.js.map → draft-OIGJUFOH.js.map} +0 -0
  52. /package/dist/{forecast-XDRKCR65.js.map → forecast-HFCRTFIF.js.map} +0 -0
  53. /package/dist/{google-UJ4YJGTJ.js.map → google-TOO3FDZ6.js.map} +0 -0
  54. /package/dist/{intent-OMSJPOJZ.js.map → intent-PU3XUDLL.js.map} +0 -0
  55. /package/dist/{keyword-gap-3P72T4NV.js.map → keyword-gap-B6T7ZFTZ.js.map} +0 -0
  56. /package/dist/{keyword-metrics-DDY2RDYK.js.map → keyword-metrics-CXGNLERO.js.map} +0 -0
  57. /package/dist/{keywords-for-site-TFHOM23P.js.map → keywords-for-site-OYMDRMMX.js.map} +0 -0
  58. /package/dist/{languages-WWKMRZCH.js.map → languages-ETKG5WM3.js.map} +0 -0
  59. /package/dist/{locations-P3CUBKYZ.js.map → locations-VPZYTFVK.js.map} +0 -0
  60. /package/dist/{metrics-PSXIV4QS.js.map → metrics-J3WD2CUD.js.map} +0 -0
  61. /package/dist/{query-PC7V3OFF.js.map → query-ZYG5RCCW.js.map} +0 -0
  62. /package/dist/{relevant-pages-3CCAQA5Y.js.map → relevant-pages-7U6I2WPM.js.map} +0 -0
  63. /package/dist/{research-M6LXE3VX.js.map → research-3BWJCS7N.js.map} +0 -0
  64. /package/dist/{themes-BGBDYQQK.js.map → themes-C4PPU427.js.map} +0 -0
  65. /package/dist/{url-options-GJ7ZIUPD.js.map → url-options-C3ZOWN2L.js.map} +0 -0
@@ -12,7 +12,7 @@ import "./chunk-4NZ47VXO.js";
12
12
  import "./chunk-D5FY4FAK.js";
13
13
  import "./chunk-4H6XLX7E.js";
14
14
  import "./chunk-QALAJNBG.js";
15
- import "./chunk-SNWFM2DB.js";
15
+ import "./chunk-Q2JDS6WL.js";
16
16
  import {
17
17
  getManagerIdForCustomer,
18
18
  writeJsonEnvelope
@@ -216,4 +216,4 @@ export {
216
216
  invoicesCommand,
217
217
  previewsCommand
218
218
  };
219
- //# sourceMappingURL=account-reads-T6GTSAXE.js.map
219
+ //# sourceMappingURL=account-reads-GQWTMLTY.js.map
@@ -5,6 +5,10 @@ import {
5
5
  AVAILABLE_ARG,
6
6
  writeAvailableAccounts
7
7
  } from "./chunk-ZU6JW3ZB.js";
8
+ import {
9
+ serverFailure
10
+ } from "./chunk-NPGPRZW2.js";
11
+ import "./chunk-Q2JDS6WL.js";
8
12
  import {
9
13
  cacheGet,
10
14
  cacheSet,
@@ -14,6 +18,7 @@ import {
14
18
  writeAdsOutput,
15
19
  writeJsonEnvelope
16
20
  } from "./chunk-QYH2UZAK.js";
21
+ import "./chunk-MSMTG6OC.js";
17
22
  import {
18
23
  ApiError,
19
24
  apiGet
@@ -49,7 +54,7 @@ function handleAccountsError(err) {
49
54
  if (needsConnectionFix(err.code, err.message)) {
50
55
  handleConnectionError("google_ads", err.message);
51
56
  }
52
- const envelope = {
57
+ const envelope = serverFailure(err.code, err.message) ?? {
53
58
  ok: false,
54
59
  error: {
55
60
  code: err.code,
@@ -111,4 +116,4 @@ Examples:
111
116
  export {
112
117
  accountsCommand
113
118
  };
114
- //# sourceMappingURL=accounts-HJAQTVDA.js.map
119
+ //# sourceMappingURL=accounts-ZFQYZT3J.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/commands/ads/google/accounts.ts"],"sourcesContent":["import { defineCommand } from \"citty\";\nimport { AVAILABLE_ARG, writeAvailableAccounts } from \"../../../available-accounts.ts\";\nimport { ApiError, apiGet } from \"../../../client.ts\";\nimport { connectedResourceHints } from \"../../../connected-resources.ts\";\nimport { registerSchema } from \"../../../schemas.ts\";\nimport { cacheGet, cacheSet } from \"../cache.ts\";\nimport { writeAdsJson, writeAdsOutput, writeJsonEnvelope } from \"../output.ts\";\nimport type { AdsErrorEnvelope } from \"../types.ts\";\nimport { serverFailure } from \"./error-parser.ts\";\n\nregisterSchema({\n command: \"ads.google.accounts\",\n description:\n \"List the Google Ads accounts this company connected — there can be several, and every one of them is yours to work in. Returns accounts with id, name, access_type (direct or managed), level in the hierarchy, and optional manager_id for managed accounts. Run this first to find account IDs for other commands.\",\n args: {\n \"no-cache\": { type: \"boolean\", description: \"Skip cache, hit API directly\", required: false },\n output: { type: \"string\", description: \"Format: json|csv|md\", required: false, default: \"json\" },\n available: AVAILABLE_ARG,\n },\n});\n\ninterface AccountInfo {\n id: string;\n name: string;\n access_type: \"direct\" | \"managed\";\n level: number;\n manager_id?: string;\n}\n\nimport { handleConnectionError, needsConnectionFix } from \"../../../error-handler.ts\";\n\nfunction accountHints(accounts: AccountInfo[]): string[] {\n return connectedResourceHints({\n plural: \"Google Ads accounts\",\n singular: \"Google Ads account\",\n flag: \"--customer-id\",\n resources: accounts.map((account) => ({ id: account.id, label: account.name })),\n });\n}\n\nfunction handleAccountsError(err: unknown): never {\n if (err instanceof ApiError) {\n if (needsConnectionFix(err.code, err.message)) {\n handleConnectionError(\"google_ads\", err.message);\n }\n const envelope: AdsErrorEnvelope = serverFailure(err.code, err.message) ?? {\n ok: false,\n error: {\n code: err.code,\n message: err.message,\n fix: {\n action: \"reject\",\n explanation: err.message,\n },\n retryable: false,\n },\n };\n writeAdsJson(envelope);\n process.exit(1);\n }\n writeAdsJson({ ok: false, error: { code: \"NETWORK_ERROR\", message: \"Unexpected error\" } });\n process.exit(1);\n}\n\nexport const accountsCommand = defineCommand({\n meta: {\n name: \"accounts\",\n description: `List accessible Google Ads accounts. Returns customer IDs needed for all other commands.\n\nExamples:\n baker ads google accounts\n baker ads google accounts --available # every account the login reaches, selected or not`,\n },\n args: {\n \"no-cache\": { type: \"boolean\", description: \"Skip cache\", required: false },\n output: { type: \"string\", description: \"Format: json|csv|md\", required: false, default: \"json\" },\n available: AVAILABLE_ARG,\n },\n run: async ({ args }) => {\n if (args.available) return writeAvailableAccounts(\"google-ads\", handleAccountsError);\n const useCache = !args[\"no-cache\"];\n\n if (useCache) {\n const cached = cacheGet<AccountInfo[]>(\"accounts\", \"list\");\n if (cached) {\n writeJsonEnvelope({ ok: true, data: cached.data, cached: true, hints: accountHints(cached.data) });\n return;\n }\n }\n\n try {\n const params = !useCache ? { \"skip-cache\": \"true\" } : undefined;\n const data = await apiGet<AccountInfo[]>(\"/api/ads/google/accounts\", params);\n if (useCache) {\n cacheSet(\"accounts\", \"list\", data, 60 * 60 * 1000);\n }\n const format = (args.output as string) || \"json\";\n if (format !== \"json\") {\n writeAdsOutput(data as unknown as Array<Record<string, unknown>>, format);\n return;\n }\n writeJsonEnvelope({ ok: true, data, hints: accountHints(data) });\n } catch (err) {\n handleAccountsError(err);\n }\n },\n});\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,SAAS,qBAAqB;AAU9B,eAAe;AAAA,EACb,SAAS;AAAA,EACT,aACE;AAAA,EACF,MAAM;AAAA,IACJ,YAAY,EAAE,MAAM,WAAW,aAAa,gCAAgC,UAAU,MAAM;AAAA,IAC5F,QAAQ,EAAE,MAAM,UAAU,aAAa,uBAAuB,UAAU,OAAO,SAAS,OAAO;AAAA,IAC/F,WAAW;AAAA,EACb;AACF,CAAC;AAYD,SAAS,aAAa,UAAmC;AACvD,SAAO,uBAAuB;AAAA,IAC5B,QAAQ;AAAA,IACR,UAAU;AAAA,IACV,MAAM;AAAA,IACN,WAAW,SAAS,IAAI,CAAC,aAAa,EAAE,IAAI,QAAQ,IAAI,OAAO,QAAQ,KAAK,EAAE;AAAA,EAChF,CAAC;AACH;AAEA,SAAS,oBAAoB,KAAqB;AAChD,MAAI,eAAe,UAAU;AAC3B,QAAI,mBAAmB,IAAI,MAAM,IAAI,OAAO,GAAG;AAC7C,4BAAsB,cAAc,IAAI,OAAO;AAAA,IACjD;AACA,UAAM,WAA6B,cAAc,IAAI,MAAM,IAAI,OAAO,KAAK;AAAA,MACzE,IAAI;AAAA,MACJ,OAAO;AAAA,QACL,MAAM,IAAI;AAAA,QACV,SAAS,IAAI;AAAA,QACb,KAAK;AAAA,UACH,QAAQ;AAAA,UACR,aAAa,IAAI;AAAA,QACnB;AAAA,QACA,WAAW;AAAA,MACb;AAAA,IACF;AACA,iBAAa,QAAQ;AACrB,YAAQ,KAAK,CAAC;AAAA,EAChB;AACA,eAAa,EAAE,IAAI,OAAO,OAAO,EAAE,MAAM,iBAAiB,SAAS,mBAAmB,EAAE,CAAC;AACzF,UAAQ,KAAK,CAAC;AAChB;AAEO,IAAM,kBAAkB,cAAc;AAAA,EAC3C,MAAM;AAAA,IACJ,MAAM;AAAA,IACN,aAAa;AAAA;AAAA;AAAA;AAAA;AAAA,EAKf;AAAA,EACA,MAAM;AAAA,IACJ,YAAY,EAAE,MAAM,WAAW,aAAa,cAAc,UAAU,MAAM;AAAA,IAC1E,QAAQ,EAAE,MAAM,UAAU,aAAa,uBAAuB,UAAU,OAAO,SAAS,OAAO;AAAA,IAC/F,WAAW;AAAA,EACb;AAAA,EACA,KAAK,OAAO,EAAE,KAAK,MAAM;AACvB,QAAI,KAAK,UAAW,QAAO,uBAAuB,cAAc,mBAAmB;AACnF,UAAM,WAAW,CAAC,KAAK,UAAU;AAEjC,QAAI,UAAU;AACZ,YAAM,SAAS,SAAwB,YAAY,MAAM;AACzD,UAAI,QAAQ;AACV,0BAAkB,EAAE,IAAI,MAAM,MAAM,OAAO,MAAM,QAAQ,MAAM,OAAO,aAAa,OAAO,IAAI,EAAE,CAAC;AACjG;AAAA,MACF;AAAA,IACF;AAEA,QAAI;AACF,YAAM,SAAS,CAAC,WAAW,EAAE,cAAc,OAAO,IAAI;AACtD,YAAM,OAAO,MAAM,OAAsB,4BAA4B,MAAM;AAC3E,UAAI,UAAU;AACZ,iBAAS,YAAY,QAAQ,MAAM,KAAK,KAAK,GAAI;AAAA,MACnD;AACA,YAAM,SAAU,KAAK,UAAqB;AAC1C,UAAI,WAAW,QAAQ;AACrB,uBAAe,MAAmD,MAAM;AACxE;AAAA,MACF;AACA,wBAAkB,EAAE,IAAI,MAAM,MAAM,OAAO,aAAa,IAAI,EAAE,CAAC;AAAA,IACjE,SAAS,KAAK;AACZ,0BAAoB,GAAG;AAAA,IACzB;AAAA,EACF;AACF,CAAC;","names":[]}
@@ -33,7 +33,7 @@ Examples:
33
33
  Full guides: __tooling__/docs/tools/baker/ads-<platform>.md (google|meta|linkedin|x|chatgpt|performance|terms)`
34
34
  },
35
35
  subCommands: {
36
- google: () => import("./google-UJ4YJGTJ.js").then((m) => m.googleCommand),
36
+ google: () => import("./google-TOO3FDZ6.js").then((m) => m.googleCommand),
37
37
  meta: () => import("./meta-XUHI3E3L.js").then((m) => m.metaCommand),
38
38
  linkedin: () => import("./linkedin-6NRMXGBT.js").then((m) => m.linkedinCommand),
39
39
  x: () => import("./x-QX4JZDM4.js").then((m) => m.xCommand),
@@ -45,4 +45,4 @@ Full guides: __tooling__/docs/tools/baker/ads-<platform>.md (google|meta|linkedi
45
45
  export {
46
46
  adsCommand
47
47
  };
48
- //# sourceMappingURL=ads-WWZK7UYG.js.map
48
+ //# sourceMappingURL=ads-VYMFRGAC.js.map
@@ -12,7 +12,7 @@ import {
12
12
  buildResearchQueryContext,
13
13
  warnDefaults
14
14
  } from "./chunk-FVDC6AKZ.js";
15
- import "./chunk-SNWFM2DB.js";
15
+ import "./chunk-Q2JDS6WL.js";
16
16
  import "./chunk-MVTPNWRQ.js";
17
17
  import "./chunk-QV6AST5T.js";
18
18
  import "./chunk-WCU362Z6.js";
@@ -120,4 +120,4 @@ Examples:
120
120
  export {
121
121
  advertisersCommand
122
122
  };
123
- //# sourceMappingURL=advertisers-FME2ABRB.js.map
123
+ //# sourceMappingURL=advertisers-A3Q53R2S.js.map
@@ -12,7 +12,7 @@ import {
12
12
  buildResearchQueryContext,
13
13
  warnDefaults
14
14
  } from "./chunk-FVDC6AKZ.js";
15
- import "./chunk-SNWFM2DB.js";
15
+ import "./chunk-Q2JDS6WL.js";
16
16
  import "./chunk-MVTPNWRQ.js";
17
17
  import "./chunk-QV6AST5T.js";
18
18
  import "./chunk-WCU362Z6.js";
@@ -111,4 +111,4 @@ Examples:
111
111
  export {
112
112
  autocompleteCommand
113
113
  };
114
- //# sourceMappingURL=autocomplete-CBOYGT73.js.map
114
+ //# sourceMappingURL=autocomplete-OOLUPUIJ.js.map
@@ -1,15 +1,17 @@
1
1
  import {
2
2
  getFieldDescriptions
3
- } from "./chunk-JPK34QPS.js";
3
+ } from "./chunk-2L24FULD.js";
4
4
  import {
5
5
  cappedChangesHint,
6
6
  parseApiError,
7
7
  resolveChangesWindow
8
- } from "./chunk-6HPTF5YS.js";
8
+ } from "./chunk-NPGPRZW2.js";
9
9
  import {
10
+ CHANGES_MAX_LIMIT,
11
+ CHANGE_RESOURCE_TYPES,
10
12
  CHANGE_SCOPE_MAX_DAYS,
11
13
  DEFAULT_CHANGE_DAYS
12
- } from "./chunk-SNWFM2DB.js";
14
+ } from "./chunk-Q2JDS6WL.js";
13
15
  import {
14
16
  getManagerIdForCustomer,
15
17
  resolveCustomerId,
@@ -54,12 +56,12 @@ registerSchema({
54
56
  },
55
57
  "resource-type": {
56
58
  type: "string",
57
- description: "Filter by type: CAMPAIGN, CAMPAIGN_CRITERION, AD_GROUP, AD_GROUP_AD, AD_GROUP_CRITERION. CAMPAIGN_CRITERION covers campaign-level negatives, location and audience exclusions.",
59
+ description: `Filter by type: ${CHANGE_RESOURCE_TYPES.join(", ")}. CAMPAIGN_CRITERION covers campaign-level negatives, location and audience exclusions.`,
58
60
  required: false
59
61
  },
60
62
  limit: {
61
63
  type: "string",
62
- description: "Max change events (default: 50, max: 1000). A read that comes back exactly this full has older changes behind it, and a hint says so.",
64
+ description: `Max change events (default: 50, max: ${CHANGES_MAX_LIMIT}). A read that comes back exactly this full has older changes behind it, and a hint says so.`,
63
65
  required: false,
64
66
  default: 50
65
67
  },
@@ -88,27 +90,30 @@ Examples:
88
90
  "customer-id": { type: "string", description: "Google Ads customer ID", required: false },
89
91
  days: { type: "string", description: `Lookback days (default ${DEFAULT_CHANGE_DAYS})`, required: false },
90
92
  scope: { type: "string", description: "detail (30 days) or summary (90 days)", required: false },
91
- "resource-type": { type: "string", description: "Filter by resource type", required: false },
92
- limit: { type: "string", description: "Max results (default 50)", required: false },
93
+ "resource-type": { type: "string", description: `One of ${CHANGE_RESOURCE_TYPES.join(", ")}`, required: false },
94
+ limit: { type: "string", description: `Max results (default 50, max ${CHANGES_MAX_LIMIT})`, required: false },
93
95
  "no-cache": { type: "boolean", description: "Skip cache, hit API directly", required: false },
94
96
  output: { type: "string", description: "Format: json|csv|jsonl|md", required: false, default: "json" }
95
97
  },
96
98
  run: async ({ args }) => {
97
99
  const customerId = await resolveCustomerId(args);
100
+ const limit = args.limit ? Number(args.limit) : 50;
101
+ const resourceType = args["resource-type"];
98
102
  const window = resolveChangesWindow({
99
103
  days: args.days ? Number(args.days) : void 0,
100
- scope: args.scope
104
+ scope: args.scope,
105
+ limit,
106
+ resourceType
101
107
  });
102
108
  if (!window.ok) {
103
109
  writeJsonEnvelope({ ok: false, error: { ...window.error, retryable: false } });
104
110
  process.exit(1);
105
111
  return;
106
112
  }
107
- const limit = args.limit ? Number(args.limit) : 50;
108
113
  const body = { customerId, days: window.days, scope: window.scope, limit };
109
114
  const managerId = getManagerIdForCustomer(customerId);
110
115
  if (managerId) body.managerId = managerId;
111
- if (args["resource-type"]) body.resourceType = args["resource-type"];
116
+ if (resourceType) body.resourceType = resourceType;
112
117
  if (args["no-cache"]) body.skipCache = true;
113
118
  try {
114
119
  const data = await apiPost("/api/ads/google/changes", body);
@@ -140,4 +145,4 @@ Examples:
140
145
  export {
141
146
  changesCommand
142
147
  };
143
- //# sourceMappingURL=changes-OIVPZJHE.js.map
148
+ //# sourceMappingURL=changes-MVCX3GLE.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/commands/ads/google/changes.ts"],"sourcesContent":["import { defineCommand } from \"citty\";\nimport { ApiError, apiPost } from \"../../../client.ts\";\nimport { registerSchema } from \"../../../schemas.ts\";\nimport { getManagerIdForCustomer } from \"../cache.ts\";\nimport { getFieldDescriptions } from \"../field-descriptions.ts\";\nimport { resolveCustomerId, writeAdsJson, writeAdsOutput, writeJsonEnvelope } from \"../output.ts\";\nimport {\n CHANGE_RESOURCE_TYPES,\n CHANGE_SCOPE_MAX_DAYS,\n CHANGES_MAX_LIMIT,\n cappedChangesHint,\n DEFAULT_CHANGE_DAYS,\n resolveChangesWindow,\n} from \"./changes-window.ts\";\nimport { parseApiError } from \"./error-parser.ts\";\n\nregisterSchema({\n command: \"ads.google.changes\",\n description:\n \"Read the account's change history. --scope detail (default) shows what changed and to what value, back 30 days; --scope summary shows which resources changed, back 90 days.\",\n args: {\n \"customer-id\": {\n type: \"string\",\n description: \"Google Ads customer ID (10 digits, no dashes). Falls back to BAKER_GOOGLE_ADS_CUSTOMER_ID env var.\",\n required: false,\n },\n days: {\n type: \"string\",\n description: `Lookback days (default: ${DEFAULT_CHANGE_DAYS}). Max ${CHANGE_SCOPE_MAX_DAYS.detail} with --scope detail, ${CHANGE_SCOPE_MAX_DAYS.summary} with --scope summary. Google keeps nothing older behind any API.`,\n required: false,\n default: DEFAULT_CHANGE_DAYS,\n },\n scope: {\n type: \"string\",\n description: `detail|summary (default: detail). detail = old/new values plus who made the change, ${CHANGE_SCOPE_MAX_DAYS.detail} days. summary = which resources were added, changed or removed, ${CHANGE_SCOPE_MAX_DAYS.summary} days, without values.`,\n required: false,\n default: \"detail\",\n },\n \"resource-type\": {\n type: \"string\",\n description: `Filter by type: ${CHANGE_RESOURCE_TYPES.join(\", \")}. CAMPAIGN_CRITERION covers campaign-level negatives, location and audience exclusions.`,\n required: false,\n },\n limit: {\n type: \"string\",\n description: `Max change events (default: 50, max: ${CHANGES_MAX_LIMIT}). A read that comes back exactly this full has older changes behind it, and a hint says so.`,\n required: false,\n default: 50,\n },\n output: { type: \"string\", description: \"Format: json|csv|jsonl|md\", required: false, default: \"json\" },\n \"no-cache\": { type: \"boolean\", description: \"Skip cache, hit API directly\", required: false },\n },\n});\n\nexport const changesCommand = defineCommand({\n meta: {\n name: \"changes\",\n description: `Read the change history of a Google Ads account.\n\nGoogle keeps account history in two layers, and neither reaches past 90 days:\n --scope detail (default) what changed, old → new value, who changed it — last ${CHANGE_SCOPE_MAX_DAYS.detail} days\n --scope summary which resources were added, changed or removed — last ${CHANGE_SCOPE_MAX_DAYS.summary} days\n\nFor anything older, infer it from performance instead — a monthly spend trend per\ncampaign shows what started or stopped, just not who did it.\n\nExamples:\n baker ads google changes --customer-id 1234567890\n baker ads google changes --customer-id 1234567890 --days 14 --resource-type CAMPAIGN\n baker ads google changes --customer-id 1234567890 --days 60 --scope summary`,\n },\n args: {\n \"customer-id\": { type: \"string\", description: \"Google Ads customer ID\", required: false },\n days: { type: \"string\", description: `Lookback days (default ${DEFAULT_CHANGE_DAYS})`, required: false },\n scope: { type: \"string\", description: \"detail (30 days) or summary (90 days)\", required: false },\n \"resource-type\": { type: \"string\", description: `One of ${CHANGE_RESOURCE_TYPES.join(\", \")}`, required: false },\n limit: { type: \"string\", description: `Max results (default 50, max ${CHANGES_MAX_LIMIT})`, required: false },\n \"no-cache\": { type: \"boolean\", description: \"Skip cache, hit API directly\", required: false },\n output: { type: \"string\", description: \"Format: json|csv|jsonl|md\", required: false, default: \"json\" },\n },\n run: async ({ args }) => {\n const customerId = await resolveCustomerId(args);\n\n const limit = args.limit ? Number(args.limit) : 50;\n const resourceType = args[\"resource-type\"] as string | undefined;\n const window = resolveChangesWindow({\n days: args.days ? Number(args.days) : undefined,\n scope: args.scope as string | undefined,\n limit,\n resourceType,\n });\n if (!window.ok) {\n writeJsonEnvelope({ ok: false, error: { ...window.error, retryable: false } });\n process.exit(1);\n return;\n }\n\n const body: Record<string, unknown> = { customerId, days: window.days, scope: window.scope, limit };\n const managerId = getManagerIdForCustomer(customerId);\n if (managerId) body.managerId = managerId;\n if (resourceType) body.resourceType = resourceType;\n if (args[\"no-cache\"]) body.skipCache = true;\n\n try {\n const data = await apiPost<Array<Record<string, unknown>>>(\"/api/ads/google/changes\", body);\n const format = (args.output as string) || \"json\";\n const capped = cappedChangesHint(data, limit, window.days);\n\n if (format !== \"json\") {\n writeAdsOutput(data, format, undefined, capped ? [capped] : []);\n return;\n }\n\n const first = data[0];\n const fields = first ? Object.keys(first) : [];\n const fieldDescs = getFieldDescriptions(fields);\n writeJsonEnvelope({\n ok: true,\n data,\n fields: fieldDescs,\n hints: capped ? [capped, ...window.hints] : window.hints,\n });\n } catch (err) {\n if (err instanceof ApiError) {\n writeAdsJson(parseApiError(err.message, \"\", customerId, err.code));\n process.exit(1);\n }\n writeAdsJson({ ok: false, error: { code: \"NETWORK_ERROR\", message: \"Unexpected error\" } });\n process.exit(1);\n }\n },\n});\n"],"mappings":"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","names":[]}
@@ -19,8 +19,8 @@ var FIELD_DESCRIPTIONS = {
19
19
  "campaign.maximize_conversion_value.target_roas": "Target ROAS as a ratio when held inside a Maximize Conversion Value strategy",
20
20
  "campaign.target_cpc.target_cpc_micros": "Target CPC in micros on a Demand Gen Target CPC strategy. Governed by the same August 2026 change as Target CPA/ROAS: on a budget-limited campaign it is what delivery moves toward, not a ceiling.",
21
21
  "ad_group.target_cpc_micros": "Ad-group override of the campaign's Demand Gen target CPC, in micros",
22
- "campaign.start_date": "Campaign start date (YYYY-MM-DD)",
23
- "campaign.end_date": "Campaign end date (YYYY-MM-DD), blank if ongoing",
22
+ "campaign.start_date_time": "Campaign start, in the account's time zone (YYYY-MM-DD HH:MM:SS)",
23
+ "campaign.end_date_time": "Campaign end, in the account's time zone (YYYY-MM-DD HH:MM:SS); blank if ongoing",
24
24
  // Campaign budget
25
25
  "campaign_budget.amount_micros": "Daily budget in micros (\xF7 1,000,000 for actual currency)",
26
26
  // Ad group
@@ -131,4 +131,4 @@ function getFieldDescriptions(fields) {
131
131
  export {
132
132
  getFieldDescriptions
133
133
  };
134
- //# sourceMappingURL=chunk-JPK34QPS.js.map
134
+ //# sourceMappingURL=chunk-2L24FULD.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/commands/ads/field-descriptions.ts"],"sourcesContent":["const FIELD_DESCRIPTIONS: Record<string, string> = {\n // Customer\n \"customer.id\": \"Google Ads account ID\",\n \"customer.descriptive_name\": \"Account display name\",\n \"customer.currency_code\": \"Account currency (e.g. USD, EUR)\",\n \"customer.time_zone\": \"Account timezone (e.g. America/New_York)\",\n\n // Campaign\n \"campaign.id\": \"Unique campaign identifier\",\n \"campaign.name\": \"Campaign display name\",\n \"campaign.status\": \"ENABLED, PAUSED, or REMOVED\",\n \"campaign.advertising_channel_type\": \"Campaign type: SEARCH, DISPLAY, SHOPPING, VIDEO, PERFORMANCE_MAX, DEMAND_GEN\",\n \"campaign.bidding_strategy_type\": \"Bidding strategy (TARGET_CPA, TARGET_ROAS, MAXIMIZE_CONVERSIONS, etc.)\",\n \"campaign.primary_status\":\n \"Whether the campaign can serve: ELIGIBLE, PAUSED, REMOVED, ENDED, PENDING, MISCONFIGURED, LIMITED, LEARNING, NOT_ELIGIBLE. Unlike the keyword equivalent, LIMITED is a real value here — but Google publishes no reason-to-status mapping, so decide 'Limited by budget' from primary_status_reasons, not from this field.\",\n \"campaign.primary_status_reasons\":\n \"Why the campaign is limited or not serving (repeated field — an array). BUDGET_CONSTRAINED is 'Limited by budget'; BUDGET_MISCONFIGURED is a broken budget setting, not a cap. Since 17 Aug 2026 a budget-limited campaign on a target-based strategy delivers toward its target instead of toward its recent actual cost, so BUDGET_CONSTRAINED is the flag that says a target above actual now costs money. BIDDING_STRATEGY_LEARNING means the bidding system is still adjusting to a change.\",\n \"campaign.target_cpa.target_cpa_micros\": \"Target CPA in micros on a standalone Target CPA strategy\",\n \"campaign.maximize_conversions.target_cpa_micros\":\n \"Target CPA in micros when the target is held inside a Maximize Conversions strategy — the shape Google uses on Search\",\n \"campaign.target_roas.target_roas\": \"Target ROAS as a ratio (2.5 = 250%) on a standalone Target ROAS strategy\",\n \"campaign.maximize_conversion_value.target_roas\":\n \"Target ROAS as a ratio when held inside a Maximize Conversion Value strategy\",\n \"campaign.target_cpc.target_cpc_micros\":\n \"Target CPC in micros on a Demand Gen Target CPC strategy. Governed by the same August 2026 change as Target CPA/ROAS: on a budget-limited campaign it is what delivery moves toward, not a ceiling.\",\n \"ad_group.target_cpc_micros\": \"Ad-group override of the campaign's Demand Gen target CPC, in micros\",\n \"campaign.start_date_time\": \"Campaign start, in the account's time zone (YYYY-MM-DD HH:MM:SS)\",\n \"campaign.end_date_time\": \"Campaign end, in the account's time zone (YYYY-MM-DD HH:MM:SS); blank if ongoing\",\n\n // Campaign budget\n \"campaign_budget.amount_micros\": \"Daily budget in micros (÷ 1,000,000 for actual currency)\",\n\n // Ad group\n \"ad_group.id\": \"Unique ad group identifier\",\n \"ad_group.name\": \"Ad group display name\",\n \"ad_group.status\": \"ENABLED, PAUSED, or REMOVED\",\n \"ad_group.type\": \"Ad group type (SEARCH_STANDARD, DISPLAY_STANDARD, etc.)\",\n\n // Ad group ad\n \"ad_group_ad.ad.id\": \"Unique ad identifier\",\n \"ad_group_ad.ad.final_urls\": \"Landing page URLs (array)\",\n \"ad_group_ad.ad.type\": \"Ad format (RESPONSIVE_SEARCH_AD, etc.)\",\n \"ad_group_ad.status\": \"ENABLED, PAUSED, or REMOVED\",\n \"ad_group_ad.ad.responsive_search_ad.headlines\": \"Ad headline variations (array)\",\n \"ad_group_ad.ad.responsive_search_ad.descriptions\": \"Ad description variations (array)\",\n\n // Keywords\n \"ad_group_criterion.criterion_id\":\n \"Keyword criterion id — derived from text + match type, so every copy of the same keyword across ad groups shares it. Pair with ad_group.id to identify one copy (writes take customers/<cid>/adGroupCriteria/<adGroupId>~<criterionId>)\",\n \"ad_group_criterion.keyword.text\": \"The keyword text\",\n \"ad_group_criterion.keyword.match_type\": \"EXACT, PHRASE, or BROAD\",\n \"ad_group_criterion.status\": \"ENABLED, PAUSED, or REMOVED\",\n \"ad_group_criterion.quality_info.quality_score\": \"Quality score 1-10 (higher is better)\",\n \"ad_group_criterion.quality_info.creative_quality_score\":\n \"Ad relevance bucket: ABOVE_AVERAGE, AVERAGE, BELOW_AVERAGE\",\n \"ad_group_criterion.quality_info.post_click_quality_score\":\n \"Landing page experience bucket: ABOVE_AVERAGE, AVERAGE, BELOW_AVERAGE\",\n \"ad_group_criterion.quality_info.search_predicted_ctr\":\n \"Expected click-through rate bucket: ABOVE_AVERAGE, AVERAGE, BELOW_AVERAGE\",\n \"ad_group_criterion.primary_status\":\n \"Whether the keyword can serve: ELIGIBLE, PAUSED, REMOVED, PENDING, NOT_ELIGIBLE. There is no LIMITED value — Google Ads' 'Eligible (Limited)' is ELIGIBLE plus a limiting entry in primary_status_reasons, so read both.\",\n \"ad_group_criterion.primary_status_reasons\":\n \"Why the keyword is in that status (array). AD_GROUP_CRITERION_BELOW_FIRST_PAGE_BID and AD_GROUP_CRITERION_LOW_QUALITY are the two that render as '(Limited)'; the CAMPAIGN_*/AD_GROUP_* entries just restate a paused or removed parent.\",\n \"ad_group_criterion.system_serving_status\": \"ELIGIBLE or RARELY_SERVED (too few searches to show)\",\n \"ad_group_criterion.approval_status\": \"Ad-approval state: APPROVED, DISAPPROVED, PENDING_REVIEW, UNDER_REVIEW\",\n \"ad_group_criterion.disapproval_reasons\": \"Policy reasons the keyword was disapproved (array)\",\n \"ad_group_criterion.cpc_bid_micros\": \"Max CPC bid set on this keyword, in micros (÷ 1,000,000)\",\n \"ad_group_criterion.effective_cpc_bid_micros\":\n \"Max CPC actually in force, in micros (÷ 1,000,000) — the keyword's own bid, or the ad group's when the keyword has none\",\n \"ad_group_criterion.effective_cpc_bid_source\":\n \"Where the effective max CPC comes from: ADGROUP, CRITERION, or CAMPAIGN_BIDDING_STRATEGY\",\n \"ad_group_criterion.position_estimates.first_page_cpc_micros\":\n \"Bid estimated to reach the first page of results, in micros (÷ 1,000,000). The number in Google Ads' 'Below first page bid (€3.63)' label — compare it against effective_cpc_bid_micros.\",\n \"ad_group_criterion.position_estimates.top_of_page_cpc_micros\":\n \"Bid estimated to reach the top of the first page, in micros (÷ 1,000,000)\",\n \"ad_group_criterion.position_estimates.first_position_cpc_micros\":\n \"Bid estimated to reach the first ad position, in micros (÷ 1,000,000)\",\n\n // Search terms\n \"search_term_view.search_term\": \"Actual user search query that triggered the ad\",\n \"search_term_view.status\": \"Whether the search term is added, excluded, or none\",\n\n // Metrics\n \"metrics.impressions\": \"Times ads were shown (integer)\",\n \"metrics.clicks\": \"Total clicks (integer)\",\n \"metrics.cost_micros\":\n \"Total cost in micros (÷ 1,000,000 for actual currency). Use 'currency' command for account currency.\",\n \"metrics.cost\": \"Total cost in actual currency (already converted from micros)\",\n \"metrics.conversions\": \"Conversion count (can be fractional from attribution models)\",\n \"metrics.conversions_value\": \"Revenue from conversions in actual currency\",\n \"metrics.all_conversions\": \"All conversions including view-through\",\n \"metrics.all_conversions_value\": \"Total attributed revenue including view-through\",\n \"metrics.ctr\": \"Click-through rate as decimal (0.05 = 5%)\",\n \"metrics.average_cpc\": \"Average cost per click in micros (÷ 1,000,000)\",\n \"metrics.average_cpm\": \"Average cost per 1000 impressions in micros (÷ 1,000,000)\",\n \"metrics.search_impression_share\": \"Impression share as decimal (0.0–1.0)\",\n \"metrics.search_budget_lost_impression_share\":\n \"Share of impressions lost because the budget ran out, as a decimal (0.0–1.0). The corroborating signal for a 'Limited by budget' campaign — a high value here alongside a target above actual cost per conversion is the combination that now raises spend.\",\n \"metrics.cost_per_conversion\":\n \"Actual cost per conversion in micros (÷ 1,000,000) — compare against the campaign's target\",\n \"metrics.interaction_rate\": \"Interaction rate as decimal\",\n \"metrics.video_views\": \"Total video views (integer)\",\n \"metrics.video_view_rate\": \"Video view rate as decimal\",\n\n // Segments\n \"segments.date\": \"Date (YYYY-MM-DD format)\",\n \"segments.device\": \"DESKTOP, MOBILE, TABLET, or OTHER\",\n \"segments.day_of_week\": \"Day of week (MONDAY, TUESDAY, etc.)\",\n \"segments.ad_network_type\": \"Network: SEARCH, SEARCH_PARTNERS, CONTENT, YOUTUBE_SEARCH, etc.\",\n \"segments.product_title\": \"Shopping product title\",\n \"segments.product_item_id\": \"Shopping product ID\",\n \"segments.product_brand\": \"Shopping product brand\",\n \"segments.product_type_l1\": \"Shopping product type (level 1)\",\n\n // Assets\n \"asset.type\": \"Asset type: IMAGE, YOUTUBE_VIDEO, TEXT, LEAD_FORM, etc.\",\n \"asset.text_asset.text\": \"Text asset content\",\n \"asset.image_asset.full_size.url\": \"Full-size image URL\",\n \"asset.youtube_video_asset.youtube_video_id\": \"YouTube video ID\",\n \"asset.youtube_video_asset.youtube_video_title\": \"YouTube video title\",\n \"asset_group.name\": \"Asset group display name\",\n \"asset_group.status\": \"ENABLED, PAUSED, or REMOVED\",\n \"asset_group_asset.field_type\": \"Where the asset is used (HEADLINE, DESCRIPTION, LOGO, etc.)\",\n \"asset_group_asset.primary_status\":\n \"Whether the asset serves: ELIGIBLE, LIMITED, NOT_ELIGIBLE, PAUSED, REMOVED, PENDING\",\n \"asset_group_asset.primary_status_reasons\":\n \"Why the asset is not fully serving (policy, pending review, paused parent …)\",\n\n // Change events (field-level detail, last 30 days)\n \"change_event.change_date_time\": \"When the change occurred (ISO 8601)\",\n \"change_event.change_resource_type\": \"What was changed: CAMPAIGN, AD_GROUP, AD, etc.\",\n \"change_event.resource_change_operation\": \"CREATE, UPDATE, or REMOVE\",\n \"change_event.user_email\": \"Email of user who made the change\",\n\n // Change status (which resources changed, last 90 days — no values, no user)\n \"change_status.last_change_date_time\": \"When this resource last changed (ISO 8601)\",\n \"change_status.resource_type\": \"What changed: CAMPAIGN, AD_GROUP, AD_GROUP_AD, AD_GROUP_CRITERION, etc.\",\n \"change_status.resource_status\": \"ADDED, CHANGED, or REMOVED — not the old or new value\",\n \"change_status.resource_name\": \"Identifier of the changed resource; query it to read its current values\",\n \"change_status.campaign\": \"Resource name of the campaign the change belongs to\",\n \"change_status.ad_group\": \"Resource name of the ad group the change belongs to\",\n \"change_status.ad_group_ad\": \"Resource name of the changed ad\",\n \"change_status.ad_group_criterion\": \"Resource name of the changed keyword or criterion\",\n \"change_status.campaign_criterion\": \"Resource name of the changed campaign-level criterion\",\n\n // Landing pages\n \"landing_page_view.unexpanded_final_url\": \"Landing page URL\",\n\n // Geo\n \"geo_target_constant.name\": \"Geographic location name\",\n \"geo_target_constant.resource_name\": \"Geo target resource identifier\",\n};\n\nexport function getFieldDescriptions(fields: string[]): Record<string, string> {\n const result: Record<string, string> = {};\n for (const field of fields) {\n const desc = FIELD_DESCRIPTIONS[field];\n if (desc) {\n result[field] = desc;\n }\n }\n return result;\n}\n"],"mappings":"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","names":[]}