siluzan-tso-cli 1.1.55 → 1.1.56-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -1
- package/dist/index.js +458 -253
- package/dist/skill/_meta.json +2 -2
- package/dist/skill/references/accounts/accounts-list.md +1 -1
- package/dist/skill/references/accounts/accounts-permissions.md +2 -2
- package/dist/skill/references/accounts/finance.md +1 -1
- package/dist/skill/references/accounts/open-account-by-media.md +1 -1
- package/dist/skill/references/accounts/open-account-google-ui.md +1 -1
- package/dist/skill/references/analytics/rag.md +1 -1
- package/dist/skill/references/analytics/reporting.md +5 -5
- package/dist/skill/references/core/setup.md +5 -5
- package/dist/skill/references/misc/tso-home.md +2 -2
- package/dist/skill/references/report-templates/google-ads-diagnosis.md +14 -9
- package/dist/skill/report-templates/GoogleAdsDiagnosisReport.html +6 -2
- package/dist/skill/report-templates/google-ads-diagnosis.md +14 -9
- package/dist/skill/scripts/install.ps1 +3 -3
- package/dist/skill/scripts/install.sh +3 -3
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -4049,7 +4049,7 @@ var DEFAULT_API_BASE;
|
|
|
4049
4049
|
var init_defaults = __esm({
|
|
4050
4050
|
"src/config/defaults.ts"() {
|
|
4051
4051
|
"use strict";
|
|
4052
|
-
DEFAULT_API_BASE = "https://tso-api.siluzan.com";
|
|
4052
|
+
DEFAULT_API_BASE = "https://tso-api-ci.siluzan.com";
|
|
4053
4053
|
}
|
|
4054
4054
|
});
|
|
4055
4055
|
|
|
@@ -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
|
|
103129
|
+
function asRecord18(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 =
|
|
103137
|
+
const narrative = asRecord18(data.narrative) ?? {};
|
|
103138
103138
|
const parts = [];
|
|
103139
|
-
const sheetAnalysis =
|
|
103139
|
+
const sheetAnalysis = asRecord18(narrative.sheetAnalysis) ?? {};
|
|
103140
103140
|
for (const block of Object.values(sheetAnalysis)) {
|
|
103141
|
-
const rec =
|
|
103141
|
+
const rec = asRecord18(block);
|
|
103142
103142
|
parts.push(...stringList(rec?.summary), ...stringList(rec?.suggestions));
|
|
103143
103143
|
}
|
|
103144
|
-
const review =
|
|
103144
|
+
const review = asRecord18(narrative.review) ?? {};
|
|
103145
103145
|
for (const block of Object.values(review)) {
|
|
103146
|
-
const rec =
|
|
103146
|
+
const rec = asRecord18(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
|
|
103163
|
+
function asRecord19(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) =>
|
|
103259
|
+
return envelope.items.filter((row) => asRecord19(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
|
|
103306
|
+
const metaAccountId = typeof asRecord19(payload.meta)?.accountId === "string" ? asRecord19(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(
|
|
103313
|
+
const preferredDateTag = dateTagFromRange(dateRange) ?? dateTagFromMeta(asRecord19(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 =
|
|
103322
|
-
const previousPeriod =
|
|
103321
|
+
const currentPeriod = asRecord19(overviewRecord?.currentPeriod);
|
|
103322
|
+
const previousPeriod = asRecord19(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
|
|
103468
|
+
function asRecord20(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 =
|
|
103482
|
+
const meta = asRecord20(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 =
|
|
103492
|
+
const snapshotSections = asRecord20(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 =
|
|
103508
|
-
const sheetAnalysis =
|
|
103509
|
-
const review =
|
|
103507
|
+
const narrative = asRecord20(data.narrative) ?? {};
|
|
103508
|
+
const sheetAnalysis = asRecord20(narrative.sheetAnalysis) ?? {};
|
|
103509
|
+
const review = asRecord20(narrative.review) ?? {};
|
|
103510
103510
|
const sheetAnalysisCounts = {};
|
|
103511
103511
|
for (const key of OKKI_SHEET_KEYS) {
|
|
103512
|
-
const block =
|
|
103512
|
+
const block = asRecord20(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 =
|
|
103538
|
+
const block = asRecord20(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) =>
|
|
103573
|
+
const deviceSpend = summarizeOkkiDeviceSpend(deviceRows2.filter((row) => asRecord20(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) =>
|
|
103662
|
+
return envelope.items.filter((row) => asRecord20(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
|
|
103706
|
+
const metaAccountId = typeof asRecord20(payload.meta)?.accountId === "string" ? asRecord20(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(
|
|
103713
|
+
const preferredDateTag = dateTagFromRange2(dateRange) ?? dateTagFromMeta2(asRecord20(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 =
|
|
103739
|
+
const currentPeriod = asRecord20(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 =
|
|
103883
|
-
const rec =
|
|
103882
|
+
const block = asRecord20(asRecord20(data.narrative)?.sheetAnalysis)?.[key];
|
|
103883
|
+
const rec = asRecord20(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 =
|
|
103890
|
+
const review = asRecord20(asRecord20(data.narrative)?.review) ?? {};
|
|
103891
103891
|
const lines = [];
|
|
103892
103892
|
OKKI_REVIEW_KEYS.forEach((key, idx) => {
|
|
103893
|
-
const block =
|
|
103893
|
+
const block = asRecord20(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 =
|
|
104025
|
+
const meta = asRecord20(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
|
|
104286
|
+
function asRecord21(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 =
|
|
104298
|
-
const narrative =
|
|
104299
|
-
const sections =
|
|
104297
|
+
const meta = asRecord21(data.meta) ?? {};
|
|
104298
|
+
const narrative = asRecord21(data.narrative) ?? {};
|
|
104299
|
+
const sections = asRecord21(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 =
|
|
104319
|
+
const kpis = asRecord21(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 =
|
|
104335
|
+
const section = asRecord21(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 =
|
|
104361
|
+
const rec = asRecord21(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
|
|
104450
|
+
function asRecord22(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 =
|
|
104465
|
+
let data = asRecord22(await readJsonFile3(dataPath));
|
|
104466
104466
|
if (opts.snapshotDir?.trim()) {
|
|
104467
104467
|
data = await mergeGoogleAnalysisSnapshotIntoReport(data, path35.resolve(opts.snapshotDir));
|
|
104468
104468
|
}
|
|
@@ -133404,7 +133404,8 @@ function resolveSkillTemplatePath2(basename13) {
|
|
|
133404
133404
|
const candidates = [
|
|
133405
133405
|
path22.join(dir, "skill", "report-templates", basename13),
|
|
133406
133406
|
path22.join(dir, "skill", "siluzan-ads", "report-templates", basename13),
|
|
133407
|
-
path22.join(dir, "..", "..", "assets", "siluzan-ads", "report-templates", basename13)
|
|
133407
|
+
path22.join(dir, "..", "..", "assets", "siluzan-ads", "report-templates", basename13),
|
|
133408
|
+
path22.join(dir, "..", "..", "..", "assets", "siluzan-ads", "report-templates", basename13)
|
|
133408
133409
|
];
|
|
133409
133410
|
for (const p of candidates) {
|
|
133410
133411
|
if (fs17.existsSync(p)) return p;
|
|
@@ -133427,7 +133428,7 @@ function buildGoogleAdsDiagnosisCollectAgentHint(snapDir) {
|
|
|
133427
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",
|
|
133428
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`,
|
|
133429
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`,
|
|
133430
|
-
"3) Read references/report-templates/google-ads-diagnosis.md\uFF0C\u64B0\u5199\u5168\u90E8 analysis/suggestions\u3001diagnosisOverview\u3001summary\u3001accountInfo.businessModel/industry\u3001budgetCompetitiveness[].strategy\u3001newFeatures[].optimizerRecommendation\u3001adCreativeOptimization.items[].suggestion \u7B49\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
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`,
|
|
133432
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`,
|
|
133433
133434
|
"\u7981\u6B62\u5199\u5165/\u8986\u76D6 campaigns/geographic/keywords \u7684 items \u884C\uFF0C\u53EA\u586B analysis/suggestions\u3002",
|
|
@@ -133494,12 +133495,148 @@ function buildGoogleAdsDiagnosisAgentBrief(input) {
|
|
|
133494
133495
|
},
|
|
133495
133496
|
agentDeliverable: {
|
|
133496
133497
|
outputFile: "google-ads-diagnosis.json",
|
|
133497
|
-
structure: "\u4E0E reportData \u540C\u7ED3\u6784\u7684 sectionData\uFF1B\u987B\u586B diagnosisOverview\u3001metrics.analysis/suggestions\u3001\u5404\u6A21\u5757 analysis/suggestions\u3001summary.keyIssues\u3001summary.optimizationRoadmap[]\uFF08priority/focusArea/actionItems/expectedOutcome\uFF09\u3001budgetCompetitiveness[].strategy\u3001newFeatures[].optimizerRecommendation \u7B49",
|
|
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",
|
|
133498
133499
|
thenRun: "siluzan-tso google-ads-diagnosis render --data ./<snap>/google-ads-diagnosis.json --out ./<snap>/google-ads-diagnosis-report.html"
|
|
133499
133500
|
}
|
|
133500
133501
|
};
|
|
133501
133502
|
}
|
|
133502
133503
|
|
|
133504
|
+
// src/commands/google-ads-diagnosis/snapshot-io.ts
|
|
133505
|
+
init_google_analysis_snapshot();
|
|
133506
|
+
import fs18 from "fs";
|
|
133507
|
+
import path23 from "path";
|
|
133508
|
+
function dateRangeManifest(start, end) {
|
|
133509
|
+
if (start && end) return { mode: "range", start, end };
|
|
133510
|
+
return null;
|
|
133511
|
+
}
|
|
133512
|
+
function findSectionSnapshotFile(snapDir, section, accountId, start, end) {
|
|
133513
|
+
const exactName = googleAnalysisSectionFilename(
|
|
133514
|
+
section,
|
|
133515
|
+
accountId,
|
|
133516
|
+
dateRangeManifest(start, end)
|
|
133517
|
+
);
|
|
133518
|
+
const exactPath = path23.join(snapDir, exactName);
|
|
133519
|
+
if (fs18.existsSync(exactPath)) return exactPath;
|
|
133520
|
+
if (start && end) return void 0;
|
|
133521
|
+
const files = fs18.readdirSync(snapDir);
|
|
133522
|
+
return files.filter((f) => f.startsWith(`${section}-${accountId}`) && f.endsWith(".json")).sort().map((f) => path23.join(snapDir, f)).at(-1);
|
|
133523
|
+
}
|
|
133524
|
+
function loadSectionSnapshot(snapDir, section, accountId, start, end) {
|
|
133525
|
+
const file = findSectionSnapshotFile(snapDir, section, accountId, start, end);
|
|
133526
|
+
if (!file || !fs18.existsSync(file)) return null;
|
|
133527
|
+
try {
|
|
133528
|
+
return JSON.parse(fs18.readFileSync(file, "utf8"));
|
|
133529
|
+
} catch {
|
|
133530
|
+
return null;
|
|
133531
|
+
}
|
|
133532
|
+
}
|
|
133533
|
+
function sectionItems(env) {
|
|
133534
|
+
if (!env?.items || !Array.isArray(env.items)) return [];
|
|
133535
|
+
return env.items.filter(
|
|
133536
|
+
(x) => !!x && typeof x === "object" && !Array.isArray(x)
|
|
133537
|
+
);
|
|
133538
|
+
}
|
|
133539
|
+
function sectionRecord(env) {
|
|
133540
|
+
const r = env?.record;
|
|
133541
|
+
if (r && typeof r === "object" && !Array.isArray(r)) return r;
|
|
133542
|
+
return null;
|
|
133543
|
+
}
|
|
133544
|
+
|
|
133545
|
+
// src/commands/google-ads-diagnosis/gold-text-ads.ts
|
|
133546
|
+
function asRecord7(value) {
|
|
133547
|
+
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
133548
|
+
return value;
|
|
133549
|
+
}
|
|
133550
|
+
return null;
|
|
133551
|
+
}
|
|
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";
|
|
133560
|
+
function statusOf(row) {
|
|
133561
|
+
return String(row.statusV2 ?? row.status ?? "").toUpperCase();
|
|
133562
|
+
}
|
|
133563
|
+
function isEnabled(row) {
|
|
133564
|
+
return statusOf(row) === "ENABLED";
|
|
133565
|
+
}
|
|
133566
|
+
function hasEnabledExtension(rows, type) {
|
|
133567
|
+
return rows.some((row) => String(row.typeV2 ?? "") === type && isEnabled(row));
|
|
133568
|
+
}
|
|
133569
|
+
function hasGa4Link(rows) {
|
|
133570
|
+
return rows.some((row) => {
|
|
133571
|
+
if (!isEnabled(row)) return false;
|
|
133572
|
+
const type = String(row.type ?? "");
|
|
133573
|
+
const name2 = String(row.name ?? "");
|
|
133574
|
+
return type.startsWith("GoogleAnalytics") || /GA4/i.test(name2);
|
|
133575
|
+
});
|
|
133576
|
+
}
|
|
133577
|
+
function hasAudienceSetup(rows) {
|
|
133578
|
+
return rows.some((row) => {
|
|
133579
|
+
const type = String(row.criterionType ?? "");
|
|
133580
|
+
return type === "UserList" || type === "CustomAudience" || type === "UserInterest" || type === "CustomIntent" || type === "CombinedAudience" || type === "CustomAffinity" || Number(row.impressions) > 0;
|
|
133581
|
+
});
|
|
133582
|
+
}
|
|
133583
|
+
function displayAds(rows) {
|
|
133584
|
+
return rows.filter((row) => String(row.typeV2 ?? "") === "RESPONSIVE_DISPLAY_AD");
|
|
133585
|
+
}
|
|
133586
|
+
function displayIsServing(rows) {
|
|
133587
|
+
return rows.some(
|
|
133588
|
+
(row) => isEnabled(row) && statusOf({ statusV2: row.parentCampaignStatusV2 }) === "ENABLED"
|
|
133589
|
+
);
|
|
133590
|
+
}
|
|
133591
|
+
function reconcileGoldAccount(gold, evidence = {}) {
|
|
133592
|
+
const next = { ...applyTextAdQualification(gold) };
|
|
133593
|
+
const conversions = evidence.conversionActions ?? [];
|
|
133594
|
+
const extensions = evidence.extensions ?? [];
|
|
133595
|
+
const audiences = evidence.audiences ?? [];
|
|
133596
|
+
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;
|
|
133603
|
+
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
|
+
}
|
|
133613
|
+
return next;
|
|
133614
|
+
}
|
|
133615
|
+
function parsePeriod(period) {
|
|
133616
|
+
const matched = period.match(/(\d{4}-\d{2}-\d{2})\D+(\d{4}-\d{2}-\d{2})/);
|
|
133617
|
+
if (!matched) return {};
|
|
133618
|
+
return { start: matched[1], end: matched[2] };
|
|
133619
|
+
}
|
|
133620
|
+
function reconcileGoldAccountInReport(data, snapDir) {
|
|
133621
|
+
const gold = asRecord7(data.goldAccount) ?? {};
|
|
133622
|
+
const account = asRecord7(data.accountInfo);
|
|
133623
|
+
const accountId = String(account?.companyId ?? "").replace(/\D/g, "");
|
|
133624
|
+
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 };
|
|
133638
|
+
}
|
|
133639
|
+
|
|
133503
133640
|
// src/commands/google-ads-diagnosis/default-section-data.ts
|
|
133504
133641
|
function createDefaultDiagnosisReportData() {
|
|
133505
133642
|
return {
|
|
@@ -133649,47 +133786,6 @@ function emptySummary() {
|
|
|
133649
133786
|
return { keyIssues: [], optimizationRoadmap: [] };
|
|
133650
133787
|
}
|
|
133651
133788
|
|
|
133652
|
-
// src/commands/google-ads-diagnosis/snapshot-io.ts
|
|
133653
|
-
init_google_analysis_snapshot();
|
|
133654
|
-
import fs18 from "fs";
|
|
133655
|
-
import path23 from "path";
|
|
133656
|
-
function dateRangeManifest(start, end) {
|
|
133657
|
-
if (start && end) return { mode: "range", start, end };
|
|
133658
|
-
return null;
|
|
133659
|
-
}
|
|
133660
|
-
function findSectionSnapshotFile(snapDir, section, accountId, start, end) {
|
|
133661
|
-
const exactName = googleAnalysisSectionFilename(
|
|
133662
|
-
section,
|
|
133663
|
-
accountId,
|
|
133664
|
-
dateRangeManifest(start, end)
|
|
133665
|
-
);
|
|
133666
|
-
const exactPath = path23.join(snapDir, exactName);
|
|
133667
|
-
if (fs18.existsSync(exactPath)) return exactPath;
|
|
133668
|
-
if (start && end) return void 0;
|
|
133669
|
-
const files = fs18.readdirSync(snapDir);
|
|
133670
|
-
return files.filter((f) => f.startsWith(`${section}-${accountId}`) && f.endsWith(".json")).sort().map((f) => path23.join(snapDir, f)).at(-1);
|
|
133671
|
-
}
|
|
133672
|
-
function loadSectionSnapshot(snapDir, section, accountId, start, end) {
|
|
133673
|
-
const file = findSectionSnapshotFile(snapDir, section, accountId, start, end);
|
|
133674
|
-
if (!file || !fs18.existsSync(file)) return null;
|
|
133675
|
-
try {
|
|
133676
|
-
return JSON.parse(fs18.readFileSync(file, "utf8"));
|
|
133677
|
-
} catch {
|
|
133678
|
-
return null;
|
|
133679
|
-
}
|
|
133680
|
-
}
|
|
133681
|
-
function sectionItems(env) {
|
|
133682
|
-
if (!env?.items || !Array.isArray(env.items)) return [];
|
|
133683
|
-
return env.items.filter(
|
|
133684
|
-
(x) => !!x && typeof x === "object" && !Array.isArray(x)
|
|
133685
|
-
);
|
|
133686
|
-
}
|
|
133687
|
-
function sectionRecord(env) {
|
|
133688
|
-
const r = env?.record;
|
|
133689
|
-
if (r && typeof r === "object" && !Array.isArray(r)) return r;
|
|
133690
|
-
return null;
|
|
133691
|
-
}
|
|
133692
|
-
|
|
133693
133789
|
// src/commands/google-ads-diagnosis/build-report-data.ts
|
|
133694
133790
|
function num(value) {
|
|
133695
133791
|
const n = Number(value);
|
|
@@ -134033,8 +134129,9 @@ function buildGoogleAdsDiagnosisCollectPayload(opts) {
|
|
|
134033
134129
|
const resourceRecord = sectionRecord(
|
|
134034
134130
|
loadSectionSnapshot(snapDir, "resource-counts", accountId, start, end)
|
|
134035
134131
|
);
|
|
134036
|
-
const
|
|
134037
|
-
delete
|
|
134132
|
+
const goldRaw = sectionRecord(loadSectionSnapshot(snapDir, "gold-account", accountId, start, end)) ?? {};
|
|
134133
|
+
delete goldRaw.adsNegativeQualified;
|
|
134134
|
+
let goldRecord = goldRaw;
|
|
134038
134135
|
const campaignCurrent = sectionItems(
|
|
134039
134136
|
loadSectionSnapshot(snapDir, "campaigns", accountId, start, end)
|
|
134040
134137
|
);
|
|
@@ -134153,7 +134250,6 @@ function buildGoogleAdsDiagnosisCollectPayload(opts) {
|
|
|
134153
134250
|
items: conversionCostItems,
|
|
134154
134251
|
...emptyModuleNarrative()
|
|
134155
134252
|
};
|
|
134156
|
-
data.goldAccount = goldRecord;
|
|
134157
134253
|
data.budgetCompetitiveness = buildBudgetCompetitiveness(overviewRecord, dimensionRecord);
|
|
134158
134254
|
data.diagnosisOverview = emptyDiagnosisOverview();
|
|
134159
134255
|
const geoTargeting = geoCurrent.map(
|
|
@@ -134178,6 +134274,16 @@ function buildGoogleAdsDiagnosisCollectPayload(opts) {
|
|
|
134178
134274
|
const audienceRows = sectionItems(
|
|
134179
134275
|
loadSectionSnapshot(snapDir, "audience", accountId, start, end)
|
|
134180
134276
|
);
|
|
134277
|
+
const extensionRows = sectionItems(
|
|
134278
|
+
loadSectionSnapshot(snapDir, "extensions", accountId, start, end)
|
|
134279
|
+
);
|
|
134280
|
+
goldRecord = reconcileGoldAccount(goldRaw, {
|
|
134281
|
+
conversionActions: conversionRows,
|
|
134282
|
+
extensions: extensionRows,
|
|
134283
|
+
audiences: audienceRows,
|
|
134284
|
+
ads: adRows
|
|
134285
|
+
});
|
|
134286
|
+
data.goldAccount = goldRecord;
|
|
134181
134287
|
data.fullAudience = {
|
|
134182
134288
|
items: audienceRows.map(
|
|
134183
134289
|
(row) => toTargetingItem(row, "audience", String(row.audienceName ?? row.audience ?? "\u53D7\u4F17"))
|
|
@@ -134284,14 +134390,14 @@ function buildGoogleAdsDiagnosisCollectPayload(opts) {
|
|
|
134284
134390
|
}
|
|
134285
134391
|
|
|
134286
134392
|
// src/commands/google-ads-diagnosis/collect-digest.ts
|
|
134287
|
-
function
|
|
134393
|
+
function asRecord8(value) {
|
|
134288
134394
|
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
134289
134395
|
return value;
|
|
134290
134396
|
}
|
|
134291
134397
|
return null;
|
|
134292
134398
|
}
|
|
134293
134399
|
function fieldsOfFirstRow(rows) {
|
|
134294
|
-
const first =
|
|
134400
|
+
const first = asRecord8(rows[0]);
|
|
134295
134401
|
return first ? Object.keys(first) : [];
|
|
134296
134402
|
}
|
|
134297
134403
|
function describeSectionShapes(reportData) {
|
|
@@ -134301,7 +134407,7 @@ function describeSectionShapes(reportData) {
|
|
|
134301
134407
|
if (Array.isArray(value)) {
|
|
134302
134408
|
return { section, itemCount: value.length, fields: fieldsOfFirstRow(value), needsNarrative };
|
|
134303
134409
|
}
|
|
134304
|
-
const record2 =
|
|
134410
|
+
const record2 = asRecord8(value);
|
|
134305
134411
|
if (!record2) {
|
|
134306
134412
|
return { section, itemCount: null, fields: [], needsNarrative };
|
|
134307
134413
|
}
|
|
@@ -134319,7 +134425,7 @@ function describeSectionShapes(reportData) {
|
|
|
134319
134425
|
function narrativeChecklist(shapes) {
|
|
134320
134426
|
const lines = [
|
|
134321
134427
|
"- `accountInfo.businessModel`\u3001`accountInfo.industry`",
|
|
134322
|
-
|
|
134428
|
+
'- `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',
|
|
134323
134429
|
"- `summary.keyIssues`\u3001`summary.optimizationRoadmap`"
|
|
134324
134430
|
];
|
|
134325
134431
|
for (const shape of shapes) {
|
|
@@ -134370,21 +134476,21 @@ function buildCollectDigest(reportData, meta) {
|
|
|
134370
134476
|
}
|
|
134371
134477
|
|
|
134372
134478
|
// src/commands/google-ads-diagnosis/collect-integrity.ts
|
|
134373
|
-
function
|
|
134479
|
+
function asRecord9(value) {
|
|
134374
134480
|
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
134375
134481
|
return value;
|
|
134376
134482
|
}
|
|
134377
134483
|
return null;
|
|
134378
134484
|
}
|
|
134379
134485
|
function itemCount(block) {
|
|
134380
|
-
const items3 =
|
|
134486
|
+
const items3 = asRecord9(block)?.items;
|
|
134381
134487
|
return Array.isArray(items3) ? items3.length : 0;
|
|
134382
134488
|
}
|
|
134383
134489
|
function getCollectIntegrityWarnings(reportData) {
|
|
134384
134490
|
const warnings = [];
|
|
134385
134491
|
const kwItems = itemCount(reportData.keywords);
|
|
134386
134492
|
const fullKwItems = itemCount(reportData.fullKeywords);
|
|
134387
|
-
const keywordCount = Number(
|
|
134493
|
+
const keywordCount = Number(asRecord9(reportData.structure)?.keywordCount ?? 0);
|
|
134388
134494
|
if (kwItems === 0 && fullKwItems > 0) {
|
|
134389
134495
|
warnings.push(
|
|
134390
134496
|
`keywords.items \u5BF9\u6BD4\u8868\u4E3A\u7A7A\uFF0C\u4F46 fullKeywords.items \u6709 ${fullKwItems} \u884C\uFF1B\u8BF7\u786E\u8BA4 collect \u662F\u5426\u6210\u529F\u62C9\u53D6\u4E0A\u4E00\u5468\u671F keywords \u5FEB\u7167\uFF08\u73AF\u6BD4\u5217\u4F9D\u8D56 prev \u5468\u671F JSON\uFF09\u3002`
|
|
@@ -134395,7 +134501,7 @@ function getCollectIntegrityWarnings(reportData) {
|
|
|
134395
134501
|
);
|
|
134396
134502
|
}
|
|
134397
134503
|
const campaignItems = itemCount(reportData.campaigns);
|
|
134398
|
-
const campaignCount = Number(
|
|
134504
|
+
const campaignCount = Number(asRecord9(reportData.structure)?.campaignCount ?? 0);
|
|
134399
134505
|
if (campaignItems === 0 && campaignCount > 0) {
|
|
134400
134506
|
warnings.push(
|
|
134401
134507
|
`campaigns.items \u5BF9\u6BD4\u8868\u4E3A\u7A7A\uFF0C\u4F46 structure.campaignCount=${campaignCount}\uFF1B\u8BF7\u786E\u8BA4\u4E0A\u4E00\u5468\u671F campaigns \u5FEB\u7167\u662F\u5426\u62C9\u53D6\u6210\u529F\u3002`
|
|
@@ -134453,7 +134559,7 @@ function invokeGoogleAnalysisCli(opts) {
|
|
|
134453
134559
|
|
|
134454
134560
|
// src/commands/google-ads-diagnosis/write-draft.ts
|
|
134455
134561
|
import fs20 from "fs/promises";
|
|
134456
|
-
function
|
|
134562
|
+
function asRecord10(value) {
|
|
134457
134563
|
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
134458
134564
|
return value;
|
|
134459
134565
|
}
|
|
@@ -134462,7 +134568,7 @@ function asRecord9(value) {
|
|
|
134462
134568
|
function valueAtPath(data, keys) {
|
|
134463
134569
|
let cursor = data;
|
|
134464
134570
|
for (const key of keys) {
|
|
134465
|
-
const record2 =
|
|
134571
|
+
const record2 = asRecord10(cursor);
|
|
134466
134572
|
if (!record2) return void 0;
|
|
134467
134573
|
cursor = record2[key];
|
|
134468
134574
|
}
|
|
@@ -134481,7 +134587,7 @@ function hasAgentNarrative(data) {
|
|
|
134481
134587
|
async function writeDiagnosisDraft(opts) {
|
|
134482
134588
|
try {
|
|
134483
134589
|
const existing = JSON.parse(await fs20.readFile(opts.draftPath, "utf8"));
|
|
134484
|
-
const record2 =
|
|
134590
|
+
const record2 = asRecord10(existing);
|
|
134485
134591
|
if (record2 && hasAgentNarrative(record2)) return "skipped-existing-narrative";
|
|
134486
134592
|
} catch {
|
|
134487
134593
|
}
|
|
@@ -134671,7 +134777,7 @@ import fs22 from "fs/promises";
|
|
|
134671
134777
|
import path26 from "path";
|
|
134672
134778
|
|
|
134673
134779
|
// src/commands/google-ads-diagnosis/report-content.ts
|
|
134674
|
-
function
|
|
134780
|
+
function asRecord11(value) {
|
|
134675
134781
|
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
134676
134782
|
return value;
|
|
134677
134783
|
}
|
|
@@ -134680,7 +134786,7 @@ function asRecord10(value) {
|
|
|
134680
134786
|
function getNestedValue(root, path55) {
|
|
134681
134787
|
let cur = root;
|
|
134682
134788
|
for (const key of path55) {
|
|
134683
|
-
const rec =
|
|
134789
|
+
const rec = asRecord11(cur);
|
|
134684
134790
|
if (!rec) return void 0;
|
|
134685
134791
|
cur = rec[key];
|
|
134686
134792
|
}
|
|
@@ -134697,7 +134803,7 @@ function validateBudgetCompetitivenessStrategies(data) {
|
|
|
134697
134803
|
}
|
|
134698
134804
|
const warnings = [];
|
|
134699
134805
|
for (let i = 0; i < items3.length; i++) {
|
|
134700
|
-
const row =
|
|
134806
|
+
const row = asRecord11(items3[i]);
|
|
134701
134807
|
const name2 = String(row?.name ?? row?.metric ?? i);
|
|
134702
134808
|
if (!hasNarrativeContent(row?.strategy)) {
|
|
134703
134809
|
warnings.push(`budgetCompetitiveness[${name2}].strategy \u4E3A\u7A7A\uFF08\u987B\u7531 Agent \u586B\u5199\uFF09`);
|
|
@@ -134706,13 +134812,13 @@ function validateBudgetCompetitivenessStrategies(data) {
|
|
|
134706
134812
|
return warnings;
|
|
134707
134813
|
}
|
|
134708
134814
|
function validateNewFeatureRecommendations(data) {
|
|
134709
|
-
const nf =
|
|
134815
|
+
const nf = asRecord11(data.newFeatures);
|
|
134710
134816
|
if (!nf) return [];
|
|
134711
134817
|
const items3 = nf.items;
|
|
134712
134818
|
if (!Array.isArray(items3)) return [];
|
|
134713
134819
|
const warnings = [];
|
|
134714
134820
|
for (let i = 0; i < items3.length; i++) {
|
|
134715
|
-
const row =
|
|
134821
|
+
const row = asRecord11(items3[i]);
|
|
134716
134822
|
const name2 = String(row?.strategyName ?? row?.feature ?? row?.strategy ?? i);
|
|
134717
134823
|
if (!hasNarrativeContent(row?.optimizerRecommendation)) {
|
|
134718
134824
|
warnings.push(`newFeatures.items[${name2}].optimizerRecommendation \u4E3A\u7A7A\uFF08\u987B\u7531 Agent \u586B\u5199\uFF09`);
|
|
@@ -134720,14 +134826,34 @@ function validateNewFeatureRecommendations(data) {
|
|
|
134720
134826
|
}
|
|
134721
134827
|
return warnings;
|
|
134722
134828
|
}
|
|
134829
|
+
function validateDiagnosisOverviewItems(data) {
|
|
134830
|
+
const overview = asRecord11(data.diagnosisOverview);
|
|
134831
|
+
if (!overview) return [];
|
|
134832
|
+
const warnings = [];
|
|
134833
|
+
for (const key of ["advantages", "disadvantages"]) {
|
|
134834
|
+
const items3 = overview[key];
|
|
134835
|
+
if (!Array.isArray(items3)) continue;
|
|
134836
|
+
items3.forEach((item, index) => {
|
|
134837
|
+
const row = asRecord11(item);
|
|
134838
|
+
const title = String(row?.title ?? "").trim();
|
|
134839
|
+
const description = String(row?.description ?? "").trim();
|
|
134840
|
+
if (!title || !description) {
|
|
134841
|
+
warnings.push(
|
|
134842
|
+
`diagnosisOverview.${key}[${index}] \u987B\u542B\u975E\u7A7A title \u4E0E description\uFF08\u6A21\u677F\u53EA\u8BFB\u8FD9\u4E24\u4E2A\u5B57\u6BB5\uFF0C\u7F3A\u4E86\u4F1A\u663E\u793A\u300C\u672A\u586B\u5199\u300D\uFF09\u3002\u5199\u6210 {"title":"\u77ED\u6807\u9898","description":"\u4F9D\u636E"}\uFF0C\u6216\u300C\u6807\u9898\uFF1A\u8BF4\u660E\u300D`
|
|
134843
|
+
);
|
|
134844
|
+
}
|
|
134845
|
+
});
|
|
134846
|
+
}
|
|
134847
|
+
return warnings;
|
|
134848
|
+
}
|
|
134723
134849
|
function validateAdCreativeItemSuggestions(data) {
|
|
134724
|
-
const creative =
|
|
134850
|
+
const creative = asRecord11(data.adCreativeOptimization);
|
|
134725
134851
|
if (!creative) return [];
|
|
134726
134852
|
const items3 = creative.items;
|
|
134727
134853
|
if (!Array.isArray(items3) || items3.length === 0) return [];
|
|
134728
134854
|
const warnings = [];
|
|
134729
134855
|
for (let i = 0; i < items3.length; i++) {
|
|
134730
|
-
const row =
|
|
134856
|
+
const row = asRecord11(items3[i]);
|
|
134731
134857
|
const title = String(row?.adTitle ?? i);
|
|
134732
134858
|
if (!hasNarrativeContent(row?.suggestion ?? row?.suggestions)) {
|
|
134733
134859
|
warnings.push(`adCreativeOptimization.items[${title}].suggestion \u4E3A\u7A7A\uFF08\u987B\u7531 Agent \u586B\u5199\uFF09`);
|
|
@@ -134738,7 +134864,7 @@ function validateAdCreativeItemSuggestions(data) {
|
|
|
134738
134864
|
function validateGoogleAdsDiagnosisContent(data) {
|
|
134739
134865
|
const missingKeys = GOOGLE_ADS_DIAGNOSIS_TOP_LEVEL_KEYS.filter((key) => data[key] === void 0);
|
|
134740
134866
|
const warnings = [];
|
|
134741
|
-
const accountInfo =
|
|
134867
|
+
const accountInfo = asRecord11(data.accountInfo);
|
|
134742
134868
|
if (accountInfo && !accountInfo.companyName && !accountInfo.period) {
|
|
134743
134869
|
warnings.push("accountInfo \u7F3A\u5C11 companyName \u4E0E period\uFF0C\u9875\u7709\u53EF\u80FD\u4E3A\u7A7A");
|
|
134744
134870
|
}
|
|
@@ -134752,6 +134878,7 @@ function validateGoogleAdsDiagnosisContent(data) {
|
|
|
134752
134878
|
}
|
|
134753
134879
|
}
|
|
134754
134880
|
warnings.push(
|
|
134881
|
+
...validateDiagnosisOverviewItems(data),
|
|
134755
134882
|
...validateBudgetCompetitivenessStrategies(data),
|
|
134756
134883
|
...validateNewFeatureRecommendations(data),
|
|
134757
134884
|
...validateAdCreativeItemSuggestions(data)
|
|
@@ -134786,7 +134913,7 @@ var GOOGLE_ADS_DIAGNOSIS_RESTORE_ITEM_MODULES = [
|
|
|
134786
134913
|
GOOGLE_ADS_DIAGNOSIS_BIDDING_STRATEGY_MODULE,
|
|
134787
134914
|
GOOGLE_ADS_DIAGNOSIS_NEW_FEATURES_MODULE
|
|
134788
134915
|
];
|
|
134789
|
-
function
|
|
134916
|
+
function asRecord12(value) {
|
|
134790
134917
|
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
134791
134918
|
return value;
|
|
134792
134919
|
}
|
|
@@ -134797,7 +134924,7 @@ function isValidRateChange(value) {
|
|
|
134797
134924
|
return typeof value === "number" && Number.isFinite(value);
|
|
134798
134925
|
}
|
|
134799
134926
|
function isComparisonTableRowValid(row) {
|
|
134800
|
-
const rec =
|
|
134927
|
+
const rec = asRecord12(row);
|
|
134801
134928
|
if (!rec) return false;
|
|
134802
134929
|
if (!String(rec.title ?? "").trim()) return false;
|
|
134803
134930
|
if (!Number.isFinite(Number(rec.currentCost))) return false;
|
|
@@ -134809,12 +134936,12 @@ function isComparisonTableRowValid(row) {
|
|
|
134809
134936
|
return true;
|
|
134810
134937
|
}
|
|
134811
134938
|
function isNewFeatureRowValid(row) {
|
|
134812
|
-
const rec =
|
|
134939
|
+
const rec = asRecord12(row);
|
|
134813
134940
|
if (!rec) return false;
|
|
134814
134941
|
return Boolean(String(rec.strategyName ?? rec.feature ?? rec.strategy ?? "").trim());
|
|
134815
134942
|
}
|
|
134816
134943
|
function isBiddingStrategyRowValid(row) {
|
|
134817
|
-
const rec =
|
|
134944
|
+
const rec = asRecord12(row);
|
|
134818
134945
|
if (!rec) return false;
|
|
134819
134946
|
if (!String(rec.campaignName ?? "").trim()) return false;
|
|
134820
134947
|
if (!Number.isFinite(Number(rec.duration)) || Number(rec.duration) <= 0) return false;
|
|
@@ -134852,11 +134979,11 @@ function getFactItemsErrors(data) {
|
|
|
134852
134979
|
for (const mod of GOOGLE_ADS_DIAGNOSIS_RESTORE_ITEM_MODULES) {
|
|
134853
134980
|
const validator = MODULE_ITEM_VALIDATORS[mod];
|
|
134854
134981
|
const hint = MODULE_ITEM_ERROR_HINT[mod];
|
|
134855
|
-
const block =
|
|
134982
|
+
const block = asRecord12(data[mod]);
|
|
134856
134983
|
const items3 = block?.items;
|
|
134857
134984
|
if (!Array.isArray(items3) || items3.length === 0) {
|
|
134858
134985
|
if (mod === "keywords") {
|
|
134859
|
-
const fullKw =
|
|
134986
|
+
const fullKw = asRecord12(data.fullKeywords)?.items;
|
|
134860
134987
|
if (Array.isArray(fullKw) && fullKw.length > 0) {
|
|
134861
134988
|
errors.push(
|
|
134862
134989
|
"keywords.items \u4E3A\u7A7A\u4F46 fullKeywords.items \u6709\u6570\u636E\uFF1B\u8BF7\u4FDD\u7559\u540C\u76EE\u5F55 google-ads-diagnosis-collect.json \u540E\u91CD\u65B0 render\uFF08\u4F1A\u81EA\u52A8\u6062\u590D\u5BF9\u6BD4\u8868\uFF09"
|
|
@@ -134881,15 +135008,15 @@ var LANDING_PAGE_SPEED_KEYS = [
|
|
|
134881
135008
|
"speedIndex"
|
|
134882
135009
|
];
|
|
134883
135010
|
function isLandingPageSpeedValid(value) {
|
|
134884
|
-
const rec =
|
|
135011
|
+
const rec = asRecord12(value);
|
|
134885
135012
|
if (!rec) return false;
|
|
134886
135013
|
return LANDING_PAGE_SPEED_KEYS.every(
|
|
134887
135014
|
(key) => typeof rec[key] === "number" && Number.isFinite(rec[key])
|
|
134888
135015
|
);
|
|
134889
135016
|
}
|
|
134890
135017
|
function restoreLandingPageSpeedFromCollect(finalData, collectReportData) {
|
|
134891
|
-
const finalBlock =
|
|
134892
|
-
const collectBlock =
|
|
135018
|
+
const finalBlock = asRecord12(finalData[GOOGLE_ADS_DIAGNOSIS_LANDING_PAGE_MODULE]);
|
|
135019
|
+
const collectBlock = asRecord12(collectReportData[GOOGLE_ADS_DIAGNOSIS_LANDING_PAGE_MODULE]);
|
|
134893
135020
|
if (!collectBlock) return { data: finalData, restored: false };
|
|
134894
135021
|
const needsDesktop = !isLandingPageSpeedValid(finalBlock?.desktop) && isLandingPageSpeedValid(collectBlock.desktop);
|
|
134895
135022
|
const needsMobile = !isLandingPageSpeedValid(finalBlock?.mobile) && isLandingPageSpeedValid(collectBlock.mobile);
|
|
@@ -134912,8 +135039,8 @@ function mergeComparisonItemsFromCollect(finalData, collectReportData) {
|
|
|
134912
135039
|
const restoredModules = [];
|
|
134913
135040
|
for (const mod of GOOGLE_ADS_DIAGNOSIS_RESTORE_ITEM_MODULES) {
|
|
134914
135041
|
const validator = MODULE_ITEM_VALIDATORS[mod];
|
|
134915
|
-
const finalBlock =
|
|
134916
|
-
const collectBlock =
|
|
135042
|
+
const finalBlock = asRecord12(data[mod]);
|
|
135043
|
+
const collectBlock = asRecord12(collectReportData[mod]);
|
|
134917
135044
|
const finalItems = finalBlock?.items;
|
|
134918
135045
|
const collectItems = collectBlock?.items;
|
|
134919
135046
|
if (!Array.isArray(collectItems) || collectItems.length === 0) continue;
|
|
@@ -134929,7 +135056,7 @@ function mergeComparisonItemsFromCollect(finalData, collectReportData) {
|
|
|
134929
135056
|
}
|
|
134930
135057
|
|
|
134931
135058
|
// src/commands/google-ads-diagnosis/narrative-consistency-check.ts
|
|
134932
|
-
function
|
|
135059
|
+
function asRecord13(value) {
|
|
134933
135060
|
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
134934
135061
|
return value;
|
|
134935
135062
|
}
|
|
@@ -134940,14 +135067,14 @@ function normalizeSuggestionText(value) {
|
|
|
134940
135067
|
}
|
|
134941
135068
|
function collectNarrativeTexts(data) {
|
|
134942
135069
|
const texts = [];
|
|
134943
|
-
const summary =
|
|
135070
|
+
const summary = asRecord13(data.summary);
|
|
134944
135071
|
if (Array.isArray(summary?.keyIssues)) {
|
|
134945
135072
|
for (const item of summary.keyIssues) texts.push(String(item ?? ""));
|
|
134946
135073
|
}
|
|
134947
|
-
const overview =
|
|
135074
|
+
const overview = asRecord13(data.diagnosisOverview);
|
|
134948
135075
|
if (Array.isArray(overview?.disadvantages)) {
|
|
134949
135076
|
for (const item of overview.disadvantages) {
|
|
134950
|
-
const rec =
|
|
135077
|
+
const rec = asRecord13(item);
|
|
134951
135078
|
if (rec) {
|
|
134952
135079
|
texts.push(String(rec.title ?? ""), String(rec.description ?? ""));
|
|
134953
135080
|
} else {
|
|
@@ -134958,16 +135085,16 @@ function collectNarrativeTexts(data) {
|
|
|
134958
135085
|
return texts.filter((t) => t.trim().length > 0);
|
|
134959
135086
|
}
|
|
134960
135087
|
function newFeatureAccountStatus(data, strategy) {
|
|
134961
|
-
const nf =
|
|
135088
|
+
const nf = asRecord13(data.newFeatures);
|
|
134962
135089
|
const items3 = nf?.items;
|
|
134963
135090
|
if (!Array.isArray(items3)) return null;
|
|
134964
|
-
const row = items3.find((it) =>
|
|
134965
|
-
const rec =
|
|
135091
|
+
const row = items3.find((it) => asRecord13(it)?.strategy === strategy);
|
|
135092
|
+
const rec = asRecord13(row);
|
|
134966
135093
|
if (!rec || typeof rec.accountStatus !== "boolean") return null;
|
|
134967
135094
|
return rec.accountStatus;
|
|
134968
135095
|
}
|
|
134969
135096
|
function goldAccountFlag(data, key) {
|
|
134970
|
-
const gold =
|
|
135097
|
+
const gold = asRecord13(data.goldAccount);
|
|
134971
135098
|
const value = gold?.[key];
|
|
134972
135099
|
return typeof value === "boolean" ? value : null;
|
|
134973
135100
|
}
|
|
@@ -135012,7 +135139,7 @@ function getRepetitiveNarrativeErrors(data) {
|
|
|
135012
135139
|
const errors = [];
|
|
135013
135140
|
const budget = data.budgetCompetitiveness;
|
|
135014
135141
|
if (Array.isArray(budget) && budget.length >= 3) {
|
|
135015
|
-
const strategies = budget.map((row) => normalizeSuggestionText(
|
|
135142
|
+
const strategies = budget.map((row) => normalizeSuggestionText(asRecord13(row)?.strategy)).filter((s) => s.length > 0);
|
|
135016
135143
|
const dup = findIdenticalTexts(strategies, 3);
|
|
135017
135144
|
if (dup) {
|
|
135018
135145
|
errors.push(
|
|
@@ -135020,10 +135147,10 @@ function getRepetitiveNarrativeErrors(data) {
|
|
|
135020
135147
|
);
|
|
135021
135148
|
}
|
|
135022
135149
|
}
|
|
135023
|
-
const creative =
|
|
135150
|
+
const creative = asRecord13(data.adCreativeOptimization);
|
|
135024
135151
|
const creativeItems = creative?.items;
|
|
135025
135152
|
if (Array.isArray(creativeItems) && creativeItems.length >= 3) {
|
|
135026
|
-
const suggestions = creativeItems.map((row) => normalizeSuggestionText(
|
|
135153
|
+
const suggestions = creativeItems.map((row) => normalizeSuggestionText(asRecord13(row)?.suggestion)).filter((s) => s.length > 0);
|
|
135027
135154
|
const dup = findIdenticalTexts(suggestions, 3);
|
|
135028
135155
|
if (dup) {
|
|
135029
135156
|
errors.push(
|
|
@@ -135034,7 +135161,7 @@ function getRepetitiveNarrativeErrors(data) {
|
|
|
135034
135161
|
return errors;
|
|
135035
135162
|
}
|
|
135036
135163
|
function getLandingPageTopicErrors(data) {
|
|
135037
|
-
const lp =
|
|
135164
|
+
const lp = asRecord13(data.landingPageAnalysis);
|
|
135038
135165
|
if (!lp) return [];
|
|
135039
135166
|
const analysis = String(lp.analysis ?? "");
|
|
135040
135167
|
if (!analysis.trim()) return [];
|
|
@@ -135063,8 +135190,75 @@ function getNarrativeConsistencyErrors(data) {
|
|
|
135063
135190
|
return errors;
|
|
135064
135191
|
}
|
|
135065
135192
|
|
|
135193
|
+
// src/commands/google-ads-diagnosis/overview-items.ts
|
|
135194
|
+
function asRecord14(value) {
|
|
135195
|
+
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
135196
|
+
return value;
|
|
135197
|
+
}
|
|
135198
|
+
return null;
|
|
135199
|
+
}
|
|
135200
|
+
function proseColonIndex(text2) {
|
|
135201
|
+
const cn = text2.indexOf("\uFF1A");
|
|
135202
|
+
if (cn > 0 && cn < text2.length - 1) return cn;
|
|
135203
|
+
for (let i = 1; i < text2.length - 1; i++) {
|
|
135204
|
+
if (text2[i] !== ":") continue;
|
|
135205
|
+
if (text2[i + 1] === "/") continue;
|
|
135206
|
+
const scheme = text2.slice(Math.max(0, i - 5), i);
|
|
135207
|
+
if (/https?$/i.test(scheme)) continue;
|
|
135208
|
+
return i;
|
|
135209
|
+
}
|
|
135210
|
+
return -1;
|
|
135211
|
+
}
|
|
135212
|
+
function splitOverviewProse(text2) {
|
|
135213
|
+
const trimmed = text2.trim();
|
|
135214
|
+
const cut = proseColonIndex(trimmed);
|
|
135215
|
+
if (cut > 0) {
|
|
135216
|
+
const title = trimmed.slice(0, cut).trim();
|
|
135217
|
+
const description = trimmed.slice(cut + 1).trim();
|
|
135218
|
+
if (title && description) return { title, description };
|
|
135219
|
+
}
|
|
135220
|
+
return { title: trimmed, description: trimmed };
|
|
135221
|
+
}
|
|
135222
|
+
function normalizeOverviewItem(item) {
|
|
135223
|
+
if (typeof item === "string" || typeof item === "number") {
|
|
135224
|
+
const text2 = String(item).trim();
|
|
135225
|
+
if (!text2) return null;
|
|
135226
|
+
return splitOverviewProse(text2);
|
|
135227
|
+
}
|
|
135228
|
+
const rec = asRecord14(item);
|
|
135229
|
+
if (!rec) return null;
|
|
135230
|
+
const title = String(rec.title ?? "").trim();
|
|
135231
|
+
const description = String(rec.description ?? "").trim();
|
|
135232
|
+
if (title && description) return { title, description };
|
|
135233
|
+
const prose = title || description;
|
|
135234
|
+
if (!prose) return null;
|
|
135235
|
+
const split = splitOverviewProse(prose);
|
|
135236
|
+
return {
|
|
135237
|
+
title: title || split.title,
|
|
135238
|
+
description: description || split.description
|
|
135239
|
+
};
|
|
135240
|
+
}
|
|
135241
|
+
function normalizeDiagnosisOverview(data) {
|
|
135242
|
+
const overview = asRecord14(data.diagnosisOverview);
|
|
135243
|
+
if (!overview) return { data, coerced: [] };
|
|
135244
|
+
const coerced = [];
|
|
135245
|
+
const nextOverview = { ...overview };
|
|
135246
|
+
for (const key of ["advantages", "disadvantages"]) {
|
|
135247
|
+
const items3 = overview[key];
|
|
135248
|
+
if (!Array.isArray(items3)) continue;
|
|
135249
|
+
nextOverview[key] = items3.map((item) => {
|
|
135250
|
+
if (typeof item === "string" || typeof item === "number") coerced.push(key);
|
|
135251
|
+
return normalizeOverviewItem(item) ?? { title: "", description: "" };
|
|
135252
|
+
});
|
|
135253
|
+
}
|
|
135254
|
+
return {
|
|
135255
|
+
data: { ...data, diagnosisOverview: nextOverview },
|
|
135256
|
+
coerced
|
|
135257
|
+
};
|
|
135258
|
+
}
|
|
135259
|
+
|
|
135066
135260
|
// src/commands/google-ads-diagnosis/render-report.ts
|
|
135067
|
-
function
|
|
135261
|
+
function asRecord15(value) {
|
|
135068
135262
|
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
135069
135263
|
return value;
|
|
135070
135264
|
}
|
|
@@ -135073,8 +135267,8 @@ function asRecord13(value) {
|
|
|
135073
135267
|
async function tryLoadCollectReportData(collectPath) {
|
|
135074
135268
|
try {
|
|
135075
135269
|
const raw = JSON.parse(await fs22.readFile(collectPath, "utf8"));
|
|
135076
|
-
const root =
|
|
135077
|
-
const reportData =
|
|
135270
|
+
const root = asRecord15(raw);
|
|
135271
|
+
const reportData = asRecord15(root?.reportData);
|
|
135078
135272
|
return reportData ?? root;
|
|
135079
135273
|
} catch {
|
|
135080
135274
|
return null;
|
|
@@ -135091,7 +135285,7 @@ async function runGoogleAdsDiagnosisRender(opts) {
|
|
|
135091
135285
|
`);
|
|
135092
135286
|
process.exit(1);
|
|
135093
135287
|
}
|
|
135094
|
-
let data =
|
|
135288
|
+
let data = asRecord15(dataRaw);
|
|
135095
135289
|
if (!opts.noMergeCollect) {
|
|
135096
135290
|
const collectPath = path26.resolve(
|
|
135097
135291
|
opts.collectFile ?? path26.join(path26.dirname(dataPath), GOOGLE_ADS_DIAGNOSIS_COLLECT_BASENAME)
|
|
@@ -135123,6 +135317,17 @@ async function runGoogleAdsDiagnosisRender(opts) {
|
|
|
135123
135317
|
}
|
|
135124
135318
|
}
|
|
135125
135319
|
}
|
|
135320
|
+
data = reconcileGoldAccountInReport(data, path26.dirname(dataPath));
|
|
135321
|
+
const overview = normalizeDiagnosisOverview(data);
|
|
135322
|
+
data = overview.data;
|
|
135323
|
+
if (overview.coerced.length > 0) {
|
|
135324
|
+
const fields = [...new Set(overview.coerced)].join("\u3001");
|
|
135325
|
+
console.log(
|
|
135326
|
+
`
|
|
135327
|
+
\u2139\uFE0F \u5DF2\u5C06 diagnosisOverview.${fields} \u7684\u7EAF\u5B57\u7B26\u4E32\u6309\u7B2C\u4E00\u4E2A\u5192\u53F7\u62C6\u6210 {title, description} \u518D\u6CE8\u5165 HTML\u3002
|
|
135328
|
+
`
|
|
135329
|
+
);
|
|
135330
|
+
}
|
|
135126
135331
|
const narrativeErrors = getNarrativeConsistencyErrors(data);
|
|
135127
135332
|
if (narrativeErrors.length > 0) {
|
|
135128
135333
|
console.error("\n\u274C \u53D9\u4E8B\u6587\u6848\u4E0E\u4E8B\u5B9E\u5B57\u6BB5\u77DB\u76FE\uFF0C\u7981\u6B62\u6E32\u67D3\uFF1A\n");
|
|
@@ -136421,26 +136626,26 @@ import fs26 from "fs/promises";
|
|
|
136421
136626
|
import path31 from "path";
|
|
136422
136627
|
|
|
136423
136628
|
// src/commands/google-quarterly/report-content.ts
|
|
136424
|
-
function
|
|
136629
|
+
function asRecord16(value) {
|
|
136425
136630
|
return value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
136426
136631
|
}
|
|
136427
136632
|
function asStringArray(value) {
|
|
136428
136633
|
return Array.isArray(value) ? value.map((x) => String(x).trim()).filter(Boolean) : [];
|
|
136429
136634
|
}
|
|
136430
136635
|
function validateQuarterlyNarrative(data) {
|
|
136431
|
-
const root =
|
|
136432
|
-
const narrative =
|
|
136636
|
+
const root = asRecord16(data);
|
|
136637
|
+
const narrative = asRecord16(root?.narrative);
|
|
136433
136638
|
const missing = [];
|
|
136434
136639
|
if (!narrative) {
|
|
136435
136640
|
return { ok: false, missing: ["narrative"] };
|
|
136436
136641
|
}
|
|
136437
136642
|
if (asStringArray(narrative.executiveSummary).length < 1) missing.push("narrative.executiveSummary");
|
|
136438
|
-
const sections =
|
|
136643
|
+
const sections = asRecord16(narrative.sections);
|
|
136439
136644
|
for (const key of ["efficiency", "adType", "region", "summary"]) {
|
|
136440
|
-
const block =
|
|
136645
|
+
const block = asRecord16(sections?.[key]);
|
|
136441
136646
|
if (asStringArray(block?.analysis).length < 1) missing.push(`narrative.sections.${key}.analysis`);
|
|
136442
136647
|
}
|
|
136443
|
-
const plan =
|
|
136648
|
+
const plan = asRecord16(narrative.plan);
|
|
136444
136649
|
if (asStringArray(plan?.analysis ?? plan?.goals).length < 1 && !String(plan?.text ?? "").trim()) {
|
|
136445
136650
|
missing.push("narrative.plan");
|
|
136446
136651
|
}
|
|
@@ -136453,8 +136658,8 @@ function mergeCollectFacts(agentData, collect) {
|
|
|
136453
136658
|
...facts,
|
|
136454
136659
|
narrative: agentData.narrative ?? collect.narrative,
|
|
136455
136660
|
meta: {
|
|
136456
|
-
...
|
|
136457
|
-
...
|
|
136661
|
+
...asRecord16(collect.meta) ?? {},
|
|
136662
|
+
...asRecord16(agentData.meta) ?? {}
|
|
136458
136663
|
}
|
|
136459
136664
|
};
|
|
136460
136665
|
}
|
|
@@ -136466,7 +136671,7 @@ import { fileURLToPath as fileURLToPath8 } from "url";
|
|
|
136466
136671
|
var TSO_CLI_ROOT = path30.join(path30.dirname(fileURLToPath8(import.meta.url)), "../..");
|
|
136467
136672
|
|
|
136468
136673
|
// src/commands/google-quarterly/render-report.ts
|
|
136469
|
-
function
|
|
136674
|
+
function asRecord17(value) {
|
|
136470
136675
|
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
136471
136676
|
return value;
|
|
136472
136677
|
}
|
|
@@ -136495,7 +136700,7 @@ async function runGoogleQuarterlyRender(opts) {
|
|
|
136495
136700
|
`);
|
|
136496
136701
|
process.exit(1);
|
|
136497
136702
|
}
|
|
136498
|
-
const merged = mergeCollectFacts(
|
|
136703
|
+
const merged = mergeCollectFacts(asRecord17(agentRaw), asRecord17(collectRaw));
|
|
136499
136704
|
const check = validateQuarterlyNarrative(merged);
|
|
136500
136705
|
if (!check.ok && !opts.lenient) {
|
|
136501
136706
|
console.error("\n\u274C narrative \u4E0D\u5B8C\u6574\uFF0C\u62D2\u7EDD\u51FA HTML\uFF1A\n");
|
|
@@ -136659,7 +136864,7 @@ import * as fs33 from "fs/promises";
|
|
|
136659
136864
|
import * as path40 from "path";
|
|
136660
136865
|
|
|
136661
136866
|
// src/commands/facebook-analysis/report-metrics.ts
|
|
136662
|
-
function
|
|
136867
|
+
function asRecord23(value) {
|
|
136663
136868
|
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
136664
136869
|
return value;
|
|
136665
136870
|
}
|
|
@@ -136796,7 +137001,7 @@ function localizeFacebookReportLabels(payload) {
|
|
|
136796
137001
|
for (const rows of Object.values(tables)) {
|
|
136797
137002
|
if (!Array.isArray(rows)) continue;
|
|
136798
137003
|
for (const row of rows) {
|
|
136799
|
-
localizeMetricLabels(
|
|
137004
|
+
localizeMetricLabels(asRecord23(row));
|
|
136800
137005
|
}
|
|
136801
137006
|
}
|
|
136802
137007
|
return payload;
|
|
@@ -136829,7 +137034,7 @@ async function readManifest3(snapshotDir) {
|
|
|
136829
137034
|
async function loadSectionFile3(snapshotDir, file) {
|
|
136830
137035
|
try {
|
|
136831
137036
|
const raw = await fs33.readFile(path40.join(snapshotDir, file), "utf8");
|
|
136832
|
-
return
|
|
137037
|
+
return asRecord23(JSON.parse(raw));
|
|
136833
137038
|
} catch {
|
|
136834
137039
|
return null;
|
|
136835
137040
|
}
|
|
@@ -136885,7 +137090,7 @@ async function mergeFacebookSnapshotIntoReport(payload, snapshotDir) {
|
|
|
136885
137090
|
if (data) sectionMap.set(art.section, data);
|
|
136886
137091
|
}
|
|
136887
137092
|
const overview = sectionMap.get("overview");
|
|
136888
|
-
const current =
|
|
137093
|
+
const current = asRecord23(overview?.currentPeriod);
|
|
136889
137094
|
const accountName = typeof overview?.accountName === "string" ? overview.accountName : void 0;
|
|
136890
137095
|
const accountId = manifest.accountId ?? (typeof overview?.accountId === "string" ? overview.accountId : void 0);
|
|
136891
137096
|
const kpis = { ...payload.kpis ?? {} };
|
|
@@ -137022,7 +137227,7 @@ function writeSimpleXlsx(filePath, sheets, opts = {}) {
|
|
|
137022
137227
|
}
|
|
137023
137228
|
|
|
137024
137229
|
// src/commands/facebook-analysis/render-excel.ts
|
|
137025
|
-
function
|
|
137230
|
+
function asRecord24(value) {
|
|
137026
137231
|
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
137027
137232
|
return value;
|
|
137028
137233
|
}
|
|
@@ -137070,8 +137275,8 @@ function bullets(value) {
|
|
|
137070
137275
|
return asArray3(value).map((item) => typeof item === "string" ? item.trim() : "").filter(Boolean);
|
|
137071
137276
|
}
|
|
137072
137277
|
function insightLines(section) {
|
|
137073
|
-
const rec =
|
|
137074
|
-
const insight =
|
|
137278
|
+
const rec = asRecord24(section);
|
|
137279
|
+
const insight = asRecord24(rec?.insight) ?? rec;
|
|
137075
137280
|
return [...bullets(insight?.analysis), ...bullets(insight?.advice)];
|
|
137076
137281
|
}
|
|
137077
137282
|
function numberedSection(index, title, lines) {
|
|
@@ -137080,15 +137285,15 @@ function numberedSection(index, title, lines) {
|
|
|
137080
137285
|
${body}`;
|
|
137081
137286
|
}
|
|
137082
137287
|
function buildSummaryNarrative(data) {
|
|
137083
|
-
const kpis =
|
|
137084
|
-
const meta =
|
|
137085
|
-
const sections =
|
|
137288
|
+
const kpis = asRecord24(data.kpis) ?? {};
|
|
137289
|
+
const meta = asRecord24(data.meta) ?? {};
|
|
137290
|
+
const sections = asRecord24(data.sections) ?? {};
|
|
137086
137291
|
const period = str2(meta.periodLabel) || `${str2(meta.startDate)} ~ ${str2(meta.endDate)}`.trim();
|
|
137087
137292
|
const account = str2(meta.accountName) || "Facebook \u5E7F\u544A\u8D26\u6237";
|
|
137088
137293
|
const campaign = str2(meta.campaignName);
|
|
137089
137294
|
const resultLabel = displayResultType(kpis.resultType ?? meta.resultType) || "\u6210\u6548";
|
|
137090
137295
|
const resultNoun = /潜在客户|lead/i.test(resultLabel) ? "\u4E2A\u6F5C\u5728\u5BA2\u6237\u8868\u5355" : `\u4E2A${resultLabel}`;
|
|
137091
|
-
const firstQuestion =
|
|
137296
|
+
const firstQuestion = asRecord24(asArray3(data.fourQuestions)[0]);
|
|
137092
137297
|
const extra = str2(firstQuestion?.verdict);
|
|
137093
137298
|
const spendLine = [
|
|
137094
137299
|
campaign ? `\uFF08\u5E7F\u544A\u7CFB\u5217\uFF1A${campaign}\uFF09` : "",
|
|
@@ -137108,7 +137313,7 @@ function buildSummaryNarrative(data) {
|
|
|
137108
137313
|
numberedSection(3, "\u5E7F\u544A\u9891\u6B21\u4E0E\u65E5\u8D8B\u52BF", insightLines(sections.daily))
|
|
137109
137314
|
].join("\n");
|
|
137110
137315
|
const recBlocks = asArray3(data.recommendations).map((raw, i) => {
|
|
137111
|
-
const rec =
|
|
137316
|
+
const rec = asRecord24(raw);
|
|
137112
137317
|
if (!rec) return "";
|
|
137113
137318
|
const title = [str2(rec.title), str2(rec.tag)].filter(Boolean).join(" \xB7 ");
|
|
137114
137319
|
return numberedSection(i + 1, title || "\u4F18\u5316\u5EFA\u8BAE", bullets(rec.items));
|
|
@@ -137119,9 +137324,9 @@ function buildSummaryNarrative(data) {
|
|
|
137119
137324
|
return [partOne, partTwo, partThree];
|
|
137120
137325
|
}
|
|
137121
137326
|
function buildMetaPeriodExcelSheets(data) {
|
|
137122
|
-
const meta =
|
|
137123
|
-
const kpis =
|
|
137124
|
-
const tables =
|
|
137327
|
+
const meta = asRecord24(data.meta) ?? {};
|
|
137328
|
+
const kpis = asRecord24(data.kpis) ?? {};
|
|
137329
|
+
const tables = asRecord24(data.tables) ?? {};
|
|
137125
137330
|
const currency = str2(meta.currency) || "USD";
|
|
137126
137331
|
const resultType = displayResultType(kpis.resultType ?? meta.resultType);
|
|
137127
137332
|
const attribution = displayAttribution(kpis.attributionSetting ?? meta.attributionSetting);
|
|
@@ -137140,19 +137345,19 @@ function buildMetaPeriodExcelSheets(data) {
|
|
|
137140
137345
|
];
|
|
137141
137346
|
const dailyRows = [metricHeaders(currency, "\u5355\u65E5")];
|
|
137142
137347
|
for (const raw of asArray3(tables.daily)) {
|
|
137143
|
-
const row =
|
|
137348
|
+
const row = asRecord24(raw);
|
|
137144
137349
|
if (!row) continue;
|
|
137145
137350
|
dailyRows.push([normalizeInsightDate(row.date), ...metricCells(row)]);
|
|
137146
137351
|
}
|
|
137147
137352
|
const countryRows = [metricHeaders(currency, "\u56FD\u5BB6/\u5730\u533A")];
|
|
137148
137353
|
for (const raw of asArray3(tables.countries)) {
|
|
137149
|
-
const row =
|
|
137354
|
+
const row = asRecord24(raw);
|
|
137150
137355
|
if (!row) continue;
|
|
137151
137356
|
countryRows.push([str2(row.countryOrRegion), ...metricCells(row)]);
|
|
137152
137357
|
}
|
|
137153
137358
|
const campaignRows3 = [metricHeaders(currency, "\u5E7F\u544A\u7CFB\u5217\u540D\u79F0")];
|
|
137154
137359
|
for (const raw of asArray3(tables.campaigns)) {
|
|
137155
|
-
const row =
|
|
137360
|
+
const row = asRecord24(raw);
|
|
137156
137361
|
if (!row) continue;
|
|
137157
137362
|
campaignRows3.push([str2(row.campaignName), ...metricCells(row)]);
|
|
137158
137363
|
}
|
|
@@ -137171,7 +137376,7 @@ function buildMetaPeriodExcelSheets(data) {
|
|
|
137171
137376
|
]
|
|
137172
137377
|
];
|
|
137173
137378
|
for (const raw of asArray3(tables.audiences)) {
|
|
137174
|
-
const row =
|
|
137379
|
+
const row = asRecord24(raw);
|
|
137175
137380
|
if (!row) continue;
|
|
137176
137381
|
audienceRows.push([str2(row.age), str2(row.gender), ...metricCells(row)]);
|
|
137177
137382
|
}
|
|
@@ -137203,7 +137408,7 @@ function isFiniteNumber(value) {
|
|
|
137203
137408
|
function asArray4(value) {
|
|
137204
137409
|
return Array.isArray(value) ? value : [];
|
|
137205
137410
|
}
|
|
137206
|
-
function
|
|
137411
|
+
function asRecord25(value) {
|
|
137207
137412
|
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
137208
137413
|
return value;
|
|
137209
137414
|
}
|
|
@@ -137216,8 +137421,8 @@ function stringList3(value) {
|
|
|
137216
137421
|
return asArray4(value).map((item) => typeof item === "string" ? item.trim() : "").filter(Boolean);
|
|
137217
137422
|
}
|
|
137218
137423
|
function insightLists(section) {
|
|
137219
|
-
const rec =
|
|
137220
|
-
const insight =
|
|
137424
|
+
const rec = asRecord25(section);
|
|
137425
|
+
const insight = asRecord25(rec?.insight) ?? rec;
|
|
137221
137426
|
return {
|
|
137222
137427
|
analysis: stringList3(insight?.analysis),
|
|
137223
137428
|
advice: stringList3(insight?.advice)
|
|
@@ -137247,11 +137452,11 @@ function requireInsight(missing, key, chapter, section, required) {
|
|
|
137247
137452
|
}
|
|
137248
137453
|
function validateMetaPeriodReportContent(data) {
|
|
137249
137454
|
const missing = [];
|
|
137250
|
-
const meta =
|
|
137251
|
-
const kpis =
|
|
137252
|
-
const tables =
|
|
137253
|
-
const charts =
|
|
137254
|
-
const sections =
|
|
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) ?? {};
|
|
137255
137460
|
const fourQuestions = asArray4(data.fourQuestions);
|
|
137256
137461
|
const recommendations = asArray4(data.recommendations);
|
|
137257
137462
|
if (!textLen3(meta.accountName) && !textLen3(meta.accountId)) {
|
|
@@ -137330,7 +137535,7 @@ function validateMetaPeriodReportContent(data) {
|
|
|
137330
137535
|
"\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 []"
|
|
137331
137536
|
);
|
|
137332
137537
|
}
|
|
137333
|
-
const dailyChart =
|
|
137538
|
+
const dailyChart = asRecord25(charts.daily);
|
|
137334
137539
|
if (!Array.isArray(dailyChart?.labels)) {
|
|
137335
137540
|
pushMissing3(
|
|
137336
137541
|
missing,
|
|
@@ -137340,7 +137545,7 @@ function validateMetaPeriodReportContent(data) {
|
|
|
137340
137545
|
"\u987B\u6709 charts.daily.labels \u6570\u7EC4\uFF1B\u53EF\u7531 --snapshot-dir \u5408\u5E76 daily\u3002\u672C\u671F\u65E0\u6D88\u8017\u53EF\u4E3A []"
|
|
137341
137546
|
);
|
|
137342
137547
|
}
|
|
137343
|
-
const countryChart =
|
|
137548
|
+
const countryChart = asRecord25(charts.country);
|
|
137344
137549
|
if (!Array.isArray(countryChart?.labels)) {
|
|
137345
137550
|
pushMissing3(
|
|
137346
137551
|
missing,
|
|
@@ -137350,7 +137555,7 @@ function validateMetaPeriodReportContent(data) {
|
|
|
137350
137555
|
"\u987B\u6709 charts.country.labels \u6570\u7EC4\uFF1B\u53EF\u7531 --snapshot-dir \u5408\u5E76 country\u3002\u672C\u671F\u65E0\u6D88\u8017\u53EF\u4E3A []"
|
|
137351
137556
|
);
|
|
137352
137557
|
}
|
|
137353
|
-
const audienceChart =
|
|
137558
|
+
const audienceChart = asRecord25(charts.audience);
|
|
137354
137559
|
if (!Array.isArray(audienceChart?.labels)) {
|
|
137355
137560
|
pushMissing3(
|
|
137356
137561
|
missing,
|
|
@@ -137369,7 +137574,7 @@ function validateMetaPeriodReportContent(data) {
|
|
|
137369
137574
|
"\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"
|
|
137370
137575
|
);
|
|
137371
137576
|
} else {
|
|
137372
|
-
const titles = fourQuestions.map((q) =>
|
|
137577
|
+
const titles = fourQuestions.map((q) => asRecord25(q)?.title);
|
|
137373
137578
|
for (const title of FOUR_QUESTION_TITLES) {
|
|
137374
137579
|
if (!titles.includes(title)) {
|
|
137375
137580
|
pushMissing3(
|
|
@@ -137382,7 +137587,7 @@ function validateMetaPeriodReportContent(data) {
|
|
|
137382
137587
|
}
|
|
137383
137588
|
}
|
|
137384
137589
|
for (let i = 0; i < fourQuestions.length; i++) {
|
|
137385
|
-
const q =
|
|
137590
|
+
const q = asRecord25(fourQuestions[i]);
|
|
137386
137591
|
if (textLen3(q?.verdict) < 8) {
|
|
137387
137592
|
pushMissing3(
|
|
137388
137593
|
missing,
|
|
@@ -137422,7 +137627,7 @@ function validateMetaPeriodReportContent(data) {
|
|
|
137422
137627
|
);
|
|
137423
137628
|
} else {
|
|
137424
137629
|
for (let i = 0; i < recommendations.length; i++) {
|
|
137425
|
-
const rec =
|
|
137630
|
+
const rec = asRecord25(recommendations[i]);
|
|
137426
137631
|
if (textLen3(rec?.title) < 2) {
|
|
137427
137632
|
pushMissing3(
|
|
137428
137633
|
missing,
|
|
@@ -137515,7 +137720,7 @@ async function readJsonFile4(filePath) {
|
|
|
137515
137720
|
throw new Error(`\u65E0\u6CD5\u89E3\u6790 JSON\uFF1A${filePath}`);
|
|
137516
137721
|
}
|
|
137517
137722
|
}
|
|
137518
|
-
function
|
|
137723
|
+
function asRecord26(value) {
|
|
137519
137724
|
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
137520
137725
|
return value;
|
|
137521
137726
|
}
|
|
@@ -137544,7 +137749,7 @@ function defaultOutPath(dataPath, format, out) {
|
|
|
137544
137749
|
}
|
|
137545
137750
|
async function runFacebookAnalysisRender(opts) {
|
|
137546
137751
|
const dataPath = path41.resolve(opts.dataFile);
|
|
137547
|
-
let data =
|
|
137752
|
+
let data = asRecord26(await readJsonFile4(dataPath));
|
|
137548
137753
|
if (opts.snapshotDir?.trim()) {
|
|
137549
137754
|
data = await mergeFacebookSnapshotIntoReport(data, path41.resolve(opts.snapshotDir));
|
|
137550
137755
|
}
|
|
@@ -137608,7 +137813,7 @@ import { fileURLToPath as fileURLToPath11 } from "url";
|
|
|
137608
137813
|
// src/commands/facebook-analysis/diagnosis-merge-snapshot.ts
|
|
137609
137814
|
import * as fs35 from "fs/promises";
|
|
137610
137815
|
import * as path42 from "path";
|
|
137611
|
-
function
|
|
137816
|
+
function asRecord27(value) {
|
|
137612
137817
|
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
137613
137818
|
return value;
|
|
137614
137819
|
}
|
|
@@ -137619,7 +137824,7 @@ function num8(value) {
|
|
|
137619
137824
|
return Number.isFinite(n) ? n : void 0;
|
|
137620
137825
|
}
|
|
137621
137826
|
function asRows(value) {
|
|
137622
|
-
return Array.isArray(value) ? value.filter((r) =>
|
|
137827
|
+
return Array.isArray(value) ? value.filter((r) => asRecord27(r) !== null) : [];
|
|
137623
137828
|
}
|
|
137624
137829
|
function pickMetricExtras(row, extras) {
|
|
137625
137830
|
const out = {
|
|
@@ -137668,14 +137873,14 @@ async function readManifest4(snapshotDir, accountId) {
|
|
|
137668
137873
|
async function loadSectionFile4(snapshotDir, file) {
|
|
137669
137874
|
try {
|
|
137670
137875
|
const raw = await fs35.readFile(path42.join(snapshotDir, file), "utf8");
|
|
137671
|
-
return
|
|
137876
|
+
return asRecord27(JSON.parse(raw));
|
|
137672
137877
|
} catch {
|
|
137673
137878
|
return null;
|
|
137674
137879
|
}
|
|
137675
137880
|
}
|
|
137676
137881
|
async function mergeMetaDiagnosisSnapshotIntoReport(payload, snapshotDir) {
|
|
137677
137882
|
const absDir = path42.resolve(snapshotDir);
|
|
137678
|
-
const metaAccountId = typeof
|
|
137883
|
+
const metaAccountId = typeof asRecord27(payload.meta)?.accountId === "string" ? asRecord27(payload.meta)?.accountId : void 0;
|
|
137679
137884
|
const manifest = await readManifest4(snapshotDir, metaAccountId);
|
|
137680
137885
|
if (!manifest) {
|
|
137681
137886
|
throw new Error(
|
|
@@ -137688,8 +137893,8 @@ async function mergeMetaDiagnosisSnapshotIntoReport(payload, snapshotDir) {
|
|
|
137688
137893
|
if (data) sectionMap.set(art.section, data);
|
|
137689
137894
|
}
|
|
137690
137895
|
const overview = sectionMap.get("overview");
|
|
137691
|
-
const currentPeriod =
|
|
137692
|
-
const previousPeriod =
|
|
137896
|
+
const currentPeriod = asRecord27(overview?.currentPeriod);
|
|
137897
|
+
const previousPeriod = asRecord27(overview?.previousPeriod);
|
|
137693
137898
|
const accountId = manifest.accountId ?? metaAccountId;
|
|
137694
137899
|
const meta = { ...payload.meta ?? {} };
|
|
137695
137900
|
if (!meta.accountId && accountId) meta.accountId = accountId;
|
|
@@ -137823,7 +138028,7 @@ function textLen4(value) {
|
|
|
137823
138028
|
function asArray5(value) {
|
|
137824
138029
|
return Array.isArray(value) ? value : [];
|
|
137825
138030
|
}
|
|
137826
|
-
function
|
|
138031
|
+
function asRecord28(value) {
|
|
137827
138032
|
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
137828
138033
|
return value;
|
|
137829
138034
|
}
|
|
@@ -137834,9 +138039,9 @@ function pushMissing4(missing, id, chapter, dimension, hint) {
|
|
|
137834
138039
|
}
|
|
137835
138040
|
function validateMetaDiagnosisReportContent(data) {
|
|
137836
138041
|
const missing = [];
|
|
137837
|
-
const meta =
|
|
137838
|
-
const narrative =
|
|
137839
|
-
const sections =
|
|
138042
|
+
const meta = asRecord28(data.meta) ?? {};
|
|
138043
|
+
const narrative = asRecord28(data.narrative) ?? {};
|
|
138044
|
+
const sections = asRecord28(narrative.sections) ?? {};
|
|
137840
138045
|
if (!textLen4(meta.accountId)) {
|
|
137841
138046
|
pushMissing4(missing, "meta-account", "\u5143\u4FE1\u606F", "\u8D26\u6237 ID", "meta.accountId \u5FC5\u586B");
|
|
137842
138047
|
}
|
|
@@ -137854,7 +138059,7 @@ function validateMetaDiagnosisReportContent(data) {
|
|
|
137854
138059
|
const sectionSuggestionCounts = {};
|
|
137855
138060
|
const sectionNotAvailable = {};
|
|
137856
138061
|
for (const key of NORMAL_SECTION_KEYS) {
|
|
137857
|
-
const section =
|
|
138062
|
+
const section = asRecord28(sections[key]);
|
|
137858
138063
|
const analysis = asArray5(section?.analysis).filter((s) => textLen4(s) > 0);
|
|
137859
138064
|
const suggestions = asArray5(section?.suggestions).filter((s) => textLen4(s) > 0);
|
|
137860
138065
|
sectionAnalysisCounts[key] = analysis.length;
|
|
@@ -137880,7 +138085,7 @@ function validateMetaDiagnosisReportContent(data) {
|
|
|
137880
138085
|
}
|
|
137881
138086
|
}
|
|
137882
138087
|
for (const key of NA_SECTION_KEYS) {
|
|
137883
|
-
const section =
|
|
138088
|
+
const section = asRecord28(sections[key]);
|
|
137884
138089
|
const analysis = asArray5(section?.analysis).filter((s) => textLen4(s) > 0);
|
|
137885
138090
|
const suggestions = asArray5(section?.suggestions).filter((s) => textLen4(s) > 0);
|
|
137886
138091
|
const notAvailable = section?.notAvailable === true;
|
|
@@ -137902,7 +138107,7 @@ function validateMetaDiagnosisReportContent(data) {
|
|
|
137902
138107
|
}
|
|
137903
138108
|
const recommendations = asArray5(narrative.recommendations).filter((r) => {
|
|
137904
138109
|
if (typeof r === "string") return textLen4(r) > 0;
|
|
137905
|
-
const rec =
|
|
138110
|
+
const rec = asRecord28(r);
|
|
137906
138111
|
return textLen4(rec?.content) > 0;
|
|
137907
138112
|
});
|
|
137908
138113
|
if (recommendations.length < 3) {
|
|
@@ -137975,7 +138180,7 @@ async function readJsonFile5(filePath) {
|
|
|
137975
138180
|
throw new Error(`\u65E0\u6CD5\u89E3\u6790 JSON\uFF1A${filePath}`);
|
|
137976
138181
|
}
|
|
137977
138182
|
}
|
|
137978
|
-
function
|
|
138183
|
+
function asRecord29(value) {
|
|
137979
138184
|
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
137980
138185
|
return value;
|
|
137981
138186
|
}
|
|
@@ -137990,7 +138195,7 @@ function injectReportData4(html, payload) {
|
|
|
137990
138195
|
}
|
|
137991
138196
|
async function runFacebookAnalysisDiagnosisRender(opts) {
|
|
137992
138197
|
const dataPath = path43.resolve(opts.dataFile);
|
|
137993
|
-
let data =
|
|
138198
|
+
let data = asRecord29(await readJsonFile5(dataPath));
|
|
137994
138199
|
if (opts.snapshotDir?.trim()) {
|
|
137995
138200
|
data = await mergeMetaDiagnosisSnapshotIntoReport(data, path43.resolve(opts.snapshotDir));
|
|
137996
138201
|
}
|
|
@@ -138189,7 +138394,7 @@ function resolveFacebookPageSize(raw, fallback = FB_ENTITY_PAGE_SIZE_DEFAULT) {
|
|
|
138189
138394
|
if (!Number.isFinite(n) || n <= 0) return fallback;
|
|
138190
138395
|
return Math.min(Math.max(Math.floor(n), 1), FB_ENTITY_PAGE_SIZE_MAX);
|
|
138191
138396
|
}
|
|
138192
|
-
function
|
|
138397
|
+
function asRecord30(value) {
|
|
138193
138398
|
return value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
|
|
138194
138399
|
}
|
|
138195
138400
|
function pickProp(obj, ...keys) {
|
|
@@ -138199,12 +138404,12 @@ function pickProp(obj, ...keys) {
|
|
|
138199
138404
|
return void 0;
|
|
138200
138405
|
}
|
|
138201
138406
|
function unwrapFacebookPaged(raw) {
|
|
138202
|
-
const rec =
|
|
138407
|
+
const rec = asRecord30(raw);
|
|
138203
138408
|
if (!rec) return { items: [] };
|
|
138204
138409
|
const data = pickProp(rec, "data", "Data");
|
|
138205
138410
|
const items3 = Array.isArray(data) ? data : [];
|
|
138206
|
-
const paging =
|
|
138207
|
-
const cursors = paging ?
|
|
138411
|
+
const paging = asRecord30(pickProp(rec, "paging", "Paging"));
|
|
138412
|
+
const cursors = paging ? asRecord30(pickProp(paging, "cursors", "Cursors")) : void 0;
|
|
138208
138413
|
const afterRaw = cursors ? pickProp(cursors, "after", "After") : void 0;
|
|
138209
138414
|
const after = typeof afterRaw === "string" && afterRaw.trim() ? afterRaw.trim() : void 0;
|
|
138210
138415
|
return { items: items3, after };
|
|
@@ -138352,7 +138557,7 @@ async function fetchFacebookInsights(config, digits, level, opts) {
|
|
|
138352
138557
|
opts.verbose
|
|
138353
138558
|
);
|
|
138354
138559
|
if (Array.isArray(raw)) return raw;
|
|
138355
|
-
const rec =
|
|
138560
|
+
const rec = asRecord30(raw);
|
|
138356
138561
|
const data = rec ? pickProp(rec, "data", "Data") : void 0;
|
|
138357
138562
|
return Array.isArray(data) ? data : [];
|
|
138358
138563
|
}
|
|
@@ -138361,7 +138566,7 @@ function sumActionArray(raw) {
|
|
|
138361
138566
|
let sum = 0;
|
|
138362
138567
|
let hit = false;
|
|
138363
138568
|
for (const item of raw) {
|
|
138364
|
-
const rec =
|
|
138569
|
+
const rec = asRecord30(item);
|
|
138365
138570
|
if (!rec) continue;
|
|
138366
138571
|
const n = Number(rec.value);
|
|
138367
138572
|
if (!Number.isFinite(n)) continue;
|
|
@@ -139640,7 +139845,7 @@ import { fileURLToPath as fileURLToPath12 } from "url";
|
|
|
139640
139845
|
// src/commands/bing-analysis/merge-snapshot.ts
|
|
139641
139846
|
import * as fs37 from "fs/promises";
|
|
139642
139847
|
import * as path46 from "path";
|
|
139643
|
-
function
|
|
139848
|
+
function asRecord31(value) {
|
|
139644
139849
|
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
139645
139850
|
return value;
|
|
139646
139851
|
}
|
|
@@ -139655,7 +139860,7 @@ function round27(value) {
|
|
|
139655
139860
|
}
|
|
139656
139861
|
function asRows2(value) {
|
|
139657
139862
|
if (Array.isArray(value)) {
|
|
139658
|
-
return value.filter((r) =>
|
|
139863
|
+
return value.filter((r) => asRecord31(r) !== null);
|
|
139659
139864
|
}
|
|
139660
139865
|
return [];
|
|
139661
139866
|
}
|
|
@@ -139737,17 +139942,17 @@ function campaignStatusDisplay(status) {
|
|
|
139737
139942
|
return status == null || status === "" ? "\u2014" : String(status);
|
|
139738
139943
|
}
|
|
139739
139944
|
function mapDeviceRows(payload) {
|
|
139740
|
-
const obj =
|
|
139945
|
+
const obj = asRecord31(payload);
|
|
139741
139946
|
return asRows2(obj?.devices).map((r) => pickMetrics(r, ["deviceType"]));
|
|
139742
139947
|
}
|
|
139743
139948
|
function mapGeographicRows(payload) {
|
|
139744
|
-
const obj =
|
|
139949
|
+
const obj = asRecord31(payload);
|
|
139745
139950
|
return asRows2(obj?.countries).map(
|
|
139746
139951
|
(r) => pickMetrics(r, ["countryOrRegion", "countryCriteriaId", "regionName", "cityName"])
|
|
139747
139952
|
);
|
|
139748
139953
|
}
|
|
139749
139954
|
function mapAudienceLabelRows(payload, labelKey) {
|
|
139750
|
-
const obj =
|
|
139955
|
+
const obj = asRecord31(payload);
|
|
139751
139956
|
return asRows2(obj?.audience).map((r) => {
|
|
139752
139957
|
const m = pickMetrics(r, ["audience", "bidModifier"]);
|
|
139753
139958
|
m[labelKey] = r.audience;
|
|
@@ -139755,7 +139960,7 @@ function mapAudienceLabelRows(payload, labelKey) {
|
|
|
139755
139960
|
});
|
|
139756
139961
|
}
|
|
139757
139962
|
function mapAudienceFromMerged(payload) {
|
|
139758
|
-
const data =
|
|
139963
|
+
const data = asRecord31(asRecord31(payload)?.data);
|
|
139759
139964
|
return {
|
|
139760
139965
|
audienceAge: mapAudienceLabelRows(data?.ageAudience, "ageRange"),
|
|
139761
139966
|
audienceGender: mapAudienceLabelRows(data?.genderAudience, "gender")
|
|
@@ -139850,7 +140055,7 @@ function mapSearchTermRows(payload) {
|
|
|
139850
140055
|
}
|
|
139851
140056
|
async function mergeBingAnalysisSnapshotIntoReport(payload, snapshotDir) {
|
|
139852
140057
|
const absDir = path46.resolve(snapshotDir);
|
|
139853
|
-
const metaAccountId = typeof
|
|
140058
|
+
const metaAccountId = typeof asRecord31(payload.meta)?.accountId === "string" ? asRecord31(payload.meta)?.accountId : void 0;
|
|
139854
140059
|
const manifest = await readManifest5(snapshotDir, metaAccountId);
|
|
139855
140060
|
if (!manifest) {
|
|
139856
140061
|
throw new Error(`\u672A\u5728 ${absDir} \u627E\u5230 report-manifest*.json\uFF0C\u8BF7\u5148\u6267\u884C bing-analysis --json-out`);
|
|
@@ -139860,9 +140065,9 @@ async function mergeBingAnalysisSnapshotIntoReport(payload, snapshotDir) {
|
|
|
139860
140065
|
const data = await loadSectionPayload(absDir, art.file);
|
|
139861
140066
|
if (data != null) sectionMap.set(art.section, data);
|
|
139862
140067
|
}
|
|
139863
|
-
const overview =
|
|
139864
|
-
const currentPeriod =
|
|
139865
|
-
const previousPeriod =
|
|
140068
|
+
const overview = asRecord31(sectionMap.get("overview"));
|
|
140069
|
+
const currentPeriod = asRecord31(overview?.currentPeriod);
|
|
140070
|
+
const previousPeriod = asRecord31(overview?.previousPeriod);
|
|
139866
140071
|
const accountId = manifest.accountId ?? metaAccountId;
|
|
139867
140072
|
const campaignRows3 = mapCampaignRows2(sectionMap.get("campaigns"));
|
|
139868
140073
|
const campaignKpis = sumBingCampaignRowsToPeriodKpis(campaignRows3);
|
|
@@ -139989,7 +140194,7 @@ function textLen5(value) {
|
|
|
139989
140194
|
function asArray6(value) {
|
|
139990
140195
|
return Array.isArray(value) ? value : [];
|
|
139991
140196
|
}
|
|
139992
|
-
function
|
|
140197
|
+
function asRecord32(value) {
|
|
139993
140198
|
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
139994
140199
|
return value;
|
|
139995
140200
|
}
|
|
@@ -140000,9 +140205,9 @@ function pushMissing5(missing, id, chapter, dimension, hint) {
|
|
|
140000
140205
|
}
|
|
140001
140206
|
function validateBingPeriodReportContent(data) {
|
|
140002
140207
|
const missing = [];
|
|
140003
|
-
const meta =
|
|
140004
|
-
const narrative =
|
|
140005
|
-
const sections =
|
|
140208
|
+
const meta = asRecord32(data.meta) ?? {};
|
|
140209
|
+
const narrative = asRecord32(data.narrative) ?? {};
|
|
140210
|
+
const sections = asRecord32(narrative.sections) ?? {};
|
|
140006
140211
|
if (!textLen5(meta.accountId)) {
|
|
140007
140212
|
pushMissing5(missing, "meta-account", "\u5143\u4FE1\u606F", "\u8D26\u6237 ID", "meta.accountId \u5FC5\u586B");
|
|
140008
140213
|
}
|
|
@@ -140019,7 +140224,7 @@ function validateBingPeriodReportContent(data) {
|
|
|
140019
140224
|
const sectionAnalysisCounts = {};
|
|
140020
140225
|
const sectionSuggestionCounts = {};
|
|
140021
140226
|
for (const key of SECTION_KEYS2) {
|
|
140022
|
-
const section =
|
|
140227
|
+
const section = asRecord32(sections[key]);
|
|
140023
140228
|
const analysis = asArray6(section?.analysis).filter((s) => textLen5(s) > 0);
|
|
140024
140229
|
const suggestions = asArray6(section?.suggestions).filter((s) => textLen5(s) > 0);
|
|
140025
140230
|
sectionAnalysisCounts[key] = analysis.length;
|
|
@@ -140045,7 +140250,7 @@ function validateBingPeriodReportContent(data) {
|
|
|
140045
140250
|
}
|
|
140046
140251
|
const recommendations = asArray6(narrative.recommendations).filter((r) => {
|
|
140047
140252
|
if (typeof r === "string") return textLen5(r) > 0;
|
|
140048
|
-
const rec =
|
|
140253
|
+
const rec = asRecord32(r);
|
|
140049
140254
|
return textLen5(rec?.content) > 0;
|
|
140050
140255
|
});
|
|
140051
140256
|
if (recommendations.length < 3) {
|
|
@@ -140106,7 +140311,7 @@ async function readJsonFile6(filePath) {
|
|
|
140106
140311
|
throw new Error(`\u65E0\u6CD5\u89E3\u6790 JSON\uFF1A${filePath}`);
|
|
140107
140312
|
}
|
|
140108
140313
|
}
|
|
140109
|
-
function
|
|
140314
|
+
function asRecord33(value) {
|
|
140110
140315
|
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
140111
140316
|
return value;
|
|
140112
140317
|
}
|
|
@@ -140121,7 +140326,7 @@ function injectReportData5(html, payload) {
|
|
|
140121
140326
|
}
|
|
140122
140327
|
async function runBingAnalysisRender(opts) {
|
|
140123
140328
|
const dataPath = path47.resolve(opts.dataFile);
|
|
140124
|
-
let data =
|
|
140329
|
+
let data = asRecord33(await readJsonFile6(dataPath));
|
|
140125
140330
|
if (opts.snapshotDir?.trim()) {
|
|
140126
140331
|
data = await mergeBingAnalysisSnapshotIntoReport(data, path47.resolve(opts.snapshotDir));
|
|
140127
140332
|
}
|
|
@@ -140594,7 +140799,7 @@ import { fileURLToPath as fileURLToPath13 } from "url";
|
|
|
140594
140799
|
// src/commands/tiktok-analysis/merge-snapshot.ts
|
|
140595
140800
|
import * as fs39 from "fs/promises";
|
|
140596
140801
|
import * as path49 from "path";
|
|
140597
|
-
function
|
|
140802
|
+
function asRecord34(value) {
|
|
140598
140803
|
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
140599
140804
|
return value;
|
|
140600
140805
|
}
|
|
@@ -140772,7 +140977,7 @@ function mapAudienceMerged(payload) {
|
|
|
140772
140977
|
}
|
|
140773
140978
|
async function mergeTikTokAnalysisSnapshotIntoReport(payload, snapshotDir) {
|
|
140774
140979
|
const absDir = path49.resolve(snapshotDir);
|
|
140775
|
-
const metaAccountId = typeof
|
|
140980
|
+
const metaAccountId = typeof asRecord34(payload.meta)?.accountId === "string" ? asRecord34(payload.meta)?.accountId : void 0;
|
|
140776
140981
|
const manifest = await readManifest6(snapshotDir, metaAccountId);
|
|
140777
140982
|
if (!manifest) {
|
|
140778
140983
|
throw new Error(
|
|
@@ -140886,7 +141091,7 @@ function textLen6(value) {
|
|
|
140886
141091
|
function asArray7(value) {
|
|
140887
141092
|
return Array.isArray(value) ? value : [];
|
|
140888
141093
|
}
|
|
140889
|
-
function
|
|
141094
|
+
function asRecord35(value) {
|
|
140890
141095
|
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
140891
141096
|
return value;
|
|
140892
141097
|
}
|
|
@@ -140897,9 +141102,9 @@ function pushMissing6(missing, id, chapter, dimension, hint) {
|
|
|
140897
141102
|
}
|
|
140898
141103
|
function validateTikTokPeriodReportContent(data) {
|
|
140899
141104
|
const missing = [];
|
|
140900
|
-
const meta =
|
|
140901
|
-
const narrative =
|
|
140902
|
-
const sections =
|
|
141105
|
+
const meta = asRecord35(data.meta) ?? {};
|
|
141106
|
+
const narrative = asRecord35(data.narrative) ?? {};
|
|
141107
|
+
const sections = asRecord35(narrative.sections) ?? {};
|
|
140903
141108
|
if (!textLen6(meta.accountId)) {
|
|
140904
141109
|
pushMissing6(missing, "meta-account", "\u5143\u4FE1\u606F", "\u8D26\u6237 ID", "meta.accountId \u5FC5\u586B");
|
|
140905
141110
|
}
|
|
@@ -140916,7 +141121,7 @@ function validateTikTokPeriodReportContent(data) {
|
|
|
140916
141121
|
const sectionAnalysisCounts = {};
|
|
140917
141122
|
const sectionSuggestionCounts = {};
|
|
140918
141123
|
for (const key of SECTION_KEYS3) {
|
|
140919
|
-
const section =
|
|
141124
|
+
const section = asRecord35(sections[key]);
|
|
140920
141125
|
const analysis = asArray7(section?.analysis).filter((s) => textLen6(s) > 0);
|
|
140921
141126
|
const suggestions = asArray7(section?.suggestions).filter((s) => textLen6(s) > 0);
|
|
140922
141127
|
sectionAnalysisCounts[key] = analysis.length;
|
|
@@ -140942,7 +141147,7 @@ function validateTikTokPeriodReportContent(data) {
|
|
|
140942
141147
|
}
|
|
140943
141148
|
const recommendations = asArray7(narrative.recommendations).filter((r) => {
|
|
140944
141149
|
if (typeof r === "string") return textLen6(r) > 0;
|
|
140945
|
-
const rec =
|
|
141150
|
+
const rec = asRecord35(r);
|
|
140946
141151
|
return textLen6(rec?.content) > 0;
|
|
140947
141152
|
});
|
|
140948
141153
|
if (recommendations.length < 3) {
|
|
@@ -141003,7 +141208,7 @@ async function readJsonFile7(filePath) {
|
|
|
141003
141208
|
throw new Error(`\u65E0\u6CD5\u89E3\u6790 JSON\uFF1A${filePath}`);
|
|
141004
141209
|
}
|
|
141005
141210
|
}
|
|
141006
|
-
function
|
|
141211
|
+
function asRecord36(value) {
|
|
141007
141212
|
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
141008
141213
|
return value;
|
|
141009
141214
|
}
|
|
@@ -141018,7 +141223,7 @@ function injectReportData6(html, payload) {
|
|
|
141018
141223
|
}
|
|
141019
141224
|
async function runTikTokAnalysisRender(opts) {
|
|
141020
141225
|
const dataPath = path50.resolve(opts.dataFile);
|
|
141021
|
-
let data =
|
|
141226
|
+
let data = asRecord36(await readJsonFile7(dataPath));
|
|
141022
141227
|
if (opts.snapshotDir?.trim()) {
|
|
141023
141228
|
data = await mergeTikTokAnalysisSnapshotIntoReport(data, path50.resolve(opts.snapshotDir));
|
|
141024
141229
|
}
|
|
@@ -142117,7 +142322,7 @@ function endpointHintForYandexSection(def) {
|
|
|
142117
142322
|
// src/commands/yandex-analysis/merge-snapshot.ts
|
|
142118
142323
|
import * as fs41 from "fs/promises";
|
|
142119
142324
|
import * as path51 from "path";
|
|
142120
|
-
function
|
|
142325
|
+
function asRecord37(value) {
|
|
142121
142326
|
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
142122
142327
|
return value;
|
|
142123
142328
|
}
|
|
@@ -142137,11 +142342,11 @@ function normalizeYandexRoi(value) {
|
|
|
142137
142342
|
return r === -1 ? void 0 : r;
|
|
142138
142343
|
}
|
|
142139
142344
|
function asItems(payload) {
|
|
142140
|
-
const obj =
|
|
142345
|
+
const obj = asRecord37(payload);
|
|
142141
142346
|
if (!obj) return [];
|
|
142142
142347
|
const items3 = obj.items;
|
|
142143
142348
|
if (!Array.isArray(items3)) return [];
|
|
142144
|
-
return items3.filter((r) =>
|
|
142349
|
+
return items3.filter((r) => asRecord37(r) !== null);
|
|
142145
142350
|
}
|
|
142146
142351
|
function pickMetrics2(row, extras = []) {
|
|
142147
142352
|
const spend = num12(row.spend) ?? 0;
|
|
@@ -142277,7 +142482,7 @@ function aggregateDailyToWeekly(dailyRows) {
|
|
|
142277
142482
|
});
|
|
142278
142483
|
}
|
|
142279
142484
|
function prepareYandexDailyPayload(payload) {
|
|
142280
|
-
const obj =
|
|
142485
|
+
const obj = asRecord37(payload) ?? {};
|
|
142281
142486
|
const items3 = asItems(obj).map((r) => {
|
|
142282
142487
|
const date = normalizeYandexDailyDate(r.date);
|
|
142283
142488
|
return date ? { ...r, date } : r;
|
|
@@ -142346,7 +142551,7 @@ function mapDeviceRows2(payload) {
|
|
|
142346
142551
|
}
|
|
142347
142552
|
async function mergeYandexAnalysisSnapshotIntoReport(payload, snapshotDir) {
|
|
142348
142553
|
const absDir = path51.resolve(snapshotDir);
|
|
142349
|
-
const metaAccountId = typeof
|
|
142554
|
+
const metaAccountId = typeof asRecord37(payload.meta)?.accountId === "string" ? asRecord37(payload.meta)?.accountId : void 0;
|
|
142350
142555
|
const manifest = await readManifest7(snapshotDir, metaAccountId);
|
|
142351
142556
|
if (!manifest) {
|
|
142352
142557
|
throw new Error(
|
|
@@ -142358,8 +142563,8 @@ async function mergeYandexAnalysisSnapshotIntoReport(payload, snapshotDir) {
|
|
|
142358
142563
|
const data = await loadSectionPayload3(absDir, art.file);
|
|
142359
142564
|
if (data != null) sectionMap.set(art.section, data);
|
|
142360
142565
|
}
|
|
142361
|
-
const overview =
|
|
142362
|
-
const totals =
|
|
142566
|
+
const overview = asRecord37(sectionMap.get("overview"));
|
|
142567
|
+
const totals = asRecord37(overview?.totals);
|
|
142363
142568
|
const accountId = manifest.accountId ?? metaAccountId;
|
|
142364
142569
|
const meta = { ...payload.meta ?? {} };
|
|
142365
142570
|
if (!meta.accountId && accountId) meta.accountId = accountId;
|
|
@@ -142581,7 +142786,7 @@ function textLen7(value) {
|
|
|
142581
142786
|
function asArray8(value) {
|
|
142582
142787
|
return Array.isArray(value) ? value : [];
|
|
142583
142788
|
}
|
|
142584
|
-
function
|
|
142789
|
+
function asRecord38(value) {
|
|
142585
142790
|
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
142586
142791
|
return value;
|
|
142587
142792
|
}
|
|
@@ -142594,23 +142799,23 @@ function normKey(value) {
|
|
|
142594
142799
|
return String(value ?? "").trim().toLowerCase();
|
|
142595
142800
|
}
|
|
142596
142801
|
function insightMatchRecord(item) {
|
|
142597
|
-
const rec =
|
|
142802
|
+
const rec = asRecord38(item);
|
|
142598
142803
|
if (!rec) return {};
|
|
142599
|
-
return
|
|
142804
|
+
return asRecord38(rec.match) ?? rec;
|
|
142600
142805
|
}
|
|
142601
142806
|
function findRowInsight(list, primaryField, primaryValue, row) {
|
|
142602
142807
|
const target = normKey(primaryValue);
|
|
142603
142808
|
if (!target) return null;
|
|
142604
142809
|
const candidates = list.filter((it) => normKey(insightMatchRecord(it)[primaryField]) === target);
|
|
142605
|
-
if (candidates.length <= 1) return
|
|
142810
|
+
if (candidates.length <= 1) return asRecord38(candidates[0]);
|
|
142606
142811
|
const campaign = normKey(row.campaignName);
|
|
142607
142812
|
const narrowed = candidates.find(
|
|
142608
142813
|
(it) => normKey(insightMatchRecord(it).campaignName) === campaign
|
|
142609
142814
|
);
|
|
142610
|
-
return
|
|
142815
|
+
return asRecord38(narrowed ?? candidates[0]);
|
|
142611
142816
|
}
|
|
142612
142817
|
function topRowsBySpend(rows, limit) {
|
|
142613
|
-
return rows.map((r) =>
|
|
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);
|
|
142614
142819
|
}
|
|
142615
142820
|
var ROW_INSIGHT_SPECS = [
|
|
142616
142821
|
{
|
|
@@ -142625,17 +142830,17 @@ var ROW_INSIGHT_SPECS = [
|
|
|
142625
142830
|
];
|
|
142626
142831
|
function validateYandexPeriodReportContent(data) {
|
|
142627
142832
|
const missing = [];
|
|
142628
|
-
const meta =
|
|
142629
|
-
const narrative =
|
|
142630
|
-
const sections =
|
|
142631
|
-
const kpis =
|
|
142833
|
+
const meta = asRecord38(data.meta) ?? {};
|
|
142834
|
+
const narrative = asRecord38(data.narrative) ?? {};
|
|
142835
|
+
const sections = asRecord38(narrative.sections) ?? {};
|
|
142836
|
+
const kpis = asRecord38(data.kpis) ?? {};
|
|
142632
142837
|
if (!textLen7(meta.accountId)) {
|
|
142633
142838
|
pushMissing7(missing, "meta-account", "\u5143\u4FE1\u606F", "\u8D26\u6237 ID", "meta.accountId \u5FC5\u586B");
|
|
142634
142839
|
}
|
|
142635
142840
|
const sectionAnalysisCounts = {};
|
|
142636
142841
|
const sectionSuggestionCounts = {};
|
|
142637
142842
|
for (const key of SECTION_KEYS4) {
|
|
142638
|
-
const section =
|
|
142843
|
+
const section = asRecord38(sections[key]);
|
|
142639
142844
|
const analysis = asArray8(section?.analysis).filter((s) => textLen7(s) > 0);
|
|
142640
142845
|
const suggestions = asArray8(section?.suggestions).filter((s) => textLen7(s) > 0);
|
|
142641
142846
|
sectionAnalysisCounts[key] = analysis.length;
|
|
@@ -142661,7 +142866,7 @@ function validateYandexPeriodReportContent(data) {
|
|
|
142661
142866
|
}
|
|
142662
142867
|
const recommendations = asArray8(narrative.recommendations).filter((r) => {
|
|
142663
142868
|
if (typeof r === "string") return textLen7(r) > 0;
|
|
142664
|
-
const rec =
|
|
142869
|
+
const rec = asRecord38(r);
|
|
142665
142870
|
return textLen7(rec?.content) > 0 || textLen7(rec?.text) > 0;
|
|
142666
142871
|
});
|
|
142667
142872
|
if (recommendations.length < 3) {
|
|
@@ -142683,8 +142888,8 @@ function validateYandexPeriodReportContent(data) {
|
|
|
142683
142888
|
"\u81F3\u5C11\u4E00\u9879\u6709\u6548\u6570\u5B57\uFF08\u8BF7\u4F20 --snapshot-dir \u7531 CLI \u4ECE overview \u5408\u5E76\uFF09"
|
|
142684
142889
|
);
|
|
142685
142890
|
}
|
|
142686
|
-
const tables =
|
|
142687
|
-
const weeklyRows = asArray8(tables.weekly).map((r) =>
|
|
142891
|
+
const tables = asRecord38(data.tables) ?? {};
|
|
142892
|
+
const weeklyRows = asArray8(tables.weekly).map((r) => asRecord38(r)).filter((r) => r != null);
|
|
142688
142893
|
for (const [i, row] of weeklyRows.entries()) {
|
|
142689
142894
|
const label2 = `${row.weekLabel ?? ""} ${row.startDate ?? ""} ${row.endDate ?? ""}`;
|
|
142690
142895
|
if (/nan/i.test(label2) || !normalizeYandexDailyDate(row.startDate) || !normalizeYandexDailyDate(row.endDate)) {
|
|
@@ -142697,7 +142902,7 @@ function validateYandexPeriodReportContent(data) {
|
|
|
142697
142902
|
);
|
|
142698
142903
|
}
|
|
142699
142904
|
}
|
|
142700
|
-
const rowInsights =
|
|
142905
|
+
const rowInsights = asRecord38(narrative.rowInsights) ?? {};
|
|
142701
142906
|
const rowInsightCoverage = {};
|
|
142702
142907
|
for (const spec of ROW_INSIGHT_SPECS) {
|
|
142703
142908
|
const shown = topRowsBySpend(asArray8(tables[spec.tableKey]), spec.limit).filter(
|
|
@@ -142777,7 +142982,7 @@ async function readJsonFile8(filePath) {
|
|
|
142777
142982
|
throw new Error(`\u65E0\u6CD5\u89E3\u6790 JSON\uFF1A${filePath}`);
|
|
142778
142983
|
}
|
|
142779
142984
|
}
|
|
142780
|
-
function
|
|
142985
|
+
function asRecord39(value) {
|
|
142781
142986
|
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
142782
142987
|
return value;
|
|
142783
142988
|
}
|
|
@@ -142805,7 +143010,7 @@ function injectReportData7(html, payload) {
|
|
|
142805
143010
|
}
|
|
142806
143011
|
async function runYandexAnalysisRender(opts) {
|
|
142807
143012
|
const dataPath = path53.resolve(opts.dataFile);
|
|
142808
|
-
let data =
|
|
143013
|
+
let data = asRecord39(await readJsonFile8(dataPath));
|
|
142809
143014
|
if (opts.snapshotDir?.trim()) {
|
|
142810
143015
|
data = await mergeYandexAnalysisSnapshotIntoReport(data, path53.resolve(opts.snapshotDir));
|
|
142811
143016
|
}
|
|
@@ -143001,7 +143206,7 @@ function unwrapYandexItems(raw) {
|
|
|
143001
143206
|
}
|
|
143002
143207
|
return [];
|
|
143003
143208
|
}
|
|
143004
|
-
function
|
|
143209
|
+
function asRecord40(raw) {
|
|
143005
143210
|
return raw && typeof raw === "object" && !Array.isArray(raw) ? raw : {};
|
|
143006
143211
|
}
|
|
143007
143212
|
|
|
@@ -143080,7 +143285,7 @@ async function runYandexAccountStatus(opts) {
|
|
|
143080
143285
|
} catch (err) {
|
|
143081
143286
|
fail3("\u67E5\u8BE2 Yandex \u8D26\u6237\u603B\u89C8\u5931\u8D25", err);
|
|
143082
143287
|
}
|
|
143083
|
-
const row =
|
|
143288
|
+
const row = asRecord40(result.raw);
|
|
143084
143289
|
await emitOrTable(opts, {
|
|
143085
143290
|
section: "yandex-account-status",
|
|
143086
143291
|
commandLabel: "yandex-analysis account-status",
|