siluzan-tso-cli 1.1.56-beta.2 → 1.1.56-beta.3

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
@@ -103126,7 +103126,7 @@ function narrativeClaimsMobileSpendLeads(text2) {
103126
103126
  if (!t.includes("\u79FB\u52A8")) return false;
103127
103127
  return /移动(?:端|设备)?(?:为|是)主要消耗/.test(t) || /移动(?:端|设备)?为主要(?:消耗)?设备/.test(t) || /移动(?:端|设备)?(?:消耗|花费|费用|流量)?占比高/.test(t);
103128
103128
  }
103129
- function asRecord18(value) {
103129
+ function asRecord19(value) {
103130
103130
  if (value && typeof value === "object" && !Array.isArray(value)) return value;
103131
103131
  return null;
103132
103132
  }
@@ -103134,16 +103134,16 @@ function stringList(value) {
103134
103134
  return (Array.isArray(value) ? value : []).map((item) => typeof item === "string" ? item.trim() : "").filter(Boolean);
103135
103135
  }
103136
103136
  function collectOkkiNarrativeText(data) {
103137
- const narrative = asRecord18(data.narrative) ?? {};
103137
+ const narrative = asRecord19(data.narrative) ?? {};
103138
103138
  const parts = [];
103139
- const sheetAnalysis = asRecord18(narrative.sheetAnalysis) ?? {};
103139
+ const sheetAnalysis = asRecord19(narrative.sheetAnalysis) ?? {};
103140
103140
  for (const block of Object.values(sheetAnalysis)) {
103141
- const rec = asRecord18(block);
103141
+ const rec = asRecord19(block);
103142
103142
  parts.push(...stringList(rec?.summary), ...stringList(rec?.suggestions));
103143
103143
  }
103144
- const review = asRecord18(narrative.review) ?? {};
103144
+ const review = asRecord19(narrative.review) ?? {};
103145
103145
  for (const block of Object.values(review)) {
103146
- const rec = asRecord18(block);
103146
+ const rec = asRecord19(block);
103147
103147
  for (const key of ["overview", "summary", "suggestion"]) {
103148
103148
  const value = rec?.[key];
103149
103149
  if (typeof value === "string" && value.trim()) parts.push(value.trim());
@@ -103160,7 +103160,7 @@ var init_okki_display = __esm({
103160
103160
  // src/commands/google-analysis/merge-snapshot.ts
103161
103161
  import * as fs27 from "fs/promises";
103162
103162
  import * as path32 from "path";
103163
- function asRecord19(value) {
103163
+ function asRecord20(value) {
103164
103164
  if (value && typeof value === "object" && !Array.isArray(value)) {
103165
103165
  return value;
103166
103166
  }
@@ -103256,7 +103256,7 @@ function periodMetricsFromRow(row) {
103256
103256
  }
103257
103257
  function items(envelope) {
103258
103258
  if (!envelope || !Array.isArray(envelope.items)) return [];
103259
- return envelope.items.filter((row) => asRecord19(row) !== null);
103259
+ return envelope.items.filter((row) => asRecord20(row) !== null);
103260
103260
  }
103261
103261
  function record(envelope) {
103262
103262
  return envelope?.record ?? null;
@@ -103303,14 +103303,14 @@ function sumCampaignRowsToPeriodKpis(rows) {
103303
103303
  }
103304
103304
  async function mergeGoogleAnalysisSnapshotIntoReport(payload, snapshotDir) {
103305
103305
  const absDir = path32.resolve(snapshotDir);
103306
- const metaAccountId = typeof asRecord19(payload.meta)?.accountId === "string" ? asRecord19(payload.meta)?.accountId : void 0;
103306
+ const metaAccountId = typeof asRecord20(payload.meta)?.accountId === "string" ? asRecord20(payload.meta)?.accountId : void 0;
103307
103307
  const manifest = await readManifest(snapshotDir, metaAccountId);
103308
103308
  if (!manifest) {
103309
103309
  throw new Error(`\u672A\u5728 ${absDir} \u627E\u5230 manifest*.json\uFF0C\u8BF7\u5148\u6267\u884C google-analysis --json-out`);
103310
103310
  }
103311
103311
  const accountId = manifest.googleAnalysis?.accountId ?? metaAccountId;
103312
103312
  const dateRange = manifest.googleAnalysis?.dateRange;
103313
- const preferredDateTag = dateTagFromRange(dateRange) ?? dateTagFromMeta(asRecord19(payload.meta) ?? void 0);
103313
+ const preferredDateTag = dateTagFromRange(dateRange) ?? dateTagFromMeta(asRecord20(payload.meta) ?? void 0);
103314
103314
  const sectionMap = /* @__PURE__ */ new Map();
103315
103315
  for (const art of manifest.artifacts ?? []) {
103316
103316
  const data = await loadSectionFile(absDir, art.file);
@@ -103318,8 +103318,8 @@ async function mergeGoogleAnalysisSnapshotIntoReport(payload, snapshotDir) {
103318
103318
  }
103319
103319
  await recoverMissingReportSections(sectionMap, absDir, accountId, preferredDateTag);
103320
103320
  const overviewRecord = record(sectionMap.get("overview"));
103321
- const currentPeriod = asRecord19(overviewRecord?.currentPeriod);
103322
- const previousPeriod = asRecord19(overviewRecord?.previousPeriod);
103321
+ const currentPeriod = asRecord20(overviewRecord?.currentPeriod);
103322
+ const previousPeriod = asRecord20(overviewRecord?.previousPeriod);
103323
103323
  const meta = { ...payload.meta ?? {} };
103324
103324
  if (!meta.accountId && accountId) meta.accountId = accountId;
103325
103325
  if (!meta.currency) {
@@ -103465,7 +103465,7 @@ function textLen(value) {
103465
103465
  function asArray(value) {
103466
103466
  return Array.isArray(value) ? value : [];
103467
103467
  }
103468
- function asRecord20(value) {
103468
+ function asRecord21(value) {
103469
103469
  if (value && typeof value === "object" && !Array.isArray(value)) {
103470
103470
  return value;
103471
103471
  }
@@ -103479,7 +103479,7 @@ function pushMissing(missing, id, chapter, dimension, hint) {
103479
103479
  }
103480
103480
  function validateOkkiWeeklyReportContent(data) {
103481
103481
  const missing = [];
103482
- const meta = asRecord20(data.meta) ?? {};
103482
+ const meta = asRecord21(data.meta) ?? {};
103483
103483
  if (!textLen(meta.accountId)) {
103484
103484
  pushMissing(
103485
103485
  missing,
@@ -103489,7 +103489,7 @@ function validateOkkiWeeklyReportContent(data) {
103489
103489
  "meta.accountId \u5FC5\u586B\uFF1B\u53EF\u7531 --snapshot-dir \u5408\u5E76 overview/manifest"
103490
103490
  );
103491
103491
  }
103492
- const snapshotSections = asRecord20(data.snapshot?.sections) ?? {};
103492
+ const snapshotSections = asRecord21(data.snapshot?.sections) ?? {};
103493
103493
  const snapshotPresent = {};
103494
103494
  for (const section of OKKI_SNAPSHOT_SECTIONS) {
103495
103495
  const present = snapshotSections[section] === true;
@@ -103504,12 +103504,12 @@ function validateOkkiWeeklyReportContent(data) {
103504
103504
  );
103505
103505
  }
103506
103506
  }
103507
- const narrative = asRecord20(data.narrative) ?? {};
103508
- const sheetAnalysis = asRecord20(narrative.sheetAnalysis) ?? {};
103509
- const review = asRecord20(narrative.review) ?? {};
103507
+ const narrative = asRecord21(data.narrative) ?? {};
103508
+ const sheetAnalysis = asRecord21(narrative.sheetAnalysis) ?? {};
103509
+ const review = asRecord21(narrative.review) ?? {};
103510
103510
  const sheetAnalysisCounts = {};
103511
103511
  for (const key of OKKI_SHEET_KEYS) {
103512
- const block = asRecord20(sheetAnalysis[key]);
103512
+ const block = asRecord21(sheetAnalysis[key]);
103513
103513
  const summary = stringList2(block?.summary);
103514
103514
  const suggestions = stringList2(block?.suggestions);
103515
103515
  sheetAnalysisCounts[key] = { summary: summary.length, suggestions: suggestions.length };
@@ -103535,7 +103535,7 @@ function validateOkkiWeeklyReportContent(data) {
103535
103535
  }
103536
103536
  const reviewFilled = {};
103537
103537
  for (const key of OKKI_REVIEW_KEYS) {
103538
- const block = asRecord20(review[key]);
103538
+ const block = asRecord21(review[key]);
103539
103539
  const overview = textLen(block?.overview);
103540
103540
  const summary = textLen(block?.summary);
103541
103541
  const suggestion = textLen(block?.suggestion);
@@ -103570,7 +103570,7 @@ function validateOkkiWeeklyReportContent(data) {
103570
103570
  }
103571
103571
  }
103572
103572
  const deviceRows2 = Array.isArray(data.tables?.devices) ? data.tables.devices : [];
103573
- const deviceSpend = summarizeOkkiDeviceSpend(deviceRows2.filter((row) => asRecord20(row) !== null));
103573
+ const deviceSpend = summarizeOkkiDeviceSpend(deviceRows2.filter((row) => asRecord21(row) !== null));
103574
103574
  if (deviceSpend.total > 0 && deviceSpend.desktop >= deviceSpend.mobile && narrativeClaimsMobileSpendLeads(collectOkkiNarrativeText(data))) {
103575
103575
  pushMissing(
103576
103576
  missing,
@@ -103659,7 +103659,7 @@ function round25(value) {
103659
103659
  }
103660
103660
  function items2(envelope) {
103661
103661
  if (!envelope || !Array.isArray(envelope.items)) return [];
103662
- return envelope.items.filter((row) => asRecord20(row) !== null);
103662
+ return envelope.items.filter((row) => asRecord21(row) !== null);
103663
103663
  }
103664
103664
  function dateTagFromRange2(dateRange) {
103665
103665
  if (!dateRange || dateRange.mode !== "range") return void 0;
@@ -103703,14 +103703,14 @@ async function loadSectionFile2(snapshotDir, file) {
103703
103703
  }
103704
103704
  async function mergeOkkiSnapshotIntoReport(payload, snapshotDir) {
103705
103705
  const absDir = path33.resolve(snapshotDir);
103706
- const metaAccountId = typeof asRecord20(payload.meta)?.accountId === "string" ? asRecord20(payload.meta)?.accountId : void 0;
103706
+ const metaAccountId = typeof asRecord21(payload.meta)?.accountId === "string" ? asRecord21(payload.meta)?.accountId : void 0;
103707
103707
  const manifest = await readManifest2(absDir, metaAccountId);
103708
103708
  if (!manifest) {
103709
103709
  throw new Error(`\u672A\u5728 ${absDir} \u627E\u5230 manifest*.json\uFF0C\u8BF7\u5148\u6267\u884C google-analysis --json-out`);
103710
103710
  }
103711
103711
  const accountId = manifest.googleAnalysis?.accountId ?? metaAccountId;
103712
103712
  const dateRange = manifest.googleAnalysis?.dateRange;
103713
- const preferredDateTag = dateTagFromRange2(dateRange) ?? dateTagFromMeta2(asRecord20(payload.meta) ?? void 0);
103713
+ const preferredDateTag = dateTagFromRange2(dateRange) ?? dateTagFromMeta2(asRecord21(payload.meta) ?? void 0);
103714
103714
  const sectionMap = /* @__PURE__ */ new Map();
103715
103715
  for (const art of manifest.artifacts ?? []) {
103716
103716
  const data = await loadSectionFile2(absDir, art.file);
@@ -103736,7 +103736,7 @@ async function mergeOkkiSnapshotIntoReport(payload, snapshotDir) {
103736
103736
  snapshotPresent[section] = sectionMap.has(section);
103737
103737
  }
103738
103738
  const overviewRecord = sectionMap.get("overview")?.record ?? null;
103739
- const currentPeriod = asRecord20(overviewRecord?.currentPeriod);
103739
+ const currentPeriod = asRecord21(overviewRecord?.currentPeriod);
103740
103740
  const meta = { ...payload.meta ?? {} };
103741
103741
  if (!meta.accountId && accountId) meta.accountId = accountId;
103742
103742
  if (!meta.currency) {
@@ -103879,18 +103879,18 @@ function prefixLines(items3, firstPrefix) {
103879
103879
  });
103880
103880
  }
103881
103881
  function analysisLines(data, key) {
103882
- const block = asRecord20(asRecord20(data.narrative)?.sheetAnalysis)?.[key];
103883
- const rec = asRecord20(block);
103882
+ const block = asRecord21(asRecord21(data.narrative)?.sheetAnalysis)?.[key];
103883
+ const rec = asRecord21(block);
103884
103884
  return [
103885
103885
  ...prefixLines(stringList2(rec?.summary), "\u603B\u7ED3\uFF1A"),
103886
103886
  ...prefixLines(stringList2(rec?.suggestions), "\u5EFA\u8BAE\uFF1A")
103887
103887
  ];
103888
103888
  }
103889
103889
  function reviewLines(data) {
103890
- const review = asRecord20(asRecord20(data.narrative)?.review) ?? {};
103890
+ const review = asRecord21(asRecord21(data.narrative)?.review) ?? {};
103891
103891
  const lines = [];
103892
103892
  OKKI_REVIEW_KEYS.forEach((key, idx) => {
103893
- const block = asRecord20(review[key]) ?? {};
103893
+ const block = asRecord21(review[key]) ?? {};
103894
103894
  lines.push(`${idx + 1}\u3001${REVIEW_LABELS2[key]}\uFF1A${str(block.overview, "")}`);
103895
103895
  lines.push(` \u603B\u7ED3\uFF1A${str(block.summary, "")}`);
103896
103896
  lines.push(` \u5EFA\u8BAE\uFF1A${str(block.suggestion, "")}`);
@@ -104022,7 +104022,7 @@ function geoRows(data) {
104022
104022
  });
104023
104023
  }
104024
104024
  function buildOkkiWeeklySheetDrafts(data) {
104025
- const meta = asRecord20(data.meta) ?? {};
104025
+ const meta = asRecord21(data.meta) ?? {};
104026
104026
  const period = formatOkkiZhDateRange(str(meta.startDate, ""), str(meta.endDate, ""));
104027
104027
  const campaigns = campaignRows(data);
104028
104028
  const drafts = {
@@ -104283,7 +104283,7 @@ function textLen2(value) {
104283
104283
  function asArray2(value) {
104284
104284
  return Array.isArray(value) ? value : [];
104285
104285
  }
104286
- function asRecord21(value) {
104286
+ function asRecord22(value) {
104287
104287
  if (value && typeof value === "object" && !Array.isArray(value)) {
104288
104288
  return value;
104289
104289
  }
@@ -104294,9 +104294,9 @@ function pushMissing2(missing, id, chapter, dimension, hint) {
104294
104294
  }
104295
104295
  function validateGooglePeriodReportContent(data) {
104296
104296
  const missing = [];
104297
- const meta = asRecord21(data.meta) ?? {};
104298
- const narrative = asRecord21(data.narrative) ?? {};
104299
- const sections = asRecord21(narrative.sections) ?? {};
104297
+ const meta = asRecord22(data.meta) ?? {};
104298
+ const narrative = asRecord22(data.narrative) ?? {};
104299
+ const sections = asRecord22(narrative.sections) ?? {};
104300
104300
  if (!textLen2(meta.accountId)) {
104301
104301
  pushMissing2(
104302
104302
  missing,
@@ -104316,7 +104316,7 @@ function validateGooglePeriodReportContent(data) {
104316
104316
  "\u22651 \u6BB5\u6267\u884C\u6458\u8981\uFF0C\u6982\u8FF0\u672C\u671F\u6D88\u8017/\u8F6C\u5316/CTR/CPA \u8D70\u52BF"
104317
104317
  );
104318
104318
  }
104319
- const kpis = asRecord21(data.kpis);
104319
+ const kpis = asRecord22(data.kpis);
104320
104320
  const hasCoreKpi = ["cost", "impressions", "clicks"].some(
104321
104321
  (key) => Number.isFinite(Number(kpis?.[key]))
104322
104322
  );
@@ -104332,7 +104332,7 @@ function validateGooglePeriodReportContent(data) {
104332
104332
  const sectionAnalysisCounts = {};
104333
104333
  const sectionSuggestionCounts = {};
104334
104334
  for (const key of SECTION_KEYS) {
104335
- const section = asRecord21(sections[key]);
104335
+ const section = asRecord22(sections[key]);
104336
104336
  const analysis = asArray2(section?.analysis).filter((s) => textLen2(s) > 0);
104337
104337
  const suggestions = asArray2(section?.suggestions).filter((s) => textLen2(s) > 0);
104338
104338
  sectionAnalysisCounts[key] = analysis.length;
@@ -104358,7 +104358,7 @@ function validateGooglePeriodReportContent(data) {
104358
104358
  }
104359
104359
  const recommendations = asArray2(narrative.recommendations).filter((r) => {
104360
104360
  if (typeof r === "string") return textLen2(r) > 0;
104361
- const rec = asRecord21(r);
104361
+ const rec = asRecord22(r);
104362
104362
  return textLen2(rec?.content) > 0;
104363
104363
  });
104364
104364
  if (recommendations.length < 3) {
@@ -104447,7 +104447,7 @@ async function readJsonFile3(filePath) {
104447
104447
  throw new Error(`\u65E0\u6CD5\u89E3\u6790 JSON\uFF1A${filePath}`);
104448
104448
  }
104449
104449
  }
104450
- function asRecord22(value) {
104450
+ function asRecord23(value) {
104451
104451
  if (value && typeof value === "object" && !Array.isArray(value)) {
104452
104452
  return value;
104453
104453
  }
@@ -104462,7 +104462,7 @@ function injectReportData2(html, payload) {
104462
104462
  }
104463
104463
  async function runGoogleAnalysisRender(opts) {
104464
104464
  const dataPath = path35.resolve(opts.dataFile);
104465
- let data = asRecord22(await readJsonFile3(dataPath));
104465
+ let data = asRecord23(await readJsonFile3(dataPath));
104466
104466
  if (opts.snapshotDir?.trim()) {
104467
104467
  data = await mergeGoogleAnalysisSnapshotIntoReport(data, path35.resolve(opts.snapshotDir));
104468
104468
  }
@@ -133428,11 +133428,12 @@ function buildGoogleAdsDiagnosisCollectAgentHint(snapDir) {
133428
133428
  "Google Ads \u8BCA\u65AD collect \u5DF2\u5B8C\u6210\uFF08\u4EC5\u4E8B\u5B9E\u6570\u636E\uFF0C\u4E0D\u542B\u5EFA\u8BAE\u6027\u6587\u6848\uFF09\u3002",
133429
133429
  `1) Read ${dir}/${GOOGLE_ADS_DIAGNOSIS_DIGEST_BASENAME}\uFF1A\u5404 section \u7ED3\u6784\u3001\u884C\u6570\u3001\u5F85\u586B\u5B57\u6BB5\u6E05\u5355\u90FD\u5728\u91CC\u9762\uFF1B`,
133430
133430
  `2) ${dir}/${GOOGLE_ADS_DIAGNOSIS_REPORT_BASENAME} \u5DF2\u662F\u7EC8\u7A3F\u9AA8\u67B6\uFF08items \u4E8B\u5B9E\u884C\u5DF2\u586B\u597D\u3001\u53D9\u4E8B\u5B57\u6BB5\u7559\u7A7A\uFF09\uFF0C\u76F4\u63A5\u5728\u5B83\u4E0A\u9762\u586B\u7A7A\uFF1B`,
133431
- "3) Read references/report-templates/google-ads-diagnosis.md\uFF0C\u64B0\u5199\u5168\u90E8 analysis/suggestions\u3001diagnosisOverview.advantages/disadvantages\uFF08\u6BCF\u6761 {title, description}\uFF0C\u7981\u6B62\u7EAF\u5B57\u7B26\u4E32\uFF09\u3001summary\u3001accountInfo.businessModel/industry\u3001budgetCompetitiveness[].strategy\u3001newFeatures[].optimizerRecommendation\u3001adCreativeOptimization.items[].suggestion \u7B49\uFF1B",
133431
+ "3) Read references/report-templates/google-ads-diagnosis.md\u3002\u53EA\u5199\u4E3B\u89C2\u5206\u6790\uFF1AdiagnosisOverview.advantages/disadvantages\uFF08\u6BCF\u6761 {title, description}\uFF09\u3001\u5404\u6A21\u5757 analysis/suggestions\u3001summary\u3001accountInfo.businessModel/industry\u3002",
133432
133432
  `4) siluzan-tso google-ads-diagnosis render --data ${dir}/${GOOGLE_ADS_DIAGNOSIS_REPORT_BASENAME} --out ${dir}/google-ads-diagnosis-report.html\uFF08\u9ED8\u8BA4\u540C\u76EE\u5F55 collect \u81EA\u52A8\u6062\u590D\u88AB\u6539\u574F\u7684 items\uFF09\u3002`,
133433
133433
  `\u7981\u6B62\u7528 node -e \u9010\u6BB5 dump ${GOOGLE_ADS_DIAGNOSIS_COLLECT_BASENAME} \u6765\u6478\u7ED3\u6784\u2014\u2014\u7ED3\u6784\u770B digest\u3001\u6570\u5B57\u770B\u9AA8\u67B6\u6587\u4EF6\u5373\u53EF\u3002`,
133434
133434
  "\u7981\u6B62\u5199\u5165/\u8986\u76D6 campaigns/geographic/keywords \u7684 items \u884C\uFF0C\u53EA\u586B analysis/suggestions\u3002",
133435
- "\u7981\u6B62\u5728\u672A\u7ECF\u8FC7 Agent \u64B0\u5199\u7684\u60C5\u51B5\u4E0B\u76F4\u63A5 render\uFF1B\u7F3A\u4EFB\u4E00\u5EFA\u8BAE\u5B57\u6BB5 render \u4F1A\u5931\u8D25\u3002"
133435
+ "\u7981\u6B62\u5728\u672A\u7ECF\u8FC7 Agent \u64B0\u5199\u7684\u60C5\u51B5\u4E0B\u76F4\u63A5 render\uFF1B\u7F3A\u4EFB\u4E00\u5EFA\u8BAE\u5B57\u6BB5 render \u4F1A\u5931\u8D25\u3002",
133436
+ "\u9EC4\u91D1\u8D26\u6237\u8868\u3001\u9884\u7B97\u7ADE\u4E89\u529B\u3001\u51FA\u4EF7\u539F\u56E0\u3001\u65B0\u4EA7\u54C1\u72B6\u6001\u3001\u521B\u610F\u884C\u5EFA\u8BAE\u7531 render \u6309\u6570\u636E\u5199\u5165\u3002Agent \u4E0D\u8981\u586B\u8FD9\u4E9B\u5B57\u6BB5\uFF0C\u4E5F\u4E0D\u8981\u628A\u672A\u8FBE\u6807\u5199\u6210\u300C\u8BF7\u521B\u5EFA/\u6CA1\u6709\u300D\u3002"
133436
133437
  ].join("\n");
133437
133438
  }
133438
133439
 
@@ -133485,17 +133486,21 @@ function buildGoogleAdsDiagnosisAgentBrief(input) {
133485
133486
  },
133486
133487
  comparisonTableItemCounts: input.comparisonTableItemCounts,
133487
133488
  agentRules: {
133488
- doNotModifyItemRows: ["campaigns.items", "geographic.items", "keywords.items"],
133489
- renderRestoresComparisonItemsFromCollect: "render \u9ED8\u8BA4\u540C\u76EE\u5F55 google-ads-diagnosis-collect.json \u4F1A\u4EE5 collect \u8986\u76D6\u4E0A\u8FF0 items\uFF08Agent \u53EA\u586B analysis/suggestions\uFF09",
133490
- mayEditItemFields: [
133489
+ doNotModifyItemRows: [
133490
+ "campaigns.items",
133491
+ "geographic.items",
133492
+ "keywords.items",
133493
+ "budgetCompetitiveness[].strategy",
133491
133494
  "biddingStrategy.items[].recommendationReason",
133492
133495
  "newFeatures.items[].optimizerRecommendation",
133493
133496
  "adCreativeOptimization.items[].suggestion"
133494
- ]
133497
+ ],
133498
+ renderRestoresComparisonItemsFromCollect: "render \u9ED8\u8BA4\u540C\u76EE\u5F55 google-ads-diagnosis-collect.json \u4F1A\u4EE5 collect \u8986\u76D6\u4E0A\u8FF0 items\u3002\u9EC4\u91D1\u8D26\u6237\u8868\u3001\u9884\u7B97\u7ADE\u4E89\u529B strategy\u3001\u51FA\u4EF7 recommendationReason\u3001\u65B0\u4EA7\u54C1 optimizerRecommendation\u3001\u521B\u610F\u884C suggestion \u4E5F\u7531 render \u6309\u884C\u6570\u636E\u91CD\u5199\u3002Agent \u53EA\u586B\u4E3B\u89C2\u5206\u6790\u3002",
133499
+ mayEditItemFields: []
133495
133500
  },
133496
133501
  agentDeliverable: {
133497
133502
  outputFile: "google-ads-diagnosis.json",
133498
- structure: "\u4E0E reportData \u540C\u7ED3\u6784\u7684 sectionData\uFF1B\u987B\u586B diagnosisOverview.advantages/disadvantages\uFF08\u6BCF\u6761 {title, description}\uFF09\u3001metrics.analysis/suggestions\u3001\u5404\u6A21\u5757 analysis/suggestions\u3001summary.keyIssues\u3001summary.optimizationRoadmap[]\uFF08priority/focusArea/actionItems/expectedOutcome\uFF09\u3001budgetCompetitiveness[].strategy\u3001newFeatures[].optimizerRecommendation \u7B49",
133503
+ structure: "\u4E0E reportData \u540C\u7ED3\u6784\u7684 sectionData\u3002\u53EA\u586B\u4E3B\u89C2\u5206\u6790\uFF1AdiagnosisOverview.advantages/disadvantages\uFF08\u6BCF\u6761 {title, description}\uFF09\u3001\u5404\u6A21\u5757 analysis/suggestions\u3001summary.keyIssues\u3001summary.optimizationRoadmap[]\uFF08priority/focusArea/actionItems/expectedOutcome\uFF09\u3001accountInfo.businessModel/industry\u3002\u4E0D\u8981\u586B\u9EC4\u91D1\u8D26\u6237\u8868\u3001budgetCompetitiveness[].strategy\u3001biddingStrategy[].recommendationReason\u3001newFeatures[].optimizerRecommendation\u3001adCreativeOptimization.items[].suggestion",
133499
133504
  thenRun: "siluzan-tso google-ads-diagnosis render --data ./<snap>/google-ads-diagnosis.json --out ./<snap>/google-ads-diagnosis-report.html"
133500
133505
  }
133501
133506
  };
@@ -133549,14 +133554,7 @@ function asRecord7(value) {
133549
133554
  }
133550
133555
  return null;
133551
133556
  }
133552
- function hasSearchTextAds(gold) {
133553
- return gold.expandedTextAdQualified === true || gold.textAdQualified === true || gold.responsiveSearchAdQualified === true;
133554
- }
133555
- function applyTextAdQualification(gold) {
133556
- if (!hasSearchTextAds(gold) || gold.expandedTextAdQualified === true) return gold;
133557
- return { ...gold, expandedTextAdQualified: true };
133558
- }
133559
- var DISPLAY_PAUSED_SUGGESTION = "\u5DF2\u6709\u81EA\u9002\u5E94\u5C55\u793A\u5E7F\u544A\uFF0C\u6240\u5728\u5C55\u793A\u7CFB\u5217\u5DF2\u6682\u505C\uFF0C\u672C\u671F\u65E0\u6D88\u8017";
133557
+ var NOT_QUALIFIED = "\u672A\u8FBE\u5230\u8BE5\u9879\u9EC4\u91D1\u8D26\u6237\u6807\u51C6";
133560
133558
  function statusOf(row) {
133561
133559
  return String(row.statusV2 ?? row.status ?? "").toUpperCase();
133562
133560
  }
@@ -133566,7 +133564,7 @@ function isEnabled(row) {
133566
133564
  function hasEnabledExtension(rows, type) {
133567
133565
  return rows.some((row) => String(row.typeV2 ?? "") === type && isEnabled(row));
133568
133566
  }
133569
- function hasGa4Link(rows) {
133567
+ function hasGa4Conversion(rows) {
133570
133568
  return rows.some((row) => {
133571
133569
  if (!isEnabled(row)) return false;
133572
133570
  const type = String(row.type ?? "");
@@ -133574,12 +133572,25 @@ function hasGa4Link(rows) {
133574
133572
  return type.startsWith("GoogleAnalytics") || /GA4/i.test(name2);
133575
133573
  });
133576
133574
  }
133577
- function hasAudienceSetup(rows) {
133575
+ function hasAudienceRows(rows) {
133578
133576
  return rows.some((row) => {
133579
133577
  const type = String(row.criterionType ?? "");
133580
133578
  return type === "UserList" || type === "CustomAudience" || type === "UserInterest" || type === "CustomIntent" || type === "CombinedAudience" || type === "CustomAffinity" || Number(row.impressions) > 0;
133581
133579
  });
133582
133580
  }
133581
+ function hasSearchTextAdRows(rows) {
133582
+ return rows.some((row) => {
133583
+ const type = String(row.typeV2 ?? "");
133584
+ return type === "RESPONSIVE_SEARCH_AD" || type === "EXPANDED_TEXT_AD";
133585
+ });
133586
+ }
133587
+ function campaignsUseBidding(rows) {
133588
+ return rows.some(
133589
+ (row) => /MAXIMIZE_|TARGET_|MANUAL_CPC|ENHANCED_CPC|COMMISSION/i.test(
133590
+ String(row.biddingStrategyTypeV2 ?? row.biddingStrategyType ?? "")
133591
+ )
133592
+ );
133593
+ }
133583
133594
  function displayAds(rows) {
133584
133595
  return rows.filter((row) => String(row.typeV2 ?? "") === "RESPONSIVE_DISPLAY_AD");
133585
133596
  }
@@ -133588,28 +133599,109 @@ function displayIsServing(rows) {
133588
133599
  (row) => isEnabled(row) && statusOf({ statusV2: row.parentCampaignStatusV2 }) === "ENABLED"
133589
133600
  );
133590
133601
  }
133602
+ function noteUnqualified(gold, suggestions, flag, itemName, present, sentence) {
133603
+ if (!present || gold[flag] !== false) return;
133604
+ suggestions[itemName] = sentence;
133605
+ }
133591
133606
  function reconcileGoldAccount(gold, evidence = {}) {
133592
- const next = { ...applyTextAdQualification(gold) };
133607
+ const next = { ...gold };
133608
+ const suggestions = { ...asRecord7(gold.goldItemSuggestions) ?? {} };
133609
+ const notes = {};
133593
133610
  const conversions = evidence.conversionActions ?? [];
133594
133611
  const extensions = evidence.extensions ?? [];
133595
133612
  const audiences = evidence.audiences ?? [];
133596
133613
  const ads = evidence.ads ?? [];
133597
- if (hasGa4Link(conversions)) next.gaConfigured = true;
133598
- if (hasEnabledExtension(extensions, "CALLOUT")) next.adsCalloutExtensionQualified = true;
133599
- if (hasEnabledExtension(extensions, "STRUCTURED_SNIPPET")) {
133600
- next.adsStructuredSnippetExtensionQualified = true;
133601
- }
133602
- if (hasAudienceSetup(audiences)) next.audienceQualified = true;
133614
+ const presentBecause = (sentence) => `${NOT_QUALIFIED}\u3002${sentence}\u662F\u5426\u8FBE\u6807\u4E0D\u7531\u8BE5\u5BF9\u8C61\u662F\u5426\u5B58\u5728\u51B3\u5B9A\u3002`;
133615
+ const textAdsPresent = hasSearchTextAdRows(ads) || gold.responsiveSearchAdQualified === true || gold.textAdQualified === true || gold.expandedTextAdQualified === true;
133616
+ if (textAdsPresent) notes.textAds = true;
133617
+ noteUnqualified(
133618
+ gold,
133619
+ suggestions,
133620
+ "expandedTextAdQualified",
133621
+ "\u6587\u5B57\u5E7F\u544A",
133622
+ textAdsPresent,
133623
+ presentBecause("\u8D26\u6237\u91CC\u5DF2\u6709\u641C\u7D22\u6587\u5B57\u5E7F\u544A\uFF0C")
133624
+ );
133625
+ noteUnqualified(
133626
+ gold,
133627
+ suggestions,
133628
+ "responsiveSearchAdQualified",
133629
+ "\u81EA\u9002\u5E94\u641C\u7D22\u5E7F\u544A",
133630
+ hasSearchTextAdRows(ads),
133631
+ presentBecause("\u8D26\u6237\u91CC\u5DF2\u6709\u81EA\u9002\u5E94\u641C\u7D22\u5E7F\u544A\uFF0C")
133632
+ );
133603
133633
  const responsiveDisplay = displayAds(ads);
133604
- if (displayIsServing(responsiveDisplay)) {
133605
- next.multiAssetResponsiveDisplayAdQualified = true;
133606
- } else if (responsiveDisplay.length > 0 && next.multiAssetResponsiveDisplayAdQualified !== true) {
133607
- const suggestions = asRecord7(next.goldItemSuggestions) ?? {};
133608
- next.goldItemSuggestions = {
133609
- ...suggestions,
133610
- \u81EA\u9002\u5E94\u5C55\u793A\u5E7F\u544A: DISPLAY_PAUSED_SUGGESTION
133611
- };
133612
- }
133634
+ if (responsiveDisplay.length > 0) {
133635
+ notes.displayAds = true;
133636
+ if (gold.multiAssetResponsiveDisplayAdQualified === false) {
133637
+ suggestions["\u81EA\u9002\u5E94\u5C55\u793A\u5E7F\u544A"] = displayIsServing(responsiveDisplay) ? presentBecause("\u8D26\u6237\u91CC\u5DF2\u6709\u81EA\u9002\u5E94\u5C55\u793A\u5E7F\u544A\uFF0C") : `${NOT_QUALIFIED}\u3002\u8D26\u6237\u91CC\u5DF2\u6709\u81EA\u9002\u5E94\u5C55\u793A\u5E7F\u544A\uFF0C\u6240\u5728\u5C55\u793A\u7CFB\u5217\u5DF2\u6682\u505C\uFF0C\u672C\u671F\u65E0\u6D88\u8017\u3002`;
133638
+ }
133639
+ }
133640
+ if (hasGa4Conversion(conversions)) notes.ga4 = true;
133641
+ noteUnqualified(
133642
+ gold,
133643
+ suggestions,
133644
+ "gaConfigured",
133645
+ "G A\u4E0E\u5E7F\u544A\u8D26\u6237\u8054\u7ED3",
133646
+ hasGa4Conversion(conversions),
133647
+ presentBecause("\u8D26\u6237\u91CC\u5DF2\u6709\u542F\u7528\u7684 Google Analytics \u8F6C\u5316\uFF0C")
133648
+ );
133649
+ if (conversions.some((row) => isEnabled(row))) notes.conversion = true;
133650
+ noteUnqualified(
133651
+ gold,
133652
+ suggestions,
133653
+ "conversionTrackingQualified",
133654
+ "\u8F6C\u5316\u8FFD\u8E2A\u8BBE\u7F6E",
133655
+ conversions.some((row) => isEnabled(row)),
133656
+ presentBecause("\u8D26\u6237\u91CC\u5DF2\u6709\u542F\u7528\u7684\u8F6C\u5316\u52A8\u4F5C\uFF0C")
133657
+ );
133658
+ const extensionNote = (type, flag, itemName, noteKey, found) => {
133659
+ if (!hasEnabledExtension(extensions, type)) return;
133660
+ notes[noteKey] = true;
133661
+ noteUnqualified(gold, suggestions, flag, itemName, true, presentBecause(found));
133662
+ };
133663
+ extensionNote(
133664
+ "CALLOUT",
133665
+ "adsCalloutExtensionQualified",
133666
+ "\u9644\u52A0\u5BA3\u4F20\u4FE1\u606F",
133667
+ "callout",
133668
+ "\u8D26\u6237\u91CC\u5DF2\u6709\u542F\u7528\u7684\u5BA3\u4F20\u4FE1\u606F\uFF0C"
133669
+ );
133670
+ extensionNote(
133671
+ "STRUCTURED_SNIPPET",
133672
+ "adsStructuredSnippetExtensionQualified",
133673
+ "\u9644\u52A0\u7ED3\u6784\u5316\u4FE1\u606F\u6458\u8981",
133674
+ "snippet",
133675
+ "\u8D26\u6237\u91CC\u5DF2\u6709\u542F\u7528\u7684\u7ED3\u6784\u5316\u4FE1\u606F\u6458\u8981\uFF0C"
133676
+ );
133677
+ extensionNote(
133678
+ "SITELINK",
133679
+ "adsSitelinkExtensionQualified",
133680
+ "\u9644\u52A0\u94FE\u63A5",
133681
+ "sitelink",
133682
+ "\u8D26\u6237\u91CC\u5DF2\u6709\u542F\u7528\u7684\u9644\u52A0\u94FE\u63A5\uFF0C"
133683
+ );
133684
+ extensionNote("CALL", "adsCallExtensionQualified", "\u9644\u52A0\u7535\u8BDD\u4FE1\u606F", "call", "\u8D26\u6237\u91CC\u5DF2\u6709\u542F\u7528\u7684\u7535\u8BDD\u4FE1\u606F\uFF0C");
133685
+ if (hasAudienceRows(audiences)) notes.audience = true;
133686
+ noteUnqualified(
133687
+ gold,
133688
+ suggestions,
133689
+ "audienceQualified",
133690
+ "\u53D7\u4F17\u7FA4\u4F53\u8BBE\u7F6E",
133691
+ hasAudienceRows(audiences),
133692
+ presentBecause("\u8D26\u6237\u91CC\u5DF2\u6709\u53D7\u4F17\uFF0C")
133693
+ );
133694
+ if (campaignsUseBidding(evidence.campaigns ?? [])) notes.bidding = true;
133695
+ noteUnqualified(
133696
+ gold,
133697
+ suggestions,
133698
+ "biddingPolicyQualified",
133699
+ "\u51FA\u4EF7\u7B56\u7565",
133700
+ campaignsUseBidding(evidence.campaigns ?? []),
133701
+ presentBecause("\u7CFB\u5217\u5DF2\u8BBE\u7F6E\u51FA\u4EF7\u7B56\u7565\uFF0C")
133702
+ );
133703
+ if (Object.keys(suggestions).length > 0) next.goldItemSuggestions = suggestions;
133704
+ if (Object.keys(notes).length > 0) next.goldAssetNotes = notes;
133613
133705
  return next;
133614
133706
  }
133615
133707
  function parsePeriod(period) {
@@ -133617,24 +133709,23 @@ function parsePeriod(period) {
133617
133709
  if (!matched) return {};
133618
133710
  return { start: matched[1], end: matched[2] };
133619
133711
  }
133712
+ function loadEvidence(snapDir, accountId, start, end) {
133713
+ const items3 = (section) => sectionItems(loadSectionSnapshot(snapDir, section, accountId, start, end));
133714
+ return {
133715
+ ads: items3("ads"),
133716
+ extensions: items3("extensions"),
133717
+ conversionActions: items3("conversion-actions"),
133718
+ audiences: items3("audience"),
133719
+ campaigns: items3("campaigns")
133720
+ };
133721
+ }
133620
133722
  function reconcileGoldAccountInReport(data, snapDir) {
133621
133723
  const gold = asRecord7(data.goldAccount) ?? {};
133622
133724
  const account = asRecord7(data.accountInfo);
133623
133725
  const accountId = String(account?.companyId ?? "").replace(/\D/g, "");
133624
133726
  const { start, end } = parsePeriod(String(account?.period ?? ""));
133625
- const evidence = {};
133626
- if (snapDir && accountId) {
133627
- evidence.conversionActions = sectionItems(
133628
- loadSectionSnapshot(snapDir, "conversion-actions", accountId, start, end)
133629
- );
133630
- evidence.extensions = sectionItems(
133631
- loadSectionSnapshot(snapDir, "extensions", accountId, start, end)
133632
- );
133633
- evidence.audiences = sectionItems(loadSectionSnapshot(snapDir, "audience", accountId, start, end));
133634
- evidence.ads = sectionItems(loadSectionSnapshot(snapDir, "ads", accountId, start, end));
133635
- }
133636
- const next = reconcileGoldAccount(gold, evidence);
133637
- return { ...data, goldAccount: next };
133727
+ const evidence = snapDir && accountId ? loadEvidence(snapDir, accountId, start, end) : {};
133728
+ return { ...data, goldAccount: reconcileGoldAccount(gold, evidence) };
133638
133729
  }
133639
133730
 
133640
133731
  // src/commands/google-ads-diagnosis/default-section-data.ts
@@ -134281,7 +134372,8 @@ function buildGoogleAdsDiagnosisCollectPayload(opts) {
134281
134372
  conversionActions: conversionRows,
134282
134373
  extensions: extensionRows,
134283
134374
  audiences: audienceRows,
134284
- ads: adRows
134375
+ ads: adRows,
134376
+ campaigns: campaignCurrent
134285
134377
  });
134286
134378
  data.goldAccount = goldRecord;
134287
134379
  data.fullAudience = {
@@ -134426,6 +134518,7 @@ function narrativeChecklist(shapes) {
134426
134518
  const lines = [
134427
134519
  "- `accountInfo.businessModel`\u3001`accountInfo.industry`",
134428
134520
  '- `diagnosisOverview.advantages`\u3001`diagnosisOverview.disadvantages`\uFF1A\u6BCF\u6761 `{"title":"\u77ED\u6807\u9898","description":"\u4F9D\u636E"}`\u3002\u7EAF\u5B57\u7B26\u4E32\u4F1A\u5728 render \u65F6\u6309\u7B2C\u4E00\u4E2A\u300C\uFF1A\u300D\u62C6\u5F00\uFF1B\u7F3A title \u6216 description \u5219\u62D2\u7EDD\u51FA HTML\uFF08\u5426\u5219\u9875\u9762\u663E\u793A\u300C\u672A\u586B\u5199\u300D\uFF09',
134521
+ "- \u9EC4\u91D1\u8D26\u6237\u8868\u3001\u9884\u7B97\u7ADE\u4E89\u529B strategy\u3001\u51FA\u4EF7 recommendationReason\u3001\u65B0\u4EA7\u54C1 optimizerRecommendation\u3001\u521B\u610F\u884C suggestion \u7531 render \u6309\u6570\u636E\u5199\u5165\uFF0CAgent \u4E0D\u8981\u586B",
134429
134522
  "- `summary.keyIssues`\u3001`summary.optimizationRoadmap`"
134430
134523
  ];
134431
134524
  for (const shape of shapes) {
@@ -134433,10 +134526,6 @@ function narrativeChecklist(shapes) {
134433
134526
  lines.push(`- \`${shape.section}.analysis\`\u3001\`${shape.section}.suggestions\``);
134434
134527
  }
134435
134528
  }
134436
- lines.push("- `budgetCompetitiveness.items[].strategy`");
134437
- lines.push("- `biddingStrategy.items[].recommendationReason`");
134438
- lines.push("- `newFeatures.items[].optimizerRecommendation`");
134439
- lines.push("- `adCreativeOptimization.items[].suggestion`\uFF08\u6BCF\u884C\u72EC\u7ACB\uFF0C\u7981\u6B62\u590D\u5236\u540C\u4E00\u53E5\uFF09");
134440
134529
  return lines;
134441
134530
  }
134442
134531
  function buildCollectDigest(reportData, meta) {
@@ -134806,7 +134895,7 @@ function validateBudgetCompetitivenessStrategies(data) {
134806
134895
  const row = asRecord11(items3[i]);
134807
134896
  const name2 = String(row?.name ?? row?.metric ?? i);
134808
134897
  if (!hasNarrativeContent(row?.strategy)) {
134809
- warnings.push(`budgetCompetitiveness[${name2}].strategy \u4E3A\u7A7A\uFF08\u987B\u7531 Agent \u586B\u5199\uFF09`);
134898
+ warnings.push(`budgetCompetitiveness[${name2}].strategy \u4E3A\u7A7A\uFF08render \u4F1A\u6309\u672C\u671F\u6570\u503C\u4E0E\u6807\u51C6\u751F\u6210\uFF0C\u4E0D\u5E94\u7531 Agent \u586B\u5199\uFF09`);
134810
134899
  }
134811
134900
  }
134812
134901
  return warnings;
@@ -134821,7 +134910,7 @@ function validateNewFeatureRecommendations(data) {
134821
134910
  const row = asRecord11(items3[i]);
134822
134911
  const name2 = String(row?.strategyName ?? row?.feature ?? row?.strategy ?? i);
134823
134912
  if (!hasNarrativeContent(row?.optimizerRecommendation)) {
134824
- warnings.push(`newFeatures.items[${name2}].optimizerRecommendation \u4E3A\u7A7A\uFF08\u987B\u7531 Agent \u586B\u5199\uFF09`);
134913
+ warnings.push(`newFeatures.items[${name2}].optimizerRecommendation \u4E3A\u7A7A\uFF08render \u4F1A\u6309\u542F\u7528\u72B6\u6001\u751F\u6210\uFF0C\u4E0D\u5E94\u7531 Agent \u586B\u5199\uFF09`);
134825
134914
  }
134826
134915
  }
134827
134916
  return warnings;
@@ -134856,7 +134945,7 @@ function validateAdCreativeItemSuggestions(data) {
134856
134945
  const row = asRecord11(items3[i]);
134857
134946
  const title = String(row?.adTitle ?? i);
134858
134947
  if (!hasNarrativeContent(row?.suggestion ?? row?.suggestions)) {
134859
- warnings.push(`adCreativeOptimization.items[${title}].suggestion \u4E3A\u7A7A\uFF08\u987B\u7531 Agent \u586B\u5199\uFF09`);
134948
+ warnings.push(`adCreativeOptimization.items[${title}].suggestion \u4E3A\u7A7A\uFF08render \u4F1A\u6309\u6807\u9898/\u63CF\u8FF0\u6761\u6570\u751F\u6210\uFF0C\u4E0D\u5E94\u7531 Agent \u586B\u5199\uFF09`);
134860
134949
  }
134861
134950
  }
134862
134951
  return warnings;
@@ -134960,7 +135049,7 @@ var MODULE_ITEM_ERROR_HINT = {
134960
135049
  geographic: "\u7F3A\u5C11 title/currentCost/currentClicks \u6216\u73AF\u6BD4\u5B57\u6BB5\u88AB\u5199\u6210\u5B57\u7B26\u4E32\uFF08\u987B\u4FDD\u7559 collect \u4E8B\u5B9E\u884C\uFF0C\u4EC5\u7531 Agent \u586B\u5199 analysis/suggestions\uFF09",
134961
135050
  keywords: "\u7F3A\u5C11 title/currentCost/currentClicks \u6216\u73AF\u6BD4\u5B57\u6BB5\u88AB\u5199\u6210\u5B57\u7B26\u4E32\uFF08\u987B\u4FDD\u7559 collect \u4E8B\u5B9E\u884C\uFF0C\u4EC5\u7531 Agent \u586B\u5199 analysis/suggestions\uFF09",
134962
135051
  biddingStrategy: "\u7F3A\u5C11 campaignName/duration(\u5929\u6570)/biddingStrategyType\uFF08Agent \u52FF\u628A duration \u5199\u6210\u6587\u6848\u5B57\u7B26\u4E32\uFF0C\u52FF\u7528 currentStrategy \u66FF\u4EE3 biddingStrategyType\uFF09",
134963
- newFeatures: "\u7F3A\u5C11 strategyName/strategy\uFF08collect \u4E8B\u5B9E\u884C\uFF1BAgent \u53EA\u586B optimizerRecommendation\uFF0C\u52FF\u6539\u7528 feature \u5B57\u6BB5\u66FF\u4EE3 strategyName\uFF09"
135052
+ newFeatures: "\u7F3A\u5C11 strategyName/strategy\uFF08collect \u4E8B\u5B9E\u884C\uFF1BoptimizerRecommendation \u7531 render \u6309 accountStatus \u751F\u6210\uFF0C\u52FF\u6539\u7528 feature \u5B57\u6BB5\u66FF\u4EE3 strategyName\uFF09"
134964
135053
  };
134965
135054
  function moduleItemsNeedRestore(items3, validator) {
134966
135055
  if (!Array.isArray(items3) || items3.length === 0) return false;
@@ -135124,6 +135213,61 @@ var NARRATIVE_CONSISTENCY_RULES = [
135124
135213
  hint: "\u7740\u9646\u9875\u6D4B\u901F\u672A\u83B7\u53D6\u662F Lighthouse API \u5931\u8D25\uFF0C\u4E0D\u662F\u8D26\u6237\u6295\u653E\u6838\u5FC3\u95EE\u9898\u3002\u7981\u6B62\u5199\u5165 keyIssues/disadvantages\uFF1B\u4EC5\u5728 \xA707 landingPageAnalysis.analysis \u8BF4\u660E source=none\u3002"
135125
135214
  }
135126
135215
  ];
135216
+ function goldAssetNoted(data, key) {
135217
+ const notes = asRecord13(asRecord13(data.goldAccount)?.goldAssetNotes);
135218
+ return notes?.[key] === true;
135219
+ }
135220
+ var GOLD_CLAIM_RULES = [
135221
+ {
135222
+ id: "ga-already-linked",
135223
+ textPattern: /(请将|请把|未将).{0,40}(Google Analytics|GA4|GA).{0,24}关联|(Google Analytics|GA4|GA).{0,12}未关联|未关联.{0,12}(Google Analytics|GA4|GA)/,
135224
+ isContradicted: (data) => goldAccountFlag(data, "gaConfigured") === true || goldAssetNoted(data, "ga4"),
135225
+ hint: "GA \u8054\u7ED3\u5DF2\u8FBE\u6807\uFF0C\u6216\u8F6C\u5316\u52A8\u4F5C\u91CC\u5DF2\u6709\u542F\u7528\u7684 Google Analytics \u8F6C\u5316\u3002\u7981\u6B62\u5199\u6210\u300C\u8BF7\u5173\u8054 / \u672A\u5173\u8054\u300D\u3002\u672A\u8FBE\u6807\u4E0D\u7B49\u4E8E\u8D26\u6237\u91CC\u6CA1\u6709\u8FD9\u4E9B\u8F6C\u5316\u3002"
135226
+ },
135227
+ {
135228
+ id: "text-ads-already-exist",
135229
+ textPattern: /请创建文字广告|没有文字广告|缺少文字广告|未创建文字广告/,
135230
+ isContradicted: (data) => goldAssetNoted(data, "textAds") || goldAccountFlag(data, "expandedTextAdQualified") === true || goldAccountFlag(data, "responsiveSearchAdQualified") === true,
135231
+ hint: "\u8D26\u6237\u5DF2\u6709\u641C\u7D22\u6587\u5B57\u5E7F\u544A\u3002\u6587\u5B57\u5E7F\u544A\u9879\u672A\u8FBE\u6807\u4E0D\u7B49\u4E8E\u6CA1\u6709\u5E7F\u544A\uFF0C\u7981\u6B62\u5199\u300C\u8BF7\u521B\u5EFA\u6587\u5B57\u5E7F\u544A\u300D\u3002"
135232
+ },
135233
+ {
135234
+ id: "callout-already-exists",
135235
+ textPattern: /请添加宣传信息|缺少宣传信息|没有宣传信息|未添加宣传信息/,
135236
+ isContradicted: (data) => goldAssetNoted(data, "callout") || goldAccountFlag(data, "adsCalloutExtensionQualified") === true,
135237
+ hint: "\u5DF2\u6709\u542F\u7528\u7684\u5BA3\u4F20\u4FE1\u606F\uFF0C\u6216\u8BE5\u9879\u5DF2\u8FBE\u6807\u3002\u7981\u6B62\u5199\u300C\u8BF7\u6DFB\u52A0\u5BA3\u4F20\u4FE1\u606F\u300D\u3002\u672A\u8FBE\u6807\u4E0D\u8868\u793A\u8D26\u6237\u91CC\u6CA1\u6709\u3002"
135238
+ },
135239
+ {
135240
+ id: "snippet-already-exists",
135241
+ textPattern: /请添加结构化信息摘要|缺少结构化信息摘要|没有结构化摘要|未添加结构化/,
135242
+ isContradicted: (data) => goldAssetNoted(data, "snippet") || goldAccountFlag(data, "adsStructuredSnippetExtensionQualified") === true,
135243
+ hint: "\u5DF2\u6709\u542F\u7528\u7684\u7ED3\u6784\u5316\u4FE1\u606F\u6458\u8981\uFF0C\u6216\u8BE5\u9879\u5DF2\u8FBE\u6807\u3002\u7981\u6B62\u5199\u300C\u8BF7\u6DFB\u52A0\u7ED3\u6784\u5316\u4FE1\u606F\u6458\u8981\u300D\u3002"
135244
+ },
135245
+ {
135246
+ id: "audience-already-exists",
135247
+ textPattern: /请设置受众|未设置受众|没有受众群体|缺少受众群体/,
135248
+ isContradicted: (data) => goldAssetNoted(data, "audience") || goldAccountFlag(data, "audienceQualified") === true,
135249
+ hint: "\u53D7\u4F17\u62A5\u8868\u91CC\u5DF2\u6709\u53D7\u4F17\uFF0C\u6216\u8BE5\u9879\u5DF2\u8FBE\u6807\u3002\u7981\u6B62\u5199\u300C\u8BF7\u8BBE\u7F6E\u53D7\u4F17\u7FA4\u4F53\u300D\u3002"
135250
+ },
135251
+ {
135252
+ id: "display-ads-already-exist",
135253
+ textPattern: /请使用自适应展示广告|没有自适应展示广告|未创建展示广告/,
135254
+ isContradicted: (data) => goldAssetNoted(data, "displayAds") || goldAccountFlag(data, "multiAssetResponsiveDisplayAdQualified") === true,
135255
+ hint: "\u5DF2\u6709\u81EA\u9002\u5E94\u5C55\u793A\u5E7F\u544A\u3002\u672A\u8FBE\u6807\u4E0D\u7B49\u4E8E\u6CA1\u6709\u5E7F\u544A\uFF1B\u7CFB\u5217\u6682\u505C\u65F6\u5199\u300C\u5C55\u793A\u7CFB\u5217\u5DF2\u6682\u505C\uFF0C\u672C\u671F\u65E0\u6D88\u8017\u300D\uFF0C\u7981\u6B62\u5199\u300C\u8BF7\u4F7F\u7528\u300D\u3002"
135256
+ }
135257
+ ];
135258
+ function collectGoldClaimTexts(data) {
135259
+ const texts = collectNarrativeTexts(data);
135260
+ const overview = asRecord13(data.diagnosisOverview);
135261
+ for (const key of ["advantages", "disadvantages"]) {
135262
+ const items3 = overview?.[key];
135263
+ if (!Array.isArray(items3)) continue;
135264
+ for (const item of items3) {
135265
+ const rec = asRecord13(item);
135266
+ if (rec) texts.push(String(rec.title ?? ""), String(rec.description ?? ""));
135267
+ }
135268
+ }
135269
+ return texts.filter((text2) => text2.trim().length > 0);
135270
+ }
135127
135271
  function findIdenticalTexts(texts, minCount) {
135128
135272
  const counts = /* @__PURE__ */ new Map();
135129
135273
  for (const t of texts) {
@@ -135185,6 +135329,13 @@ function getNarrativeConsistencyErrors(data) {
135185
135329
  errors.push(`\u53D9\u4E8B\u77DB\u76FE\uFF08${rule.id}\uFF09\uFF1A\u300C${hitText}\u300D\u2014\u2014 ${rule.hint}`);
135186
135330
  }
135187
135331
  }
135332
+ const goldClaimTexts = collectGoldClaimTexts(data);
135333
+ for (const rule of GOLD_CLAIM_RULES) {
135334
+ const hitText = goldClaimTexts.find((t) => rule.textPattern.test(t));
135335
+ if (hitText && rule.isContradicted(data)) {
135336
+ errors.push(`\u53D9\u4E8B\u77DB\u76FE\uFF08${rule.id}\uFF09\uFF1A\u300C${hitText}\u300D\u2014\u2014 ${rule.hint}`);
135337
+ }
135338
+ }
135188
135339
  errors.push(...getRepetitiveNarrativeErrors(data));
135189
135340
  errors.push(...getLandingPageTopicErrors(data));
135190
135341
  return errors;
@@ -135257,8 +135408,118 @@ function normalizeDiagnosisOverview(data) {
135257
135408
  };
135258
135409
  }
135259
135410
 
135260
- // src/commands/google-ads-diagnosis/render-report.ts
135411
+ // src/commands/google-ads-diagnosis/data-rendered-copy.ts
135261
135412
  function asRecord15(value) {
135413
+ if (value && typeof value === "object" && !Array.isArray(value)) {
135414
+ return value;
135415
+ }
135416
+ return null;
135417
+ }
135418
+ function compareToBenchmark(reportValue, benchmark) {
135419
+ const value = Number(reportValue.replace(/[^\d.]/g, ""));
135420
+ const matched = benchmark.match(/(≥|>=|≤|<=|>|<)\s*([\d.]+)/);
135421
+ if (!Number.isFinite(value) || !matched) return "\u5BF9\u7167\u6807\u51C6\u89C1\u672C\u884C";
135422
+ const bar = Number(matched[2]);
135423
+ const op = matched[1];
135424
+ const reached = op === "\u2265" || op === ">=" ? value >= bar : op === ">" ? value > bar : op === "\u2264" || op === "<=" ? value <= bar : value < bar;
135425
+ return reached ? "\u8FBE\u5230\u672C\u884C\u6807\u51C6" : "\u672A\u8FBE\u5230\u672C\u884C\u6807\u51C6";
135426
+ }
135427
+ function budgetStrategyFromRow(row) {
135428
+ const name2 = String(row.name ?? row.metric ?? "\u8BE5\u9879");
135429
+ const reportValue = String(row.reportValue ?? "\u2014");
135430
+ const benchmark = String(row.benchmark ?? "\u2014");
135431
+ return `${name2}\u672C\u671F\u4E3A ${reportValue}\uFF0C\u6807\u51C6 ${benchmark}\uFF0C${compareToBenchmark(reportValue, benchmark)}\u3002`;
135432
+ }
135433
+ function biddingReasonFromRow(row) {
135434
+ const name2 = String(row.campaignName ?? "\u8BE5\u7CFB\u5217");
135435
+ const current = String(row.biddingStrategyType ?? row.currentStrategy ?? "\u672A\u77E5");
135436
+ const recommended = String(row.recommendedStrategy ?? "\u2014");
135437
+ if (row.isCorrect === true) {
135438
+ return `${name2}\u5F53\u524D\u51FA\u4EF7\u7B56\u7565\u4E3A ${current}\uFF0C\u4E0E\u672C\u671F\u5EFA\u8BAE ${recommended} \u4E00\u81F4\u3002`;
135439
+ }
135440
+ return `${name2}\u5F53\u524D\u51FA\u4EF7\u7B56\u7565\u4E3A ${current}\uFF0C\u672C\u671F\u5BF9\u7167\u5EFA\u8BAE\u4E3A ${recommended}\u3002`;
135441
+ }
135442
+ function featureRecommendationFromRow(row) {
135443
+ const name2 = String(row.strategyName ?? row.strategy ?? "\u8BE5\u80FD\u529B");
135444
+ if (row.accountStatus === true) return `${name2}\uFF1A\u8D26\u6237\u5DF2\u542F\u7528\u3002`;
135445
+ return `${name2}\uFF1A\u672C\u671F\u672A\u68C0\u6D4B\u5230\u5BF9\u5E94\u7CFB\u5217\u3002`;
135446
+ }
135447
+ function creativeSuggestionFromRow(row) {
135448
+ const title = String(row.adTitle ?? "\u8BE5\u5E7F\u544A");
135449
+ const headlines = row.headlinesCount ?? "\u2014";
135450
+ const descriptions = row.descriptionsCount ?? "\u2014";
135451
+ const headlinesStatus = String(row.headlinesStatus ?? "\u2014");
135452
+ const descriptionsStatus = String(row.descriptionsStatus ?? "\u2014");
135453
+ return `${title}\uFF1A\u6807\u9898 ${headlines} \u6761\uFF08${headlinesStatus}\uFF09\uFF0C\u63CF\u8FF0 ${descriptions} \u6761\uFF08${descriptionsStatus}\uFF09\u3002`;
135454
+ }
135455
+ function distinctCopies(rows, key) {
135456
+ const counts = /* @__PURE__ */ new Map();
135457
+ for (const row of rows) {
135458
+ const text2 = String(row[key] ?? "");
135459
+ counts.set(text2, (counts.get(text2) ?? 0) + 1);
135460
+ }
135461
+ const seen = /* @__PURE__ */ new Map();
135462
+ return rows.map((row) => {
135463
+ const text2 = String(row[key] ?? "");
135464
+ if ((counts.get(text2) ?? 0) < 3) return row;
135465
+ const n = (seen.get(text2) ?? 0) + 1;
135466
+ seen.set(text2, n);
135467
+ return { ...row, [key]: `${text2}\uFF08\u540C\u7ED3\u6784\u7B2C ${n} \u6761\uFF09` };
135468
+ });
135469
+ }
135470
+ function mapRows(rows, write) {
135471
+ if (!Array.isArray(rows)) return rows;
135472
+ const written = rows.map((item) => {
135473
+ const row = asRecord15(item);
135474
+ return row ? write(row) : item;
135475
+ });
135476
+ return written;
135477
+ }
135478
+ function applyDataRenderedCopy(data) {
135479
+ const next = { ...data };
135480
+ const budgetRows = mapRows(data.budgetCompetitiveness, (row) => ({
135481
+ ...row,
135482
+ strategy: budgetStrategyFromRow(row)
135483
+ }));
135484
+ if (Array.isArray(budgetRows)) {
135485
+ next.budgetCompetitiveness = distinctCopies(budgetRows, "strategy");
135486
+ }
135487
+ const bidding = asRecord15(data.biddingStrategy);
135488
+ if (bidding) {
135489
+ next.biddingStrategy = {
135490
+ ...bidding,
135491
+ items: mapRows(bidding.items, (row) => ({
135492
+ ...row,
135493
+ recommendationReason: biddingReasonFromRow(row)
135494
+ }))
135495
+ };
135496
+ }
135497
+ const features = asRecord15(data.newFeatures);
135498
+ if (features) {
135499
+ next.newFeatures = {
135500
+ ...features,
135501
+ items: mapRows(features.items, (row) => ({
135502
+ ...row,
135503
+ optimizerRecommendation: featureRecommendationFromRow(row)
135504
+ }))
135505
+ };
135506
+ }
135507
+ const creative = asRecord15(data.adCreativeOptimization);
135508
+ if (creative) {
135509
+ const creativeRows = mapRows(creative.items, (row) => ({
135510
+ ...row,
135511
+ suggestion: creativeSuggestionFromRow(row)
135512
+ }));
135513
+ next.adCreativeOptimization = {
135514
+ ...creative,
135515
+ items: Array.isArray(creativeRows) ? distinctCopies(creativeRows, "suggestion") : creativeRows
135516
+ };
135517
+ }
135518
+ return next;
135519
+ }
135520
+
135521
+ // src/commands/google-ads-diagnosis/render-report.ts
135522
+ function asRecord16(value) {
135262
135523
  if (value && typeof value === "object" && !Array.isArray(value)) {
135263
135524
  return value;
135264
135525
  }
@@ -135267,8 +135528,8 @@ function asRecord15(value) {
135267
135528
  async function tryLoadCollectReportData(collectPath) {
135268
135529
  try {
135269
135530
  const raw = JSON.parse(await fs22.readFile(collectPath, "utf8"));
135270
- const root = asRecord15(raw);
135271
- const reportData = asRecord15(root?.reportData);
135531
+ const root = asRecord16(raw);
135532
+ const reportData = asRecord16(root?.reportData);
135272
135533
  return reportData ?? root;
135273
135534
  } catch {
135274
135535
  return null;
@@ -135285,7 +135546,7 @@ async function runGoogleAdsDiagnosisRender(opts) {
135285
135546
  `);
135286
135547
  process.exit(1);
135287
135548
  }
135288
- let data = asRecord15(dataRaw);
135549
+ let data = asRecord16(dataRaw);
135289
135550
  if (!opts.noMergeCollect) {
135290
135551
  const collectPath = path26.resolve(
135291
135552
  opts.collectFile ?? path26.join(path26.dirname(dataPath), GOOGLE_ADS_DIAGNOSIS_COLLECT_BASENAME)
@@ -135318,6 +135579,7 @@ async function runGoogleAdsDiagnosisRender(opts) {
135318
135579
  }
135319
135580
  }
135320
135581
  data = reconcileGoldAccountInReport(data, path26.dirname(dataPath));
135582
+ data = applyDataRenderedCopy(data);
135321
135583
  const overview = normalizeDiagnosisOverview(data);
135322
135584
  data = overview.data;
135323
135585
  if (overview.coerced.length > 0) {
@@ -135365,7 +135627,7 @@ async function runGoogleAdsDiagnosisRender(opts) {
135365
135627
  \u274C ${formatGoogleAdsDiagnosisContentErrors(contentCheck)}
135366
135628
  `);
135367
135629
  console.error(
135368
- " \u987B\u5148\u7531 Agent \u8865\u5168\u5168\u90E8 analysis/suggestions \u53CA budgetCompetitiveness[].strategy \u7B49\u5EFA\u8BAE\u5B57\u6BB5\u540E\u518D render\uFF1B\u8C03\u8BD5\u53EF\u52A0 --lenient\u3002\n"
135630
+ " \u987B\u5148\u7531 Agent \u8865\u5168\u4E3B\u89C2\u5206\u6790\uFF08\u5404\u6A21\u5757 analysis/suggestions\u3001\u8BCA\u65AD\u6982\u89C8\u3001\u603B\u7ED3\u3001\u4E1A\u52A1\u6A21\u5F0F\u4E0E\u884C\u4E1A\uFF09\u540E\u518D render\uFF1B\u8C03\u8BD5\u53EF\u52A0 --lenient\u3002\n"
135369
135631
  );
135370
135632
  process.exit(1);
135371
135633
  }
@@ -136626,26 +136888,26 @@ import fs26 from "fs/promises";
136626
136888
  import path31 from "path";
136627
136889
 
136628
136890
  // src/commands/google-quarterly/report-content.ts
136629
- function asRecord16(value) {
136891
+ function asRecord17(value) {
136630
136892
  return value && typeof value === "object" && !Array.isArray(value) ? value : null;
136631
136893
  }
136632
136894
  function asStringArray(value) {
136633
136895
  return Array.isArray(value) ? value.map((x) => String(x).trim()).filter(Boolean) : [];
136634
136896
  }
136635
136897
  function validateQuarterlyNarrative(data) {
136636
- const root = asRecord16(data);
136637
- const narrative = asRecord16(root?.narrative);
136898
+ const root = asRecord17(data);
136899
+ const narrative = asRecord17(root?.narrative);
136638
136900
  const missing = [];
136639
136901
  if (!narrative) {
136640
136902
  return { ok: false, missing: ["narrative"] };
136641
136903
  }
136642
136904
  if (asStringArray(narrative.executiveSummary).length < 1) missing.push("narrative.executiveSummary");
136643
- const sections = asRecord16(narrative.sections);
136905
+ const sections = asRecord17(narrative.sections);
136644
136906
  for (const key of ["efficiency", "adType", "region", "summary"]) {
136645
- const block = asRecord16(sections?.[key]);
136907
+ const block = asRecord17(sections?.[key]);
136646
136908
  if (asStringArray(block?.analysis).length < 1) missing.push(`narrative.sections.${key}.analysis`);
136647
136909
  }
136648
- const plan = asRecord16(narrative.plan);
136910
+ const plan = asRecord17(narrative.plan);
136649
136911
  if (asStringArray(plan?.analysis ?? plan?.goals).length < 1 && !String(plan?.text ?? "").trim()) {
136650
136912
  missing.push("narrative.plan");
136651
136913
  }
@@ -136658,8 +136920,8 @@ function mergeCollectFacts(agentData, collect) {
136658
136920
  ...facts,
136659
136921
  narrative: agentData.narrative ?? collect.narrative,
136660
136922
  meta: {
136661
- ...asRecord16(collect.meta) ?? {},
136662
- ...asRecord16(agentData.meta) ?? {}
136923
+ ...asRecord17(collect.meta) ?? {},
136924
+ ...asRecord17(agentData.meta) ?? {}
136663
136925
  }
136664
136926
  };
136665
136927
  }
@@ -136671,7 +136933,7 @@ import { fileURLToPath as fileURLToPath8 } from "url";
136671
136933
  var TSO_CLI_ROOT = path30.join(path30.dirname(fileURLToPath8(import.meta.url)), "../..");
136672
136934
 
136673
136935
  // src/commands/google-quarterly/render-report.ts
136674
- function asRecord17(value) {
136936
+ function asRecord18(value) {
136675
136937
  if (value && typeof value === "object" && !Array.isArray(value)) {
136676
136938
  return value;
136677
136939
  }
@@ -136700,7 +136962,7 @@ async function runGoogleQuarterlyRender(opts) {
136700
136962
  `);
136701
136963
  process.exit(1);
136702
136964
  }
136703
- const merged = mergeCollectFacts(asRecord17(agentRaw), asRecord17(collectRaw));
136965
+ const merged = mergeCollectFacts(asRecord18(agentRaw), asRecord18(collectRaw));
136704
136966
  const check = validateQuarterlyNarrative(merged);
136705
136967
  if (!check.ok && !opts.lenient) {
136706
136968
  console.error("\n\u274C narrative \u4E0D\u5B8C\u6574\uFF0C\u62D2\u7EDD\u51FA HTML\uFF1A\n");
@@ -136864,7 +137126,7 @@ import * as fs33 from "fs/promises";
136864
137126
  import * as path40 from "path";
136865
137127
 
136866
137128
  // src/commands/facebook-analysis/report-metrics.ts
136867
- function asRecord23(value) {
137129
+ function asRecord24(value) {
136868
137130
  if (value && typeof value === "object" && !Array.isArray(value)) {
136869
137131
  return value;
136870
137132
  }
@@ -137001,7 +137263,7 @@ function localizeFacebookReportLabels(payload) {
137001
137263
  for (const rows of Object.values(tables)) {
137002
137264
  if (!Array.isArray(rows)) continue;
137003
137265
  for (const row of rows) {
137004
- localizeMetricLabels(asRecord23(row));
137266
+ localizeMetricLabels(asRecord24(row));
137005
137267
  }
137006
137268
  }
137007
137269
  return payload;
@@ -137034,7 +137296,7 @@ async function readManifest3(snapshotDir) {
137034
137296
  async function loadSectionFile3(snapshotDir, file) {
137035
137297
  try {
137036
137298
  const raw = await fs33.readFile(path40.join(snapshotDir, file), "utf8");
137037
- return asRecord23(JSON.parse(raw));
137299
+ return asRecord24(JSON.parse(raw));
137038
137300
  } catch {
137039
137301
  return null;
137040
137302
  }
@@ -137090,7 +137352,7 @@ async function mergeFacebookSnapshotIntoReport(payload, snapshotDir) {
137090
137352
  if (data) sectionMap.set(art.section, data);
137091
137353
  }
137092
137354
  const overview = sectionMap.get("overview");
137093
- const current = asRecord23(overview?.currentPeriod);
137355
+ const current = asRecord24(overview?.currentPeriod);
137094
137356
  const accountName = typeof overview?.accountName === "string" ? overview.accountName : void 0;
137095
137357
  const accountId = manifest.accountId ?? (typeof overview?.accountId === "string" ? overview.accountId : void 0);
137096
137358
  const kpis = { ...payload.kpis ?? {} };
@@ -137227,7 +137489,7 @@ function writeSimpleXlsx(filePath, sheets, opts = {}) {
137227
137489
  }
137228
137490
 
137229
137491
  // src/commands/facebook-analysis/render-excel.ts
137230
- function asRecord24(value) {
137492
+ function asRecord25(value) {
137231
137493
  if (value && typeof value === "object" && !Array.isArray(value)) {
137232
137494
  return value;
137233
137495
  }
@@ -137275,8 +137537,8 @@ function bullets(value) {
137275
137537
  return asArray3(value).map((item) => typeof item === "string" ? item.trim() : "").filter(Boolean);
137276
137538
  }
137277
137539
  function insightLines(section) {
137278
- const rec = asRecord24(section);
137279
- const insight = asRecord24(rec?.insight) ?? rec;
137540
+ const rec = asRecord25(section);
137541
+ const insight = asRecord25(rec?.insight) ?? rec;
137280
137542
  return [...bullets(insight?.analysis), ...bullets(insight?.advice)];
137281
137543
  }
137282
137544
  function numberedSection(index, title, lines) {
@@ -137285,15 +137547,15 @@ function numberedSection(index, title, lines) {
137285
137547
  ${body}`;
137286
137548
  }
137287
137549
  function buildSummaryNarrative(data) {
137288
- const kpis = asRecord24(data.kpis) ?? {};
137289
- const meta = asRecord24(data.meta) ?? {};
137290
- const sections = asRecord24(data.sections) ?? {};
137550
+ const kpis = asRecord25(data.kpis) ?? {};
137551
+ const meta = asRecord25(data.meta) ?? {};
137552
+ const sections = asRecord25(data.sections) ?? {};
137291
137553
  const period = str2(meta.periodLabel) || `${str2(meta.startDate)} ~ ${str2(meta.endDate)}`.trim();
137292
137554
  const account = str2(meta.accountName) || "Facebook \u5E7F\u544A\u8D26\u6237";
137293
137555
  const campaign = str2(meta.campaignName);
137294
137556
  const resultLabel = displayResultType(kpis.resultType ?? meta.resultType) || "\u6210\u6548";
137295
137557
  const resultNoun = /潜在客户|lead/i.test(resultLabel) ? "\u4E2A\u6F5C\u5728\u5BA2\u6237\u8868\u5355" : `\u4E2A${resultLabel}`;
137296
- const firstQuestion = asRecord24(asArray3(data.fourQuestions)[0]);
137558
+ const firstQuestion = asRecord25(asArray3(data.fourQuestions)[0]);
137297
137559
  const extra = str2(firstQuestion?.verdict);
137298
137560
  const spendLine = [
137299
137561
  campaign ? `\uFF08\u5E7F\u544A\u7CFB\u5217\uFF1A${campaign}\uFF09` : "",
@@ -137313,7 +137575,7 @@ function buildSummaryNarrative(data) {
137313
137575
  numberedSection(3, "\u5E7F\u544A\u9891\u6B21\u4E0E\u65E5\u8D8B\u52BF", insightLines(sections.daily))
137314
137576
  ].join("\n");
137315
137577
  const recBlocks = asArray3(data.recommendations).map((raw, i) => {
137316
- const rec = asRecord24(raw);
137578
+ const rec = asRecord25(raw);
137317
137579
  if (!rec) return "";
137318
137580
  const title = [str2(rec.title), str2(rec.tag)].filter(Boolean).join(" \xB7 ");
137319
137581
  return numberedSection(i + 1, title || "\u4F18\u5316\u5EFA\u8BAE", bullets(rec.items));
@@ -137324,9 +137586,9 @@ function buildSummaryNarrative(data) {
137324
137586
  return [partOne, partTwo, partThree];
137325
137587
  }
137326
137588
  function buildMetaPeriodExcelSheets(data) {
137327
- const meta = asRecord24(data.meta) ?? {};
137328
- const kpis = asRecord24(data.kpis) ?? {};
137329
- const tables = asRecord24(data.tables) ?? {};
137589
+ const meta = asRecord25(data.meta) ?? {};
137590
+ const kpis = asRecord25(data.kpis) ?? {};
137591
+ const tables = asRecord25(data.tables) ?? {};
137330
137592
  const currency = str2(meta.currency) || "USD";
137331
137593
  const resultType = displayResultType(kpis.resultType ?? meta.resultType);
137332
137594
  const attribution = displayAttribution(kpis.attributionSetting ?? meta.attributionSetting);
@@ -137345,19 +137607,19 @@ function buildMetaPeriodExcelSheets(data) {
137345
137607
  ];
137346
137608
  const dailyRows = [metricHeaders(currency, "\u5355\u65E5")];
137347
137609
  for (const raw of asArray3(tables.daily)) {
137348
- const row = asRecord24(raw);
137610
+ const row = asRecord25(raw);
137349
137611
  if (!row) continue;
137350
137612
  dailyRows.push([normalizeInsightDate(row.date), ...metricCells(row)]);
137351
137613
  }
137352
137614
  const countryRows = [metricHeaders(currency, "\u56FD\u5BB6/\u5730\u533A")];
137353
137615
  for (const raw of asArray3(tables.countries)) {
137354
- const row = asRecord24(raw);
137616
+ const row = asRecord25(raw);
137355
137617
  if (!row) continue;
137356
137618
  countryRows.push([str2(row.countryOrRegion), ...metricCells(row)]);
137357
137619
  }
137358
137620
  const campaignRows3 = [metricHeaders(currency, "\u5E7F\u544A\u7CFB\u5217\u540D\u79F0")];
137359
137621
  for (const raw of asArray3(tables.campaigns)) {
137360
- const row = asRecord24(raw);
137622
+ const row = asRecord25(raw);
137361
137623
  if (!row) continue;
137362
137624
  campaignRows3.push([str2(row.campaignName), ...metricCells(row)]);
137363
137625
  }
@@ -137376,7 +137638,7 @@ function buildMetaPeriodExcelSheets(data) {
137376
137638
  ]
137377
137639
  ];
137378
137640
  for (const raw of asArray3(tables.audiences)) {
137379
- const row = asRecord24(raw);
137641
+ const row = asRecord25(raw);
137380
137642
  if (!row) continue;
137381
137643
  audienceRows.push([str2(row.age), str2(row.gender), ...metricCells(row)]);
137382
137644
  }
@@ -137408,7 +137670,7 @@ function isFiniteNumber(value) {
137408
137670
  function asArray4(value) {
137409
137671
  return Array.isArray(value) ? value : [];
137410
137672
  }
137411
- function asRecord25(value) {
137673
+ function asRecord26(value) {
137412
137674
  if (value && typeof value === "object" && !Array.isArray(value)) {
137413
137675
  return value;
137414
137676
  }
@@ -137421,8 +137683,8 @@ function stringList3(value) {
137421
137683
  return asArray4(value).map((item) => typeof item === "string" ? item.trim() : "").filter(Boolean);
137422
137684
  }
137423
137685
  function insightLists(section) {
137424
- const rec = asRecord25(section);
137425
- const insight = asRecord25(rec?.insight) ?? rec;
137686
+ const rec = asRecord26(section);
137687
+ const insight = asRecord26(rec?.insight) ?? rec;
137426
137688
  return {
137427
137689
  analysis: stringList3(insight?.analysis),
137428
137690
  advice: stringList3(insight?.advice)
@@ -137452,11 +137714,11 @@ function requireInsight(missing, key, chapter, section, required) {
137452
137714
  }
137453
137715
  function validateMetaPeriodReportContent(data) {
137454
137716
  const missing = [];
137455
- const meta = asRecord25(data.meta) ?? {};
137456
- const kpis = asRecord25(data.kpis) ?? {};
137457
- const tables = asRecord25(data.tables) ?? {};
137458
- const charts = asRecord25(data.charts) ?? {};
137459
- const sections = asRecord25(data.sections) ?? {};
137717
+ const meta = asRecord26(data.meta) ?? {};
137718
+ const kpis = asRecord26(data.kpis) ?? {};
137719
+ const tables = asRecord26(data.tables) ?? {};
137720
+ const charts = asRecord26(data.charts) ?? {};
137721
+ const sections = asRecord26(data.sections) ?? {};
137460
137722
  const fourQuestions = asArray4(data.fourQuestions);
137461
137723
  const recommendations = asArray4(data.recommendations);
137462
137724
  if (!textLen3(meta.accountName) && !textLen3(meta.accountId)) {
@@ -137535,7 +137797,7 @@ function validateMetaPeriodReportContent(data) {
137535
137797
  "\u987B\u6709 tables.audiences \u6570\u7EC4\uFF1B\u62C9\u6570\u52A0 --sections audience\uFF0Crender \u65F6\u4F20 --snapshot-dir\u3002\u672C\u671F\u65E0\u6D88\u8017\u53EF\u4E3A []"
137536
137798
  );
137537
137799
  }
137538
- const dailyChart = asRecord25(charts.daily);
137800
+ const dailyChart = asRecord26(charts.daily);
137539
137801
  if (!Array.isArray(dailyChart?.labels)) {
137540
137802
  pushMissing3(
137541
137803
  missing,
@@ -137545,7 +137807,7 @@ function validateMetaPeriodReportContent(data) {
137545
137807
  "\u987B\u6709 charts.daily.labels \u6570\u7EC4\uFF1B\u53EF\u7531 --snapshot-dir \u5408\u5E76 daily\u3002\u672C\u671F\u65E0\u6D88\u8017\u53EF\u4E3A []"
137546
137808
  );
137547
137809
  }
137548
- const countryChart = asRecord25(charts.country);
137810
+ const countryChart = asRecord26(charts.country);
137549
137811
  if (!Array.isArray(countryChart?.labels)) {
137550
137812
  pushMissing3(
137551
137813
  missing,
@@ -137555,7 +137817,7 @@ function validateMetaPeriodReportContent(data) {
137555
137817
  "\u987B\u6709 charts.country.labels \u6570\u7EC4\uFF1B\u53EF\u7531 --snapshot-dir \u5408\u5E76 country\u3002\u672C\u671F\u65E0\u6D88\u8017\u53EF\u4E3A []"
137556
137818
  );
137557
137819
  }
137558
- const audienceChart = asRecord25(charts.audience);
137820
+ const audienceChart = asRecord26(charts.audience);
137559
137821
  if (!Array.isArray(audienceChart?.labels)) {
137560
137822
  pushMissing3(
137561
137823
  missing,
@@ -137574,7 +137836,7 @@ function validateMetaPeriodReportContent(data) {
137574
137836
  "\u5FC5\u987B\u6070\u597D 4 \u95EE\uFF1A\u94B1\u82B1\u5F97\u503C\u4E0D\u503C / \u8C01\u771F\u7684\u60F3\u4E70 / \u5E7F\u544A\u8FD8\u65B0\u9C9C\u5417 / \u7528\u6237\u4E3A\u4EC0\u4E48\u4E0D\u7559\u8D44"
137575
137837
  );
137576
137838
  } else {
137577
- const titles = fourQuestions.map((q) => asRecord25(q)?.title);
137839
+ const titles = fourQuestions.map((q) => asRecord26(q)?.title);
137578
137840
  for (const title of FOUR_QUESTION_TITLES) {
137579
137841
  if (!titles.includes(title)) {
137580
137842
  pushMissing3(
@@ -137587,7 +137849,7 @@ function validateMetaPeriodReportContent(data) {
137587
137849
  }
137588
137850
  }
137589
137851
  for (let i = 0; i < fourQuestions.length; i++) {
137590
- const q = asRecord25(fourQuestions[i]);
137852
+ const q = asRecord26(fourQuestions[i]);
137591
137853
  if (textLen3(q?.verdict) < 8) {
137592
137854
  pushMissing3(
137593
137855
  missing,
@@ -137627,7 +137889,7 @@ function validateMetaPeriodReportContent(data) {
137627
137889
  );
137628
137890
  } else {
137629
137891
  for (let i = 0; i < recommendations.length; i++) {
137630
- const rec = asRecord25(recommendations[i]);
137892
+ const rec = asRecord26(recommendations[i]);
137631
137893
  if (textLen3(rec?.title) < 2) {
137632
137894
  pushMissing3(
137633
137895
  missing,
@@ -137720,7 +137982,7 @@ async function readJsonFile4(filePath) {
137720
137982
  throw new Error(`\u65E0\u6CD5\u89E3\u6790 JSON\uFF1A${filePath}`);
137721
137983
  }
137722
137984
  }
137723
- function asRecord26(value) {
137985
+ function asRecord27(value) {
137724
137986
  if (value && typeof value === "object" && !Array.isArray(value)) {
137725
137987
  return value;
137726
137988
  }
@@ -137749,7 +138011,7 @@ function defaultOutPath(dataPath, format, out) {
137749
138011
  }
137750
138012
  async function runFacebookAnalysisRender(opts) {
137751
138013
  const dataPath = path41.resolve(opts.dataFile);
137752
- let data = asRecord26(await readJsonFile4(dataPath));
138014
+ let data = asRecord27(await readJsonFile4(dataPath));
137753
138015
  if (opts.snapshotDir?.trim()) {
137754
138016
  data = await mergeFacebookSnapshotIntoReport(data, path41.resolve(opts.snapshotDir));
137755
138017
  }
@@ -137813,7 +138075,7 @@ import { fileURLToPath as fileURLToPath11 } from "url";
137813
138075
  // src/commands/facebook-analysis/diagnosis-merge-snapshot.ts
137814
138076
  import * as fs35 from "fs/promises";
137815
138077
  import * as path42 from "path";
137816
- function asRecord27(value) {
138078
+ function asRecord28(value) {
137817
138079
  if (value && typeof value === "object" && !Array.isArray(value)) {
137818
138080
  return value;
137819
138081
  }
@@ -137824,7 +138086,7 @@ function num8(value) {
137824
138086
  return Number.isFinite(n) ? n : void 0;
137825
138087
  }
137826
138088
  function asRows(value) {
137827
- return Array.isArray(value) ? value.filter((r) => asRecord27(r) !== null) : [];
138089
+ return Array.isArray(value) ? value.filter((r) => asRecord28(r) !== null) : [];
137828
138090
  }
137829
138091
  function pickMetricExtras(row, extras) {
137830
138092
  const out = {
@@ -137873,14 +138135,14 @@ async function readManifest4(snapshotDir, accountId) {
137873
138135
  async function loadSectionFile4(snapshotDir, file) {
137874
138136
  try {
137875
138137
  const raw = await fs35.readFile(path42.join(snapshotDir, file), "utf8");
137876
- return asRecord27(JSON.parse(raw));
138138
+ return asRecord28(JSON.parse(raw));
137877
138139
  } catch {
137878
138140
  return null;
137879
138141
  }
137880
138142
  }
137881
138143
  async function mergeMetaDiagnosisSnapshotIntoReport(payload, snapshotDir) {
137882
138144
  const absDir = path42.resolve(snapshotDir);
137883
- const metaAccountId = typeof asRecord27(payload.meta)?.accountId === "string" ? asRecord27(payload.meta)?.accountId : void 0;
138145
+ const metaAccountId = typeof asRecord28(payload.meta)?.accountId === "string" ? asRecord28(payload.meta)?.accountId : void 0;
137884
138146
  const manifest = await readManifest4(snapshotDir, metaAccountId);
137885
138147
  if (!manifest) {
137886
138148
  throw new Error(
@@ -137893,8 +138155,8 @@ async function mergeMetaDiagnosisSnapshotIntoReport(payload, snapshotDir) {
137893
138155
  if (data) sectionMap.set(art.section, data);
137894
138156
  }
137895
138157
  const overview = sectionMap.get("overview");
137896
- const currentPeriod = asRecord27(overview?.currentPeriod);
137897
- const previousPeriod = asRecord27(overview?.previousPeriod);
138158
+ const currentPeriod = asRecord28(overview?.currentPeriod);
138159
+ const previousPeriod = asRecord28(overview?.previousPeriod);
137898
138160
  const accountId = manifest.accountId ?? metaAccountId;
137899
138161
  const meta = { ...payload.meta ?? {} };
137900
138162
  if (!meta.accountId && accountId) meta.accountId = accountId;
@@ -138028,7 +138290,7 @@ function textLen4(value) {
138028
138290
  function asArray5(value) {
138029
138291
  return Array.isArray(value) ? value : [];
138030
138292
  }
138031
- function asRecord28(value) {
138293
+ function asRecord29(value) {
138032
138294
  if (value && typeof value === "object" && !Array.isArray(value)) {
138033
138295
  return value;
138034
138296
  }
@@ -138039,9 +138301,9 @@ function pushMissing4(missing, id, chapter, dimension, hint) {
138039
138301
  }
138040
138302
  function validateMetaDiagnosisReportContent(data) {
138041
138303
  const missing = [];
138042
- const meta = asRecord28(data.meta) ?? {};
138043
- const narrative = asRecord28(data.narrative) ?? {};
138044
- const sections = asRecord28(narrative.sections) ?? {};
138304
+ const meta = asRecord29(data.meta) ?? {};
138305
+ const narrative = asRecord29(data.narrative) ?? {};
138306
+ const sections = asRecord29(narrative.sections) ?? {};
138045
138307
  if (!textLen4(meta.accountId)) {
138046
138308
  pushMissing4(missing, "meta-account", "\u5143\u4FE1\u606F", "\u8D26\u6237 ID", "meta.accountId \u5FC5\u586B");
138047
138309
  }
@@ -138059,7 +138321,7 @@ function validateMetaDiagnosisReportContent(data) {
138059
138321
  const sectionSuggestionCounts = {};
138060
138322
  const sectionNotAvailable = {};
138061
138323
  for (const key of NORMAL_SECTION_KEYS) {
138062
- const section = asRecord28(sections[key]);
138324
+ const section = asRecord29(sections[key]);
138063
138325
  const analysis = asArray5(section?.analysis).filter((s) => textLen4(s) > 0);
138064
138326
  const suggestions = asArray5(section?.suggestions).filter((s) => textLen4(s) > 0);
138065
138327
  sectionAnalysisCounts[key] = analysis.length;
@@ -138085,7 +138347,7 @@ function validateMetaDiagnosisReportContent(data) {
138085
138347
  }
138086
138348
  }
138087
138349
  for (const key of NA_SECTION_KEYS) {
138088
- const section = asRecord28(sections[key]);
138350
+ const section = asRecord29(sections[key]);
138089
138351
  const analysis = asArray5(section?.analysis).filter((s) => textLen4(s) > 0);
138090
138352
  const suggestions = asArray5(section?.suggestions).filter((s) => textLen4(s) > 0);
138091
138353
  const notAvailable = section?.notAvailable === true;
@@ -138107,7 +138369,7 @@ function validateMetaDiagnosisReportContent(data) {
138107
138369
  }
138108
138370
  const recommendations = asArray5(narrative.recommendations).filter((r) => {
138109
138371
  if (typeof r === "string") return textLen4(r) > 0;
138110
- const rec = asRecord28(r);
138372
+ const rec = asRecord29(r);
138111
138373
  return textLen4(rec?.content) > 0;
138112
138374
  });
138113
138375
  if (recommendations.length < 3) {
@@ -138180,7 +138442,7 @@ async function readJsonFile5(filePath) {
138180
138442
  throw new Error(`\u65E0\u6CD5\u89E3\u6790 JSON\uFF1A${filePath}`);
138181
138443
  }
138182
138444
  }
138183
- function asRecord29(value) {
138445
+ function asRecord30(value) {
138184
138446
  if (value && typeof value === "object" && !Array.isArray(value)) {
138185
138447
  return value;
138186
138448
  }
@@ -138195,7 +138457,7 @@ function injectReportData4(html, payload) {
138195
138457
  }
138196
138458
  async function runFacebookAnalysisDiagnosisRender(opts) {
138197
138459
  const dataPath = path43.resolve(opts.dataFile);
138198
- let data = asRecord29(await readJsonFile5(dataPath));
138460
+ let data = asRecord30(await readJsonFile5(dataPath));
138199
138461
  if (opts.snapshotDir?.trim()) {
138200
138462
  data = await mergeMetaDiagnosisSnapshotIntoReport(data, path43.resolve(opts.snapshotDir));
138201
138463
  }
@@ -138394,7 +138656,7 @@ function resolveFacebookPageSize(raw, fallback = FB_ENTITY_PAGE_SIZE_DEFAULT) {
138394
138656
  if (!Number.isFinite(n) || n <= 0) return fallback;
138395
138657
  return Math.min(Math.max(Math.floor(n), 1), FB_ENTITY_PAGE_SIZE_MAX);
138396
138658
  }
138397
- function asRecord30(value) {
138659
+ function asRecord31(value) {
138398
138660
  return value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
138399
138661
  }
138400
138662
  function pickProp(obj, ...keys) {
@@ -138404,12 +138666,12 @@ function pickProp(obj, ...keys) {
138404
138666
  return void 0;
138405
138667
  }
138406
138668
  function unwrapFacebookPaged(raw) {
138407
- const rec = asRecord30(raw);
138669
+ const rec = asRecord31(raw);
138408
138670
  if (!rec) return { items: [] };
138409
138671
  const data = pickProp(rec, "data", "Data");
138410
138672
  const items3 = Array.isArray(data) ? data : [];
138411
- const paging = asRecord30(pickProp(rec, "paging", "Paging"));
138412
- const cursors = paging ? asRecord30(pickProp(paging, "cursors", "Cursors")) : void 0;
138673
+ const paging = asRecord31(pickProp(rec, "paging", "Paging"));
138674
+ const cursors = paging ? asRecord31(pickProp(paging, "cursors", "Cursors")) : void 0;
138413
138675
  const afterRaw = cursors ? pickProp(cursors, "after", "After") : void 0;
138414
138676
  const after = typeof afterRaw === "string" && afterRaw.trim() ? afterRaw.trim() : void 0;
138415
138677
  return { items: items3, after };
@@ -138557,7 +138819,7 @@ async function fetchFacebookInsights(config, digits, level, opts) {
138557
138819
  opts.verbose
138558
138820
  );
138559
138821
  if (Array.isArray(raw)) return raw;
138560
- const rec = asRecord30(raw);
138822
+ const rec = asRecord31(raw);
138561
138823
  const data = rec ? pickProp(rec, "data", "Data") : void 0;
138562
138824
  return Array.isArray(data) ? data : [];
138563
138825
  }
@@ -138566,7 +138828,7 @@ function sumActionArray(raw) {
138566
138828
  let sum = 0;
138567
138829
  let hit = false;
138568
138830
  for (const item of raw) {
138569
- const rec = asRecord30(item);
138831
+ const rec = asRecord31(item);
138570
138832
  if (!rec) continue;
138571
138833
  const n = Number(rec.value);
138572
138834
  if (!Number.isFinite(n)) continue;
@@ -139845,7 +140107,7 @@ import { fileURLToPath as fileURLToPath12 } from "url";
139845
140107
  // src/commands/bing-analysis/merge-snapshot.ts
139846
140108
  import * as fs37 from "fs/promises";
139847
140109
  import * as path46 from "path";
139848
- function asRecord31(value) {
140110
+ function asRecord32(value) {
139849
140111
  if (value && typeof value === "object" && !Array.isArray(value)) {
139850
140112
  return value;
139851
140113
  }
@@ -139860,7 +140122,7 @@ function round27(value) {
139860
140122
  }
139861
140123
  function asRows2(value) {
139862
140124
  if (Array.isArray(value)) {
139863
- return value.filter((r) => asRecord31(r) !== null);
140125
+ return value.filter((r) => asRecord32(r) !== null);
139864
140126
  }
139865
140127
  return [];
139866
140128
  }
@@ -139942,17 +140204,17 @@ function campaignStatusDisplay(status) {
139942
140204
  return status == null || status === "" ? "\u2014" : String(status);
139943
140205
  }
139944
140206
  function mapDeviceRows(payload) {
139945
- const obj = asRecord31(payload);
140207
+ const obj = asRecord32(payload);
139946
140208
  return asRows2(obj?.devices).map((r) => pickMetrics(r, ["deviceType"]));
139947
140209
  }
139948
140210
  function mapGeographicRows(payload) {
139949
- const obj = asRecord31(payload);
140211
+ const obj = asRecord32(payload);
139950
140212
  return asRows2(obj?.countries).map(
139951
140213
  (r) => pickMetrics(r, ["countryOrRegion", "countryCriteriaId", "regionName", "cityName"])
139952
140214
  );
139953
140215
  }
139954
140216
  function mapAudienceLabelRows(payload, labelKey) {
139955
- const obj = asRecord31(payload);
140217
+ const obj = asRecord32(payload);
139956
140218
  return asRows2(obj?.audience).map((r) => {
139957
140219
  const m = pickMetrics(r, ["audience", "bidModifier"]);
139958
140220
  m[labelKey] = r.audience;
@@ -139960,7 +140222,7 @@ function mapAudienceLabelRows(payload, labelKey) {
139960
140222
  });
139961
140223
  }
139962
140224
  function mapAudienceFromMerged(payload) {
139963
- const data = asRecord31(asRecord31(payload)?.data);
140225
+ const data = asRecord32(asRecord32(payload)?.data);
139964
140226
  return {
139965
140227
  audienceAge: mapAudienceLabelRows(data?.ageAudience, "ageRange"),
139966
140228
  audienceGender: mapAudienceLabelRows(data?.genderAudience, "gender")
@@ -140055,7 +140317,7 @@ function mapSearchTermRows(payload) {
140055
140317
  }
140056
140318
  async function mergeBingAnalysisSnapshotIntoReport(payload, snapshotDir) {
140057
140319
  const absDir = path46.resolve(snapshotDir);
140058
- const metaAccountId = typeof asRecord31(payload.meta)?.accountId === "string" ? asRecord31(payload.meta)?.accountId : void 0;
140320
+ const metaAccountId = typeof asRecord32(payload.meta)?.accountId === "string" ? asRecord32(payload.meta)?.accountId : void 0;
140059
140321
  const manifest = await readManifest5(snapshotDir, metaAccountId);
140060
140322
  if (!manifest) {
140061
140323
  throw new Error(`\u672A\u5728 ${absDir} \u627E\u5230 report-manifest*.json\uFF0C\u8BF7\u5148\u6267\u884C bing-analysis --json-out`);
@@ -140065,9 +140327,9 @@ async function mergeBingAnalysisSnapshotIntoReport(payload, snapshotDir) {
140065
140327
  const data = await loadSectionPayload(absDir, art.file);
140066
140328
  if (data != null) sectionMap.set(art.section, data);
140067
140329
  }
140068
- const overview = asRecord31(sectionMap.get("overview"));
140069
- const currentPeriod = asRecord31(overview?.currentPeriod);
140070
- const previousPeriod = asRecord31(overview?.previousPeriod);
140330
+ const overview = asRecord32(sectionMap.get("overview"));
140331
+ const currentPeriod = asRecord32(overview?.currentPeriod);
140332
+ const previousPeriod = asRecord32(overview?.previousPeriod);
140071
140333
  const accountId = manifest.accountId ?? metaAccountId;
140072
140334
  const campaignRows3 = mapCampaignRows2(sectionMap.get("campaigns"));
140073
140335
  const campaignKpis = sumBingCampaignRowsToPeriodKpis(campaignRows3);
@@ -140194,7 +140456,7 @@ function textLen5(value) {
140194
140456
  function asArray6(value) {
140195
140457
  return Array.isArray(value) ? value : [];
140196
140458
  }
140197
- function asRecord32(value) {
140459
+ function asRecord33(value) {
140198
140460
  if (value && typeof value === "object" && !Array.isArray(value)) {
140199
140461
  return value;
140200
140462
  }
@@ -140205,9 +140467,9 @@ function pushMissing5(missing, id, chapter, dimension, hint) {
140205
140467
  }
140206
140468
  function validateBingPeriodReportContent(data) {
140207
140469
  const missing = [];
140208
- const meta = asRecord32(data.meta) ?? {};
140209
- const narrative = asRecord32(data.narrative) ?? {};
140210
- const sections = asRecord32(narrative.sections) ?? {};
140470
+ const meta = asRecord33(data.meta) ?? {};
140471
+ const narrative = asRecord33(data.narrative) ?? {};
140472
+ const sections = asRecord33(narrative.sections) ?? {};
140211
140473
  if (!textLen5(meta.accountId)) {
140212
140474
  pushMissing5(missing, "meta-account", "\u5143\u4FE1\u606F", "\u8D26\u6237 ID", "meta.accountId \u5FC5\u586B");
140213
140475
  }
@@ -140224,7 +140486,7 @@ function validateBingPeriodReportContent(data) {
140224
140486
  const sectionAnalysisCounts = {};
140225
140487
  const sectionSuggestionCounts = {};
140226
140488
  for (const key of SECTION_KEYS2) {
140227
- const section = asRecord32(sections[key]);
140489
+ const section = asRecord33(sections[key]);
140228
140490
  const analysis = asArray6(section?.analysis).filter((s) => textLen5(s) > 0);
140229
140491
  const suggestions = asArray6(section?.suggestions).filter((s) => textLen5(s) > 0);
140230
140492
  sectionAnalysisCounts[key] = analysis.length;
@@ -140250,7 +140512,7 @@ function validateBingPeriodReportContent(data) {
140250
140512
  }
140251
140513
  const recommendations = asArray6(narrative.recommendations).filter((r) => {
140252
140514
  if (typeof r === "string") return textLen5(r) > 0;
140253
- const rec = asRecord32(r);
140515
+ const rec = asRecord33(r);
140254
140516
  return textLen5(rec?.content) > 0;
140255
140517
  });
140256
140518
  if (recommendations.length < 3) {
@@ -140311,7 +140573,7 @@ async function readJsonFile6(filePath) {
140311
140573
  throw new Error(`\u65E0\u6CD5\u89E3\u6790 JSON\uFF1A${filePath}`);
140312
140574
  }
140313
140575
  }
140314
- function asRecord33(value) {
140576
+ function asRecord34(value) {
140315
140577
  if (value && typeof value === "object" && !Array.isArray(value)) {
140316
140578
  return value;
140317
140579
  }
@@ -140326,7 +140588,7 @@ function injectReportData5(html, payload) {
140326
140588
  }
140327
140589
  async function runBingAnalysisRender(opts) {
140328
140590
  const dataPath = path47.resolve(opts.dataFile);
140329
- let data = asRecord33(await readJsonFile6(dataPath));
140591
+ let data = asRecord34(await readJsonFile6(dataPath));
140330
140592
  if (opts.snapshotDir?.trim()) {
140331
140593
  data = await mergeBingAnalysisSnapshotIntoReport(data, path47.resolve(opts.snapshotDir));
140332
140594
  }
@@ -140799,7 +141061,7 @@ import { fileURLToPath as fileURLToPath13 } from "url";
140799
141061
  // src/commands/tiktok-analysis/merge-snapshot.ts
140800
141062
  import * as fs39 from "fs/promises";
140801
141063
  import * as path49 from "path";
140802
- function asRecord34(value) {
141064
+ function asRecord35(value) {
140803
141065
  if (value && typeof value === "object" && !Array.isArray(value)) {
140804
141066
  return value;
140805
141067
  }
@@ -140977,7 +141239,7 @@ function mapAudienceMerged(payload) {
140977
141239
  }
140978
141240
  async function mergeTikTokAnalysisSnapshotIntoReport(payload, snapshotDir) {
140979
141241
  const absDir = path49.resolve(snapshotDir);
140980
- const metaAccountId = typeof asRecord34(payload.meta)?.accountId === "string" ? asRecord34(payload.meta)?.accountId : void 0;
141242
+ const metaAccountId = typeof asRecord35(payload.meta)?.accountId === "string" ? asRecord35(payload.meta)?.accountId : void 0;
140981
141243
  const manifest = await readManifest6(snapshotDir, metaAccountId);
140982
141244
  if (!manifest) {
140983
141245
  throw new Error(
@@ -141091,7 +141353,7 @@ function textLen6(value) {
141091
141353
  function asArray7(value) {
141092
141354
  return Array.isArray(value) ? value : [];
141093
141355
  }
141094
- function asRecord35(value) {
141356
+ function asRecord36(value) {
141095
141357
  if (value && typeof value === "object" && !Array.isArray(value)) {
141096
141358
  return value;
141097
141359
  }
@@ -141102,9 +141364,9 @@ function pushMissing6(missing, id, chapter, dimension, hint) {
141102
141364
  }
141103
141365
  function validateTikTokPeriodReportContent(data) {
141104
141366
  const missing = [];
141105
- const meta = asRecord35(data.meta) ?? {};
141106
- const narrative = asRecord35(data.narrative) ?? {};
141107
- const sections = asRecord35(narrative.sections) ?? {};
141367
+ const meta = asRecord36(data.meta) ?? {};
141368
+ const narrative = asRecord36(data.narrative) ?? {};
141369
+ const sections = asRecord36(narrative.sections) ?? {};
141108
141370
  if (!textLen6(meta.accountId)) {
141109
141371
  pushMissing6(missing, "meta-account", "\u5143\u4FE1\u606F", "\u8D26\u6237 ID", "meta.accountId \u5FC5\u586B");
141110
141372
  }
@@ -141121,7 +141383,7 @@ function validateTikTokPeriodReportContent(data) {
141121
141383
  const sectionAnalysisCounts = {};
141122
141384
  const sectionSuggestionCounts = {};
141123
141385
  for (const key of SECTION_KEYS3) {
141124
- const section = asRecord35(sections[key]);
141386
+ const section = asRecord36(sections[key]);
141125
141387
  const analysis = asArray7(section?.analysis).filter((s) => textLen6(s) > 0);
141126
141388
  const suggestions = asArray7(section?.suggestions).filter((s) => textLen6(s) > 0);
141127
141389
  sectionAnalysisCounts[key] = analysis.length;
@@ -141147,7 +141409,7 @@ function validateTikTokPeriodReportContent(data) {
141147
141409
  }
141148
141410
  const recommendations = asArray7(narrative.recommendations).filter((r) => {
141149
141411
  if (typeof r === "string") return textLen6(r) > 0;
141150
- const rec = asRecord35(r);
141412
+ const rec = asRecord36(r);
141151
141413
  return textLen6(rec?.content) > 0;
141152
141414
  });
141153
141415
  if (recommendations.length < 3) {
@@ -141208,7 +141470,7 @@ async function readJsonFile7(filePath) {
141208
141470
  throw new Error(`\u65E0\u6CD5\u89E3\u6790 JSON\uFF1A${filePath}`);
141209
141471
  }
141210
141472
  }
141211
- function asRecord36(value) {
141473
+ function asRecord37(value) {
141212
141474
  if (value && typeof value === "object" && !Array.isArray(value)) {
141213
141475
  return value;
141214
141476
  }
@@ -141223,7 +141485,7 @@ function injectReportData6(html, payload) {
141223
141485
  }
141224
141486
  async function runTikTokAnalysisRender(opts) {
141225
141487
  const dataPath = path50.resolve(opts.dataFile);
141226
- let data = asRecord36(await readJsonFile7(dataPath));
141488
+ let data = asRecord37(await readJsonFile7(dataPath));
141227
141489
  if (opts.snapshotDir?.trim()) {
141228
141490
  data = await mergeTikTokAnalysisSnapshotIntoReport(data, path50.resolve(opts.snapshotDir));
141229
141491
  }
@@ -142322,7 +142584,7 @@ function endpointHintForYandexSection(def) {
142322
142584
  // src/commands/yandex-analysis/merge-snapshot.ts
142323
142585
  import * as fs41 from "fs/promises";
142324
142586
  import * as path51 from "path";
142325
- function asRecord37(value) {
142587
+ function asRecord38(value) {
142326
142588
  if (value && typeof value === "object" && !Array.isArray(value)) {
142327
142589
  return value;
142328
142590
  }
@@ -142342,11 +142604,11 @@ function normalizeYandexRoi(value) {
142342
142604
  return r === -1 ? void 0 : r;
142343
142605
  }
142344
142606
  function asItems(payload) {
142345
- const obj = asRecord37(payload);
142607
+ const obj = asRecord38(payload);
142346
142608
  if (!obj) return [];
142347
142609
  const items3 = obj.items;
142348
142610
  if (!Array.isArray(items3)) return [];
142349
- return items3.filter((r) => asRecord37(r) !== null);
142611
+ return items3.filter((r) => asRecord38(r) !== null);
142350
142612
  }
142351
142613
  function pickMetrics2(row, extras = []) {
142352
142614
  const spend = num12(row.spend) ?? 0;
@@ -142482,7 +142744,7 @@ function aggregateDailyToWeekly(dailyRows) {
142482
142744
  });
142483
142745
  }
142484
142746
  function prepareYandexDailyPayload(payload) {
142485
- const obj = asRecord37(payload) ?? {};
142747
+ const obj = asRecord38(payload) ?? {};
142486
142748
  const items3 = asItems(obj).map((r) => {
142487
142749
  const date = normalizeYandexDailyDate(r.date);
142488
142750
  return date ? { ...r, date } : r;
@@ -142551,7 +142813,7 @@ function mapDeviceRows2(payload) {
142551
142813
  }
142552
142814
  async function mergeYandexAnalysisSnapshotIntoReport(payload, snapshotDir) {
142553
142815
  const absDir = path51.resolve(snapshotDir);
142554
- const metaAccountId = typeof asRecord37(payload.meta)?.accountId === "string" ? asRecord37(payload.meta)?.accountId : void 0;
142816
+ const metaAccountId = typeof asRecord38(payload.meta)?.accountId === "string" ? asRecord38(payload.meta)?.accountId : void 0;
142555
142817
  const manifest = await readManifest7(snapshotDir, metaAccountId);
142556
142818
  if (!manifest) {
142557
142819
  throw new Error(
@@ -142563,8 +142825,8 @@ async function mergeYandexAnalysisSnapshotIntoReport(payload, snapshotDir) {
142563
142825
  const data = await loadSectionPayload3(absDir, art.file);
142564
142826
  if (data != null) sectionMap.set(art.section, data);
142565
142827
  }
142566
- const overview = asRecord37(sectionMap.get("overview"));
142567
- const totals = asRecord37(overview?.totals);
142828
+ const overview = asRecord38(sectionMap.get("overview"));
142829
+ const totals = asRecord38(overview?.totals);
142568
142830
  const accountId = manifest.accountId ?? metaAccountId;
142569
142831
  const meta = { ...payload.meta ?? {} };
142570
142832
  if (!meta.accountId && accountId) meta.accountId = accountId;
@@ -142786,7 +143048,7 @@ function textLen7(value) {
142786
143048
  function asArray8(value) {
142787
143049
  return Array.isArray(value) ? value : [];
142788
143050
  }
142789
- function asRecord38(value) {
143051
+ function asRecord39(value) {
142790
143052
  if (value && typeof value === "object" && !Array.isArray(value)) {
142791
143053
  return value;
142792
143054
  }
@@ -142799,23 +143061,23 @@ function normKey(value) {
142799
143061
  return String(value ?? "").trim().toLowerCase();
142800
143062
  }
142801
143063
  function insightMatchRecord(item) {
142802
- const rec = asRecord38(item);
143064
+ const rec = asRecord39(item);
142803
143065
  if (!rec) return {};
142804
- return asRecord38(rec.match) ?? rec;
143066
+ return asRecord39(rec.match) ?? rec;
142805
143067
  }
142806
143068
  function findRowInsight(list, primaryField, primaryValue, row) {
142807
143069
  const target = normKey(primaryValue);
142808
143070
  if (!target) return null;
142809
143071
  const candidates = list.filter((it) => normKey(insightMatchRecord(it)[primaryField]) === target);
142810
- if (candidates.length <= 1) return asRecord38(candidates[0]);
143072
+ if (candidates.length <= 1) return asRecord39(candidates[0]);
142811
143073
  const campaign = normKey(row.campaignName);
142812
143074
  const narrowed = candidates.find(
142813
143075
  (it) => normKey(insightMatchRecord(it).campaignName) === campaign
142814
143076
  );
142815
- return asRecord38(narrowed ?? candidates[0]);
143077
+ return asRecord39(narrowed ?? candidates[0]);
142816
143078
  }
142817
143079
  function topRowsBySpend(rows, limit) {
142818
- return rows.map((r) => asRecord38(r)).filter((r) => r != null).sort((a, b) => (Number(b.spend) || 0) - (Number(a.spend) || 0)).slice(0, limit);
143080
+ return rows.map((r) => asRecord39(r)).filter((r) => r != null).sort((a, b) => (Number(b.spend) || 0) - (Number(a.spend) || 0)).slice(0, limit);
142819
143081
  }
142820
143082
  var ROW_INSIGHT_SPECS = [
142821
143083
  {
@@ -142830,17 +143092,17 @@ var ROW_INSIGHT_SPECS = [
142830
143092
  ];
142831
143093
  function validateYandexPeriodReportContent(data) {
142832
143094
  const missing = [];
142833
- const meta = asRecord38(data.meta) ?? {};
142834
- const narrative = asRecord38(data.narrative) ?? {};
142835
- const sections = asRecord38(narrative.sections) ?? {};
142836
- const kpis = asRecord38(data.kpis) ?? {};
143095
+ const meta = asRecord39(data.meta) ?? {};
143096
+ const narrative = asRecord39(data.narrative) ?? {};
143097
+ const sections = asRecord39(narrative.sections) ?? {};
143098
+ const kpis = asRecord39(data.kpis) ?? {};
142837
143099
  if (!textLen7(meta.accountId)) {
142838
143100
  pushMissing7(missing, "meta-account", "\u5143\u4FE1\u606F", "\u8D26\u6237 ID", "meta.accountId \u5FC5\u586B");
142839
143101
  }
142840
143102
  const sectionAnalysisCounts = {};
142841
143103
  const sectionSuggestionCounts = {};
142842
143104
  for (const key of SECTION_KEYS4) {
142843
- const section = asRecord38(sections[key]);
143105
+ const section = asRecord39(sections[key]);
142844
143106
  const analysis = asArray8(section?.analysis).filter((s) => textLen7(s) > 0);
142845
143107
  const suggestions = asArray8(section?.suggestions).filter((s) => textLen7(s) > 0);
142846
143108
  sectionAnalysisCounts[key] = analysis.length;
@@ -142866,7 +143128,7 @@ function validateYandexPeriodReportContent(data) {
142866
143128
  }
142867
143129
  const recommendations = asArray8(narrative.recommendations).filter((r) => {
142868
143130
  if (typeof r === "string") return textLen7(r) > 0;
142869
- const rec = asRecord38(r);
143131
+ const rec = asRecord39(r);
142870
143132
  return textLen7(rec?.content) > 0 || textLen7(rec?.text) > 0;
142871
143133
  });
142872
143134
  if (recommendations.length < 3) {
@@ -142888,8 +143150,8 @@ function validateYandexPeriodReportContent(data) {
142888
143150
  "\u81F3\u5C11\u4E00\u9879\u6709\u6548\u6570\u5B57\uFF08\u8BF7\u4F20 --snapshot-dir \u7531 CLI \u4ECE overview \u5408\u5E76\uFF09"
142889
143151
  );
142890
143152
  }
142891
- const tables = asRecord38(data.tables) ?? {};
142892
- const weeklyRows = asArray8(tables.weekly).map((r) => asRecord38(r)).filter((r) => r != null);
143153
+ const tables = asRecord39(data.tables) ?? {};
143154
+ const weeklyRows = asArray8(tables.weekly).map((r) => asRecord39(r)).filter((r) => r != null);
142893
143155
  for (const [i, row] of weeklyRows.entries()) {
142894
143156
  const label2 = `${row.weekLabel ?? ""} ${row.startDate ?? ""} ${row.endDate ?? ""}`;
142895
143157
  if (/nan/i.test(label2) || !normalizeYandexDailyDate(row.startDate) || !normalizeYandexDailyDate(row.endDate)) {
@@ -142902,7 +143164,7 @@ function validateYandexPeriodReportContent(data) {
142902
143164
  );
142903
143165
  }
142904
143166
  }
142905
- const rowInsights = asRecord38(narrative.rowInsights) ?? {};
143167
+ const rowInsights = asRecord39(narrative.rowInsights) ?? {};
142906
143168
  const rowInsightCoverage = {};
142907
143169
  for (const spec of ROW_INSIGHT_SPECS) {
142908
143170
  const shown = topRowsBySpend(asArray8(tables[spec.tableKey]), spec.limit).filter(
@@ -142982,7 +143244,7 @@ async function readJsonFile8(filePath) {
142982
143244
  throw new Error(`\u65E0\u6CD5\u89E3\u6790 JSON\uFF1A${filePath}`);
142983
143245
  }
142984
143246
  }
142985
- function asRecord39(value) {
143247
+ function asRecord40(value) {
142986
143248
  if (value && typeof value === "object" && !Array.isArray(value)) {
142987
143249
  return value;
142988
143250
  }
@@ -143010,7 +143272,7 @@ function injectReportData7(html, payload) {
143010
143272
  }
143011
143273
  async function runYandexAnalysisRender(opts) {
143012
143274
  const dataPath = path53.resolve(opts.dataFile);
143013
- let data = asRecord39(await readJsonFile8(dataPath));
143275
+ let data = asRecord40(await readJsonFile8(dataPath));
143014
143276
  if (opts.snapshotDir?.trim()) {
143015
143277
  data = await mergeYandexAnalysisSnapshotIntoReport(data, path53.resolve(opts.snapshotDir));
143016
143278
  }
@@ -143206,7 +143468,7 @@ function unwrapYandexItems(raw) {
143206
143468
  }
143207
143469
  return [];
143208
143470
  }
143209
- function asRecord40(raw) {
143471
+ function asRecord41(raw) {
143210
143472
  return raw && typeof raw === "object" && !Array.isArray(raw) ? raw : {};
143211
143473
  }
143212
143474
 
@@ -143285,7 +143547,7 @@ async function runYandexAccountStatus(opts) {
143285
143547
  } catch (err) {
143286
143548
  fail3("\u67E5\u8BE2 Yandex \u8D26\u6237\u603B\u89C8\u5931\u8D25", err);
143287
143549
  }
143288
- const row = asRecord40(result.raw);
143550
+ const row = asRecord41(result.raw);
143289
143551
  await emitOrTable(opts, {
143290
143552
  section: "yandex-account-status",
143291
143553
  commandLabel: "yandex-analysis account-status",