siluzan-tso-cli 1.1.58-beta.2 → 1.1.58-beta.4

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/index.js CHANGED
@@ -2172,8 +2172,8 @@ function jsonParseReviverStringifyIds(key, value) {
2172
2172
  }
2173
2173
  return value;
2174
2174
  }
2175
- function parseJsonWithStringIds(text2) {
2176
- return JSON.parse(text2, jsonParseReviverStringifyIds);
2175
+ function parseJsonWithStringIds(text3) {
2176
+ return JSON.parse(text3, jsonParseReviverStringifyIds);
2177
2177
  }
2178
2178
  function hasSiluzanAgentCredentials() {
2179
2179
  const apiKey = process.env.SILUZAN_API_KEY?.trim();
@@ -2486,8 +2486,8 @@ function redactSensitive(input) {
2486
2486
  );
2487
2487
  return output;
2488
2488
  }
2489
- function trimDetail(text2, maxLen) {
2490
- const t = text2.trim();
2489
+ function trimDetail(text3, maxLen) {
2490
+ const t = text3.trim();
2491
2491
  if (t.length <= maxLen) return t;
2492
2492
  return `${t.slice(0, maxLen)}\u2026`;
2493
2493
  }
@@ -2533,25 +2533,25 @@ function messageFromJsonValue(value) {
2533
2533
  return void 0;
2534
2534
  }
2535
2535
  function extractHttpErrorDetail(rawBody) {
2536
- const text2 = rawBody?.trim();
2537
- if (!text2) return void 0;
2538
- if (text2.startsWith('"')) {
2536
+ const text3 = rawBody?.trim();
2537
+ if (!text3) return void 0;
2538
+ if (text3.startsWith('"')) {
2539
2539
  try {
2540
- const parsed = JSON.parse(text2);
2540
+ const parsed = JSON.parse(text3);
2541
2541
  if (typeof parsed === "string" && parsed.trim()) return parsed.trim();
2542
2542
  } catch {
2543
2543
  }
2544
2544
  }
2545
- if (text2.startsWith("{") || text2.startsWith("[")) {
2545
+ if (text3.startsWith("{") || text3.startsWith("[")) {
2546
2546
  try {
2547
- const parsed = JSON.parse(text2);
2547
+ const parsed = JSON.parse(text3);
2548
2548
  const msg = messageFromJsonValue(parsed);
2549
2549
  if (msg) return msg;
2550
2550
  } catch {
2551
2551
  }
2552
2552
  }
2553
- if (text2.startsWith("<")) return void 0;
2554
- return text2;
2553
+ if (text3.startsWith("<")) return void 0;
2554
+ return text3;
2555
2555
  }
2556
2556
  function formatHttpError(status, rawBody, opts) {
2557
2557
  const verbose = opts?.verbose === true;
@@ -2568,18 +2568,18 @@ function formatHttpError(status, rawBody, opts) {
2568
2568
  function buildAuthHeaders(config) {
2569
2569
  return buildSiluzanAuthHeaders(config);
2570
2570
  }
2571
- async function parseApiResponse(status, text2, verbose) {
2571
+ async function parseApiResponse(status, text3, verbose) {
2572
2572
  if (status < 200 || status >= 300) {
2573
- throw new Error(formatHttpError(status, text2, { verbose }));
2573
+ throw new Error(formatHttpError(status, text3, { verbose }));
2574
2574
  }
2575
- if (!text2.trim()) return null;
2575
+ if (!text3.trim()) return null;
2576
2576
  try {
2577
- return parseJsonWithStringIds(text2);
2577
+ return parseJsonWithStringIds(text3);
2578
2578
  } catch {
2579
2579
  if (verbose) {
2580
- console.error(` \u26A0\uFE0F \u54CD\u5E94\u975E JSON\uFF0C\u539F\u59CB\u5185\u5BB9\uFF1A${redactSensitive(text2).slice(0, 200)}`);
2580
+ console.error(` \u26A0\uFE0F \u54CD\u5E94\u975E JSON\uFF0C\u539F\u59CB\u5185\u5BB9\uFF1A${redactSensitive(text3).slice(0, 200)}`);
2581
2581
  }
2582
- return text2;
2582
+ return text3;
2583
2583
  }
2584
2584
  }
2585
2585
  function isTransientNetworkError(err) {
@@ -2635,18 +2635,18 @@ async function apiFetchWithHeaders(url, config, options = {}, verbose = false) {
2635
2635
  };
2636
2636
  const body = typeof options.body === "string" ? options.body : void 0;
2637
2637
  const res = await requestWithReadRetry(url, { method, headers: reqHeaders, body });
2638
- const text2 = res.text;
2638
+ const text3 = res.text;
2639
2639
  if (res.status < 200 || res.status >= 300) {
2640
- throw new Error(formatHttpError(res.status, text2, { verbose }));
2640
+ throw new Error(formatHttpError(res.status, text3, { verbose }));
2641
2641
  }
2642
2642
  let data;
2643
- if (!text2.trim()) {
2643
+ if (!text3.trim()) {
2644
2644
  data = null;
2645
2645
  } else {
2646
2646
  try {
2647
- data = parseJsonWithStringIds(text2);
2647
+ data = parseJsonWithStringIds(text3);
2648
2648
  } catch {
2649
- data = text2;
2649
+ data = text3;
2650
2650
  }
2651
2651
  }
2652
2652
  return { data, headers: res.headers };
@@ -3248,9 +3248,9 @@ function pickCompanyId(data) {
3248
3248
  }
3249
3249
  return void 0;
3250
3250
  }
3251
- function parseMeResponse(text2) {
3251
+ function parseMeResponse(text3) {
3252
3252
  try {
3253
- const json = JSON.parse(text2);
3253
+ const json = JSON.parse(text3);
3254
3254
  const data = json?.data && typeof json.data === "object" ? json.data : json;
3255
3255
  const id = pickStr(data, "entityId") ?? pickStr(data, "id") ?? pickStr(data, "userId") ?? pickStr(data, "accountId");
3256
3256
  const email = pickStr(data, "email");
@@ -4397,12 +4397,12 @@ async function apiDeleteRaw(url, config, options = {}) {
4397
4397
  if (res.status < 200 || res.status >= 300) {
4398
4398
  throw new Error(formatHttpError(res.status, res.text, { verbose: options.verbose }));
4399
4399
  }
4400
- const text2 = res.text;
4401
- const value = !text2.trim() ? null : (() => {
4400
+ const text3 = res.text;
4401
+ const value = !text3.trim() ? null : (() => {
4402
4402
  try {
4403
- return JSON.parse(text2);
4403
+ return JSON.parse(text3);
4404
4404
  } catch {
4405
- return text2;
4405
+ return text3;
4406
4406
  }
4407
4407
  })();
4408
4408
  return { value, httpStatus: res.status };
@@ -5440,13 +5440,13 @@ function skippedNoSpendCampaignTypes() {
5440
5440
  };
5441
5441
  }
5442
5442
  function errorCampaignTypes(message) {
5443
- const text2 = message.replace(/\s+/g, " ").trim().slice(0, 180);
5443
+ const text3 = message.replace(/\s+/g, " ").trim().slice(0, 180);
5444
5444
  return {
5445
5445
  campaignTypesStatus: "error",
5446
5446
  campaignTypes: null,
5447
5447
  hasDisplay: null,
5448
5448
  hasPmax: null,
5449
- campaignTypesError: text2 || "unknown error"
5449
+ campaignTypesError: text3 || "unknown error"
5450
5450
  };
5451
5451
  }
5452
5452
  function errorMessage(err) {
@@ -5489,6 +5489,231 @@ var init_campaign_types = __esm({
5489
5489
  }
5490
5490
  });
5491
5491
 
5492
+ // src/commands/ad/_shared.ts
5493
+ function resolveAdAccountId(account, label2 = "-a, --account") {
5494
+ try {
5495
+ return requireNumericMediaCustomerId(account, label2);
5496
+ } catch (e) {
5497
+ console.error(`
5498
+ \u274C ${e instanceof Error ? e.message : String(e)}
5499
+ `);
5500
+ process.exit(1);
5501
+ }
5502
+ }
5503
+ function withResolvedAdAccount(opts) {
5504
+ return { ...opts, account: resolveAdAccountId(opts.account) };
5505
+ }
5506
+ async function findItemInList(listUrl, config, id, verbose) {
5507
+ const res = await apiFetch2(listUrl, config, {}, verbose);
5508
+ const item = (res.data ?? []).find((x) => x["id"] === id);
5509
+ if (!item) {
5510
+ throw new Error(`\u672A\u5728\u5217\u8868\u4E2D\u627E\u5230 id="${id}" \u7684\u6761\u76EE`);
5511
+ }
5512
+ return item;
5513
+ }
5514
+ function toGoogleDate(dateStr, offsetDays) {
5515
+ if (dateStr) return dateStr.replace(/-/g, "/");
5516
+ const d = /* @__PURE__ */ new Date();
5517
+ d.setDate(d.getDate() + offsetDays);
5518
+ return d.toISOString().slice(0, 10).replace(/-/g, "/");
5519
+ }
5520
+ function toDisplayMoney(raw) {
5521
+ if (raw == null) return null;
5522
+ const n = Number(raw);
5523
+ if (!Number.isFinite(n)) return null;
5524
+ return n / 100;
5525
+ }
5526
+ function toCentAmount(displayValue) {
5527
+ if (!Number.isFinite(displayValue)) {
5528
+ throw new Error(`toCentAmount: \u8F93\u5165\u5FC5\u987B\u662F\u6709\u9650\u6570\u5B57\uFF0C\u6536\u5230 ${displayValue}`);
5529
+ }
5530
+ return Math.round(displayValue * 100);
5531
+ }
5532
+ function convertBatchMoneyTreeFromYuan(value) {
5533
+ return convertBatchMoneyTreeFromYuanInner(value);
5534
+ }
5535
+ function convertBatchMoneyTreeFromYuanInner(value) {
5536
+ if (Array.isArray(value)) {
5537
+ return value.map(convertBatchMoneyTreeFromYuanInner);
5538
+ }
5539
+ if (value && typeof value === "object") {
5540
+ const out = {};
5541
+ for (const [key, val] of Object.entries(value)) {
5542
+ if (typeof val === "number" && CAMPAIGN_BATCH_MONEY_KEYS.has(key)) {
5543
+ out[key] = toCentAmount(val);
5544
+ } else {
5545
+ out[key] = convertBatchMoneyTreeFromYuanInner(val);
5546
+ }
5547
+ }
5548
+ return out;
5549
+ }
5550
+ return value;
5551
+ }
5552
+ function adgroupListUrl(googleApiUrl, account, startDate, endDate, fresh) {
5553
+ const params = new URLSearchParams();
5554
+ params.set("startDate", toGoogleDate(startDate, -30));
5555
+ params.set("endDate", toGoogleDate(endDate, 0));
5556
+ if (fresh) params.set("newest", "true");
5557
+ return `${googleApiUrl}/adgroupnmanagement/v2/list/${account}?${params}`;
5558
+ }
5559
+ function campaignListUrl(googleApiUrl, account, startDate, endDate, fresh) {
5560
+ const params = new URLSearchParams();
5561
+ params.set("startDate", toGoogleDate(startDate, -30));
5562
+ params.set("endDate", toGoogleDate(endDate, 0));
5563
+ if (fresh) params.set("newest", "true");
5564
+ return `${googleApiUrl}/campaignmanagement/v2/list/${account}?${params}`;
5565
+ }
5566
+ function requireGoogleApi(config) {
5567
+ if (!config.googleApiUrl) {
5568
+ console.error(
5569
+ "\n\u274C \u672A\u627E\u5230 Google API \u5730\u5740\u3002\n Google API \u5730\u5740\u4ECE TSO API \u5730\u5740\u81EA\u52A8\u63A8\u5BFC\uFF0C\u4E5F\u53EF\u901A\u8FC7\u73AF\u5883\u53D8\u91CF SILUZAN_GOOGLE_API \u8986\u76D6\u3002\n"
5570
+ );
5571
+ process.exit(1);
5572
+ }
5573
+ return config.googleApiUrl;
5574
+ }
5575
+ var CAMPAIGN_BATCH_MONEY_KEYS;
5576
+ var init_shared = __esm({
5577
+ "src/commands/ad/_shared.ts"() {
5578
+ "use strict";
5579
+ init_auth();
5580
+ init_media_customer_id();
5581
+ CAMPAIGN_BATCH_MONEY_KEYS = /* @__PURE__ */ new Set([
5582
+ "Budget",
5583
+ "MaxCPCAmount",
5584
+ "MaxCpmAmount",
5585
+ "MaxCPVAmount",
5586
+ "TargetCpa_BidingAmount",
5587
+ "TargetSpend_BidCeilingAmount",
5588
+ "TargetCpaAmount",
5589
+ "MaxCPC"
5590
+ ]);
5591
+ }
5592
+ });
5593
+
5594
+ // src/commands/accounts-digest/enabled-campaigns.ts
5595
+ function isPmaxChannel(channel) {
5596
+ return PMAX_CHANNEL.test(String(channel ?? ""));
5597
+ }
5598
+ function isDisplayChannel(channel) {
5599
+ return DISPLAY_CHANNEL.test(String(channel ?? "").trim());
5600
+ }
5601
+ function text(value) {
5602
+ return value == null ? "" : String(value);
5603
+ }
5604
+ function impressionsOf(row) {
5605
+ const n = Number(row["impressions"]);
5606
+ return Number.isFinite(n) ? n : 0;
5607
+ }
5608
+ function summarizeEnabledCampaigns(rows) {
5609
+ const list = Array.isArray(rows) ? rows : [];
5610
+ let enabledCampaignCount = 0;
5611
+ let hasEnabledPmax = false;
5612
+ const zeroImpressionEnabled = [];
5613
+ for (const item of list) {
5614
+ if (!item || typeof item !== "object") continue;
5615
+ const row = item;
5616
+ const status = text(row["statusV2"] || row["status"]).toLowerCase();
5617
+ if (status !== "enabled") continue;
5618
+ const id = text(row["id"]).trim();
5619
+ if (!id) continue;
5620
+ enabledCampaignCount += 1;
5621
+ const channelType = text(row["channelTypeV2"] || row["channelType"]);
5622
+ if (isPmaxChannel(channelType)) hasEnabledPmax = true;
5623
+ if (impressionsOf(row) !== 0) continue;
5624
+ if (isPmaxChannel(channelType) || isDisplayChannel(channelType)) continue;
5625
+ zeroImpressionEnabled.push({
5626
+ id,
5627
+ name: text(row["name"]),
5628
+ channelType
5629
+ });
5630
+ }
5631
+ return {
5632
+ enabledCampaignStatus: "ok",
5633
+ hasEnabledPmax,
5634
+ enabledCampaignCount,
5635
+ zeroImpressionEnabled,
5636
+ enabledCampaignError: null
5637
+ };
5638
+ }
5639
+ function skippedNoSpendEnabledCampaigns() {
5640
+ return {
5641
+ enabledCampaignStatus: "skipped-no-spend",
5642
+ hasEnabledPmax: null,
5643
+ enabledCampaignCount: null,
5644
+ zeroImpressionEnabled: null,
5645
+ enabledCampaignError: null
5646
+ };
5647
+ }
5648
+ function errorEnabledCampaigns(message) {
5649
+ const textMessage = message.replace(/\s+/g, " ").trim().slice(0, 180);
5650
+ return {
5651
+ enabledCampaignStatus: "error",
5652
+ hasEnabledPmax: null,
5653
+ enabledCampaignCount: null,
5654
+ zeroImpressionEnabled: null,
5655
+ enabledCampaignError: textMessage || "unknown error"
5656
+ };
5657
+ }
5658
+ function errorMessage2(err) {
5659
+ if (err instanceof Error) return err.message;
5660
+ return String(err);
5661
+ }
5662
+ async function fetchEnabledCampaignScans(accountIds, fetchOne, opts) {
5663
+ const ids = [...new Set(accountIds.filter((id) => id.trim() !== ""))];
5664
+ const out = /* @__PURE__ */ new Map();
5665
+ if (ids.length === 0) return out;
5666
+ const concurrency = Math.max(1, Math.min(opts?.concurrency ?? 8, 16, ids.length));
5667
+ let cursor = 0;
5668
+ let done = 0;
5669
+ const workers = new Array(concurrency).fill(0).map(async () => {
5670
+ while (true) {
5671
+ const index = cursor++;
5672
+ if (index >= ids.length) return;
5673
+ const id = ids[index];
5674
+ try {
5675
+ out.set(id, summarizeEnabledCampaigns(await fetchOne(id)));
5676
+ } catch (err) {
5677
+ out.set(id, errorEnabledCampaigns(errorMessage2(err)));
5678
+ }
5679
+ done += 1;
5680
+ opts?.onProgress?.(done, ids.length);
5681
+ }
5682
+ });
5683
+ await Promise.all(workers);
5684
+ return out;
5685
+ }
5686
+ var PMAX_CHANNEL, DISPLAY_CHANNEL;
5687
+ var init_enabled_campaigns = __esm({
5688
+ "src/commands/accounts-digest/enabled-campaigns.ts"() {
5689
+ "use strict";
5690
+ PMAX_CHANNEL = /performance[\s_-]?max/i;
5691
+ DISPLAY_CHANNEL = /^display$/i;
5692
+ }
5693
+ });
5694
+
5695
+ // src/commands/accounts-digest/pace.ts
5696
+ function digestWindowDays(start, end) {
5697
+ const s = Date.parse(`${start}T00:00:00Z`);
5698
+ const e = Date.parse(`${end}T00:00:00Z`);
5699
+ if (!Number.isFinite(s) || !Number.isFinite(e) || e < s) return 7;
5700
+ const days = Math.round((e - s) / 864e5) + 1;
5701
+ return days >= 1 ? days : 7;
5702
+ }
5703
+ function spendPace(spend, balance, days) {
5704
+ const windowDays = days >= 1 ? days : 7;
5705
+ const dailySpend = spend != null && spend > 0 ? +(spend / windowDays).toFixed(2) : null;
5706
+ const remainingDays = dailySpend != null && dailySpend >= MIN_DAILY_SPEND && balance != null ? +(balance / dailySpend).toFixed(1) : null;
5707
+ return { dailySpend, remainingDays };
5708
+ }
5709
+ var MIN_DAILY_SPEND;
5710
+ var init_pace = __esm({
5711
+ "src/commands/accounts-digest/pace.ts"() {
5712
+ "use strict";
5713
+ MIN_DAILY_SPEND = 0.01;
5714
+ }
5715
+ });
5716
+
5492
5717
  // src/commands/accounts-digest/filters.ts
5493
5718
  function keepDigestRow(row, opts) {
5494
5719
  const { maxCpa, zeroConversions } = opts;
@@ -5519,16 +5744,23 @@ var init_filters = __esm({
5519
5744
  // src/commands/accounts-digest/run.ts
5520
5745
  function formatCampaignTypesCell(row) {
5521
5746
  if (row.campaignTypesStatus === "ok") {
5522
- const flags = [
5523
- row.hasDisplay ? "Display" : "\u65E0Display",
5524
- row.hasPmax ? "PMax" : "\u65E0PMax"
5525
- ];
5747
+ const flags = [row.hasDisplay ? "Display" : "\u65E0Display", row.hasPmax ? "PMax" : "\u65E0PMax"];
5526
5748
  return flags.join(" ");
5527
5749
  }
5528
5750
  if (row.campaignTypesStatus === "skipped-no-spend") return "\u672A\u67E5(\u65E0\u6D88\u8017)";
5529
5751
  if (row.campaignTypesStatus === "error") return "\u67E5\u8BE2\u5931\u8D25";
5530
5752
  return "-";
5531
5753
  }
5754
+ function formatEnabledCampaignCell(row) {
5755
+ if (row.enabledCampaignStatus === "ok") {
5756
+ const pmax = row.hasEnabledPmax ? "\u6709\u542F\u7528PMax" : "\u65E0\u542F\u7528PMax";
5757
+ const zeros = row.zeroImpressionEnabled?.length ?? 0;
5758
+ return zeros > 0 ? `${pmax} 0\u5C55\u793A${zeros}` : pmax;
5759
+ }
5760
+ if (row.enabledCampaignStatus === "skipped-no-spend") return "\u672A\u67E5(\u65E0\u6D88\u8017)";
5761
+ if (row.enabledCampaignStatus === "error") return "\u67E5\u8BE2\u5931\u8D25";
5762
+ return "-";
5763
+ }
5532
5764
  function round2(n) {
5533
5765
  if (n == null) return null;
5534
5766
  const v = Number(n);
@@ -5557,6 +5789,21 @@ async function runAccountsDigest(opts) {
5557
5789
  console.error("\n\u274C \u5F53\u524D\u914D\u7F6E\u6CA1\u6709 googleApiUrl\uFF0C\u65E0\u6CD5\u62C9\u53D6\u5E7F\u544A\u7CFB\u5217\u7C7B\u578B\u3002\n");
5558
5790
  process.exit(1);
5559
5791
  }
5792
+ if (opts.withEnabledCampaigns && media !== "Google") {
5793
+ console.error(
5794
+ "\n\u274C --with-enabled-campaigns \u53EA\u652F\u6301 -m Google\uFF08\u7CFB\u5217\u5217\u8868\u6765\u81EA Google campaignmanagement\uFF09\u3002\n"
5795
+ );
5796
+ process.exit(1);
5797
+ }
5798
+ if (opts.withEnabledCampaigns && !config.googleApiUrl) {
5799
+ console.error("\n\u274C \u5F53\u524D\u914D\u7F6E\u6CA1\u6709 googleApiUrl\uFF0C\u65E0\u6CD5\u62C9\u53D6\u5E7F\u544A\u7CFB\u5217\u3002\n");
5800
+ process.exit(1);
5801
+ }
5802
+ if (opts.withCampaignTypes && opts.withEnabledCampaigns) {
5803
+ process.stderr.write(
5804
+ "\u26A0 --with-campaign-types \u4E0E --with-enabled-campaigns \u4F1A\u5404\u6253\u4E00\u8F6E\u6309\u6237\u8BF7\u6C42\u3002\u542F\u7528 PMax / 0 \u5C55\u793A\u53EA\u7528\u540E\u8005\u3002\n"
5805
+ );
5806
+ }
5560
5807
  const filterIds = (opts.accounts ?? "").split(",").map((s) => s.trim()).filter(Boolean);
5561
5808
  const filterSet = new Set(filterIds);
5562
5809
  const minSpend = opts.minSpend ?? 0;
@@ -5658,6 +5905,10 @@ async function runAccountsDigest(opts) {
5658
5905
  overviewMap = fetched.overviewMap;
5659
5906
  batchChunkSize = fetched.chunkSize;
5660
5907
  }
5908
+ const fallbackWindow = defaultDateRange();
5909
+ const windowStart = opts.startDate ?? fallbackWindow.startDate;
5910
+ const windowEnd = opts.endDate ?? fallbackWindow.endDate;
5911
+ const windowDays = digestWindowDays(windowStart, windowEnd);
5661
5912
  const rows = [];
5662
5913
  let spendPositiveCount = 0;
5663
5914
  for (const item of accountsList) {
@@ -5689,7 +5940,8 @@ async function runAccountsDigest(opts) {
5689
5940
  // 百分比
5690
5941
  cpc: round2(cpc),
5691
5942
  cpa: round2(cpa),
5692
- invalidOAuthToken: !!ma.invalidOAuthToken
5943
+ invalidOAuthToken: !!ma.invalidOAuthToken,
5944
+ ...spendPace(round2(spend), round2(bal?.remainingAccountBudget ?? null), windowDays)
5693
5945
  };
5694
5946
  if (!keepDigestRow(row, {
5695
5947
  maxCpa: opts.maxCpa,
@@ -5747,6 +5999,61 @@ async function runAccountsDigest(opts) {
5747
5999
  };
5748
6000
  process.stderr.write(
5749
6001
  ` \u2713 [\u7CFB\u5217\u7C7B\u578B] \u5B8C\u6210\uFF1A\u6210\u529F ${ok}\uFF0C\u5931\u8D25 ${failed}\uFF0C\u672A\u67E5\uFF08\u65E0\u6D88\u8017\uFF09${skippedNoSpend}
6002
+ `
6003
+ );
6004
+ }
6005
+ let enabledCampaignMeta = null;
6006
+ if (opts.withEnabledCampaigns) {
6007
+ const concurrency = Math.max(1, Math.min(opts.enabledCampaignConcurrency ?? 8, 16));
6008
+ const spendIds = rows.filter((row) => (row.spend ?? 0) > 0).map((row) => row.mediaCustomerId);
6009
+ process.stderr.write(
6010
+ `\u23F3 [accounts-digest] \u542F\u7528\u7CFB\u5217\uFF1A${spendIds.length} \u4E2A\u6709\u6D88\u8017\u8D26\u6237\uFF0C\u5E76\u53D1 ${concurrency}\uFF08\u6D88\u8017\u4E3A 0 \u7684\u4E0D\u8BF7\u6C42\uFF09\u3002
6011
+ `
6012
+ );
6013
+ const googleApiUrl = config.googleApiUrl ?? "";
6014
+ const scans = await fetchEnabledCampaignScans(
6015
+ spendIds,
6016
+ async (accountId) => {
6017
+ const url = campaignListUrl(googleApiUrl, accountId, windowStart, windowEnd);
6018
+ const data = await apiFetch2(url, config, {}, !!opts.verbose);
6019
+ if (!Array.isArray(data?.data)) {
6020
+ throw new Error("\u7CFB\u5217\u5217\u8868\u6CA1\u6709 data \u6570\u7EC4");
6021
+ }
6022
+ return data.data;
6023
+ },
6024
+ {
6025
+ concurrency,
6026
+ onProgress: (done, total) => {
6027
+ if (done === total || done % 50 === 0) {
6028
+ process.stderr.write(` \u2713 [\u542F\u7528\u7CFB\u5217] ${done}/${total}
6029
+ `);
6030
+ }
6031
+ }
6032
+ }
6033
+ );
6034
+ let ok = 0;
6035
+ let failed = 0;
6036
+ let skippedNoSpend = 0;
6037
+ for (const row of rows) {
6038
+ const fields = (row.spend ?? 0) > 0 ? scans.get(row.mediaCustomerId) ?? errorEnabledCampaigns("\u672A\u8FD4\u56DE\u8BE5\u8D26\u6237\u7684\u7CFB\u5217\u5217\u8868") : skippedNoSpendEnabledCampaigns();
6039
+ row.enabledCampaignStatus = fields.enabledCampaignStatus;
6040
+ row.hasEnabledPmax = fields.hasEnabledPmax;
6041
+ row.enabledCampaignCount = fields.enabledCampaignCount;
6042
+ row.zeroImpressionEnabled = fields.zeroImpressionEnabled;
6043
+ row.enabledCampaignError = fields.enabledCampaignError;
6044
+ if (fields.enabledCampaignStatus === "ok") ok += 1;
6045
+ else if (fields.enabledCampaignStatus === "error") failed += 1;
6046
+ else skippedNoSpend += 1;
6047
+ }
6048
+ enabledCampaignMeta = {
6049
+ requested: spendIds.length,
6050
+ ok,
6051
+ failed,
6052
+ skippedNoSpend,
6053
+ concurrency
6054
+ };
6055
+ process.stderr.write(
6056
+ ` \u2713 [\u542F\u7528\u7CFB\u5217] \u5B8C\u6210\uFF1A\u6210\u529F ${ok}\uFF0C\u5931\u8D25 ${failed}\uFF0C\u672A\u67E5\uFF08\u65E0\u6D88\u8017\uFF09${skippedNoSpend}
5750
6057
  `
5751
6058
  );
5752
6059
  }
@@ -5765,7 +6072,8 @@ async function runAccountsDigest(opts) {
5765
6072
  window: {
5766
6073
  startDate: opts.startDate ?? null,
5767
6074
  endDate: opts.endDate ?? null,
5768
- note: opts.startDate && opts.endDate ? "\u7528\u6237/AI \u660E\u786E\u6307\u5B9A\u533A\u95F4" : "\u672A\u6307\u5B9A\u533A\u95F4\uFF0CCLI \u9ED8\u8BA4\u8FD1 7 \u5929\uFF1BSKILL \u8981\u6C42\u5148\u5411\u7528\u6237\u786E\u8BA4"
6075
+ dayCount: windowDays,
6076
+ note: opts.startDate && opts.endDate ? "\u7528\u6237/AI \u660E\u786E\u6307\u5B9A\u533A\u95F4\u3002dailySpend = spend / dayCount\uFF0CremainingDays = balance / dailySpend" : "\u672A\u6307\u5B9A\u533A\u95F4\uFF0CCLI \u9ED8\u8BA4\u8FD1 7 \u5929\uFF1BSKILL \u8981\u6C42\u5148\u5411\u7528\u6237\u786E\u8BA4\u3002dailySpend = spend / dayCount"
5769
6077
  },
5770
6078
  scanned: accountsList.length,
5771
6079
  validAccountCount: validIds.length,
@@ -5793,7 +6101,9 @@ async function runAccountsDigest(opts) {
5793
6101
  currencyNote: "\u5404\u8D26\u6237\u4E3B\u5E01\u79CD\u53EF\u80FD\u4E0D\u540C\uFF1Btotals \u53EA\u5408\u8BA1 returned\uFF08\u547D\u4E2D\uFF09\u884C\uFF0C\u7981\u6B62\u5199\u6210\u5168\u90E8\u8D26\u6237\u3002\u8DE8\u5E01\u79CD\u8BF7\u6309 currencyCode \u5206\u8868",
5794
6102
  generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
5795
6103
  /** 未传 --with-campaign-types 时为 null。失败户的 hasDisplay 是 null,不能当成「无展示」。 */
5796
- campaignTypes: campaignTypeMeta
6104
+ campaignTypes: campaignTypeMeta,
6105
+ /** 未传 --with-enabled-campaigns 时为 null。失败户的 hasEnabledPmax 是 null,不能当成「无 PMax」。 */
6106
+ enabledCampaigns: enabledCampaignMeta
5797
6107
  };
5798
6108
  const agentBrief = buildDigestAgentBrief({
5799
6109
  rows,
@@ -5839,6 +6149,9 @@ ${media} \u8D26\u6237\u6295\u653E\u753B\u50CF\uFF1A\u8FD4\u56DE ${rows.length} \
5839
6149
  if (opts.withCampaignTypes) {
5840
6150
  cols.push({ key: "types", header: "\u7CFB\u5217\u7C7B\u578B" });
5841
6151
  }
6152
+ if (opts.withEnabledCampaigns) {
6153
+ cols.push({ key: "enabled", header: "\u542F\u7528\u7CFB\u5217" });
6154
+ }
5842
6155
  const tableRows = rows.map((r) => ({
5843
6156
  id: r.mediaCustomerId,
5844
6157
  company: r.advertiserName ?? "-",
@@ -5851,7 +6164,8 @@ ${media} \u8D26\u6237\u6295\u653E\u753B\u50CF\uFF1A\u8FD4\u56DE ${rows.length} \
5851
6164
  cpc: formatMoneyDisplay(r.cpc, r.currencyCode),
5852
6165
  cpa: formatMoneyDisplay(r.cpa, r.currencyCode),
5853
6166
  balance: formatMoneyDisplay(r.balance, r.currencyCode),
5854
- types: formatCampaignTypesCell(r)
6167
+ types: formatCampaignTypesCell(r),
6168
+ enabled: formatEnabledCampaignCell(r)
5855
6169
  }));
5856
6170
  printCliTable(tableRows, cols);
5857
6171
  if (meta.nullConversionCount > 0) {
@@ -5885,6 +6199,9 @@ var init_run = __esm({
5885
6199
  init_login_customer_id();
5886
6200
  init_brief();
5887
6201
  init_campaign_types();
6202
+ init_shared();
6203
+ init_enabled_campaigns();
6204
+ init_pace();
5888
6205
  init_filters();
5889
6206
  }
5890
6207
  });
@@ -5916,6 +6233,14 @@ function register10(program2) {
5916
6233
  "--campaign-type-concurrency <n>",
5917
6234
  "\u7CFB\u5217\u7C7B\u578B\u8BF7\u6C42\u5E76\u53D1\uFF081\u201316\uFF0C\u9ED8\u8BA4 8\uFF09",
5918
6235
  (v) => parseInt(v, 10)
6236
+ ).option(
6237
+ "--with-enabled-campaigns",
6238
+ "\u4EC5 Google\uFF1A\u5BF9\u533A\u95F4\u6D88\u8017 > 0 \u7684\u8D26\u6237\u5E76\u53D1\u62C9\u53D6\u7CFB\u5217\u5217\u8868\uFF0C\u5728\u6BCF\u884C\u5199\u5165 hasEnabledPmax\uFF08\u53EA\u770B\u542F\u7528\u4E2D\u7684\u7CFB\u5217\uFF09\u548C zeroImpressionEnabled\uFF08\u542F\u7528\u3001\u533A\u95F4\u5C55\u793A\u4E3A 0\u3001\u4E14\u4E0D\u662F PMax/Display\uFF09\u3002\u7528\u6765\u505A D \u7EC4\u8FD9\u7C7B\u5DE1\u68C0\uFF0C\u4E0D\u8981\u518D\u9010\u6237\u8C03 ad campaigns\uFF0C\u4E5F\u4E0D\u8981\u548C --with-campaign-types \u4E00\u8D77\u5F00\u3002",
6239
+ false
6240
+ ).option(
6241
+ "--enabled-campaign-concurrency <n>",
6242
+ "\u542F\u7528\u7CFB\u5217\u5217\u8868\u8BF7\u6C42\u5E76\u53D1\uFF081\u201316\uFF0C\u9ED8\u8BA4 8\uFF09",
6243
+ (v) => parseInt(v, 10)
5919
6244
  ).option("--max-pages <n>", "\u6700\u591A\u626B\u63CF\u9875\u6570\uFF08\u9ED8\u8BA4 20\uFF0C\u4E0A\u9650 200\uFF09", (v) => parseInt(v, 10)).option("-t, --token <token>", "Token\uFF08\u53EF\u9009\uFF1B\u4F18\u5148\u4E8E ~/.siluzan/config.json\uFF09").option(
5920
6245
  "--json-out <path>",
5921
6246
  "\u843D\u76D8\uFF08\u76EE\u5F55\u6216 *.json \u6587\u4EF6\u8DEF\u5F84\uFF09\u5E76\u66F4\u65B0 cli-manifest[-<\u67E5\u8BE2id>].json\uFF1B\u76EE\u5F55\u6A21\u5F0F\u6587\u4EF6\u540D\u4E3A `<section>[-<\u67E5\u8BE2id>].json`\uFF1Bstdout \u4E00\u884C\u6458\u8981 JSON\uFF0C\u542B outlineFile\uFF08TS \u5F0F\u7C7B\u578B\u5728 `*.outline.txt`\uFF09",
@@ -5939,7 +6264,9 @@ function register10(program2) {
5939
6264
  verbose: opts.verbose,
5940
6265
  loginCustomerId: opts.loginCustomerId,
5941
6266
  withCampaignTypes: opts.withCampaignTypes,
5942
- campaignTypeConcurrency: opts.campaignTypeConcurrency
6267
+ campaignTypeConcurrency: opts.campaignTypeConcurrency,
6268
+ withEnabledCampaigns: opts.withEnabledCampaigns,
6269
+ enabledCampaignConcurrency: opts.enabledCampaignConcurrency
5943
6270
  });
5944
6271
  }
5945
6272
  );
@@ -6082,6 +6409,7 @@ var init_wrap_snapshot_envelope = __esm({
6082
6409
  "geo-matched": "countries",
6083
6410
  "campaign-geo": "countries",
6084
6411
  "campaign-geo-matched": "countries",
6412
+ "geo-targeted": "countries",
6085
6413
  "campaign-device": "devices",
6086
6414
  audience: "audience"
6087
6415
  };
@@ -6297,6 +6625,7 @@ var init_google_analysis = __esm({
6297
6625
  "geo-matched",
6298
6626
  "campaign-geo",
6299
6627
  "campaign-geo-matched",
6628
+ "geo-targeted",
6300
6629
  "campaign-device",
6301
6630
  "audience",
6302
6631
  "asset-images",
@@ -6331,6 +6660,10 @@ var init_google_analysis = __esm({
6331
6660
  "campaign-geo-matched": [
6332
6661
  "// \u4E2D\u6587\u8BD1\u540D\uFF1A`countryOrRegionZh`\uFF08\u7531 `countryOrRegion` \u7ECF geo-en2zh.json \u7FFB\u8BD1\uFF0C\u76EE\u524D\u8986\u76D6 105 \u4E2A\u56FD\u5BB6/\u5730\u533A\uFF1B\u672A\u547D\u4E2D\u65F6\u8BE5\u5B57\u6BB5\u7F3A\u7701\uFF09\u3002"
6333
6662
  ],
6663
+ "geo-targeted": [
6664
+ "// \u5F53\u524D\u4ECD\u5728\u6295\u653E\u7684\u5B9A\u4F4D\uFF08\u5DF2\u79FB\u9664\u56FD\u5BB6\u4E0D\u4F1A\u51FA\u73B0\uFF09\u3002\u4E2D\u6587\u8BD1\u540D\uFF1A`countryOrRegionZh`\uFF08\u7531 `countryOrRegion` \u7ECF geo-en2zh.json \u7FFB\u8BD1\uFF1B\u672A\u547D\u4E2D\u65F6\u8BE5\u5B57\u6BB5\u7F3A\u7701\uFF09\u3002",
6665
+ "// `ctr` \u5DF2\u6298\u6210 0~1 \u5C0F\u6570\uFF1B`conversionRate` \u7F51\u5173\u672C\u8EAB\u5C31\u662F 0~1\uFF0C\u4E0D\u8981\u518D \xF7100\u3002"
6666
+ ],
6334
6667
  "geo-matched": [
6335
6668
  "// \u4E2D\u6587\u8BD1\u540D\uFF1A`countryOrRegionZh`\uFF08\u7531 `countryOrRegion` \u7ECF geo-en2zh.json \u7FFB\u8BD1\uFF0C\u76EE\u524D\u8986\u76D6 105 \u4E2A\u56FD\u5BB6/\u5730\u533A\uFF1B\u672A\u547D\u4E2D\u65F6\u8BE5\u5B57\u6BB5\u7F3A\u7701\uFF09\u3002"
6336
6669
  ]
@@ -6410,8 +6743,8 @@ function relLuminance(hex) {
6410
6743
  function textColorFor(bgHex) {
6411
6744
  return relLuminance(bgHex) > 0.55 ? NEUTRAL.textDark : NEUTRAL.white;
6412
6745
  }
6413
- function xmlEscape(text2) {
6414
- return text2.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
6746
+ function xmlEscape(text3) {
6747
+ return text3.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
6415
6748
  }
6416
6749
  function colLetter(index) {
6417
6750
  let n = index + 1;
@@ -6533,8 +6866,8 @@ function sheetRelsXml(sheet) {
6533
6866
  ).join("");
6534
6867
  return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?><Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">${rels}</Relationships>`;
6535
6868
  }
6536
- function estimateWrapHeight(text2, colWidthChars) {
6537
- const str3 = String(text2 ?? "");
6869
+ function estimateWrapHeight(text3, colWidthChars) {
6870
+ const str3 = String(text3 ?? "");
6538
6871
  if (!str3) return void 0;
6539
6872
  const lines = str3.split("\n").reduce((sum, line) => {
6540
6873
  const width = Math.max(colWidthChars || 20, 8);
@@ -6682,7 +7015,7 @@ var init_xlsx_style_engine = __esm({
6682
7015
  for (let i = 0; i < n; i++) this.pushRow([]);
6683
7016
  return this;
6684
7017
  }
6685
- titleBar(text2, opts = {}) {
7018
+ titleBar(text3, opts = {}) {
6686
7019
  const fill = opts.fill === null ? void 0 : opts.fill ?? NEUTRAL.titleFill;
6687
7020
  const fontColor = opts.fontColor ?? (fill ? textColorFor(fill) : NEUTRAL.textDark);
6688
7021
  const s = this.registry.resolve({
@@ -6693,22 +7026,22 @@ var init_xlsx_style_engine = __esm({
6693
7026
  border: opts.border ?? false,
6694
7027
  hAlign: opts.hAlign ?? "left"
6695
7028
  });
6696
- this.pushRow([{ value: text2, styleId: s }], { height: 28 });
7029
+ this.pushRow([{ value: text3, styleId: s }], { height: 28 });
6697
7030
  this.mergeCurrentRow(opts.span || this.colCount);
6698
7031
  return this;
6699
7032
  }
6700
- subtitleBar(text2, opts = {}) {
7033
+ subtitleBar(text3, opts = {}) {
6701
7034
  const s = this.registry.resolve({
6702
7035
  fontSize: 10.5,
6703
7036
  fontColor: NEUTRAL.subtitleText,
6704
7037
  fill: NEUTRAL.subtitleFill,
6705
7038
  border: false
6706
7039
  });
6707
- this.pushRow([{ value: text2, styleId: s }], { height: 20 });
7040
+ this.pushRow([{ value: text3, styleId: s }], { height: 20 });
6708
7041
  this.mergeCurrentRow(opts.span || this.colCount);
6709
7042
  return this;
6710
7043
  }
6711
- sectionBar(text2, opts = {}) {
7044
+ sectionBar(text3, opts = {}) {
6712
7045
  const fill = opts.fill ?? this.accentHex;
6713
7046
  const s = this.registry.resolve({
6714
7047
  fontSize: 11,
@@ -6718,11 +7051,11 @@ var init_xlsx_style_engine = __esm({
6718
7051
  border: false,
6719
7052
  hAlign: "center"
6720
7053
  });
6721
- this.pushRow([{ value: text2, styleId: s }], { height: 22 });
7054
+ this.pushRow([{ value: text3, styleId: s }], { height: 22 });
6722
7055
  this.mergeCurrentRow(opts.span || this.colCount);
6723
7056
  return this;
6724
7057
  }
6725
- noteRow(text2, span) {
7058
+ noteRow(text3, span) {
6726
7059
  const s = this.registry.resolve({
6727
7060
  fontSize: 9,
6728
7061
  fontColor: NEUTRAL.textMuted,
@@ -6730,8 +7063,8 @@ var init_xlsx_style_engine = __esm({
6730
7063
  border: true,
6731
7064
  vAlign: "top"
6732
7065
  });
6733
- const height = estimateWrapHeight(text2, this.colWidths.reduce((a, b) => a + b, 0) || 60);
6734
- this.pushRow([{ value: text2, styleId: s }], { height });
7066
+ const height = estimateWrapHeight(text3, this.colWidths.reduce((a, b) => a + b, 0) || 60);
7067
+ this.pushRow([{ value: text3, styleId: s }], { height });
6735
7068
  this.mergeCurrentRow(span || this.colCount);
6736
7069
  return this;
6737
7070
  }
@@ -7397,11 +7730,17 @@ var init_sections = __esm({
7397
7730
  },
7398
7731
  {
7399
7732
  name: "campaign-geo-matched",
7400
- description: "\u76F8\u7B26\u5730\u7406\u4F4D\u7F6E\uFF08Matched locations\uFF0Cuser_location_view\uFF09\u7CFB\u5217\u7EA7\u660E\u7EC6 GET \u2026/campaigns/reports/geo/matched/combined",
7733
+ description: "\u76F8\u7B26\u5730\u7406\u4F4D\u7F6E\uFF08Matched locations\uFF0Cuser_location_view\uFF09\u7CFB\u5217\u7EA7\u660E\u7EC6 GET \u2026/campaigns/reports/geo/matched/combined\u3002\u542B\u65E5\u671F\u5185\u66FE\u7ECF\u547D\u4E2D\u3001\u540E\u6765\u5DF2\u79FB\u9664\u7684\u56FD\u5BB6",
7401
7734
  dateMode: "range",
7402
7735
  path: (id) => `/reporting/media-account/${id}/campaigns/reports/geo/matched/combined`,
7403
7736
  campaignGeoFilterOption: true
7404
7737
  },
7738
+ {
7739
+ name: "geo-targeted",
7740
+ description: "\u5F53\u524D\u4ECD\u5728\u6295\u653E\u7684\u5730\u7406\u4F4D\u7F6E\uFF08\u672A\u79FB\u9664\u7684 campaign location criterion\uFF09GET \u2026/campaignmanagement/v2/targetedlocations\u3002\u4E0D\u542B\u5DF2\u5220\u9664\u56FD\u5BB6\uFF1BOKKI \u56FD\u5BB6\u8868\u7528\u8FD9\u4E2A",
7741
+ dateMode: "range",
7742
+ path: (id) => `/campaignmanagement/v2/targetedlocations/${id}`
7743
+ },
7405
7744
  {
7406
7745
  name: "campaign-device",
7407
7746
  description: "\u5E7F\u544A\u7CFB\u5217\u7EF4\u5EA6\u8BBE\u5907 GET \u2026/campaigns/reports/device\uFF08GetCampaignDeviceReport\uFF1B\u884C\u53EF\u542B campaign/adGroup\uFF09",
@@ -7805,6 +8144,47 @@ var init_normalize_overview = __esm({
7805
8144
  }
7806
8145
  });
7807
8146
 
8147
+ // src/commands/google-analysis/geo-targeted.ts
8148
+ function isRecord3(value) {
8149
+ return value != null && typeof value === "object" && !Array.isArray(value);
8150
+ }
8151
+ function finiteNumber(value) {
8152
+ const n = typeof value === "number" ? value : Number(value);
8153
+ return Number.isFinite(n) ? n : 0;
8154
+ }
8155
+ function extractRows(raw) {
8156
+ if (Array.isArray(raw)) return raw.filter(isRecord3);
8157
+ if (!isRecord3(raw)) return [];
8158
+ if (Array.isArray(raw.data)) return raw.data.filter(isRecord3);
8159
+ if (isRecord3(raw.data) && Array.isArray(raw.data.data)) return raw.data.data.filter(isRecord3);
8160
+ return [];
8161
+ }
8162
+ function targetedLocationsToGeographic(raw) {
8163
+ const countries = extractRows(raw).map((row) => ({
8164
+ countryCriteriaId: row.id,
8165
+ countryOrRegion: typeof row.locationName === "string" ? row.locationName : "",
8166
+ campaignId: row.campaignId == null ? void 0 : String(row.campaignId),
8167
+ campaignName: typeof row.campaignName === "string" ? row.campaignName : "",
8168
+ impressions: finiteNumber(row.impressions),
8169
+ clicks: finiteNumber(row.clicks),
8170
+ ctr: finiteNumber(row.ctr) / 100,
8171
+ spend: finiteNumber(row.spend),
8172
+ conversions: finiteNumber(row.conversions),
8173
+ allConversions: finiteNumber(row.allConversions),
8174
+ averageCpc: finiteNumber(row.averageCpc),
8175
+ conversionRate: finiteNumber(row.conversionRate),
8176
+ costPerConversion: finiteNumber(row.costPerConversion),
8177
+ interactions: finiteNumber(row.interactions),
8178
+ interactionRate: row.interactionRate ?? null
8179
+ }));
8180
+ return { countries };
8181
+ }
8182
+ var init_geo_targeted = __esm({
8183
+ "src/commands/google-analysis/geo-targeted.ts"() {
8184
+ "use strict";
8185
+ }
8186
+ });
8187
+
7808
8188
  // src/commands/google-analysis/match-type-en2zh.json
7809
8189
  var match_type_en2zh_default;
7810
8190
  var init_match_type_en2zh = __esm({
@@ -102947,6 +103327,7 @@ function annotateZhFields(payload, section) {
102947
103327
  case "campaign-geo":
102948
103328
  case "campaign-geo-matched":
102949
103329
  case "geo-matched":
103330
+ case "geo-targeted":
102950
103331
  case "geographic": {
102951
103332
  const rows = payload.countries;
102952
103333
  annotateArrayRows(rows, "countryOrRegion", "countryOrRegionZh", GEO_DICT2);
@@ -103100,6 +103481,10 @@ async function fetchGoogleAnalysisSectionJson(config, fullPath, verbose, name2,
103100
103481
  return fetchSection();
103101
103482
  case "campaign-geo-matched":
103102
103483
  return fetchSection();
103484
+ case "geo-targeted":
103485
+ return targetedLocationsToGeographic(
103486
+ await fetchJson(config, fullPath, verbose, label2, http2)
103487
+ );
103103
103488
  case "campaign-device":
103104
103489
  return fetchSection();
103105
103490
  case "audience":
@@ -103303,6 +103688,7 @@ var init_fetch = __esm({
103303
103688
  init_normalize_rates();
103304
103689
  init_normalize_money();
103305
103690
  init_normalize_overview();
103691
+ init_geo_targeted();
103306
103692
  init_translate_fields();
103307
103693
  init_daily_metrics_range();
103308
103694
  }
@@ -103397,8 +103783,8 @@ function summarizeOkkiDeviceSpend(rows) {
103397
103783
  otherConversions: conv.other
103398
103784
  };
103399
103785
  }
103400
- function narrativeClaimsMobileSpendLeads(text2) {
103401
- const t = text2.replace(/\s+/g, "");
103786
+ function narrativeClaimsMobileSpendLeads(text3) {
103787
+ const t = text3.replace(/\s+/g, "");
103402
103788
  if (!t.includes("\u79FB\u52A8")) return false;
103403
103789
  return /移动(?:端|设备)?(?:为|是)主要消耗/.test(t) || /移动(?:端|设备)?为主要(?:消耗)?设备/.test(t) || /移动(?:端|设备)?(?:消耗|花费|费用|流量)?占比高/.test(t);
103404
103790
  }
@@ -103870,7 +104256,7 @@ function formatOkkiWeeklyReportErrors(result) {
103870
104256
  ...lines,
103871
104257
  "",
103872
104258
  "\u8BF7\u8865\u5168 narrative.sheetAnalysis.<sheet>.{summary,suggestions}\uFF085 \u4E2A Sheet\uFF09\u4E0E narrative.review.<dimension>.{overview,summary,suggestion}\uFF08\u56FA\u5B9A 5 \u7EF4\uFF1A\u8D26\u6237/\u5173\u952E\u8BCD/\u641C\u7D22\u5B57\u8BCD/\u8BBE\u5907/\u56FD\u5BB6\uFF09\u540E\u91CD\u65B0\u6267\u884C okki-render\u3002",
103873
- "\u8868\u683C\u6570\u636E\u987B\u7531 --snapshot-dir \u4ECE google-analysis \u5FEB\u7167\u5408\u5E76\uFF08campaigns / keywords / search-terms / campaign-device / campaign-geo-matched\uFF09\u3002\u7981\u6B62\u624B\u5199 xlsx\u3002"
104259
+ "\u8868\u683C\u6570\u636E\u987B\u7531 --snapshot-dir \u4ECE google-analysis \u5FEB\u7167\u5408\u5E76\uFF08campaigns / keywords / search-terms / campaign-device / geo-targeted\uFF09\u3002\u7981\u6B62\u624B\u5199 xlsx\u3002"
103874
104260
  ].join("\n");
103875
104261
  }
103876
104262
  var OKKI_SHEET_KEYS, OKKI_REVIEW_KEYS, OKKI_SNAPSHOT_SECTIONS, SHEET_LABELS, REVIEW_LABELS, SNAPSHOT_HINTS;
@@ -103897,7 +104283,7 @@ var init_okki_report_content = __esm({
103897
104283
  "keywords",
103898
104284
  "search-terms",
103899
104285
  "campaign-device",
103900
- "campaign-geo-matched"
104286
+ "geo-targeted"
103901
104287
  ];
103902
104288
  SHEET_LABELS = {
103903
104289
  campaigns: "\u8D26\u6237\u62A5\u544A",
@@ -103918,7 +104304,7 @@ var init_okki_report_content = __esm({
103918
104304
  keywords: "google-analysis --sections keywords",
103919
104305
  "search-terms": "google-analysis --sections search-terms",
103920
104306
  "campaign-device": "\u987B\u62C9 campaign-device\uFF08\u6309\u7CFB\u5217\u62C6\u884C\uFF09\uFF0C\u7981\u6B62\u7528\u8D26\u6237\u7EA7 devices \u4EE3\u66FF",
103921
- "campaign-geo-matched": "\u987B\u62C9 campaign-geo-matched\uFF08\u76F8\u7B26\u5730\u7406\u4F4D\u7F6E\uFF09\uFF0C\u7981\u6B62\u7528 geographic / campaign-geo \u4EE3\u66FF"
104307
+ "geo-targeted": "\u987B\u62C9 geo-targeted\uFF08\u5F53\u524D\u4ECD\u5728\u6295\u653E\u7684\u5730\u7406\u4F4D\u7F6E\uFF09\u3002\u7981\u6B62\u7528 campaign-geo-matched / geographic / campaign-geo\uFF1A\u524D\u8005\u4F1A\u628A\u5DF2\u79FB\u9664\u7684\u56FD\u5BB6\u4E5F\u7B97\u8FDB\u65E5\u671F\u533A\u95F4"
103922
104308
  };
103923
104309
  }
103924
104310
  });
@@ -104058,7 +104444,7 @@ async function mergeOkkiSnapshotIntoReport(payload, snapshotDir) {
104058
104444
  keywords: items2(sectionMap.get("keywords")),
104059
104445
  searchTerms: items2(sectionMap.get("search-terms")),
104060
104446
  devices: deviceRows2,
104061
- geo: items2(sectionMap.get("campaign-geo-matched"))
104447
+ geo: items2(sectionMap.get("geo-targeted"))
104062
104448
  },
104063
104449
  facts: {
104064
104450
  devices: summarizeOkkiDeviceSpend(deviceRows2)
@@ -104149,9 +104535,9 @@ function sortBySpendDesc(rows) {
104149
104535
  return [...rows].sort((a, b) => (num6(b.spend) ?? 0) - (num6(a.spend) ?? 0));
104150
104536
  }
104151
104537
  function prefixLines(items3, firstPrefix) {
104152
- return items3.map((text2, idx) => {
104153
- if (idx === 0 && !text2.startsWith(firstPrefix)) return `${firstPrefix}${text2}`;
104154
- return text2;
104538
+ return items3.map((text3, idx) => {
104539
+ if (idx === 0 && !text3.startsWith(firstPrefix)) return `${firstPrefix}${text3}`;
104540
+ return text3;
104155
104541
  });
104156
104542
  }
104157
104543
  function analysisLines(data, key) {
@@ -104492,9 +104878,9 @@ var init_okki_render_excel = __esm({
104492
104878
  import fs29 from "fs/promises";
104493
104879
  import path34 from "path";
104494
104880
  async function readJsonFile2(filePath) {
104495
- const text2 = await fs29.readFile(filePath, "utf8");
104881
+ const text3 = await fs29.readFile(filePath, "utf8");
104496
104882
  try {
104497
- return JSON.parse(text2);
104883
+ return JSON.parse(text3);
104498
104884
  } catch {
104499
104885
  throw new Error(`\u65E0\u6CD5\u89E3\u6790 JSON\uFF1A${filePath}`);
104500
104886
  }
@@ -104716,9 +105102,9 @@ function googlePeriodReportTemplatePath() {
104716
105102
  return resolveSkillTemplatePath3(TEMPLATE_BASENAME);
104717
105103
  }
104718
105104
  async function readJsonFile3(filePath) {
104719
- const text2 = await fsPromises2.readFile(filePath, "utf8");
105105
+ const text3 = await fsPromises2.readFile(filePath, "utf8");
104720
105106
  try {
104721
- return JSON.parse(text2);
105107
+ return JSON.parse(text3);
104722
105108
  } catch {
104723
105109
  throw new Error(`\u65E0\u6CD5\u89E3\u6790 JSON\uFF1A${filePath}`);
104724
105110
  }
@@ -104930,10 +105316,10 @@ async function readTaskLog(paths) {
104930
105316
  const entries = [];
104931
105317
  let skipped = 0;
104932
105318
  for (const line of raw.split("\n")) {
104933
- const text2 = line.trim();
104934
- if (!text2) continue;
105319
+ const text3 = line.trim();
105320
+ if (!text3) continue;
104935
105321
  try {
104936
- const parsed = JSON.parse(text2);
105322
+ const parsed = JSON.parse(text3);
104937
105323
  if (!isTaskLogEntry(parsed)) {
104938
105324
  skipped += 1;
104939
105325
  continue;
@@ -106389,7 +106775,7 @@ function registerGoogleAnalysisCommands(program2) {
106389
106775
  "\u6839\u636E Agent \u64B0\u5199\u7684 OKKI \u53D9\u4E8B JSON \u751F\u6210 5 Sheet xlsx\uFF08\u6821\u9A8C\u6BCF Sheet \u5206\u6790 + \u8D26\u6237 Sheet 5 \u7EF4\u6570\u636E\u590D\u76D8\uFF0C\u7F3A\u9879\u62D2\u7EDD\u6E32\u67D3\uFF09"
106390
106776
  ).requiredOption("--data <file>", "Agent \u4EA7\u51FA\u7684 okki-weekly-report.json").requiredOption(
106391
106777
  "--snapshot-dir <dir>",
106392
- "google-analysis --json-out \u76EE\u5F55\uFF08\u987B\u542B campaigns / keywords / search-terms / campaign-device / campaign-geo-matched\uFF09"
106778
+ "google-analysis --json-out \u76EE\u5F55\uFF08\u987B\u542B campaigns / keywords / search-terms / campaign-device / geo-targeted\uFF09"
106393
106779
  ).option("--out <file>", "\u8F93\u51FA xlsx \u8DEF\u5F84\uFF08\u9ED8\u8BA4\u540C --data \u76EE\u5F55\u7684 okki-weekly-report.xlsx\uFF09").action(async (opts) => {
106394
106780
  await runGoogleAnalysisOkkiRender({
106395
106781
  dataFile: opts.data,
@@ -109493,104 +109879,7 @@ init_cli_json_snapshot();
109493
109879
  init_auth();
109494
109880
  init_list_fetch();
109495
109881
  init_login_customer_id();
109496
-
109497
- // src/commands/ad/_shared.ts
109498
- init_auth();
109499
- init_media_customer_id();
109500
- function resolveAdAccountId(account, label2 = "-a, --account") {
109501
- try {
109502
- return requireNumericMediaCustomerId(account, label2);
109503
- } catch (e) {
109504
- console.error(`
109505
- \u274C ${e instanceof Error ? e.message : String(e)}
109506
- `);
109507
- process.exit(1);
109508
- }
109509
- }
109510
- function withResolvedAdAccount(opts) {
109511
- return { ...opts, account: resolveAdAccountId(opts.account) };
109512
- }
109513
- async function findItemInList(listUrl, config, id, verbose) {
109514
- const res = await apiFetch2(listUrl, config, {}, verbose);
109515
- const item = (res.data ?? []).find((x) => x["id"] === id);
109516
- if (!item) {
109517
- throw new Error(`\u672A\u5728\u5217\u8868\u4E2D\u627E\u5230 id="${id}" \u7684\u6761\u76EE`);
109518
- }
109519
- return item;
109520
- }
109521
- function toGoogleDate(dateStr, offsetDays) {
109522
- if (dateStr) return dateStr.replace(/-/g, "/");
109523
- const d = /* @__PURE__ */ new Date();
109524
- d.setDate(d.getDate() + offsetDays);
109525
- return d.toISOString().slice(0, 10).replace(/-/g, "/");
109526
- }
109527
- function toDisplayMoney(raw) {
109528
- if (raw == null) return null;
109529
- const n = Number(raw);
109530
- if (!Number.isFinite(n)) return null;
109531
- return n / 100;
109532
- }
109533
- function toCentAmount(displayValue) {
109534
- if (!Number.isFinite(displayValue)) {
109535
- throw new Error(`toCentAmount: \u8F93\u5165\u5FC5\u987B\u662F\u6709\u9650\u6570\u5B57\uFF0C\u6536\u5230 ${displayValue}`);
109536
- }
109537
- return Math.round(displayValue * 100);
109538
- }
109539
- var CAMPAIGN_BATCH_MONEY_KEYS = /* @__PURE__ */ new Set([
109540
- "Budget",
109541
- "MaxCPCAmount",
109542
- "MaxCpmAmount",
109543
- "MaxCPVAmount",
109544
- "TargetCpa_BidingAmount",
109545
- "TargetSpend_BidCeilingAmount",
109546
- "TargetCpaAmount",
109547
- "MaxCPC"
109548
- ]);
109549
- function convertBatchMoneyTreeFromYuan(value) {
109550
- return convertBatchMoneyTreeFromYuanInner(value);
109551
- }
109552
- function convertBatchMoneyTreeFromYuanInner(value) {
109553
- if (Array.isArray(value)) {
109554
- return value.map(convertBatchMoneyTreeFromYuanInner);
109555
- }
109556
- if (value && typeof value === "object") {
109557
- const out = {};
109558
- for (const [key, val] of Object.entries(value)) {
109559
- if (typeof val === "number" && CAMPAIGN_BATCH_MONEY_KEYS.has(key)) {
109560
- out[key] = toCentAmount(val);
109561
- } else {
109562
- out[key] = convertBatchMoneyTreeFromYuanInner(val);
109563
- }
109564
- }
109565
- return out;
109566
- }
109567
- return value;
109568
- }
109569
- function adgroupListUrl(googleApiUrl, account, startDate, endDate, fresh) {
109570
- const params = new URLSearchParams();
109571
- params.set("startDate", toGoogleDate(startDate, -30));
109572
- params.set("endDate", toGoogleDate(endDate, 0));
109573
- if (fresh) params.set("newest", "true");
109574
- return `${googleApiUrl}/adgroupnmanagement/v2/list/${account}?${params}`;
109575
- }
109576
- function campaignListUrl(googleApiUrl, account, startDate, endDate, fresh) {
109577
- const params = new URLSearchParams();
109578
- params.set("startDate", toGoogleDate(startDate, -30));
109579
- params.set("endDate", toGoogleDate(endDate, 0));
109580
- if (fresh) params.set("newest", "true");
109581
- return `${googleApiUrl}/campaignmanagement/v2/list/${account}?${params}`;
109582
- }
109583
- function requireGoogleApi(config) {
109584
- if (!config.googleApiUrl) {
109585
- console.error(
109586
- "\n\u274C \u672A\u627E\u5230 Google API \u5730\u5740\u3002\n Google API \u5730\u5740\u4ECE TSO API \u5730\u5740\u81EA\u52A8\u63A8\u5BFC\uFF0C\u4E5F\u53EF\u901A\u8FC7\u73AF\u5883\u53D8\u91CF SILUZAN_GOOGLE_API \u8986\u76D6\u3002\n"
109587
- );
109588
- process.exit(1);
109589
- }
109590
- return config.googleApiUrl;
109591
- }
109592
-
109593
- // src/commands/guard/shared.ts
109882
+ init_shared();
109594
109883
  init_cli_table();
109595
109884
  var GUARD_ACCOUNT_CONCURRENCY = 4;
109596
109885
  async function resolveGuardConfig(opts) {
@@ -113570,6 +113859,9 @@ function register20(program2) {
113570
113859
  });
113571
113860
  }
113572
113861
 
113862
+ // src/commands/ad/index.ts
113863
+ init_shared();
113864
+
113573
113865
  // src/commands/ad/campaign.ts
113574
113866
  init_auth();
113575
113867
  init_cli_json_snapshot();
@@ -113589,6 +113881,9 @@ function ensureIConfirm(opts) {
113589
113881
  process.exit(1);
113590
113882
  }
113591
113883
 
113884
+ // src/commands/ad/campaign.ts
113885
+ init_shared();
113886
+
113592
113887
  // src/commands/ad/campaign-create-validate.ts
113593
113888
  init_media_customer_id();
113594
113889
 
@@ -113826,27 +114121,27 @@ function validateCalloutProperties(prefix, rec, errors, _warnings, lengthViolati
113826
114121
  );
113827
114122
  return;
113828
114123
  }
113829
- const text2 = raw["Text"];
113830
- if (typeof text2 !== "string" || !text2.trim()) {
114124
+ const text3 = raw["Text"];
114125
+ if (typeof text3 !== "string" || !text3.trim()) {
113831
114126
  pushErr(
113832
114127
  errors,
113833
114128
  `${prefix}.Properties.Text \u4E0D\u80FD\u4E3A\u7A7A\uFF08CALLOUT \u5FC5\u586B\uFF0C\u540E\u7AEF CreateCalloutFeedItem \u8BFB\u53D6 Properties["Text"]\uFF09`
113834
114129
  );
113835
114130
  return;
113836
114131
  }
113837
- if (text2.length > CALLOUT_TEXT_MAX_LEN) {
114132
+ if (text3.length > CALLOUT_TEXT_MAX_LEN) {
113838
114133
  pushLengthViolation(lengthViolations, {
113839
114134
  path: `${prefix}.Properties.Text`,
113840
114135
  field: "Text",
113841
114136
  kind: "callout_text",
113842
114137
  limit: CALLOUT_TEXT_MAX_LEN,
113843
- actual: text2.length,
114138
+ actual: text3.length,
113844
114139
  countMode: "ascii",
113845
- text: text2
114140
+ text: text3
113846
114141
  });
113847
114142
  pushErr(
113848
114143
  errors,
113849
- `${prefix}.Properties.Text \u8D85\u8FC7 ${CALLOUT_TEXT_MAX_LEN} \u5B57\u7B26\uFF08\u5F53\u524D ${text2.length}\uFF0CGoogle \u786C\u7EA6\u675F\uFF09\uFF1A"${text2}"`
114144
+ `${prefix}.Properties.Text \u8D85\u8FC7 ${CALLOUT_TEXT_MAX_LEN} \u5B57\u7B26\uFF08\u5F53\u524D ${text3.length}\uFF0CGoogle \u786C\u7EA6\u675F\uFF09\uFF1A"${text3}"`
113850
114145
  );
113851
114146
  }
113852
114147
  }
@@ -113958,6 +114253,7 @@ import { readFileSync as readFileSync4 } from "fs";
113958
114253
  init_cli_json_snapshot();
113959
114254
  init_strip_legacy_google_fields();
113960
114255
  init_cli_table();
114256
+ init_shared();
113961
114257
 
113962
114258
  // src/commands/ad/search-field-patch.ts
113963
114259
  function buildSearchFieldPatch(pairs) {
@@ -114135,8 +114431,8 @@ function judgeBudgetAfterFailedPut(input) {
114135
114431
  }
114136
114432
  return { kind: "unchanged" };
114137
114433
  }
114138
- function coreKeyword(text2) {
114139
- const t = text2.trim();
114434
+ function coreKeyword(text3) {
114435
+ const t = text3.trim();
114140
114436
  if (t.length >= 2 && (t.startsWith('"') && t.endsWith('"') || t.startsWith("[") && t.endsWith("]"))) {
114141
114437
  return t.slice(1, -1).trim();
114142
114438
  }
@@ -114179,8 +114475,8 @@ function summarizeNegativeKeywordCreate(requested, response, listItems) {
114179
114475
  const pools = listItems == null ? responseRecords : [...responseRecords, ...listItems];
114180
114476
  const matched = [];
114181
114477
  const pending = [];
114182
- for (const text2 of requested) {
114183
- const core = coreKeyword(text2);
114478
+ for (const text3 of requested) {
114479
+ const core = coreKeyword(text3);
114184
114480
  const hit = pools.find((item) => {
114185
114481
  const cores = [
114186
114482
  ...collectKeywordCores(item["keywordText"]),
@@ -114190,11 +114486,11 @@ function summarizeNegativeKeywordCreate(requested, response, listItems) {
114190
114486
  return cores.includes(core);
114191
114487
  });
114192
114488
  if (!hit) {
114193
- pending.push(text2);
114489
+ pending.push(text3);
114194
114490
  continue;
114195
114491
  }
114196
114492
  const idRaw = hit["id"] ?? hit["Id"];
114197
- matched.push({ text: text2, id: typeof idRaw === "string" ? idRaw : void 0 });
114493
+ matched.push({ text: text3, id: typeof idRaw === "string" ? idRaw : void 0 });
114198
114494
  }
114199
114495
  let verification;
114200
114496
  if (requested.length > 0 && matched.length === requested.length) verification = "verified";
@@ -114910,8 +115206,8 @@ async function runAdNegativeKeywordDelete(opts) {
114910
115206
 
114911
115207
  // src/commands/ad/campaign-keywords.ts
114912
115208
  var GOOGLE_KEYWORD_MAX_CORE_LENGTH = 80;
114913
- function collapseDuplicateSpacesInKeywordText(text2) {
114914
- return text2.replace(/\s{2,}/g, " ");
115209
+ function collapseDuplicateSpacesInKeywordText(text3) {
115210
+ return text3.replace(/\s{2,}/g, " ");
114915
115211
  }
114916
115212
  var VALID_CORE_REGEX = /^[\p{L}\p{N}\s.\-]+$/u;
114917
115213
  function matchTypeV2ToUi(raw) {
@@ -114936,8 +115232,8 @@ function matchTypeUiToV2(ui) {
114936
115232
  }
114937
115233
  }
114938
115234
  }
114939
- function inferMatchTypeFromDisplayText(text2) {
114940
- const t = text2.trim();
115235
+ function inferMatchTypeFromDisplayText(text3) {
115236
+ const t = text3.trim();
114941
115237
  if (t.length >= 2 && t.startsWith('"') && t.endsWith('"')) return "Phrase";
114942
115238
  if (t.length >= 2 && t.startsWith("[") && t.endsWith("]")) return "Exact";
114943
115239
  return "Broad";
@@ -115475,9 +115771,9 @@ var POLICY_RISK_RULES = [
115475
115771
  { re: /\b(alcohol|whisk(e)?y|vodka|liquor)\b/i, label: "\u9152\u7CBE\uFF08\u5730\u57DF\u9650\u5236\uFF09" },
115476
115772
  { re: /(最好|第一|国家级|绝对|100%)/, label: "\u5E7F\u544A\u6CD5\u7EDD\u5BF9\u5316\u7528\u8BED" }
115477
115773
  ];
115478
- function matchRisk(text2) {
115774
+ function matchRisk(text3) {
115479
115775
  for (const r of POLICY_RISK_RULES) {
115480
- if (r.re.test(text2)) return r.label;
115776
+ if (r.re.test(text3)) return r.label;
115481
115777
  }
115482
115778
  return void 0;
115483
115779
  }
@@ -115500,10 +115796,10 @@ function warnPolicyRiskTerms(campaign, warnings) {
115500
115796
  const groups = campaign["AdGroupsForBatchJob"];
115501
115797
  if (!Array.isArray(groups)) return;
115502
115798
  const hitsByLabel = /* @__PURE__ */ new Map();
115503
- const add = (label2, text2) => {
115799
+ const add = (label2, text3) => {
115504
115800
  let set = hitsByLabel.get(label2);
115505
115801
  if (!set) hitsByLabel.set(label2, set = /* @__PURE__ */ new Set());
115506
- set.add(text2.trim());
115802
+ set.add(text3.trim());
115507
115803
  };
115508
115804
  for (const g of groups) {
115509
115805
  const gr = g;
@@ -115539,8 +115835,8 @@ function warnPolicyRiskTerms(campaign, warnings) {
115539
115835
  }
115540
115836
 
115541
115837
  // src/commands/ad/campaign-rsa.ts
115542
- function normalizeRsaAssetKey(text2) {
115543
- return text2.trim().replace(/\s+/g, " ").toLowerCase();
115838
+ function normalizeRsaAssetKey(text3) {
115839
+ return text3.trim().replace(/\s+/g, " ").toLowerCase();
115544
115840
  }
115545
115841
  function listRsaHeadlines(ad) {
115546
115842
  const parts = [
@@ -115708,9 +116004,9 @@ function validateLocationsFidelity(cfg, campaign, errors) {
115708
116004
  }
115709
116005
  }
115710
116006
  }
115711
- function calcGoogleCharLength(text2) {
116007
+ function calcGoogleCharLength(text3) {
115712
116008
  let len = 0;
115713
- for (const ch of text2) {
116009
+ for (const ch of text3) {
115714
116010
  const code = ch.codePointAt(0) ?? 0;
115715
116011
  if (code >= 19968 && code <= 40959 || code >= 13312 && code <= 19903 || code >= 131072 && code <= 173791 || code >= 12288 && code <= 12351 || code >= 65280 && code <= 65519) {
115716
116012
  len += 2;
@@ -116477,8 +116773,8 @@ async function reportFailedCampaignPut(opts) {
116477
116773
  async function reportCampaignWrite(opts) {
116478
116774
  const nextQuery = `siluzan-tso ad campaigns -a ${opts.account} --json-out ./snap`;
116479
116775
  const unmatched = describeUnmatchedChecks(opts.checks);
116480
- const text2 = opts.verification === "verified" ? opts.detail : unmatched || opts.detail;
116481
- const headline = writeVerificationHeadline(opts.verification, text2);
116776
+ const text3 = opts.verification === "verified" ? opts.detail : unmatched || opts.detail;
116777
+ const headline = writeVerificationHeadline(opts.verification, text3);
116482
116778
  if (opts.verification !== "verified") {
116483
116779
  console.error(`
116484
116780
  ${headline}
@@ -116981,6 +117277,7 @@ init_auth();
116981
117277
  init_cli_json_snapshot();
116982
117278
  init_strip_legacy_google_fields();
116983
117279
  init_cli_table();
117280
+ init_shared();
116984
117281
  async function runAdGroups(opts) {
116985
117282
  opts = withResolvedAdAccount(opts);
116986
117283
  const config = loadConfig(opts.token);
@@ -117241,6 +117538,7 @@ init_auth();
117241
117538
  init_cli_json_snapshot();
117242
117539
  init_strip_legacy_google_fields();
117243
117540
  init_cli_table();
117541
+ init_shared();
117244
117542
 
117245
117543
  // src/commands/ad/rsa-cli-validate.ts
117246
117544
  var AD_CLI_MIN_HEADLINES = 3;
@@ -117566,6 +117864,7 @@ init_auth();
117566
117864
  import { readFileSync as readFileSync8 } from "fs";
117567
117865
  init_cli_json_snapshot();
117568
117866
  init_strip_legacy_google_fields();
117867
+ init_shared();
117569
117868
 
117570
117869
  // src/commands/ad/extension-lead-form.ts
117571
117870
  import { readFileSync as readFileSync6 } from "fs";
@@ -118253,6 +118552,7 @@ init_auth();
118253
118552
  init_cli_json_snapshot();
118254
118553
  init_strip_legacy_google_fields();
118255
118554
  init_cli_table();
118555
+ init_shared();
118256
118556
  async function runAdSearchTerms(opts) {
118257
118557
  opts = withResolvedAdAccount(opts);
118258
118558
  const config = loadConfig(opts.token);
@@ -118339,11 +118639,13 @@ init_auth();
118339
118639
  import { readFileSync as readFileSync9 } from "fs";
118340
118640
  init_cli_json_snapshot();
118341
118641
  init_strip_legacy_google_fields();
118642
+ init_shared();
118342
118643
 
118343
118644
  // src/commands/ad/device-bid.ts
118344
118645
  init_auth();
118345
118646
  init_cli_json_snapshot();
118346
118647
  init_bid_modifier_display();
118648
+ init_shared();
118347
118649
  init_bid_modifier_display();
118348
118650
  var CAMPAIGN_ZERO_AMBIGUOUS_DISPLAY = "\u2014 \u6216 -100%\uFF08\u7F51\u5173\u4E0D\u533A\u5206\uFF0C\u9700\u5728 Google Ads \u540E\u53F0\u786E\u8BA4\uFF09";
118349
118651
  function multiplierToAdGroupBidPercent(multiplier) {
@@ -119137,6 +119439,7 @@ init_cli_table();
119137
119439
  // src/commands/ad/campaign-batch-diff.ts
119138
119440
  init_auth();
119139
119441
  init_cli_json_snapshot();
119442
+ init_shared();
119140
119443
  import fs9 from "fs";
119141
119444
  import path14 from "path";
119142
119445
 
@@ -119170,14 +119473,14 @@ function buildExtensionRemediateCommandLine(opts) {
119170
119473
  const campaignFlag = level === "Campaign" ? `--campaign-id ${campaignId}` : "";
119171
119474
  const base = ["siluzan-tso", "ad", "extension"];
119172
119475
  if (type === "SITELINK") {
119173
- const text2 = pickString(props["Text"], props["LinkText"]);
119476
+ const text3 = pickString(props["Text"], props["LinkText"]);
119174
119477
  const url = pickString(props["DestinationUrl"]);
119175
- if (!text2 || !url) return null;
119478
+ if (!text3 || !url) return null;
119176
119479
  const parts = [
119177
119480
  ...base,
119178
119481
  "sitelink",
119179
119482
  `-a ${accountId}`,
119180
- `--text ${shellArg(text2)}`,
119483
+ `--text ${shellArg(text3)}`,
119181
119484
  `--url ${shellArg(url)}`
119182
119485
  ];
119183
119486
  const line2 = pickString(props["Line2"], props["Description1"]);
@@ -119191,9 +119494,9 @@ function buildExtensionRemediateCommandLine(opts) {
119191
119494
  return parts.join(" ");
119192
119495
  }
119193
119496
  if (type === "CALLOUT") {
119194
- const text2 = pickString(props["Text"], props["CalloutText"]);
119195
- if (!text2) return null;
119196
- const parts = [...base, "callout", `-a ${accountId}`, `--text ${shellArg(text2)}`, levelFlag];
119497
+ const text3 = pickString(props["Text"], props["CalloutText"]);
119498
+ if (!text3) return null;
119499
+ const parts = [...base, "callout", `-a ${accountId}`, `--text ${shellArg(text3)}`, levelFlag];
119197
119500
  if (campaignFlag) parts.push(campaignFlag);
119198
119501
  parts.push(`--json-out ${jsonOut}`);
119199
119502
  parts.push(`--commit ${shellArg("batch-diff remediate extension")}`);
@@ -119353,11 +119656,11 @@ function buildBatchOperationSlots(cfg) {
119353
119656
  return;
119354
119657
  }
119355
119658
  pickArray(block, "Items", "items").forEach((it, ii) => {
119356
- const text2 = pickText(asRecord3(it), "Text", "text");
119659
+ const text3 = pickText(asRecord3(it), "Text", "text");
119357
119660
  push(
119358
119661
  "ad_group_criterion",
119359
119662
  `${groupPath}.KeywordsForBatchJob[${bi}].Items[${ii}]`,
119360
- `\u5173\u952E\u8BCD\u300C${text2}\u300D\uFF08\u7EC4\u300C${groupName}\u300D\uFF09`
119663
+ `\u5173\u952E\u8BCD\u300C${text3}\u300D\uFF08\u7EC4\u300C${groupName}\u300D\uFF09`
119361
119664
  );
119362
119665
  });
119363
119666
  });
@@ -119390,9 +119693,9 @@ function buildSubmittedOperationSlots(cfg) {
119390
119693
  return buildBatchOperationSlots(clone);
119391
119694
  }
119392
119695
  var OP_RE = /mutate_operations\[(\d+)\]\.([a-z_]+?)_operation\.([a-z_.]+)/;
119393
- function parseBatchOperationErrors(text2) {
119696
+ function parseBatchOperationErrors(text3) {
119394
119697
  const out = [];
119395
- for (const seg of text2.split(/Processing error:\s*/).slice(1)) {
119698
+ for (const seg of text3.split(/Processing error:\s*/).slice(1)) {
119396
119699
  const m = OP_RE.exec(seg);
119397
119700
  if (!m) continue;
119398
119701
  const head = seg.slice(0, m.index);
@@ -119549,8 +119852,8 @@ function pickLiveAdForPlannedRsa(plannedHeadlines, primary, unmatchedLiveAds) {
119549
119852
  const [picked] = unmatchedLiveAds.splice(bestIdx, 1);
119550
119853
  return picked ?? null;
119551
119854
  }
119552
- function livePoolHasText(pool, text2) {
119553
- const t = text2.trim().toLowerCase();
119855
+ function livePoolHasText(pool, text3) {
119856
+ const t = text3.trim().toLowerCase();
119554
119857
  return t.length > 0 && pool.has(t);
119555
119858
  }
119556
119859
  function listPlannedRsaAssets(ad, groupPath, adIndex, adGroupName) {
@@ -120436,8 +120739,8 @@ function printPlanItemsByAdGroup(configFile, layer, layerItems) {
120436
120739
  }
120437
120740
  }
120438
120741
  }
120439
- function mdCell(text2) {
120440
- return text2.replace(/\r?\n/g, " ").replace(/\|/g, "\uFF5C").trim();
120742
+ function mdCell(text3) {
120743
+ return text3.replace(/\r?\n/g, " ").replace(/\|/g, "\uFF5C").trim();
120441
120744
  }
120442
120745
  function statusLabel(status) {
120443
120746
  if (status === "ok") return "\u2705 \u5DF2\u521B\u5EFA";
@@ -120764,6 +121067,7 @@ async function runCampaignBatchDiff(opts) {
120764
121067
  }
120765
121068
 
120766
121069
  // src/commands/ad/ai-creation.ts
121070
+ init_shared();
120767
121071
  async function runAiCreationList(opts) {
120768
121072
  const config = loadConfig(opts.token);
120769
121073
  const params = new URLSearchParams();
@@ -121179,6 +121483,7 @@ async function runAiCreationUpdate(opts) {
121179
121483
  // src/commands/ad/pmax-create.ts
121180
121484
  init_auth();
121181
121485
  init_cli_json_snapshot();
121486
+ init_shared();
121182
121487
 
121183
121488
  // src/commands/ad/pmax-create-validate.ts
121184
121489
  var VALID_PMAX_BIDDING_STRATEGIES = [
@@ -121266,8 +121571,8 @@ function normalizeBidding(raw) {
121266
121571
  return byNum[n] ?? null;
121267
121572
  }
121268
121573
  function validateTextLengths(errors, lengthViolations, texts, arrayField, kind, limit, label2) {
121269
- texts.forEach((text2, i) => {
121270
- const len = calcGoogleCharLength(text2);
121574
+ texts.forEach((text3, i) => {
121575
+ const len = calcGoogleCharLength(text3);
121271
121576
  if (len > limit) {
121272
121577
  const field = `${arrayField}[${i}]`;
121273
121578
  pushLengthViolation(lengthViolations, {
@@ -121277,9 +121582,9 @@ function validateTextLengths(errors, lengthViolations, texts, arrayField, kind,
121277
121582
  limit,
121278
121583
  actual: len,
121279
121584
  countMode: "google",
121280
- text: text2
121585
+ text: text3
121281
121586
  });
121282
- pushErr3(errors, `${label2}[${i}] \u8D85\u8FC7 ${limit} \u5B57\u7B26\uFF08\u5F53\u524D ${len}\uFF0CCJK \u8BA1 2\uFF09\uFF1A"${text2}"`);
121587
+ pushErr3(errors, `${label2}[${i}] \u8D85\u8FC7 ${limit} \u5B57\u7B26\uFF08\u5F53\u524D ${len}\uFF0CCJK \u8BA1 2\uFF09\uFF1A"${text3}"`);
121283
121588
  }
121284
121589
  });
121285
121590
  }
@@ -121801,14 +122106,14 @@ async function uploadPmaxImageFileMultipart(config, googleApiUrl, accountId, abs
121801
122106
  if (config.apiKey) headers["x-api-key"] = config.apiKey;
121802
122107
  else headers.Authorization = `Bearer ${config.authToken}`;
121803
122108
  const res = await fetch(url, { method: "POST", headers, body: form });
121804
- const text2 = await res.text();
122109
+ const text3 = await res.text();
121805
122110
  if (!res.ok) {
121806
- const detail = verbose ? `\uFF1A${text2.slice(0, 300)}` : "";
122111
+ const detail = verbose ? `\uFF1A${text3.slice(0, 300)}` : "";
121807
122112
  throw new Error(`\u4E0A\u4F20\u56FE\u7247\u8D44\u4EA7 HTTP ${res.status}${detail}\uFF08${absImagePath}\uFF09`);
121808
122113
  }
121809
122114
  let data;
121810
122115
  try {
121811
- data = JSON.parse(text2);
122116
+ data = JSON.parse(text3);
121812
122117
  } catch {
121813
122118
  throw new Error(`\u4E0A\u4F20\u56FE\u7247\u8D44\u4EA7\u54CD\u5E94\u975E JSON\uFF08${absImagePath}\uFF09`);
121814
122119
  }
@@ -121943,6 +122248,7 @@ function assertPmaxImageSlotsResolved(slots, requiredKinds = ["marketing", "squa
121943
122248
 
121944
122249
  // src/commands/ad/pmax-load.ts
121945
122250
  import { readFileSync as readFileSync11 } from "fs";
122251
+ init_shared();
121946
122252
 
121947
122253
  // src/commands/ad/pmax-campaign-extensions.ts
121948
122254
  init_auth();
@@ -121969,19 +122275,19 @@ function validatePmaxSitelinkSpec(link, prefix, errors, warnings) {
121969
122275
  errors.push(`${prefix} \u4E0D\u80FD\u4E3A\u7A7A`);
121970
122276
  return;
121971
122277
  }
121972
- const text2 = link.text?.trim();
122278
+ const text3 = link.text?.trim();
121973
122279
  const url = link.destinationUrl?.trim();
121974
- if (!text2) errors.push(`${prefix}.text \u4E0D\u80FD\u4E3A\u7A7A`);
122280
+ if (!text3) errors.push(`${prefix}.text \u4E0D\u80FD\u4E3A\u7A7A`);
121975
122281
  if (!url) {
121976
122282
  errors.push(`${prefix}.destinationUrl \u4E0D\u80FD\u4E3A\u7A7A`);
121977
122283
  } else if (!/^https?:\/\/.+/i.test(url)) {
121978
122284
  errors.push(`${prefix}.destinationUrl \u683C\u5F0F\u4E0D\u6B63\u786E\uFF1A${url}`);
121979
122285
  }
121980
- if (text2 && url) {
122286
+ if (text3 && url) {
121981
122287
  validateSitelinkProperties(
121982
122288
  prefix,
121983
122289
  {
121984
- Text: text2,
122290
+ Text: text3,
121985
122291
  DestinationUrl: url,
121986
122292
  Line2: link.line2?.trim() ?? "",
121987
122293
  Line3: link.line3?.trim() ?? ""
@@ -122032,16 +122338,16 @@ function convertExtensionsForBatchJobToCampaignExtensions(items3) {
122032
122338
  const type = String(ext.typeV2 ?? ext.TypeV2 ?? ext.AssetFieldType ?? "").toUpperCase();
122033
122339
  const props = ext.Properties ?? {};
122034
122340
  if (type === "CALLOUT") {
122035
- const text2 = String(props.Text ?? props.CalloutText ?? "").trim();
122036
- if (text2) config.callouts.push(text2);
122341
+ const text3 = String(props.Text ?? props.CalloutText ?? "").trim();
122342
+ if (text3) config.callouts.push(text3);
122037
122343
  continue;
122038
122344
  }
122039
122345
  if (type === "SITELINK") {
122040
- const text2 = String(props.Text ?? props.LinkText ?? "").trim();
122346
+ const text3 = String(props.Text ?? props.LinkText ?? "").trim();
122041
122347
  const destinationUrl = String(props.DestinationUrl ?? "").trim();
122042
- if (text2 && destinationUrl) {
122348
+ if (text3 && destinationUrl) {
122043
122349
  config.sitelinks.push({
122044
- text: text2,
122350
+ text: text3,
122045
122351
  destinationUrl,
122046
122352
  line2: String(props.Line2 ?? props.Description1 ?? "").trim() || void 0,
122047
122353
  line3: String(props.Line3 ?? props.Description2 ?? "").trim() || void 0
@@ -122110,14 +122416,14 @@ function validatePmaxCampaignExtensions(ext, opts) {
122110
122416
  }
122111
122417
  const callouts = ext.callouts ?? [];
122112
122418
  for (let i = 0; i < callouts.length; i++) {
122113
- const text2 = callouts[i]?.trim();
122114
- if (!text2) {
122419
+ const text3 = callouts[i]?.trim();
122420
+ if (!text3) {
122115
122421
  errors.push(`campaignExtensions.callouts[${i}] \u4E0D\u80FD\u4E3A\u7A7A`);
122116
122422
  continue;
122117
122423
  }
122118
- if (text2.length > CALLOUT_TEXT_MAX_LEN) {
122424
+ if (text3.length > CALLOUT_TEXT_MAX_LEN) {
122119
122425
  errors.push(
122120
- `campaignExtensions.callouts[${i}] \u8D85\u8FC7 ${CALLOUT_TEXT_MAX_LEN} \u5B57\u7B26\uFF08\u5F53\u524D ${text2.length}\uFF09\uFF1A"${text2}"`
122426
+ `campaignExtensions.callouts[${i}] \u8D85\u8FC7 ${CALLOUT_TEXT_MAX_LEN} \u5B57\u7B26\uFF08\u5F53\u524D ${text3.length}\uFF09\uFF1A"${text3}"`
122121
122427
  );
122122
122428
  }
122123
122429
  }
@@ -122190,14 +122496,14 @@ async function postExtension(config, googleApiUrl, accountId, body, verbose) {
122190
122496
  return { ok: false, error: err instanceof Error ? err.message : String(err) };
122191
122497
  }
122192
122498
  }
122193
- function buildCalloutBody(accountId, campaignId, text2) {
122499
+ function buildCalloutBody(accountId, campaignId, text3) {
122194
122500
  return {
122195
122501
  activeuseridg: accountId,
122196
122502
  campaignId,
122197
122503
  level: "Campaign",
122198
122504
  typeV2: "CALLOUT",
122199
122505
  assetFieldType: "CALLOUT",
122200
- properties: { Text: text2.trim() }
122506
+ properties: { Text: text3.trim() }
122201
122507
  };
122202
122508
  }
122203
122509
  function buildSnippetBody(accountId, campaignId, header, values) {
@@ -122214,8 +122520,8 @@ function buildSnippetBody(accountId, campaignId, header, values) {
122214
122520
  };
122215
122521
  }
122216
122522
  function buildSitelinkBody(accountId, campaignId, link) {
122217
- const text2 = link.text.trim();
122218
- const line2 = link.line2?.trim() || text2;
122523
+ const text3 = link.text.trim();
122524
+ const line2 = link.line2?.trim() || text3;
122219
122525
  const line3 = link.line3?.trim() || line2;
122220
122526
  return {
122221
122527
  activeuseridg: accountId,
@@ -122224,7 +122530,7 @@ function buildSitelinkBody(accountId, campaignId, link) {
122224
122530
  typeV2: "SITELINK",
122225
122531
  assetFieldType: "SITELINK",
122226
122532
  properties: {
122227
- Text: text2,
122533
+ Text: text3,
122228
122534
  Line2: line2,
122229
122535
  Line3: line3,
122230
122536
  DestinationUrl: link.destinationUrl.trim()
@@ -122252,26 +122558,26 @@ async function attachPmaxCampaignExtensions(opts) {
122252
122558
  allOk: true
122253
122559
  };
122254
122560
  for (const raw of ext.callouts ?? []) {
122255
- const text2 = raw?.trim();
122256
- if (!text2) continue;
122561
+ const text3 = raw?.trim();
122562
+ if (!text3) continue;
122257
122563
  if (opts.logProgress) console.log(`
122258
- \u6302\u8F7D\u5BA3\u4F20\u4FE1\u606F\uFF1A${text2}`);
122564
+ \u6302\u8F7D\u5BA3\u4F20\u4FE1\u606F\uFF1A${text3}`);
122259
122565
  const posted = await postExtension(
122260
122566
  opts.config,
122261
122567
  opts.googleApiUrl,
122262
122568
  opts.accountId,
122263
- buildCalloutBody(opts.accountId, opts.campaignId, text2),
122569
+ buildCalloutBody(opts.accountId, opts.campaignId, text3),
122264
122570
  opts.verbose
122265
122571
  );
122266
122572
  if (posted.ok) {
122267
122573
  result.callouts.push({
122268
122574
  ok: true,
122269
122575
  id: String(posted.data["id"] ?? ""),
122270
- label: text2
122576
+ label: text3
122271
122577
  });
122272
122578
  } else {
122273
122579
  result.allOk = false;
122274
- result.callouts.push({ ok: false, error: posted.error, label: text2 });
122580
+ result.callouts.push({ ok: false, error: posted.error, label: text3 });
122275
122581
  }
122276
122582
  }
122277
122583
  for (const s of ext.structuredSnippets ?? []) {
@@ -122300,11 +122606,11 @@ async function attachPmaxCampaignExtensions(opts) {
122300
122606
  }
122301
122607
  }
122302
122608
  for (const link of ext.sitelinks ?? []) {
122303
- const text2 = link?.text?.trim();
122609
+ const text3 = link?.text?.trim();
122304
122610
  const url = link?.destinationUrl?.trim();
122305
- if (!text2 || !url) continue;
122611
+ if (!text3 || !url) continue;
122306
122612
  if (opts.logProgress) console.log(`
122307
- \u6302\u8F7D\u7AD9\u5185\u94FE\u63A5\uFF1A${text2} \u2192 ${url}`);
122613
+ \u6302\u8F7D\u7AD9\u5185\u94FE\u63A5\uFF1A${text3} \u2192 ${url}`);
122308
122614
  const posted = await postExtension(
122309
122615
  opts.config,
122310
122616
  opts.googleApiUrl,
@@ -122316,11 +122622,11 @@ async function attachPmaxCampaignExtensions(opts) {
122316
122622
  result.sitelinks.push({
122317
122623
  ok: true,
122318
122624
  id: String(posted.data["id"] ?? ""),
122319
- label: text2
122625
+ label: text3
122320
122626
  });
122321
122627
  } else {
122322
122628
  result.allOk = false;
122323
- result.sitelinks.push({ ok: false, error: posted.error, label: text2 });
122629
+ result.sitelinks.push({ ok: false, error: posted.error, label: text3 });
122324
122630
  }
122325
122631
  }
122326
122632
  if (ext.leadForm) {
@@ -122573,6 +122879,7 @@ init_auth();
122573
122879
  init_cli_json_snapshot();
122574
122880
  import { readFileSync as readFileSync12 } from "fs";
122575
122881
  import { dirname as dirname9, isAbsolute as isAbsolute4, resolve as resolve8 } from "path";
122882
+ init_shared();
122576
122883
  var PMAX_MONEY_KEYS = /* @__PURE__ */ new Set(["budget", "targetCpa_BidingAmount"]);
122577
122884
  function loadPmaxJsonFile(configFile) {
122578
122885
  let raw;
@@ -122784,13 +123091,13 @@ function runPmaxVideoValidation(configFile, cfg) {
122784
123091
  async function fetchVideoUploadStatus(config, googleApiUrl, mediaAccountId, resourceName, verbose) {
122785
123092
  const url = pyapiVideoStatusUrl(googleApiUrl, mediaAccountId, resourceName);
122786
123093
  const res = await fetch(url, { method: "GET", headers: authHeaders(config) });
122787
- const text2 = await res.text();
123094
+ const text3 = await res.text();
122788
123095
  if (!res.ok) {
122789
- const detail = verbose ? `\uFF1A${text2.slice(0, 300)}` : "";
123096
+ const detail = verbose ? `\uFF1A${text3.slice(0, 300)}` : "";
122790
123097
  throw new Error(`\u67E5\u8BE2\u89C6\u9891\u5904\u7406\u72B6\u6001 HTTP ${res.status}${detail}`);
122791
123098
  }
122792
123099
  try {
122793
- return JSON.parse(text2);
123100
+ return JSON.parse(text3);
122794
123101
  } catch {
122795
123102
  throw new Error("\u67E5\u8BE2\u89C6\u9891\u5904\u7406\u72B6\u6001\u54CD\u5E94\u975E JSON");
122796
123103
  }
@@ -122846,14 +123153,14 @@ async function uploadPmaxLocalVideo(opts) {
122846
123153
  );
122847
123154
  const url = pyapiVideoUploadUrl(opts.googleApiUrl);
122848
123155
  const res = await fetch(url, { method: "POST", headers: authHeaders(opts.config), body: form });
122849
- const text2 = await res.text();
123156
+ const text3 = await res.text();
122850
123157
  if (!res.ok) {
122851
- const detail = opts.verbose ? `\uFF1A${text2.slice(0, 400)}` : "";
123158
+ const detail = opts.verbose ? `\uFF1A${text3.slice(0, 400)}` : "";
122852
123159
  throw new Error(`\u4E0A\u4F20\u89C6\u9891 HTTP ${res.status}${detail}\uFF08${opts.absVideoPath}\uFF09`);
122853
123160
  }
122854
123161
  let data;
122855
123162
  try {
122856
- data = JSON.parse(text2);
123163
+ data = JSON.parse(text3);
122857
123164
  } catch {
122858
123165
  throw new Error(`\u4E0A\u4F20\u89C6\u9891\u54CD\u5E94\u975E JSON\uFF08${opts.absVideoPath}\uFF09`);
122859
123166
  }
@@ -123456,6 +123763,7 @@ async function buildEnableBrandGuidelinesApiBody(configFile, cfg, accountId, con
123456
123763
  }
123457
123764
 
123458
123765
  // src/commands/ad/pmax-mgmt.ts
123766
+ init_shared();
123459
123767
  function buildReportQuery(opts) {
123460
123768
  const params = new URLSearchParams();
123461
123769
  params.set("startDate", toGoogleDate(opts.startDate, -30).replace(/\//g, "-"));
@@ -126030,15 +126338,15 @@ function formatMetaAdError(err) {
126030
126338
  }
126031
126339
  return appendMetaAdErrorHint(raw);
126032
126340
  }
126033
- function appendMetaAdErrorHint(text2) {
126034
- if (!/advantage_audience|targeting_automation/i.test(text2)) return text2;
126035
- return `${text2}
126341
+ function appendMetaAdErrorHint(text3) {
126342
+ if (!/advantage_audience|targeting_automation/i.test(text3)) return text3;
126343
+ return `${text3}
126036
126344
  \u63D0\u793A\uFF1Acreate / adset-create \u5DF2\u63D0\u4EA4 targeting_automation.advantage_audience\uFF08\u9ED8\u8BA4 1\uFF1B\u6709 flexibleSpec \u65F6\u9ED8\u8BA4 0\uFF09\u3002
126037
126345
  \u4E0D\u8981\u65B0\u5EFA\u7CFB\u5217\u3002\u7528 --json-out \u91CC\u5DF2\u5EFA\u6210\u7684 campaignId \u8DD1 adset-create \u7EED\u8DD1\uFF1B\u7EC6\u5B9A\u5411\u53EF\u663E\u5F0F\u8BBE adset.advantageAudience=0\u3002`;
126038
126346
  }
126039
126347
  function isBidAmountRequired(err) {
126040
- const text2 = formatMetaAdError(err);
126041
- return /BID_AMOUNT_REQUIRED/i.test(text2) || /缺少竞价金额/.test(text2) || /必须提供 bid_amount/.test(text2) || /必须在 bid_amount/.test(text2);
126348
+ const text3 = formatMetaAdError(err);
126349
+ return /BID_AMOUNT_REQUIRED/i.test(text3) || /缺少竞价金额/.test(text3) || /必须提供 bid_amount/.test(text3) || /必须在 bid_amount/.test(text3);
126042
126350
  }
126043
126351
  function failMetaAd(action, err) {
126044
126352
  console.error(`
@@ -127435,7 +127743,7 @@ var QUESTION_TYPE_LABEL = {
127435
127743
  JOB_TITLE: "Job Title",
127436
127744
  CUSTOM: "Custom"
127437
127745
  };
127438
- function text(value) {
127746
+ function text2(value) {
127439
127747
  if (value == null) return PENDING;
127440
127748
  const s = String(value).trim();
127441
127749
  return s || PENDING;
@@ -127456,7 +127764,7 @@ function dailyBudgetText(cfg) {
127456
127764
  return `$${amount} / \u5929\uFF08${mode || "CBO"}\uFF09`;
127457
127765
  }
127458
127766
  function questionLabel(q) {
127459
- return q.label?.trim() || QUESTION_TYPE_LABEL[String(q.type || "").toUpperCase()] || text(q.type);
127767
+ return q.label?.trim() || QUESTION_TYPE_LABEL[String(q.type || "").toUpperCase()] || text2(q.type);
127460
127768
  }
127461
127769
  function questionRequired(q) {
127462
127770
  if (q.required === false) return "\u5426";
@@ -127563,21 +127871,21 @@ function writeOpsExcel(outPath, cfg) {
127563
127871
  hAlign: "center"
127564
127872
  });
127565
127873
  overview.blankRow();
127566
- overview.kvRow("\u5BA2\u6237\u540D\u79F0", text(plan.customerName));
127567
- overview.kvRow("Facebook \u4E3B\u9875", text(plan.pageUrl || "\u5F85 pages \u540E\u8865"), {
127874
+ overview.kvRow("\u5BA2\u6237\u540D\u79F0", text2(plan.customerName));
127875
+ overview.kvRow("Facebook \u4E3B\u9875", text2(plan.pageUrl || "\u5F85 pages \u540E\u8865"), {
127568
127876
  hyperlink: plan.pageUrl?.startsWith("http") ? plan.pageUrl : void 0
127569
127877
  });
127570
- overview.kvRow("\u5B98\u65B9\u7F51\u7AD9", text(plan.siteUrl), {
127878
+ overview.kvRow("\u5B98\u65B9\u7F51\u7AD9", text2(plan.siteUrl), {
127571
127879
  hyperlink: plan.siteUrl?.startsWith("http") ? plan.siteUrl : void 0
127572
127880
  });
127573
- overview.kvRow("\u4E3B\u8425\u4E1A\u52A1", text(plan.businessOneLiner));
127574
- overview.kvRow("\u6838\u5FC3\u4EA7\u54C1", text(plan.products));
127575
- overview.kvRow("\u6838\u5FC3\u5356\u70B9", text(plan.sellingPoints));
127576
- overview.kvRow("\u76EE\u6807\u5E02\u573A", text(plan.targetMarkets || joinList(cfg.adset?.countries, ", ")));
127577
- overview.kvRow("\u8425\u9500\u76EE\u6807", text(plan.mainResult || "\u8868\u5355\u8BE2\u76D8\uFF08Lead Form\uFF09"));
127881
+ overview.kvRow("\u4E3B\u8425\u4E1A\u52A1", text2(plan.businessOneLiner));
127882
+ overview.kvRow("\u6838\u5FC3\u4EA7\u54C1", text2(plan.products));
127883
+ overview.kvRow("\u6838\u5FC3\u5356\u70B9", text2(plan.sellingPoints));
127884
+ overview.kvRow("\u76EE\u6807\u5E02\u573A", text2(plan.targetMarkets || joinList(cfg.adset?.countries, ", ")));
127885
+ overview.kvRow("\u8425\u9500\u76EE\u6807", text2(plan.mainResult || "\u8868\u5355\u8BE2\u76D8\uFF08Lead Form\uFF09"));
127578
127886
  overview.kvRow("\u65E5\u9884\u7B97", dailyBudgetText(cfg));
127579
127887
  overview.kvRow("\u5E7F\u544A\u7ED3\u6784", structureSummary(cfg));
127580
- overview.kvRow("\u6295\u653E\u7B56\u7565", text(plan.strategy || plan.landingType));
127888
+ overview.kvRow("\u6295\u653E\u7B56\u7565", text2(plan.strategy || plan.landingType));
127581
127889
  const profile = wb.addSheet(SHEET_NAMES[1]);
127582
127890
  profile.setColWidths([26, 48, 26, 26]);
127583
127891
  profile.titleBar(profileTitle(plan), { fill: OPS.title });
@@ -127586,13 +127894,13 @@ function writeOpsExcel(outPath, cfg) {
127586
127894
  profile.sectionBar("\u4E00\u3001\u516C\u53F8\u57FA\u672C\u4FE1\u606F", { fill: OPS.section });
127587
127895
  profile.tableHeader(["\u9879\u76EE", "\u5185\u5BB9", "\u5907\u6CE8", ""], { fill: OPS.header });
127588
127896
  const profileRows = [
127589
- ["\u516C\u53F8\u540D\u79F0", text(plan.customerName), "\u5355\u4E3B\u4F53\u5B9A\u4F4D"],
127590
- ["\u5B98\u7F51", text(plan.siteUrl), "\u9A8C\u8BC1\u516C\u53F8\u771F\u5B9E\u6027"],
127591
- ["\u6838\u5FC3\u4EA7\u54C1", text(plan.products), ""],
127592
- ["\u5E7F\u544A\u805A\u7126", text(plan.adFocus), ""],
127593
- ["\u5DE5\u5382\u5B9E\u529B", text(plan.factory), "\u6838\u5FC3\u5B9E\u529B\u80CC\u4E66"],
127594
- ["\u8BA4\u8BC1\u8D44\u8D28", text(plan.certifications), "B2B\u4FE1\u4EFB\u4FE1\u53F7"],
127595
- ["\u6838\u5FC3\u5356\u70B9", text(plan.sellingPoints || endorsementText(plan)), "\u5DEE\u5F02\u5316\u4F18\u52BF"]
127897
+ ["\u516C\u53F8\u540D\u79F0", text2(plan.customerName), "\u5355\u4E3B\u4F53\u5B9A\u4F4D"],
127898
+ ["\u5B98\u7F51", text2(plan.siteUrl), "\u9A8C\u8BC1\u516C\u53F8\u771F\u5B9E\u6027"],
127899
+ ["\u6838\u5FC3\u4EA7\u54C1", text2(plan.products), ""],
127900
+ ["\u5E7F\u544A\u805A\u7126", text2(plan.adFocus), ""],
127901
+ ["\u5DE5\u5382\u5B9E\u529B", text2(plan.factory), "\u6838\u5FC3\u5B9E\u529B\u80CC\u4E66"],
127902
+ ["\u8BA4\u8BC1\u8D44\u8D28", text2(plan.certifications), "B2B\u4FE1\u4EFB\u4FE1\u53F7"],
127903
+ ["\u6838\u5FC3\u5356\u70B9", text2(plan.sellingPoints || endorsementText(plan)), "\u5DEE\u5F02\u5316\u4F18\u52BF"]
127596
127904
  ];
127597
127905
  for (const [label2, value, note] of profileRows) {
127598
127906
  profile.dataRow([label2, value, note || "", ""], { labelColumns: [0] });
@@ -127609,20 +127917,20 @@ function writeOpsExcel(outPath, cfg) {
127609
127917
  } else {
127610
127918
  for (const [industry, job] of termRows) {
127611
127919
  profile.dataRow(
127612
- [text(industry.en), optionalText(industry.zh), text(job.en), optionalText(job.zh)],
127920
+ [text2(industry.en), optionalText(industry.zh), text2(job.en), optionalText(job.zh)],
127613
127921
  { labelColumns: [0, 2] }
127614
127922
  );
127615
127923
  }
127616
127924
  }
127617
127925
  profile.blankRow();
127618
127926
  profile.sectionBar("\u8865\u5145\u5174\u8DA3\u8BCD\uFF08Broad Targeting\uFF09", { fill: OPS.section });
127619
- profile.noteRow(text(plan.targeting?.extraInterests));
127927
+ profile.noteRow(text2(plan.targeting?.extraInterests));
127620
127928
  profile.noteRow(
127621
127929
  plan.targeting?.disclaimer?.trim() || "\u672C\u6279\u7EC4\u5B9A\u5411\u53EA\u8BA4\u56FD\u5BB6/\u5E74\u9F84/\u6027\u522B\uFF0C\u5174\u8DA3\u8BCD\u4EC5\u4F5C\u7D20\u6750\u4E0E\u83B7\u5BA2\u8BF4\u660E"
127622
127930
  );
127623
127931
  const structure = wb.addSheet(SHEET_NAMES[2]);
127624
127932
  structure.setColWidths([18, 44, 26, 22]);
127625
- structure.titleBar(text(plan.brand ? `\u5E7F\u544A\u8D26\u6237\u7ED3\u6784\uFF1A${plan.brand}` : "\u5E7F\u544A\u8D26\u6237\u7ED3\u6784"), {
127933
+ structure.titleBar(text2(plan.brand ? `\u5E7F\u544A\u8D26\u6237\u7ED3\u6784\uFF1A${plan.brand}` : "\u5E7F\u544A\u8D26\u6237\u7ED3\u6784"), {
127626
127934
  fill: OPS.title
127627
127935
  });
127628
127936
  structure.subtitleBar(subtitle);
@@ -127633,13 +127941,13 @@ function writeOpsExcel(outPath, cfg) {
127633
127941
  structure.dataRow(
127634
127942
  [
127635
127943
  "\u5E7F\u544A\u76EE\u6807",
127636
- text(cfg.campaign?.name),
127637
- text(cfg.adset?.optimizationGoal || "Leads\uFF08\u6F5C\u5728\u5BA2\u6237\u5F00\u53D1\uFF09"),
127944
+ text2(cfg.campaign?.name),
127945
+ text2(cfg.adset?.optimizationGoal || "Leads\uFF08\u6F5C\u5728\u5BA2\u6237\u5F00\u53D1\uFF09"),
127638
127946
  dailyBudgetText(cfg)
127639
127947
  ],
127640
127948
  { labelColumns: [0] }
127641
127949
  );
127642
- structure.dataRow(["\u5E7F\u544A\u7EC4\u7B56\u7565", `${adSets.length} \u4E2A\u5E7F\u544A\u7EC4`, text(plan.strategy), ""], {
127950
+ structure.dataRow(["\u5E7F\u544A\u7EC4\u7B56\u7565", `${adSets.length} \u4E2A\u5E7F\u544A\u7EC4`, text2(plan.strategy), ""], {
127643
127951
  labelColumns: [0]
127644
127952
  });
127645
127953
  structure.dataRow(
@@ -127671,11 +127979,11 @@ function writeOpsExcel(outPath, cfg) {
127671
127979
  for (const row of adSets) {
127672
127980
  lead.dataRow(
127673
127981
  [
127674
- text(row.label || row.name),
127675
- text(row.region || joinList(row.countries, "\u3001")),
127676
- text(row.targeting),
127677
- text(row.creatives),
127678
- text(row.cpl),
127982
+ text2(row.label || row.name),
127983
+ text2(row.region || joinList(row.countries, "\u3001")),
127984
+ text2(row.targeting),
127985
+ text2(row.creatives),
127986
+ text2(row.cpl),
127679
127987
  optionalText(row.note)
127680
127988
  ],
127681
127989
  { labelColumns: [0] }
@@ -127701,9 +128009,9 @@ function writeOpsExcel(outPath, cfg) {
127701
128009
  for (const block of resolveCopyBlocks(cfg)) {
127702
128010
  lead.dataRow(
127703
128011
  [
127704
- text(block.element),
128012
+ text2(block.element),
127705
128013
  optionalText(block.kind),
127706
- text(block.text),
128014
+ text2(block.text),
127707
128015
  optionalText(block.note),
127708
128016
  "",
127709
128017
  ""
@@ -127723,28 +128031,28 @@ function renderMarkdown(cfg) {
127723
128031
  const jobs = (plan.targeting?.jobs ?? []).map((row) => `- ${row.en ?? PENDING}${row.zh ? `\uFF08${row.zh}\uFF09` : ""}`).join("\n");
127724
128032
  const copies = resolveCopyBlocks(cfg).map((block) => `### ${block.element?.replace(/\n/g, " ") || "\u6587\u6848"}
127725
128033
 
127726
- ${text(block.text)}
128034
+ ${text2(block.text)}
127727
128035
  `).join("\n");
127728
128036
  return `# ${overviewTitle(plan)}
127729
128037
 
127730
- - \u5BA2\u6237/\u7AD9\u70B9\uFF1A${text(plan.customerName)} + ${text(plan.siteUrl)} + ${text(plan.businessOneLiner)}
127731
- - \u4E3B\u7ED3\u679C\uFF1A${text(plan.mainResult)}
127732
- - \u843D\u5730\u5EFA\u8BAE\uFF1A${text(plan.landingType)}
128038
+ - \u5BA2\u6237/\u7AD9\u70B9\uFF1A${text2(plan.customerName)} + ${text2(plan.siteUrl)} + ${text2(plan.businessOneLiner)}
128039
+ - \u4E3B\u7ED3\u679C\uFF1A${text2(plan.mainResult)}
128040
+ - \u843D\u5730\u5EFA\u8BAE\uFF1A${text2(plan.landingType)}
127733
128041
  - \u65E5\u9884\u7B97\uFF1A${dailyBudgetText(cfg)}\u3000\u56FD\u5BB6\uFF1A${joinList(cfg.adset?.countries, ", ")}
127734
128042
  - \u5E7F\u544A\u7ED3\u6784\uFF1A${structureSummary(cfg)}
127735
128043
  - \u8BA1\u5212\u72B6\u6001\uFF1A**\u5F85\u786E\u8BA4**\u3000\u672C\u6279\u53EF\u6267\u884C\uFF1AInstant Form \u7EBF\u7D22\u5E7F\u544A\uFF08\u975E Advantage+\uFF09
127736
128044
 
127737
- > \u8D26\u6237\uFF1A${text(cfg.account)}\u3000\u4E3B\u9875\uFF1A${text(cfg.pageId)}
128045
+ > \u8D26\u6237\uFF1A${text2(cfg.account)}\u3000\u4E3B\u9875\uFF1A${text2(cfg.pageId)}
127738
128046
 
127739
128047
  ## \u4E00\u3001\u516C\u53F8\u57FA\u672C\u4FE1\u606F
127740
128048
 
127741
128049
  | \u9879\u76EE | \u5185\u5BB9 |
127742
128050
  | --- | --- |
127743
- | \u6838\u5FC3\u4EA7\u54C1 | ${text(plan.products)} |
127744
- | \u5E7F\u544A\u805A\u7126 | ${text(plan.adFocus)} |
127745
- | \u5DE5\u5382\u5B9E\u529B | ${text(plan.factory)} |
127746
- | \u8BA4\u8BC1\u8D44\u8D28 | ${text(plan.certifications)} |
127747
- | \u6838\u5FC3\u5356\u70B9 | ${text(plan.sellingPoints)} |
128051
+ | \u6838\u5FC3\u4EA7\u54C1 | ${text2(plan.products)} |
128052
+ | \u5E7F\u544A\u805A\u7126 | ${text2(plan.adFocus)} |
128053
+ | \u5DE5\u5382\u5B9E\u529B | ${text2(plan.factory)} |
128054
+ | \u8BA4\u8BC1\u8D44\u8D28 | ${text2(plan.certifications)} |
128055
+ | \u6838\u5FC3\u5356\u70B9 | ${text2(plan.sellingPoints)} |
127748
128056
  | \u53EF\u5F15\u7528\u80CC\u4E66 | ${endorsementText(plan)} |
127749
128057
 
127750
128058
  ## \u5B9A\u5411\u8BF4\u660E\uFF08\u5BA1\u67E5\u7A3F\uFF09
@@ -127757,7 +128065,7 @@ ${industries || PENDING}
127757
128065
 
127758
128066
  ${jobs || PENDING}
127759
128067
 
127760
- \u8865\u5145\u5174\u8DA3\u8BCD\uFF1A${text(plan.targeting?.extraInterests)}
128068
+ \u8865\u5145\u5174\u8DA3\u8BCD\uFF1A${text2(plan.targeting?.extraInterests)}
127761
128069
 
127762
128070
  ${plan.targeting?.disclaimer || "\u672C\u6279\u7EC4\u5B9A\u5411\u53EA\u8BA4\u56FD\u5BB6/\u5E74\u9F84/\u6027\u522B\uFF0C\u5174\u8DA3\u8BCD\u4EC5\u4F5C\u7D20\u6750\u4E0E\u83B7\u5BA2\u8BF4\u660E\u3002"}
127763
128071
 
@@ -127766,7 +128074,7 @@ ${plan.targeting?.disclaimer || "\u672C\u6279\u7EC4\u5B9A\u5411\u53EA\u8BA4\u56F
127766
128074
  | \u5E7F\u544A\u7EC4 | \u5730\u533A | \u7D20\u6750 | \u8BE2\u76D8\u6210\u672C |
127767
128075
  | --- | --- | --- | --- |
127768
128076
  ${adSets.map(
127769
- (row) => `| ${text(row.label || row.name)} | ${text(row.region || joinList(row.countries, "\u3001"))} | ${text(row.creatives)} | ${text(row.cpl)} |`
128077
+ (row) => `| ${text2(row.label || row.name)} | ${text2(row.region || joinList(row.countries, "\u3001"))} | ${text2(row.creatives)} | ${text2(row.cpl)} |`
127770
128078
  ).join("\n")}
127771
128079
 
127772
128080
  ## Instant Form \u5B57\u6BB5
@@ -127783,10 +128091,10 @@ ${copies}
127783
128091
 
127784
128092
  | \u9879 | \u5185\u5BB9 |
127785
128093
  | --- | --- |
127786
- | message | ${text(cfg.creative?.message)} |
127787
- | headline | ${text(cfg.creative?.headline)} |
127788
- | link | ${text(cfg.creative?.link)} |
127789
- | \u8868\u5355 | ${text(cfg.form?.name || cfg.form?.reuseId)} |
128094
+ | message | ${text2(cfg.creative?.message)} |
128095
+ | headline | ${text2(cfg.creative?.headline)} |
128096
+ | link | ${text2(cfg.creative?.link)} |
128097
+ | \u8868\u5355 | ${text2(cfg.form?.name || cfg.form?.reuseId)} |
127790
128098
 
127791
128099
  \u8349\u6848\u4F9D\u636E\u4E3A\u516C\u5F00\u9875\u9762/\u5BA2\u6237\u6750\u6599\uFF0C\u975E\u5E7F\u544A\u540E\u53F0\u6570\u636E\u3002
127792
128100
  `;
@@ -128784,6 +129092,7 @@ init_cli_table();
128784
129092
 
128785
129093
  // src/commands/keyword-account-match.ts
128786
129094
  init_auth();
129095
+ init_shared();
128787
129096
  function normalizeKeywordForAccountMatch(raw) {
128788
129097
  return unwrapKeywordDisplayTextForEdit(raw.trim()).toLowerCase();
128789
129098
  }
@@ -128814,9 +129123,9 @@ async function fetchAccountPositiveKeywordNormSet(config, googleApiUrl, accountI
128814
129123
  if (seenIds.has(id)) continue;
128815
129124
  seenIds.add(id);
128816
129125
  }
128817
- const text2 = extractKeywordTextFromManagementRow(row);
128818
- if (!text2) continue;
128819
- const norm = normalizeKeywordForAccountMatch(text2);
129126
+ const text3 = extractKeywordTextFromManagementRow(row);
129127
+ if (!text3) continue;
129128
+ const norm = normalizeKeywordForAccountMatch(text3);
128820
129129
  if (norm) normSet.add(norm);
128821
129130
  }
128822
129131
  return normSet;
@@ -129335,8 +129644,8 @@ function similarityPercent(score) {
129335
129644
  const s = Math.min(1, Math.max(0, score));
129336
129645
  return `${Math.round(s * 100)}%`;
129337
129646
  }
129338
- function blockquoteMarkdown(text2) {
129339
- return text2.split("\n").map((line) => `> ${line}`).join("\n");
129647
+ function blockquoteMarkdown(text3) {
129648
+ return text3.split("\n").map((line) => `> ${line}`).join("\n");
129340
129649
  }
129341
129650
  function formatTime(iso) {
129342
129651
  if (!iso) return "";
@@ -130004,8 +130313,8 @@ function assertGoogleMediaCustomerId(raw) {
130004
130313
  process.exit(1);
130005
130314
  }
130006
130315
  }
130007
- function parseCustomerAccessPermissionBody(text2) {
130008
- const trimmed = text2.trim();
130316
+ function parseCustomerAccessPermissionBody(text3) {
130317
+ const trimmed = text3.trim();
130009
130318
  if (!trimmed) return null;
130010
130319
  try {
130011
130320
  const parsed = JSON.parse(trimmed);
@@ -130047,8 +130356,8 @@ function interpretCustomerAccessPermission(mediaCustomerId, httpStatus, rawBody)
130047
130356
  };
130048
130357
  }
130049
130358
  if (httpStatus === 403) {
130050
- const text2 = typeof rawBody === "string" ? rawBody : rawBody == null ? "" : String(rawBody);
130051
- if (text2.includes("token\u4E0D\u80FD\u4E3A\u7A7A") || text2.toLowerCase().includes("token") && text2.includes("\u7A7A")) {
130359
+ const text3 = typeof rawBody === "string" ? rawBody : rawBody == null ? "" : String(rawBody);
130360
+ if (text3.includes("token\u4E0D\u80FD\u4E3A\u7A7A") || text3.toLowerCase().includes("token") && text3.includes("\u7A7A")) {
130052
130361
  return {
130053
130362
  ...base,
130054
130363
  status: "google_not_bound",
@@ -131963,12 +132272,12 @@ async function uploadAttachment(filePath, apiBaseUrl, config, verbose) {
131963
132272
  headers,
131964
132273
  body: form
131965
132274
  });
131966
- const text2 = await res.text();
132275
+ const text3 = await res.text();
131967
132276
  if (!res.ok) {
131968
- const detail = verbose ? `\uFF1A${text2.slice(0, 300)}` : "";
132277
+ const detail = verbose ? `\uFF1A${text3.slice(0, 300)}` : "";
131969
132278
  throw new Error(`\u4E0A\u4F20\u6587\u4EF6 HTTP ${res.status}${detail}`);
131970
132279
  }
131971
- const data = JSON.parse(text2);
132280
+ const data = JSON.parse(text3);
131972
132281
  if (!data.id) {
131973
132282
  throw new Error("\u4E0A\u4F20\u6587\u4EF6\u5931\u8D25\uFF1A\u54CD\u5E94\u4E2D\u672A\u5305\u542B\u6587\u4EF6 ID");
131974
132283
  }
@@ -133713,9 +134022,9 @@ init_cli_json_snapshot();
133713
134022
  import fs14 from "fs";
133714
134023
  import path19 from "path";
133715
134024
  function readJson(filePath) {
133716
- const text2 = fs14.readFileSync(filePath, "utf8");
134025
+ const text3 = fs14.readFileSync(filePath, "utf8");
133717
134026
  try {
133718
- const v = JSON.parse(text2);
134027
+ const v = JSON.parse(text3);
133719
134028
  if (!v || typeof v !== "object" || Array.isArray(v)) {
133720
134029
  throw new Error("\u987B\u4E3A JSON \u5BF9\u8C61");
133721
134030
  }
@@ -133822,9 +134131,9 @@ function websiteDiagnosisRuntimePath() {
133822
134131
  }
133823
134132
  function readJsonFile(filePath) {
133824
134133
  const raw = fsPromises.readFile(filePath, "utf8");
133825
- return raw.then((text2) => {
134134
+ return raw.then((text3) => {
133826
134135
  try {
133827
- return JSON.parse(text2);
134136
+ return JSON.parse(text3);
133828
134137
  } catch {
133829
134138
  throw new Error(`\u65E0\u6CD5\u89E3\u6790 JSON\uFF1A${filePath}`);
133830
134139
  }
@@ -136549,7 +136858,7 @@ function collectGoldClaimTexts(data) {
136549
136858
  if (rec) texts.push(String(rec.title ?? ""), String(rec.description ?? ""));
136550
136859
  }
136551
136860
  }
136552
- return texts.filter((text2) => text2.trim().length > 0);
136861
+ return texts.filter((text3) => text3.trim().length > 0);
136553
136862
  }
136554
136863
  function findIdenticalTexts(texts, minCount) {
136555
136864
  const counts = /* @__PURE__ */ new Map();
@@ -136557,8 +136866,8 @@ function findIdenticalTexts(texts, minCount) {
136557
136866
  if (!t) continue;
136558
136867
  counts.set(t, (counts.get(t) ?? 0) + 1);
136559
136868
  }
136560
- for (const [text2, count2] of counts) {
136561
- if (count2 >= minCount) return text2;
136869
+ for (const [text3, count2] of counts) {
136870
+ if (count2 >= minCount) return text3;
136562
136871
  }
136563
136872
  return null;
136564
136873
  }
@@ -136631,20 +136940,20 @@ function asRecord14(value) {
136631
136940
  }
136632
136941
  return null;
136633
136942
  }
136634
- function proseColonIndex(text2) {
136635
- const cn = text2.indexOf("\uFF1A");
136636
- if (cn > 0 && cn < text2.length - 1) return cn;
136637
- for (let i = 1; i < text2.length - 1; i++) {
136638
- if (text2[i] !== ":") continue;
136639
- if (text2[i + 1] === "/") continue;
136640
- const scheme = text2.slice(Math.max(0, i - 5), i);
136943
+ function proseColonIndex(text3) {
136944
+ const cn = text3.indexOf("\uFF1A");
136945
+ if (cn > 0 && cn < text3.length - 1) return cn;
136946
+ for (let i = 1; i < text3.length - 1; i++) {
136947
+ if (text3[i] !== ":") continue;
136948
+ if (text3[i + 1] === "/") continue;
136949
+ const scheme = text3.slice(Math.max(0, i - 5), i);
136641
136950
  if (/https?$/i.test(scheme)) continue;
136642
136951
  return i;
136643
136952
  }
136644
136953
  return -1;
136645
136954
  }
136646
- function splitOverviewProse(text2) {
136647
- const trimmed = text2.trim();
136955
+ function splitOverviewProse(text3) {
136956
+ const trimmed = text3.trim();
136648
136957
  const cut = proseColonIndex(trimmed);
136649
136958
  if (cut > 0) {
136650
136959
  const title = trimmed.slice(0, cut).trim();
@@ -136655,9 +136964,9 @@ function splitOverviewProse(text2) {
136655
136964
  }
136656
136965
  function normalizeOverviewItem(item) {
136657
136966
  if (typeof item === "string" || typeof item === "number") {
136658
- const text2 = String(item).trim();
136659
- if (!text2) return null;
136660
- return splitOverviewProse(text2);
136967
+ const text3 = String(item).trim();
136968
+ if (!text3) return null;
136969
+ return splitOverviewProse(text3);
136661
136970
  }
136662
136971
  const rec = asRecord14(item);
136663
136972
  if (!rec) return null;
@@ -136738,16 +137047,16 @@ function creativeSuggestionFromRow(row) {
136738
137047
  function distinctCopies(rows, key) {
136739
137048
  const counts = /* @__PURE__ */ new Map();
136740
137049
  for (const row of rows) {
136741
- const text2 = String(row[key] ?? "");
136742
- counts.set(text2, (counts.get(text2) ?? 0) + 1);
137050
+ const text3 = String(row[key] ?? "");
137051
+ counts.set(text3, (counts.get(text3) ?? 0) + 1);
136743
137052
  }
136744
137053
  const seen = /* @__PURE__ */ new Map();
136745
137054
  return rows.map((row) => {
136746
- const text2 = String(row[key] ?? "");
136747
- if ((counts.get(text2) ?? 0) < 3) return row;
136748
- const n = (seen.get(text2) ?? 0) + 1;
136749
- seen.set(text2, n);
136750
- return { ...row, [key]: `${text2}\uFF08\u540C\u7ED3\u6784\u7B2C ${n} \u6761\uFF09` };
137055
+ const text3 = String(row[key] ?? "");
137056
+ if ((counts.get(text3) ?? 0) < 3) return row;
137057
+ const n = (seen.get(text3) ?? 0) + 1;
137058
+ seen.set(text3, n);
137059
+ return { ...row, [key]: `${text3}\uFF08\u540C\u7ED3\u6784\u7B2C ${n} \u6761\uFF09` };
136751
137060
  });
136752
137061
  }
136753
137062
  function mapRows(rows, write) {
@@ -138908,7 +139217,7 @@ function buildMetaPeriodExcelSheets(data) {
138908
139217
  })
138909
139218
  ],
138910
139219
  [],
138911
- ...buildSummaryNarrative(data).map((text2) => [text2])
139220
+ ...buildSummaryNarrative(data).map((text3) => [text3])
138912
139221
  ];
138913
139222
  const dailyRows = [metricHeaders(currency, "\u5355\u65E5")];
138914
139223
  for (const raw of asArray3(tables.daily)) {
@@ -139281,9 +139590,9 @@ function parseRenderFormats(raw) {
139281
139590
  return [...new Set(out)];
139282
139591
  }
139283
139592
  async function readJsonFile4(filePath) {
139284
- const text2 = await fsPromises3.readFile(filePath, "utf8");
139593
+ const text3 = await fsPromises3.readFile(filePath, "utf8");
139285
139594
  try {
139286
- return JSON.parse(text2);
139595
+ return JSON.parse(text3);
139287
139596
  } catch {
139288
139597
  throw new Error(`\u65E0\u6CD5\u89E3\u6790 JSON\uFF1A${filePath}`);
139289
139598
  }
@@ -139742,9 +140051,9 @@ function metaDiagnosisReportTemplatePath() {
139742
140051
  return resolveSkillTemplatePath5(TEMPLATE_BASENAME2);
139743
140052
  }
139744
140053
  async function readJsonFile5(filePath) {
139745
- const text2 = await fsPromises4.readFile(filePath, "utf8");
140054
+ const text3 = await fsPromises4.readFile(filePath, "utf8");
139746
140055
  try {
139747
- return JSON.parse(text2);
140056
+ return JSON.parse(text3);
139748
140057
  } catch {
139749
140058
  throw new Error(`\u65E0\u6CD5\u89E3\u6790 JSON\uFF1A${filePath}`);
139750
140059
  }
@@ -141874,9 +142183,9 @@ function bingPeriodReportTemplatePath() {
141874
142183
  return resolveSkillTemplatePath6(TEMPLATE_BASENAME3);
141875
142184
  }
141876
142185
  async function readJsonFile6(filePath) {
141877
- const text2 = await fsPromises5.readFile(filePath, "utf8");
142186
+ const text3 = await fsPromises5.readFile(filePath, "utf8");
141878
142187
  try {
141879
- return JSON.parse(text2);
142188
+ return JSON.parse(text3);
141880
142189
  } catch {
141881
142190
  throw new Error(`\u65E0\u6CD5\u89E3\u6790 JSON\uFF1A${filePath}`);
141882
142191
  }
@@ -142773,9 +143082,9 @@ function tiktokPeriodReportTemplatePath() {
142773
143082
  return resolveSkillTemplatePath7(TEMPLATE_BASENAME4);
142774
143083
  }
142775
143084
  async function readJsonFile7(filePath) {
142776
- const text2 = await fsPromises6.readFile(filePath, "utf8");
143085
+ const text3 = await fsPromises6.readFile(filePath, "utf8");
142777
143086
  try {
142778
- return JSON.parse(text2);
143087
+ return JSON.parse(text3);
142779
143088
  } catch {
142780
143089
  throw new Error(`\u65E0\u6CD5\u89E3\u6790 JSON\uFF1A${filePath}`);
142781
143090
  }
@@ -144549,9 +144858,9 @@ function yandexPeriodReportTemplatePath() {
144549
144858
  return resolveSkillTemplatePath8(TEMPLATE_BASENAME5);
144550
144859
  }
144551
144860
  async function readJsonFile8(filePath) {
144552
- const text2 = await fsPromises7.readFile(filePath, "utf8");
144861
+ const text3 = await fsPromises7.readFile(filePath, "utf8");
144553
144862
  try {
144554
- return JSON.parse(text2);
144863
+ return JSON.parse(text3);
144555
144864
  } catch {
144556
144865
  throw new Error(`\u65E0\u6CD5\u89E3\u6790 JSON\uFF1A${filePath}`);
144557
144866
  }
@@ -145210,12 +145519,12 @@ function hasDateFromTo(args) {
145210
145519
  }
145211
145520
  var RERUN_HINT = "\u7ACB\u523B\u6539\u8DD1\u63D0\u793A\u4E2D\u7684\u90A3\u6761\uFF0C\u4E0D\u8981\u505C\u5728\u9519\u8BEF\u547D\u4EE4\u3002";
145212
145521
  function fatal(body) {
145213
- const text2 = body.replace(/\s+$/g, "");
145214
- if (text2.includes(RERUN_HINT)) return `
145215
- ${text2}
145522
+ const text3 = body.replace(/\s+$/g, "");
145523
+ if (text3.includes(RERUN_HINT)) return `
145524
+ ${text3}
145216
145525
  `;
145217
145526
  return `
145218
- ${text2}
145527
+ ${text3}
145219
145528
  ${RERUN_HINT}
145220
145529
  `;
145221
145530
  }