siluzan-tso-cli 1.1.55 → 1.1.56-beta.1
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 +265 -167
- 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 +10 -9
- package/dist/skill/report-templates/google-ads-diagnosis.md +10 -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 asRecord17(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 = asRecord17(data.narrative) ?? {};
|
|
103138
103138
|
const parts = [];
|
|
103139
|
-
const sheetAnalysis =
|
|
103139
|
+
const sheetAnalysis = asRecord17(narrative.sheetAnalysis) ?? {};
|
|
103140
103140
|
for (const block of Object.values(sheetAnalysis)) {
|
|
103141
|
-
const rec =
|
|
103141
|
+
const rec = asRecord17(block);
|
|
103142
103142
|
parts.push(...stringList(rec?.summary), ...stringList(rec?.suggestions));
|
|
103143
103143
|
}
|
|
103144
|
-
const review =
|
|
103144
|
+
const review = asRecord17(narrative.review) ?? {};
|
|
103145
103145
|
for (const block of Object.values(review)) {
|
|
103146
|
-
const rec =
|
|
103146
|
+
const rec = asRecord17(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 asRecord18(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) => asRecord18(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 asRecord18(payload.meta)?.accountId === "string" ? asRecord18(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(asRecord18(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 = asRecord18(overviewRecord?.currentPeriod);
|
|
103322
|
+
const previousPeriod = asRecord18(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 asRecord19(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 = asRecord19(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 = asRecord19(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 = asRecord19(data.narrative) ?? {};
|
|
103508
|
+
const sheetAnalysis = asRecord19(narrative.sheetAnalysis) ?? {};
|
|
103509
|
+
const review = asRecord19(narrative.review) ?? {};
|
|
103510
103510
|
const sheetAnalysisCounts = {};
|
|
103511
103511
|
for (const key of OKKI_SHEET_KEYS) {
|
|
103512
|
-
const block =
|
|
103512
|
+
const block = asRecord19(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 = asRecord19(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) => asRecord19(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) => asRecord19(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 asRecord19(payload.meta)?.accountId === "string" ? asRecord19(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(asRecord19(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 = asRecord19(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 = asRecord19(asRecord19(data.narrative)?.sheetAnalysis)?.[key];
|
|
103883
|
+
const rec = asRecord19(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 = asRecord19(asRecord19(data.narrative)?.review) ?? {};
|
|
103891
103891
|
const lines = [];
|
|
103892
103892
|
OKKI_REVIEW_KEYS.forEach((key, idx) => {
|
|
103893
|
-
const block =
|
|
103893
|
+
const block = asRecord19(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 = asRecord19(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 asRecord20(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 = asRecord20(data.meta) ?? {};
|
|
104298
|
+
const narrative = asRecord20(data.narrative) ?? {};
|
|
104299
|
+
const sections = asRecord20(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 = asRecord20(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 = asRecord20(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 = asRecord20(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 asRecord21(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 = asRecord21(await readJsonFile3(dataPath));
|
|
104466
104466
|
if (opts.snapshotDir?.trim()) {
|
|
104467
104467
|
data = await mergeGoogleAnalysisSnapshotIntoReport(data, path35.resolve(opts.snapshotDir));
|
|
104468
104468
|
}
|
|
@@ -133427,7 +133427,7 @@ function buildGoogleAdsDiagnosisCollectAgentHint(snapDir) {
|
|
|
133427
133427
|
"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
133428
|
`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
133429
|
`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",
|
|
133430
|
+
"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
133431
|
`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
133432
|
`\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
133433
|
"\u7981\u6B62\u5199\u5165/\u8986\u76D6 campaigns/geographic/keywords \u7684 items \u884C\uFF0C\u53EA\u586B analysis/suggestions\u3002",
|
|
@@ -133494,7 +133494,7 @@ function buildGoogleAdsDiagnosisAgentBrief(input) {
|
|
|
133494
133494
|
},
|
|
133495
133495
|
agentDeliverable: {
|
|
133496
133496
|
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",
|
|
133497
|
+
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
133498
|
thenRun: "siluzan-tso google-ads-diagnosis render --data ./<snap>/google-ads-diagnosis.json --out ./<snap>/google-ads-diagnosis-report.html"
|
|
133499
133499
|
}
|
|
133500
133500
|
};
|
|
@@ -134319,7 +134319,7 @@ function describeSectionShapes(reportData) {
|
|
|
134319
134319
|
function narrativeChecklist(shapes) {
|
|
134320
134320
|
const lines = [
|
|
134321
134321
|
"- `accountInfo.businessModel`\u3001`accountInfo.industry`",
|
|
134322
|
-
|
|
134322
|
+
'- `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
134323
|
"- `summary.keyIssues`\u3001`summary.optimizationRoadmap`"
|
|
134324
134324
|
];
|
|
134325
134325
|
for (const shape of shapes) {
|
|
@@ -134720,6 +134720,26 @@ function validateNewFeatureRecommendations(data) {
|
|
|
134720
134720
|
}
|
|
134721
134721
|
return warnings;
|
|
134722
134722
|
}
|
|
134723
|
+
function validateDiagnosisOverviewItems(data) {
|
|
134724
|
+
const overview = asRecord10(data.diagnosisOverview);
|
|
134725
|
+
if (!overview) return [];
|
|
134726
|
+
const warnings = [];
|
|
134727
|
+
for (const key of ["advantages", "disadvantages"]) {
|
|
134728
|
+
const items3 = overview[key];
|
|
134729
|
+
if (!Array.isArray(items3)) continue;
|
|
134730
|
+
items3.forEach((item, index) => {
|
|
134731
|
+
const row = asRecord10(item);
|
|
134732
|
+
const title = String(row?.title ?? "").trim();
|
|
134733
|
+
const description = String(row?.description ?? "").trim();
|
|
134734
|
+
if (!title || !description) {
|
|
134735
|
+
warnings.push(
|
|
134736
|
+
`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`
|
|
134737
|
+
);
|
|
134738
|
+
}
|
|
134739
|
+
});
|
|
134740
|
+
}
|
|
134741
|
+
return warnings;
|
|
134742
|
+
}
|
|
134723
134743
|
function validateAdCreativeItemSuggestions(data) {
|
|
134724
134744
|
const creative = asRecord10(data.adCreativeOptimization);
|
|
134725
134745
|
if (!creative) return [];
|
|
@@ -134752,6 +134772,7 @@ function validateGoogleAdsDiagnosisContent(data) {
|
|
|
134752
134772
|
}
|
|
134753
134773
|
}
|
|
134754
134774
|
warnings.push(
|
|
134775
|
+
...validateDiagnosisOverviewItems(data),
|
|
134755
134776
|
...validateBudgetCompetitivenessStrategies(data),
|
|
134756
134777
|
...validateNewFeatureRecommendations(data),
|
|
134757
134778
|
...validateAdCreativeItemSuggestions(data)
|
|
@@ -135063,8 +135084,75 @@ function getNarrativeConsistencyErrors(data) {
|
|
|
135063
135084
|
return errors;
|
|
135064
135085
|
}
|
|
135065
135086
|
|
|
135066
|
-
// src/commands/google-ads-diagnosis/
|
|
135087
|
+
// src/commands/google-ads-diagnosis/overview-items.ts
|
|
135067
135088
|
function asRecord13(value) {
|
|
135089
|
+
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
135090
|
+
return value;
|
|
135091
|
+
}
|
|
135092
|
+
return null;
|
|
135093
|
+
}
|
|
135094
|
+
function proseColonIndex(text2) {
|
|
135095
|
+
const cn = text2.indexOf("\uFF1A");
|
|
135096
|
+
if (cn > 0 && cn < text2.length - 1) return cn;
|
|
135097
|
+
for (let i = 1; i < text2.length - 1; i++) {
|
|
135098
|
+
if (text2[i] !== ":") continue;
|
|
135099
|
+
if (text2[i + 1] === "/") continue;
|
|
135100
|
+
const scheme = text2.slice(Math.max(0, i - 5), i);
|
|
135101
|
+
if (/https?$/i.test(scheme)) continue;
|
|
135102
|
+
return i;
|
|
135103
|
+
}
|
|
135104
|
+
return -1;
|
|
135105
|
+
}
|
|
135106
|
+
function splitOverviewProse(text2) {
|
|
135107
|
+
const trimmed = text2.trim();
|
|
135108
|
+
const cut = proseColonIndex(trimmed);
|
|
135109
|
+
if (cut > 0) {
|
|
135110
|
+
const title = trimmed.slice(0, cut).trim();
|
|
135111
|
+
const description = trimmed.slice(cut + 1).trim();
|
|
135112
|
+
if (title && description) return { title, description };
|
|
135113
|
+
}
|
|
135114
|
+
return { title: trimmed, description: trimmed };
|
|
135115
|
+
}
|
|
135116
|
+
function normalizeOverviewItem(item) {
|
|
135117
|
+
if (typeof item === "string" || typeof item === "number") {
|
|
135118
|
+
const text2 = String(item).trim();
|
|
135119
|
+
if (!text2) return null;
|
|
135120
|
+
return splitOverviewProse(text2);
|
|
135121
|
+
}
|
|
135122
|
+
const rec = asRecord13(item);
|
|
135123
|
+
if (!rec) return null;
|
|
135124
|
+
const title = String(rec.title ?? "").trim();
|
|
135125
|
+
const description = String(rec.description ?? "").trim();
|
|
135126
|
+
if (title && description) return { title, description };
|
|
135127
|
+
const prose = title || description;
|
|
135128
|
+
if (!prose) return null;
|
|
135129
|
+
const split = splitOverviewProse(prose);
|
|
135130
|
+
return {
|
|
135131
|
+
title: title || split.title,
|
|
135132
|
+
description: description || split.description
|
|
135133
|
+
};
|
|
135134
|
+
}
|
|
135135
|
+
function normalizeDiagnosisOverview(data) {
|
|
135136
|
+
const overview = asRecord13(data.diagnosisOverview);
|
|
135137
|
+
if (!overview) return { data, coerced: [] };
|
|
135138
|
+
const coerced = [];
|
|
135139
|
+
const nextOverview = { ...overview };
|
|
135140
|
+
for (const key of ["advantages", "disadvantages"]) {
|
|
135141
|
+
const items3 = overview[key];
|
|
135142
|
+
if (!Array.isArray(items3)) continue;
|
|
135143
|
+
nextOverview[key] = items3.map((item) => {
|
|
135144
|
+
if (typeof item === "string" || typeof item === "number") coerced.push(key);
|
|
135145
|
+
return normalizeOverviewItem(item) ?? { title: "", description: "" };
|
|
135146
|
+
});
|
|
135147
|
+
}
|
|
135148
|
+
return {
|
|
135149
|
+
data: { ...data, diagnosisOverview: nextOverview },
|
|
135150
|
+
coerced
|
|
135151
|
+
};
|
|
135152
|
+
}
|
|
135153
|
+
|
|
135154
|
+
// src/commands/google-ads-diagnosis/render-report.ts
|
|
135155
|
+
function asRecord14(value) {
|
|
135068
135156
|
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
135069
135157
|
return value;
|
|
135070
135158
|
}
|
|
@@ -135073,8 +135161,8 @@ function asRecord13(value) {
|
|
|
135073
135161
|
async function tryLoadCollectReportData(collectPath) {
|
|
135074
135162
|
try {
|
|
135075
135163
|
const raw = JSON.parse(await fs22.readFile(collectPath, "utf8"));
|
|
135076
|
-
const root =
|
|
135077
|
-
const reportData =
|
|
135164
|
+
const root = asRecord14(raw);
|
|
135165
|
+
const reportData = asRecord14(root?.reportData);
|
|
135078
135166
|
return reportData ?? root;
|
|
135079
135167
|
} catch {
|
|
135080
135168
|
return null;
|
|
@@ -135091,7 +135179,7 @@ async function runGoogleAdsDiagnosisRender(opts) {
|
|
|
135091
135179
|
`);
|
|
135092
135180
|
process.exit(1);
|
|
135093
135181
|
}
|
|
135094
|
-
let data =
|
|
135182
|
+
let data = asRecord14(dataRaw);
|
|
135095
135183
|
if (!opts.noMergeCollect) {
|
|
135096
135184
|
const collectPath = path26.resolve(
|
|
135097
135185
|
opts.collectFile ?? path26.join(path26.dirname(dataPath), GOOGLE_ADS_DIAGNOSIS_COLLECT_BASENAME)
|
|
@@ -135123,6 +135211,16 @@ async function runGoogleAdsDiagnosisRender(opts) {
|
|
|
135123
135211
|
}
|
|
135124
135212
|
}
|
|
135125
135213
|
}
|
|
135214
|
+
const overview = normalizeDiagnosisOverview(data);
|
|
135215
|
+
data = overview.data;
|
|
135216
|
+
if (overview.coerced.length > 0) {
|
|
135217
|
+
const fields = [...new Set(overview.coerced)].join("\u3001");
|
|
135218
|
+
console.log(
|
|
135219
|
+
`
|
|
135220
|
+
\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
|
|
135221
|
+
`
|
|
135222
|
+
);
|
|
135223
|
+
}
|
|
135126
135224
|
const narrativeErrors = getNarrativeConsistencyErrors(data);
|
|
135127
135225
|
if (narrativeErrors.length > 0) {
|
|
135128
135226
|
console.error("\n\u274C \u53D9\u4E8B\u6587\u6848\u4E0E\u4E8B\u5B9E\u5B57\u6BB5\u77DB\u76FE\uFF0C\u7981\u6B62\u6E32\u67D3\uFF1A\n");
|
|
@@ -136421,26 +136519,26 @@ import fs26 from "fs/promises";
|
|
|
136421
136519
|
import path31 from "path";
|
|
136422
136520
|
|
|
136423
136521
|
// src/commands/google-quarterly/report-content.ts
|
|
136424
|
-
function
|
|
136522
|
+
function asRecord15(value) {
|
|
136425
136523
|
return value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
136426
136524
|
}
|
|
136427
136525
|
function asStringArray(value) {
|
|
136428
136526
|
return Array.isArray(value) ? value.map((x) => String(x).trim()).filter(Boolean) : [];
|
|
136429
136527
|
}
|
|
136430
136528
|
function validateQuarterlyNarrative(data) {
|
|
136431
|
-
const root =
|
|
136432
|
-
const narrative =
|
|
136529
|
+
const root = asRecord15(data);
|
|
136530
|
+
const narrative = asRecord15(root?.narrative);
|
|
136433
136531
|
const missing = [];
|
|
136434
136532
|
if (!narrative) {
|
|
136435
136533
|
return { ok: false, missing: ["narrative"] };
|
|
136436
136534
|
}
|
|
136437
136535
|
if (asStringArray(narrative.executiveSummary).length < 1) missing.push("narrative.executiveSummary");
|
|
136438
|
-
const sections =
|
|
136536
|
+
const sections = asRecord15(narrative.sections);
|
|
136439
136537
|
for (const key of ["efficiency", "adType", "region", "summary"]) {
|
|
136440
|
-
const block =
|
|
136538
|
+
const block = asRecord15(sections?.[key]);
|
|
136441
136539
|
if (asStringArray(block?.analysis).length < 1) missing.push(`narrative.sections.${key}.analysis`);
|
|
136442
136540
|
}
|
|
136443
|
-
const plan =
|
|
136541
|
+
const plan = asRecord15(narrative.plan);
|
|
136444
136542
|
if (asStringArray(plan?.analysis ?? plan?.goals).length < 1 && !String(plan?.text ?? "").trim()) {
|
|
136445
136543
|
missing.push("narrative.plan");
|
|
136446
136544
|
}
|
|
@@ -136453,8 +136551,8 @@ function mergeCollectFacts(agentData, collect) {
|
|
|
136453
136551
|
...facts,
|
|
136454
136552
|
narrative: agentData.narrative ?? collect.narrative,
|
|
136455
136553
|
meta: {
|
|
136456
|
-
...
|
|
136457
|
-
...
|
|
136554
|
+
...asRecord15(collect.meta) ?? {},
|
|
136555
|
+
...asRecord15(agentData.meta) ?? {}
|
|
136458
136556
|
}
|
|
136459
136557
|
};
|
|
136460
136558
|
}
|
|
@@ -136466,7 +136564,7 @@ import { fileURLToPath as fileURLToPath8 } from "url";
|
|
|
136466
136564
|
var TSO_CLI_ROOT = path30.join(path30.dirname(fileURLToPath8(import.meta.url)), "../..");
|
|
136467
136565
|
|
|
136468
136566
|
// src/commands/google-quarterly/render-report.ts
|
|
136469
|
-
function
|
|
136567
|
+
function asRecord16(value) {
|
|
136470
136568
|
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
136471
136569
|
return value;
|
|
136472
136570
|
}
|
|
@@ -136495,7 +136593,7 @@ async function runGoogleQuarterlyRender(opts) {
|
|
|
136495
136593
|
`);
|
|
136496
136594
|
process.exit(1);
|
|
136497
136595
|
}
|
|
136498
|
-
const merged = mergeCollectFacts(
|
|
136596
|
+
const merged = mergeCollectFacts(asRecord16(agentRaw), asRecord16(collectRaw));
|
|
136499
136597
|
const check = validateQuarterlyNarrative(merged);
|
|
136500
136598
|
if (!check.ok && !opts.lenient) {
|
|
136501
136599
|
console.error("\n\u274C narrative \u4E0D\u5B8C\u6574\uFF0C\u62D2\u7EDD\u51FA HTML\uFF1A\n");
|
|
@@ -136659,7 +136757,7 @@ import * as fs33 from "fs/promises";
|
|
|
136659
136757
|
import * as path40 from "path";
|
|
136660
136758
|
|
|
136661
136759
|
// src/commands/facebook-analysis/report-metrics.ts
|
|
136662
|
-
function
|
|
136760
|
+
function asRecord22(value) {
|
|
136663
136761
|
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
136664
136762
|
return value;
|
|
136665
136763
|
}
|
|
@@ -136796,7 +136894,7 @@ function localizeFacebookReportLabels(payload) {
|
|
|
136796
136894
|
for (const rows of Object.values(tables)) {
|
|
136797
136895
|
if (!Array.isArray(rows)) continue;
|
|
136798
136896
|
for (const row of rows) {
|
|
136799
|
-
localizeMetricLabels(
|
|
136897
|
+
localizeMetricLabels(asRecord22(row));
|
|
136800
136898
|
}
|
|
136801
136899
|
}
|
|
136802
136900
|
return payload;
|
|
@@ -136829,7 +136927,7 @@ async function readManifest3(snapshotDir) {
|
|
|
136829
136927
|
async function loadSectionFile3(snapshotDir, file) {
|
|
136830
136928
|
try {
|
|
136831
136929
|
const raw = await fs33.readFile(path40.join(snapshotDir, file), "utf8");
|
|
136832
|
-
return
|
|
136930
|
+
return asRecord22(JSON.parse(raw));
|
|
136833
136931
|
} catch {
|
|
136834
136932
|
return null;
|
|
136835
136933
|
}
|
|
@@ -136885,7 +136983,7 @@ async function mergeFacebookSnapshotIntoReport(payload, snapshotDir) {
|
|
|
136885
136983
|
if (data) sectionMap.set(art.section, data);
|
|
136886
136984
|
}
|
|
136887
136985
|
const overview = sectionMap.get("overview");
|
|
136888
|
-
const current =
|
|
136986
|
+
const current = asRecord22(overview?.currentPeriod);
|
|
136889
136987
|
const accountName = typeof overview?.accountName === "string" ? overview.accountName : void 0;
|
|
136890
136988
|
const accountId = manifest.accountId ?? (typeof overview?.accountId === "string" ? overview.accountId : void 0);
|
|
136891
136989
|
const kpis = { ...payload.kpis ?? {} };
|
|
@@ -137022,7 +137120,7 @@ function writeSimpleXlsx(filePath, sheets, opts = {}) {
|
|
|
137022
137120
|
}
|
|
137023
137121
|
|
|
137024
137122
|
// src/commands/facebook-analysis/render-excel.ts
|
|
137025
|
-
function
|
|
137123
|
+
function asRecord23(value) {
|
|
137026
137124
|
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
137027
137125
|
return value;
|
|
137028
137126
|
}
|
|
@@ -137070,8 +137168,8 @@ function bullets(value) {
|
|
|
137070
137168
|
return asArray3(value).map((item) => typeof item === "string" ? item.trim() : "").filter(Boolean);
|
|
137071
137169
|
}
|
|
137072
137170
|
function insightLines(section) {
|
|
137073
|
-
const rec =
|
|
137074
|
-
const insight =
|
|
137171
|
+
const rec = asRecord23(section);
|
|
137172
|
+
const insight = asRecord23(rec?.insight) ?? rec;
|
|
137075
137173
|
return [...bullets(insight?.analysis), ...bullets(insight?.advice)];
|
|
137076
137174
|
}
|
|
137077
137175
|
function numberedSection(index, title, lines) {
|
|
@@ -137080,15 +137178,15 @@ function numberedSection(index, title, lines) {
|
|
|
137080
137178
|
${body}`;
|
|
137081
137179
|
}
|
|
137082
137180
|
function buildSummaryNarrative(data) {
|
|
137083
|
-
const kpis =
|
|
137084
|
-
const meta =
|
|
137085
|
-
const sections =
|
|
137181
|
+
const kpis = asRecord23(data.kpis) ?? {};
|
|
137182
|
+
const meta = asRecord23(data.meta) ?? {};
|
|
137183
|
+
const sections = asRecord23(data.sections) ?? {};
|
|
137086
137184
|
const period = str2(meta.periodLabel) || `${str2(meta.startDate)} ~ ${str2(meta.endDate)}`.trim();
|
|
137087
137185
|
const account = str2(meta.accountName) || "Facebook \u5E7F\u544A\u8D26\u6237";
|
|
137088
137186
|
const campaign = str2(meta.campaignName);
|
|
137089
137187
|
const resultLabel = displayResultType(kpis.resultType ?? meta.resultType) || "\u6210\u6548";
|
|
137090
137188
|
const resultNoun = /潜在客户|lead/i.test(resultLabel) ? "\u4E2A\u6F5C\u5728\u5BA2\u6237\u8868\u5355" : `\u4E2A${resultLabel}`;
|
|
137091
|
-
const firstQuestion =
|
|
137189
|
+
const firstQuestion = asRecord23(asArray3(data.fourQuestions)[0]);
|
|
137092
137190
|
const extra = str2(firstQuestion?.verdict);
|
|
137093
137191
|
const spendLine = [
|
|
137094
137192
|
campaign ? `\uFF08\u5E7F\u544A\u7CFB\u5217\uFF1A${campaign}\uFF09` : "",
|
|
@@ -137108,7 +137206,7 @@ function buildSummaryNarrative(data) {
|
|
|
137108
137206
|
numberedSection(3, "\u5E7F\u544A\u9891\u6B21\u4E0E\u65E5\u8D8B\u52BF", insightLines(sections.daily))
|
|
137109
137207
|
].join("\n");
|
|
137110
137208
|
const recBlocks = asArray3(data.recommendations).map((raw, i) => {
|
|
137111
|
-
const rec =
|
|
137209
|
+
const rec = asRecord23(raw);
|
|
137112
137210
|
if (!rec) return "";
|
|
137113
137211
|
const title = [str2(rec.title), str2(rec.tag)].filter(Boolean).join(" \xB7 ");
|
|
137114
137212
|
return numberedSection(i + 1, title || "\u4F18\u5316\u5EFA\u8BAE", bullets(rec.items));
|
|
@@ -137119,9 +137217,9 @@ function buildSummaryNarrative(data) {
|
|
|
137119
137217
|
return [partOne, partTwo, partThree];
|
|
137120
137218
|
}
|
|
137121
137219
|
function buildMetaPeriodExcelSheets(data) {
|
|
137122
|
-
const meta =
|
|
137123
|
-
const kpis =
|
|
137124
|
-
const tables =
|
|
137220
|
+
const meta = asRecord23(data.meta) ?? {};
|
|
137221
|
+
const kpis = asRecord23(data.kpis) ?? {};
|
|
137222
|
+
const tables = asRecord23(data.tables) ?? {};
|
|
137125
137223
|
const currency = str2(meta.currency) || "USD";
|
|
137126
137224
|
const resultType = displayResultType(kpis.resultType ?? meta.resultType);
|
|
137127
137225
|
const attribution = displayAttribution(kpis.attributionSetting ?? meta.attributionSetting);
|
|
@@ -137140,19 +137238,19 @@ function buildMetaPeriodExcelSheets(data) {
|
|
|
137140
137238
|
];
|
|
137141
137239
|
const dailyRows = [metricHeaders(currency, "\u5355\u65E5")];
|
|
137142
137240
|
for (const raw of asArray3(tables.daily)) {
|
|
137143
|
-
const row =
|
|
137241
|
+
const row = asRecord23(raw);
|
|
137144
137242
|
if (!row) continue;
|
|
137145
137243
|
dailyRows.push([normalizeInsightDate(row.date), ...metricCells(row)]);
|
|
137146
137244
|
}
|
|
137147
137245
|
const countryRows = [metricHeaders(currency, "\u56FD\u5BB6/\u5730\u533A")];
|
|
137148
137246
|
for (const raw of asArray3(tables.countries)) {
|
|
137149
|
-
const row =
|
|
137247
|
+
const row = asRecord23(raw);
|
|
137150
137248
|
if (!row) continue;
|
|
137151
137249
|
countryRows.push([str2(row.countryOrRegion), ...metricCells(row)]);
|
|
137152
137250
|
}
|
|
137153
137251
|
const campaignRows3 = [metricHeaders(currency, "\u5E7F\u544A\u7CFB\u5217\u540D\u79F0")];
|
|
137154
137252
|
for (const raw of asArray3(tables.campaigns)) {
|
|
137155
|
-
const row =
|
|
137253
|
+
const row = asRecord23(raw);
|
|
137156
137254
|
if (!row) continue;
|
|
137157
137255
|
campaignRows3.push([str2(row.campaignName), ...metricCells(row)]);
|
|
137158
137256
|
}
|
|
@@ -137171,7 +137269,7 @@ function buildMetaPeriodExcelSheets(data) {
|
|
|
137171
137269
|
]
|
|
137172
137270
|
];
|
|
137173
137271
|
for (const raw of asArray3(tables.audiences)) {
|
|
137174
|
-
const row =
|
|
137272
|
+
const row = asRecord23(raw);
|
|
137175
137273
|
if (!row) continue;
|
|
137176
137274
|
audienceRows.push([str2(row.age), str2(row.gender), ...metricCells(row)]);
|
|
137177
137275
|
}
|
|
@@ -137203,7 +137301,7 @@ function isFiniteNumber(value) {
|
|
|
137203
137301
|
function asArray4(value) {
|
|
137204
137302
|
return Array.isArray(value) ? value : [];
|
|
137205
137303
|
}
|
|
137206
|
-
function
|
|
137304
|
+
function asRecord24(value) {
|
|
137207
137305
|
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
137208
137306
|
return value;
|
|
137209
137307
|
}
|
|
@@ -137216,8 +137314,8 @@ function stringList3(value) {
|
|
|
137216
137314
|
return asArray4(value).map((item) => typeof item === "string" ? item.trim() : "").filter(Boolean);
|
|
137217
137315
|
}
|
|
137218
137316
|
function insightLists(section) {
|
|
137219
|
-
const rec =
|
|
137220
|
-
const insight =
|
|
137317
|
+
const rec = asRecord24(section);
|
|
137318
|
+
const insight = asRecord24(rec?.insight) ?? rec;
|
|
137221
137319
|
return {
|
|
137222
137320
|
analysis: stringList3(insight?.analysis),
|
|
137223
137321
|
advice: stringList3(insight?.advice)
|
|
@@ -137247,11 +137345,11 @@ function requireInsight(missing, key, chapter, section, required) {
|
|
|
137247
137345
|
}
|
|
137248
137346
|
function validateMetaPeriodReportContent(data) {
|
|
137249
137347
|
const missing = [];
|
|
137250
|
-
const meta =
|
|
137251
|
-
const kpis =
|
|
137252
|
-
const tables =
|
|
137253
|
-
const charts =
|
|
137254
|
-
const sections =
|
|
137348
|
+
const meta = asRecord24(data.meta) ?? {};
|
|
137349
|
+
const kpis = asRecord24(data.kpis) ?? {};
|
|
137350
|
+
const tables = asRecord24(data.tables) ?? {};
|
|
137351
|
+
const charts = asRecord24(data.charts) ?? {};
|
|
137352
|
+
const sections = asRecord24(data.sections) ?? {};
|
|
137255
137353
|
const fourQuestions = asArray4(data.fourQuestions);
|
|
137256
137354
|
const recommendations = asArray4(data.recommendations);
|
|
137257
137355
|
if (!textLen3(meta.accountName) && !textLen3(meta.accountId)) {
|
|
@@ -137330,7 +137428,7 @@ function validateMetaPeriodReportContent(data) {
|
|
|
137330
137428
|
"\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
137429
|
);
|
|
137332
137430
|
}
|
|
137333
|
-
const dailyChart =
|
|
137431
|
+
const dailyChart = asRecord24(charts.daily);
|
|
137334
137432
|
if (!Array.isArray(dailyChart?.labels)) {
|
|
137335
137433
|
pushMissing3(
|
|
137336
137434
|
missing,
|
|
@@ -137340,7 +137438,7 @@ function validateMetaPeriodReportContent(data) {
|
|
|
137340
137438
|
"\u987B\u6709 charts.daily.labels \u6570\u7EC4\uFF1B\u53EF\u7531 --snapshot-dir \u5408\u5E76 daily\u3002\u672C\u671F\u65E0\u6D88\u8017\u53EF\u4E3A []"
|
|
137341
137439
|
);
|
|
137342
137440
|
}
|
|
137343
|
-
const countryChart =
|
|
137441
|
+
const countryChart = asRecord24(charts.country);
|
|
137344
137442
|
if (!Array.isArray(countryChart?.labels)) {
|
|
137345
137443
|
pushMissing3(
|
|
137346
137444
|
missing,
|
|
@@ -137350,7 +137448,7 @@ function validateMetaPeriodReportContent(data) {
|
|
|
137350
137448
|
"\u987B\u6709 charts.country.labels \u6570\u7EC4\uFF1B\u53EF\u7531 --snapshot-dir \u5408\u5E76 country\u3002\u672C\u671F\u65E0\u6D88\u8017\u53EF\u4E3A []"
|
|
137351
137449
|
);
|
|
137352
137450
|
}
|
|
137353
|
-
const audienceChart =
|
|
137451
|
+
const audienceChart = asRecord24(charts.audience);
|
|
137354
137452
|
if (!Array.isArray(audienceChart?.labels)) {
|
|
137355
137453
|
pushMissing3(
|
|
137356
137454
|
missing,
|
|
@@ -137369,7 +137467,7 @@ function validateMetaPeriodReportContent(data) {
|
|
|
137369
137467
|
"\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
137468
|
);
|
|
137371
137469
|
} else {
|
|
137372
|
-
const titles = fourQuestions.map((q) =>
|
|
137470
|
+
const titles = fourQuestions.map((q) => asRecord24(q)?.title);
|
|
137373
137471
|
for (const title of FOUR_QUESTION_TITLES) {
|
|
137374
137472
|
if (!titles.includes(title)) {
|
|
137375
137473
|
pushMissing3(
|
|
@@ -137382,7 +137480,7 @@ function validateMetaPeriodReportContent(data) {
|
|
|
137382
137480
|
}
|
|
137383
137481
|
}
|
|
137384
137482
|
for (let i = 0; i < fourQuestions.length; i++) {
|
|
137385
|
-
const q =
|
|
137483
|
+
const q = asRecord24(fourQuestions[i]);
|
|
137386
137484
|
if (textLen3(q?.verdict) < 8) {
|
|
137387
137485
|
pushMissing3(
|
|
137388
137486
|
missing,
|
|
@@ -137422,7 +137520,7 @@ function validateMetaPeriodReportContent(data) {
|
|
|
137422
137520
|
);
|
|
137423
137521
|
} else {
|
|
137424
137522
|
for (let i = 0; i < recommendations.length; i++) {
|
|
137425
|
-
const rec =
|
|
137523
|
+
const rec = asRecord24(recommendations[i]);
|
|
137426
137524
|
if (textLen3(rec?.title) < 2) {
|
|
137427
137525
|
pushMissing3(
|
|
137428
137526
|
missing,
|
|
@@ -137515,7 +137613,7 @@ async function readJsonFile4(filePath) {
|
|
|
137515
137613
|
throw new Error(`\u65E0\u6CD5\u89E3\u6790 JSON\uFF1A${filePath}`);
|
|
137516
137614
|
}
|
|
137517
137615
|
}
|
|
137518
|
-
function
|
|
137616
|
+
function asRecord25(value) {
|
|
137519
137617
|
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
137520
137618
|
return value;
|
|
137521
137619
|
}
|
|
@@ -137544,7 +137642,7 @@ function defaultOutPath(dataPath, format, out) {
|
|
|
137544
137642
|
}
|
|
137545
137643
|
async function runFacebookAnalysisRender(opts) {
|
|
137546
137644
|
const dataPath = path41.resolve(opts.dataFile);
|
|
137547
|
-
let data =
|
|
137645
|
+
let data = asRecord25(await readJsonFile4(dataPath));
|
|
137548
137646
|
if (opts.snapshotDir?.trim()) {
|
|
137549
137647
|
data = await mergeFacebookSnapshotIntoReport(data, path41.resolve(opts.snapshotDir));
|
|
137550
137648
|
}
|
|
@@ -137608,7 +137706,7 @@ import { fileURLToPath as fileURLToPath11 } from "url";
|
|
|
137608
137706
|
// src/commands/facebook-analysis/diagnosis-merge-snapshot.ts
|
|
137609
137707
|
import * as fs35 from "fs/promises";
|
|
137610
137708
|
import * as path42 from "path";
|
|
137611
|
-
function
|
|
137709
|
+
function asRecord26(value) {
|
|
137612
137710
|
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
137613
137711
|
return value;
|
|
137614
137712
|
}
|
|
@@ -137619,7 +137717,7 @@ function num8(value) {
|
|
|
137619
137717
|
return Number.isFinite(n) ? n : void 0;
|
|
137620
137718
|
}
|
|
137621
137719
|
function asRows(value) {
|
|
137622
|
-
return Array.isArray(value) ? value.filter((r) =>
|
|
137720
|
+
return Array.isArray(value) ? value.filter((r) => asRecord26(r) !== null) : [];
|
|
137623
137721
|
}
|
|
137624
137722
|
function pickMetricExtras(row, extras) {
|
|
137625
137723
|
const out = {
|
|
@@ -137668,14 +137766,14 @@ async function readManifest4(snapshotDir, accountId) {
|
|
|
137668
137766
|
async function loadSectionFile4(snapshotDir, file) {
|
|
137669
137767
|
try {
|
|
137670
137768
|
const raw = await fs35.readFile(path42.join(snapshotDir, file), "utf8");
|
|
137671
|
-
return
|
|
137769
|
+
return asRecord26(JSON.parse(raw));
|
|
137672
137770
|
} catch {
|
|
137673
137771
|
return null;
|
|
137674
137772
|
}
|
|
137675
137773
|
}
|
|
137676
137774
|
async function mergeMetaDiagnosisSnapshotIntoReport(payload, snapshotDir) {
|
|
137677
137775
|
const absDir = path42.resolve(snapshotDir);
|
|
137678
|
-
const metaAccountId = typeof
|
|
137776
|
+
const metaAccountId = typeof asRecord26(payload.meta)?.accountId === "string" ? asRecord26(payload.meta)?.accountId : void 0;
|
|
137679
137777
|
const manifest = await readManifest4(snapshotDir, metaAccountId);
|
|
137680
137778
|
if (!manifest) {
|
|
137681
137779
|
throw new Error(
|
|
@@ -137688,8 +137786,8 @@ async function mergeMetaDiagnosisSnapshotIntoReport(payload, snapshotDir) {
|
|
|
137688
137786
|
if (data) sectionMap.set(art.section, data);
|
|
137689
137787
|
}
|
|
137690
137788
|
const overview = sectionMap.get("overview");
|
|
137691
|
-
const currentPeriod =
|
|
137692
|
-
const previousPeriod =
|
|
137789
|
+
const currentPeriod = asRecord26(overview?.currentPeriod);
|
|
137790
|
+
const previousPeriod = asRecord26(overview?.previousPeriod);
|
|
137693
137791
|
const accountId = manifest.accountId ?? metaAccountId;
|
|
137694
137792
|
const meta = { ...payload.meta ?? {} };
|
|
137695
137793
|
if (!meta.accountId && accountId) meta.accountId = accountId;
|
|
@@ -137823,7 +137921,7 @@ function textLen4(value) {
|
|
|
137823
137921
|
function asArray5(value) {
|
|
137824
137922
|
return Array.isArray(value) ? value : [];
|
|
137825
137923
|
}
|
|
137826
|
-
function
|
|
137924
|
+
function asRecord27(value) {
|
|
137827
137925
|
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
137828
137926
|
return value;
|
|
137829
137927
|
}
|
|
@@ -137834,9 +137932,9 @@ function pushMissing4(missing, id, chapter, dimension, hint) {
|
|
|
137834
137932
|
}
|
|
137835
137933
|
function validateMetaDiagnosisReportContent(data) {
|
|
137836
137934
|
const missing = [];
|
|
137837
|
-
const meta =
|
|
137838
|
-
const narrative =
|
|
137839
|
-
const sections =
|
|
137935
|
+
const meta = asRecord27(data.meta) ?? {};
|
|
137936
|
+
const narrative = asRecord27(data.narrative) ?? {};
|
|
137937
|
+
const sections = asRecord27(narrative.sections) ?? {};
|
|
137840
137938
|
if (!textLen4(meta.accountId)) {
|
|
137841
137939
|
pushMissing4(missing, "meta-account", "\u5143\u4FE1\u606F", "\u8D26\u6237 ID", "meta.accountId \u5FC5\u586B");
|
|
137842
137940
|
}
|
|
@@ -137854,7 +137952,7 @@ function validateMetaDiagnosisReportContent(data) {
|
|
|
137854
137952
|
const sectionSuggestionCounts = {};
|
|
137855
137953
|
const sectionNotAvailable = {};
|
|
137856
137954
|
for (const key of NORMAL_SECTION_KEYS) {
|
|
137857
|
-
const section =
|
|
137955
|
+
const section = asRecord27(sections[key]);
|
|
137858
137956
|
const analysis = asArray5(section?.analysis).filter((s) => textLen4(s) > 0);
|
|
137859
137957
|
const suggestions = asArray5(section?.suggestions).filter((s) => textLen4(s) > 0);
|
|
137860
137958
|
sectionAnalysisCounts[key] = analysis.length;
|
|
@@ -137880,7 +137978,7 @@ function validateMetaDiagnosisReportContent(data) {
|
|
|
137880
137978
|
}
|
|
137881
137979
|
}
|
|
137882
137980
|
for (const key of NA_SECTION_KEYS) {
|
|
137883
|
-
const section =
|
|
137981
|
+
const section = asRecord27(sections[key]);
|
|
137884
137982
|
const analysis = asArray5(section?.analysis).filter((s) => textLen4(s) > 0);
|
|
137885
137983
|
const suggestions = asArray5(section?.suggestions).filter((s) => textLen4(s) > 0);
|
|
137886
137984
|
const notAvailable = section?.notAvailable === true;
|
|
@@ -137902,7 +138000,7 @@ function validateMetaDiagnosisReportContent(data) {
|
|
|
137902
138000
|
}
|
|
137903
138001
|
const recommendations = asArray5(narrative.recommendations).filter((r) => {
|
|
137904
138002
|
if (typeof r === "string") return textLen4(r) > 0;
|
|
137905
|
-
const rec =
|
|
138003
|
+
const rec = asRecord27(r);
|
|
137906
138004
|
return textLen4(rec?.content) > 0;
|
|
137907
138005
|
});
|
|
137908
138006
|
if (recommendations.length < 3) {
|
|
@@ -137975,7 +138073,7 @@ async function readJsonFile5(filePath) {
|
|
|
137975
138073
|
throw new Error(`\u65E0\u6CD5\u89E3\u6790 JSON\uFF1A${filePath}`);
|
|
137976
138074
|
}
|
|
137977
138075
|
}
|
|
137978
|
-
function
|
|
138076
|
+
function asRecord28(value) {
|
|
137979
138077
|
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
137980
138078
|
return value;
|
|
137981
138079
|
}
|
|
@@ -137990,7 +138088,7 @@ function injectReportData4(html, payload) {
|
|
|
137990
138088
|
}
|
|
137991
138089
|
async function runFacebookAnalysisDiagnosisRender(opts) {
|
|
137992
138090
|
const dataPath = path43.resolve(opts.dataFile);
|
|
137993
|
-
let data =
|
|
138091
|
+
let data = asRecord28(await readJsonFile5(dataPath));
|
|
137994
138092
|
if (opts.snapshotDir?.trim()) {
|
|
137995
138093
|
data = await mergeMetaDiagnosisSnapshotIntoReport(data, path43.resolve(opts.snapshotDir));
|
|
137996
138094
|
}
|
|
@@ -138189,7 +138287,7 @@ function resolveFacebookPageSize(raw, fallback = FB_ENTITY_PAGE_SIZE_DEFAULT) {
|
|
|
138189
138287
|
if (!Number.isFinite(n) || n <= 0) return fallback;
|
|
138190
138288
|
return Math.min(Math.max(Math.floor(n), 1), FB_ENTITY_PAGE_SIZE_MAX);
|
|
138191
138289
|
}
|
|
138192
|
-
function
|
|
138290
|
+
function asRecord29(value) {
|
|
138193
138291
|
return value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
|
|
138194
138292
|
}
|
|
138195
138293
|
function pickProp(obj, ...keys) {
|
|
@@ -138199,12 +138297,12 @@ function pickProp(obj, ...keys) {
|
|
|
138199
138297
|
return void 0;
|
|
138200
138298
|
}
|
|
138201
138299
|
function unwrapFacebookPaged(raw) {
|
|
138202
|
-
const rec =
|
|
138300
|
+
const rec = asRecord29(raw);
|
|
138203
138301
|
if (!rec) return { items: [] };
|
|
138204
138302
|
const data = pickProp(rec, "data", "Data");
|
|
138205
138303
|
const items3 = Array.isArray(data) ? data : [];
|
|
138206
|
-
const paging =
|
|
138207
|
-
const cursors = paging ?
|
|
138304
|
+
const paging = asRecord29(pickProp(rec, "paging", "Paging"));
|
|
138305
|
+
const cursors = paging ? asRecord29(pickProp(paging, "cursors", "Cursors")) : void 0;
|
|
138208
138306
|
const afterRaw = cursors ? pickProp(cursors, "after", "After") : void 0;
|
|
138209
138307
|
const after = typeof afterRaw === "string" && afterRaw.trim() ? afterRaw.trim() : void 0;
|
|
138210
138308
|
return { items: items3, after };
|
|
@@ -138352,7 +138450,7 @@ async function fetchFacebookInsights(config, digits, level, opts) {
|
|
|
138352
138450
|
opts.verbose
|
|
138353
138451
|
);
|
|
138354
138452
|
if (Array.isArray(raw)) return raw;
|
|
138355
|
-
const rec =
|
|
138453
|
+
const rec = asRecord29(raw);
|
|
138356
138454
|
const data = rec ? pickProp(rec, "data", "Data") : void 0;
|
|
138357
138455
|
return Array.isArray(data) ? data : [];
|
|
138358
138456
|
}
|
|
@@ -138361,7 +138459,7 @@ function sumActionArray(raw) {
|
|
|
138361
138459
|
let sum = 0;
|
|
138362
138460
|
let hit = false;
|
|
138363
138461
|
for (const item of raw) {
|
|
138364
|
-
const rec =
|
|
138462
|
+
const rec = asRecord29(item);
|
|
138365
138463
|
if (!rec) continue;
|
|
138366
138464
|
const n = Number(rec.value);
|
|
138367
138465
|
if (!Number.isFinite(n)) continue;
|
|
@@ -139640,7 +139738,7 @@ import { fileURLToPath as fileURLToPath12 } from "url";
|
|
|
139640
139738
|
// src/commands/bing-analysis/merge-snapshot.ts
|
|
139641
139739
|
import * as fs37 from "fs/promises";
|
|
139642
139740
|
import * as path46 from "path";
|
|
139643
|
-
function
|
|
139741
|
+
function asRecord30(value) {
|
|
139644
139742
|
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
139645
139743
|
return value;
|
|
139646
139744
|
}
|
|
@@ -139655,7 +139753,7 @@ function round27(value) {
|
|
|
139655
139753
|
}
|
|
139656
139754
|
function asRows2(value) {
|
|
139657
139755
|
if (Array.isArray(value)) {
|
|
139658
|
-
return value.filter((r) =>
|
|
139756
|
+
return value.filter((r) => asRecord30(r) !== null);
|
|
139659
139757
|
}
|
|
139660
139758
|
return [];
|
|
139661
139759
|
}
|
|
@@ -139737,17 +139835,17 @@ function campaignStatusDisplay(status) {
|
|
|
139737
139835
|
return status == null || status === "" ? "\u2014" : String(status);
|
|
139738
139836
|
}
|
|
139739
139837
|
function mapDeviceRows(payload) {
|
|
139740
|
-
const obj =
|
|
139838
|
+
const obj = asRecord30(payload);
|
|
139741
139839
|
return asRows2(obj?.devices).map((r) => pickMetrics(r, ["deviceType"]));
|
|
139742
139840
|
}
|
|
139743
139841
|
function mapGeographicRows(payload) {
|
|
139744
|
-
const obj =
|
|
139842
|
+
const obj = asRecord30(payload);
|
|
139745
139843
|
return asRows2(obj?.countries).map(
|
|
139746
139844
|
(r) => pickMetrics(r, ["countryOrRegion", "countryCriteriaId", "regionName", "cityName"])
|
|
139747
139845
|
);
|
|
139748
139846
|
}
|
|
139749
139847
|
function mapAudienceLabelRows(payload, labelKey) {
|
|
139750
|
-
const obj =
|
|
139848
|
+
const obj = asRecord30(payload);
|
|
139751
139849
|
return asRows2(obj?.audience).map((r) => {
|
|
139752
139850
|
const m = pickMetrics(r, ["audience", "bidModifier"]);
|
|
139753
139851
|
m[labelKey] = r.audience;
|
|
@@ -139755,7 +139853,7 @@ function mapAudienceLabelRows(payload, labelKey) {
|
|
|
139755
139853
|
});
|
|
139756
139854
|
}
|
|
139757
139855
|
function mapAudienceFromMerged(payload) {
|
|
139758
|
-
const data =
|
|
139856
|
+
const data = asRecord30(asRecord30(payload)?.data);
|
|
139759
139857
|
return {
|
|
139760
139858
|
audienceAge: mapAudienceLabelRows(data?.ageAudience, "ageRange"),
|
|
139761
139859
|
audienceGender: mapAudienceLabelRows(data?.genderAudience, "gender")
|
|
@@ -139850,7 +139948,7 @@ function mapSearchTermRows(payload) {
|
|
|
139850
139948
|
}
|
|
139851
139949
|
async function mergeBingAnalysisSnapshotIntoReport(payload, snapshotDir) {
|
|
139852
139950
|
const absDir = path46.resolve(snapshotDir);
|
|
139853
|
-
const metaAccountId = typeof
|
|
139951
|
+
const metaAccountId = typeof asRecord30(payload.meta)?.accountId === "string" ? asRecord30(payload.meta)?.accountId : void 0;
|
|
139854
139952
|
const manifest = await readManifest5(snapshotDir, metaAccountId);
|
|
139855
139953
|
if (!manifest) {
|
|
139856
139954
|
throw new Error(`\u672A\u5728 ${absDir} \u627E\u5230 report-manifest*.json\uFF0C\u8BF7\u5148\u6267\u884C bing-analysis --json-out`);
|
|
@@ -139860,9 +139958,9 @@ async function mergeBingAnalysisSnapshotIntoReport(payload, snapshotDir) {
|
|
|
139860
139958
|
const data = await loadSectionPayload(absDir, art.file);
|
|
139861
139959
|
if (data != null) sectionMap.set(art.section, data);
|
|
139862
139960
|
}
|
|
139863
|
-
const overview =
|
|
139864
|
-
const currentPeriod =
|
|
139865
|
-
const previousPeriod =
|
|
139961
|
+
const overview = asRecord30(sectionMap.get("overview"));
|
|
139962
|
+
const currentPeriod = asRecord30(overview?.currentPeriod);
|
|
139963
|
+
const previousPeriod = asRecord30(overview?.previousPeriod);
|
|
139866
139964
|
const accountId = manifest.accountId ?? metaAccountId;
|
|
139867
139965
|
const campaignRows3 = mapCampaignRows2(sectionMap.get("campaigns"));
|
|
139868
139966
|
const campaignKpis = sumBingCampaignRowsToPeriodKpis(campaignRows3);
|
|
@@ -139989,7 +140087,7 @@ function textLen5(value) {
|
|
|
139989
140087
|
function asArray6(value) {
|
|
139990
140088
|
return Array.isArray(value) ? value : [];
|
|
139991
140089
|
}
|
|
139992
|
-
function
|
|
140090
|
+
function asRecord31(value) {
|
|
139993
140091
|
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
139994
140092
|
return value;
|
|
139995
140093
|
}
|
|
@@ -140000,9 +140098,9 @@ function pushMissing5(missing, id, chapter, dimension, hint) {
|
|
|
140000
140098
|
}
|
|
140001
140099
|
function validateBingPeriodReportContent(data) {
|
|
140002
140100
|
const missing = [];
|
|
140003
|
-
const meta =
|
|
140004
|
-
const narrative =
|
|
140005
|
-
const sections =
|
|
140101
|
+
const meta = asRecord31(data.meta) ?? {};
|
|
140102
|
+
const narrative = asRecord31(data.narrative) ?? {};
|
|
140103
|
+
const sections = asRecord31(narrative.sections) ?? {};
|
|
140006
140104
|
if (!textLen5(meta.accountId)) {
|
|
140007
140105
|
pushMissing5(missing, "meta-account", "\u5143\u4FE1\u606F", "\u8D26\u6237 ID", "meta.accountId \u5FC5\u586B");
|
|
140008
140106
|
}
|
|
@@ -140019,7 +140117,7 @@ function validateBingPeriodReportContent(data) {
|
|
|
140019
140117
|
const sectionAnalysisCounts = {};
|
|
140020
140118
|
const sectionSuggestionCounts = {};
|
|
140021
140119
|
for (const key of SECTION_KEYS2) {
|
|
140022
|
-
const section =
|
|
140120
|
+
const section = asRecord31(sections[key]);
|
|
140023
140121
|
const analysis = asArray6(section?.analysis).filter((s) => textLen5(s) > 0);
|
|
140024
140122
|
const suggestions = asArray6(section?.suggestions).filter((s) => textLen5(s) > 0);
|
|
140025
140123
|
sectionAnalysisCounts[key] = analysis.length;
|
|
@@ -140045,7 +140143,7 @@ function validateBingPeriodReportContent(data) {
|
|
|
140045
140143
|
}
|
|
140046
140144
|
const recommendations = asArray6(narrative.recommendations).filter((r) => {
|
|
140047
140145
|
if (typeof r === "string") return textLen5(r) > 0;
|
|
140048
|
-
const rec =
|
|
140146
|
+
const rec = asRecord31(r);
|
|
140049
140147
|
return textLen5(rec?.content) > 0;
|
|
140050
140148
|
});
|
|
140051
140149
|
if (recommendations.length < 3) {
|
|
@@ -140106,7 +140204,7 @@ async function readJsonFile6(filePath) {
|
|
|
140106
140204
|
throw new Error(`\u65E0\u6CD5\u89E3\u6790 JSON\uFF1A${filePath}`);
|
|
140107
140205
|
}
|
|
140108
140206
|
}
|
|
140109
|
-
function
|
|
140207
|
+
function asRecord32(value) {
|
|
140110
140208
|
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
140111
140209
|
return value;
|
|
140112
140210
|
}
|
|
@@ -140121,7 +140219,7 @@ function injectReportData5(html, payload) {
|
|
|
140121
140219
|
}
|
|
140122
140220
|
async function runBingAnalysisRender(opts) {
|
|
140123
140221
|
const dataPath = path47.resolve(opts.dataFile);
|
|
140124
|
-
let data =
|
|
140222
|
+
let data = asRecord32(await readJsonFile6(dataPath));
|
|
140125
140223
|
if (opts.snapshotDir?.trim()) {
|
|
140126
140224
|
data = await mergeBingAnalysisSnapshotIntoReport(data, path47.resolve(opts.snapshotDir));
|
|
140127
140225
|
}
|
|
@@ -140594,7 +140692,7 @@ import { fileURLToPath as fileURLToPath13 } from "url";
|
|
|
140594
140692
|
// src/commands/tiktok-analysis/merge-snapshot.ts
|
|
140595
140693
|
import * as fs39 from "fs/promises";
|
|
140596
140694
|
import * as path49 from "path";
|
|
140597
|
-
function
|
|
140695
|
+
function asRecord33(value) {
|
|
140598
140696
|
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
140599
140697
|
return value;
|
|
140600
140698
|
}
|
|
@@ -140772,7 +140870,7 @@ function mapAudienceMerged(payload) {
|
|
|
140772
140870
|
}
|
|
140773
140871
|
async function mergeTikTokAnalysisSnapshotIntoReport(payload, snapshotDir) {
|
|
140774
140872
|
const absDir = path49.resolve(snapshotDir);
|
|
140775
|
-
const metaAccountId = typeof
|
|
140873
|
+
const metaAccountId = typeof asRecord33(payload.meta)?.accountId === "string" ? asRecord33(payload.meta)?.accountId : void 0;
|
|
140776
140874
|
const manifest = await readManifest6(snapshotDir, metaAccountId);
|
|
140777
140875
|
if (!manifest) {
|
|
140778
140876
|
throw new Error(
|
|
@@ -140886,7 +140984,7 @@ function textLen6(value) {
|
|
|
140886
140984
|
function asArray7(value) {
|
|
140887
140985
|
return Array.isArray(value) ? value : [];
|
|
140888
140986
|
}
|
|
140889
|
-
function
|
|
140987
|
+
function asRecord34(value) {
|
|
140890
140988
|
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
140891
140989
|
return value;
|
|
140892
140990
|
}
|
|
@@ -140897,9 +140995,9 @@ function pushMissing6(missing, id, chapter, dimension, hint) {
|
|
|
140897
140995
|
}
|
|
140898
140996
|
function validateTikTokPeriodReportContent(data) {
|
|
140899
140997
|
const missing = [];
|
|
140900
|
-
const meta =
|
|
140901
|
-
const narrative =
|
|
140902
|
-
const sections =
|
|
140998
|
+
const meta = asRecord34(data.meta) ?? {};
|
|
140999
|
+
const narrative = asRecord34(data.narrative) ?? {};
|
|
141000
|
+
const sections = asRecord34(narrative.sections) ?? {};
|
|
140903
141001
|
if (!textLen6(meta.accountId)) {
|
|
140904
141002
|
pushMissing6(missing, "meta-account", "\u5143\u4FE1\u606F", "\u8D26\u6237 ID", "meta.accountId \u5FC5\u586B");
|
|
140905
141003
|
}
|
|
@@ -140916,7 +141014,7 @@ function validateTikTokPeriodReportContent(data) {
|
|
|
140916
141014
|
const sectionAnalysisCounts = {};
|
|
140917
141015
|
const sectionSuggestionCounts = {};
|
|
140918
141016
|
for (const key of SECTION_KEYS3) {
|
|
140919
|
-
const section =
|
|
141017
|
+
const section = asRecord34(sections[key]);
|
|
140920
141018
|
const analysis = asArray7(section?.analysis).filter((s) => textLen6(s) > 0);
|
|
140921
141019
|
const suggestions = asArray7(section?.suggestions).filter((s) => textLen6(s) > 0);
|
|
140922
141020
|
sectionAnalysisCounts[key] = analysis.length;
|
|
@@ -140942,7 +141040,7 @@ function validateTikTokPeriodReportContent(data) {
|
|
|
140942
141040
|
}
|
|
140943
141041
|
const recommendations = asArray7(narrative.recommendations).filter((r) => {
|
|
140944
141042
|
if (typeof r === "string") return textLen6(r) > 0;
|
|
140945
|
-
const rec =
|
|
141043
|
+
const rec = asRecord34(r);
|
|
140946
141044
|
return textLen6(rec?.content) > 0;
|
|
140947
141045
|
});
|
|
140948
141046
|
if (recommendations.length < 3) {
|
|
@@ -141003,7 +141101,7 @@ async function readJsonFile7(filePath) {
|
|
|
141003
141101
|
throw new Error(`\u65E0\u6CD5\u89E3\u6790 JSON\uFF1A${filePath}`);
|
|
141004
141102
|
}
|
|
141005
141103
|
}
|
|
141006
|
-
function
|
|
141104
|
+
function asRecord35(value) {
|
|
141007
141105
|
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
141008
141106
|
return value;
|
|
141009
141107
|
}
|
|
@@ -141018,7 +141116,7 @@ function injectReportData6(html, payload) {
|
|
|
141018
141116
|
}
|
|
141019
141117
|
async function runTikTokAnalysisRender(opts) {
|
|
141020
141118
|
const dataPath = path50.resolve(opts.dataFile);
|
|
141021
|
-
let data =
|
|
141119
|
+
let data = asRecord35(await readJsonFile7(dataPath));
|
|
141022
141120
|
if (opts.snapshotDir?.trim()) {
|
|
141023
141121
|
data = await mergeTikTokAnalysisSnapshotIntoReport(data, path50.resolve(opts.snapshotDir));
|
|
141024
141122
|
}
|
|
@@ -142117,7 +142215,7 @@ function endpointHintForYandexSection(def) {
|
|
|
142117
142215
|
// src/commands/yandex-analysis/merge-snapshot.ts
|
|
142118
142216
|
import * as fs41 from "fs/promises";
|
|
142119
142217
|
import * as path51 from "path";
|
|
142120
|
-
function
|
|
142218
|
+
function asRecord36(value) {
|
|
142121
142219
|
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
142122
142220
|
return value;
|
|
142123
142221
|
}
|
|
@@ -142137,11 +142235,11 @@ function normalizeYandexRoi(value) {
|
|
|
142137
142235
|
return r === -1 ? void 0 : r;
|
|
142138
142236
|
}
|
|
142139
142237
|
function asItems(payload) {
|
|
142140
|
-
const obj =
|
|
142238
|
+
const obj = asRecord36(payload);
|
|
142141
142239
|
if (!obj) return [];
|
|
142142
142240
|
const items3 = obj.items;
|
|
142143
142241
|
if (!Array.isArray(items3)) return [];
|
|
142144
|
-
return items3.filter((r) =>
|
|
142242
|
+
return items3.filter((r) => asRecord36(r) !== null);
|
|
142145
142243
|
}
|
|
142146
142244
|
function pickMetrics2(row, extras = []) {
|
|
142147
142245
|
const spend = num12(row.spend) ?? 0;
|
|
@@ -142277,7 +142375,7 @@ function aggregateDailyToWeekly(dailyRows) {
|
|
|
142277
142375
|
});
|
|
142278
142376
|
}
|
|
142279
142377
|
function prepareYandexDailyPayload(payload) {
|
|
142280
|
-
const obj =
|
|
142378
|
+
const obj = asRecord36(payload) ?? {};
|
|
142281
142379
|
const items3 = asItems(obj).map((r) => {
|
|
142282
142380
|
const date = normalizeYandexDailyDate(r.date);
|
|
142283
142381
|
return date ? { ...r, date } : r;
|
|
@@ -142346,7 +142444,7 @@ function mapDeviceRows2(payload) {
|
|
|
142346
142444
|
}
|
|
142347
142445
|
async function mergeYandexAnalysisSnapshotIntoReport(payload, snapshotDir) {
|
|
142348
142446
|
const absDir = path51.resolve(snapshotDir);
|
|
142349
|
-
const metaAccountId = typeof
|
|
142447
|
+
const metaAccountId = typeof asRecord36(payload.meta)?.accountId === "string" ? asRecord36(payload.meta)?.accountId : void 0;
|
|
142350
142448
|
const manifest = await readManifest7(snapshotDir, metaAccountId);
|
|
142351
142449
|
if (!manifest) {
|
|
142352
142450
|
throw new Error(
|
|
@@ -142358,8 +142456,8 @@ async function mergeYandexAnalysisSnapshotIntoReport(payload, snapshotDir) {
|
|
|
142358
142456
|
const data = await loadSectionPayload3(absDir, art.file);
|
|
142359
142457
|
if (data != null) sectionMap.set(art.section, data);
|
|
142360
142458
|
}
|
|
142361
|
-
const overview =
|
|
142362
|
-
const totals =
|
|
142459
|
+
const overview = asRecord36(sectionMap.get("overview"));
|
|
142460
|
+
const totals = asRecord36(overview?.totals);
|
|
142363
142461
|
const accountId = manifest.accountId ?? metaAccountId;
|
|
142364
142462
|
const meta = { ...payload.meta ?? {} };
|
|
142365
142463
|
if (!meta.accountId && accountId) meta.accountId = accountId;
|
|
@@ -142581,7 +142679,7 @@ function textLen7(value) {
|
|
|
142581
142679
|
function asArray8(value) {
|
|
142582
142680
|
return Array.isArray(value) ? value : [];
|
|
142583
142681
|
}
|
|
142584
|
-
function
|
|
142682
|
+
function asRecord37(value) {
|
|
142585
142683
|
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
142586
142684
|
return value;
|
|
142587
142685
|
}
|
|
@@ -142594,23 +142692,23 @@ function normKey(value) {
|
|
|
142594
142692
|
return String(value ?? "").trim().toLowerCase();
|
|
142595
142693
|
}
|
|
142596
142694
|
function insightMatchRecord(item) {
|
|
142597
|
-
const rec =
|
|
142695
|
+
const rec = asRecord37(item);
|
|
142598
142696
|
if (!rec) return {};
|
|
142599
|
-
return
|
|
142697
|
+
return asRecord37(rec.match) ?? rec;
|
|
142600
142698
|
}
|
|
142601
142699
|
function findRowInsight(list, primaryField, primaryValue, row) {
|
|
142602
142700
|
const target = normKey(primaryValue);
|
|
142603
142701
|
if (!target) return null;
|
|
142604
142702
|
const candidates = list.filter((it) => normKey(insightMatchRecord(it)[primaryField]) === target);
|
|
142605
|
-
if (candidates.length <= 1) return
|
|
142703
|
+
if (candidates.length <= 1) return asRecord37(candidates[0]);
|
|
142606
142704
|
const campaign = normKey(row.campaignName);
|
|
142607
142705
|
const narrowed = candidates.find(
|
|
142608
142706
|
(it) => normKey(insightMatchRecord(it).campaignName) === campaign
|
|
142609
142707
|
);
|
|
142610
|
-
return
|
|
142708
|
+
return asRecord37(narrowed ?? candidates[0]);
|
|
142611
142709
|
}
|
|
142612
142710
|
function topRowsBySpend(rows, limit) {
|
|
142613
|
-
return rows.map((r) =>
|
|
142711
|
+
return rows.map((r) => asRecord37(r)).filter((r) => r != null).sort((a, b) => (Number(b.spend) || 0) - (Number(a.spend) || 0)).slice(0, limit);
|
|
142614
142712
|
}
|
|
142615
142713
|
var ROW_INSIGHT_SPECS = [
|
|
142616
142714
|
{
|
|
@@ -142625,17 +142723,17 @@ var ROW_INSIGHT_SPECS = [
|
|
|
142625
142723
|
];
|
|
142626
142724
|
function validateYandexPeriodReportContent(data) {
|
|
142627
142725
|
const missing = [];
|
|
142628
|
-
const meta =
|
|
142629
|
-
const narrative =
|
|
142630
|
-
const sections =
|
|
142631
|
-
const kpis =
|
|
142726
|
+
const meta = asRecord37(data.meta) ?? {};
|
|
142727
|
+
const narrative = asRecord37(data.narrative) ?? {};
|
|
142728
|
+
const sections = asRecord37(narrative.sections) ?? {};
|
|
142729
|
+
const kpis = asRecord37(data.kpis) ?? {};
|
|
142632
142730
|
if (!textLen7(meta.accountId)) {
|
|
142633
142731
|
pushMissing7(missing, "meta-account", "\u5143\u4FE1\u606F", "\u8D26\u6237 ID", "meta.accountId \u5FC5\u586B");
|
|
142634
142732
|
}
|
|
142635
142733
|
const sectionAnalysisCounts = {};
|
|
142636
142734
|
const sectionSuggestionCounts = {};
|
|
142637
142735
|
for (const key of SECTION_KEYS4) {
|
|
142638
|
-
const section =
|
|
142736
|
+
const section = asRecord37(sections[key]);
|
|
142639
142737
|
const analysis = asArray8(section?.analysis).filter((s) => textLen7(s) > 0);
|
|
142640
142738
|
const suggestions = asArray8(section?.suggestions).filter((s) => textLen7(s) > 0);
|
|
142641
142739
|
sectionAnalysisCounts[key] = analysis.length;
|
|
@@ -142661,7 +142759,7 @@ function validateYandexPeriodReportContent(data) {
|
|
|
142661
142759
|
}
|
|
142662
142760
|
const recommendations = asArray8(narrative.recommendations).filter((r) => {
|
|
142663
142761
|
if (typeof r === "string") return textLen7(r) > 0;
|
|
142664
|
-
const rec =
|
|
142762
|
+
const rec = asRecord37(r);
|
|
142665
142763
|
return textLen7(rec?.content) > 0 || textLen7(rec?.text) > 0;
|
|
142666
142764
|
});
|
|
142667
142765
|
if (recommendations.length < 3) {
|
|
@@ -142683,8 +142781,8 @@ function validateYandexPeriodReportContent(data) {
|
|
|
142683
142781
|
"\u81F3\u5C11\u4E00\u9879\u6709\u6548\u6570\u5B57\uFF08\u8BF7\u4F20 --snapshot-dir \u7531 CLI \u4ECE overview \u5408\u5E76\uFF09"
|
|
142684
142782
|
);
|
|
142685
142783
|
}
|
|
142686
|
-
const tables =
|
|
142687
|
-
const weeklyRows = asArray8(tables.weekly).map((r) =>
|
|
142784
|
+
const tables = asRecord37(data.tables) ?? {};
|
|
142785
|
+
const weeklyRows = asArray8(tables.weekly).map((r) => asRecord37(r)).filter((r) => r != null);
|
|
142688
142786
|
for (const [i, row] of weeklyRows.entries()) {
|
|
142689
142787
|
const label2 = `${row.weekLabel ?? ""} ${row.startDate ?? ""} ${row.endDate ?? ""}`;
|
|
142690
142788
|
if (/nan/i.test(label2) || !normalizeYandexDailyDate(row.startDate) || !normalizeYandexDailyDate(row.endDate)) {
|
|
@@ -142697,7 +142795,7 @@ function validateYandexPeriodReportContent(data) {
|
|
|
142697
142795
|
);
|
|
142698
142796
|
}
|
|
142699
142797
|
}
|
|
142700
|
-
const rowInsights =
|
|
142798
|
+
const rowInsights = asRecord37(narrative.rowInsights) ?? {};
|
|
142701
142799
|
const rowInsightCoverage = {};
|
|
142702
142800
|
for (const spec of ROW_INSIGHT_SPECS) {
|
|
142703
142801
|
const shown = topRowsBySpend(asArray8(tables[spec.tableKey]), spec.limit).filter(
|
|
@@ -142777,7 +142875,7 @@ async function readJsonFile8(filePath) {
|
|
|
142777
142875
|
throw new Error(`\u65E0\u6CD5\u89E3\u6790 JSON\uFF1A${filePath}`);
|
|
142778
142876
|
}
|
|
142779
142877
|
}
|
|
142780
|
-
function
|
|
142878
|
+
function asRecord38(value) {
|
|
142781
142879
|
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
142782
142880
|
return value;
|
|
142783
142881
|
}
|
|
@@ -142805,7 +142903,7 @@ function injectReportData7(html, payload) {
|
|
|
142805
142903
|
}
|
|
142806
142904
|
async function runYandexAnalysisRender(opts) {
|
|
142807
142905
|
const dataPath = path53.resolve(opts.dataFile);
|
|
142808
|
-
let data =
|
|
142906
|
+
let data = asRecord38(await readJsonFile8(dataPath));
|
|
142809
142907
|
if (opts.snapshotDir?.trim()) {
|
|
142810
142908
|
data = await mergeYandexAnalysisSnapshotIntoReport(data, path53.resolve(opts.snapshotDir));
|
|
142811
142909
|
}
|
|
@@ -143001,7 +143099,7 @@ function unwrapYandexItems(raw) {
|
|
|
143001
143099
|
}
|
|
143002
143100
|
return [];
|
|
143003
143101
|
}
|
|
143004
|
-
function
|
|
143102
|
+
function asRecord39(raw) {
|
|
143005
143103
|
return raw && typeof raw === "object" && !Array.isArray(raw) ? raw : {};
|
|
143006
143104
|
}
|
|
143007
143105
|
|
|
@@ -143080,7 +143178,7 @@ async function runYandexAccountStatus(opts) {
|
|
|
143080
143178
|
} catch (err) {
|
|
143081
143179
|
fail3("\u67E5\u8BE2 Yandex \u8D26\u6237\u603B\u89C8\u5931\u8D25", err);
|
|
143082
143180
|
}
|
|
143083
|
-
const row =
|
|
143181
|
+
const row = asRecord39(result.raw);
|
|
143084
143182
|
await emitOrTable(opts, {
|
|
143085
143183
|
section: "yandex-account-status",
|
|
143086
143184
|
commandLabel: "yandex-analysis account-status",
|