siluzan-tso-cli 1.1.35 → 1.1.36-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 +297 -121
- package/dist/skill/SKILL.md +1 -1
- package/dist/skill/_meta.json +2 -2
- package/dist/skill/assets/campaign-create-template.md +1 -1
- package/dist/skill/references/README.md +1 -0
- package/dist/skill/references/accounts/accounts.md +3 -3
- 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/account-analytics.md +13 -1
- package/dist/skill/references/analytics/rag.md +1 -1
- package/dist/skill/references/analytics/reporting.md +5 -5
- package/dist/skill/references/core/agent-conventions.md +8 -2
- package/dist/skill/references/core/setup.md +5 -5
- package/dist/skill/references/core/user-communication-guide.md +112 -0
- package/dist/skill/references/core/workflows.md +3 -2
- package/dist/skill/references/google-ads/google-ads-campaign-plan.md +30 -2
- package/dist/skill/references/google-ads/google-ads.md +15 -2
- package/dist/skill/references/misc/tso-home.md +2 -2
- package/dist/skill/references/report-templates/bing-period-report.md +20 -2
- package/dist/skill/report-templates/bing-period-report.md +20 -2
- 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
|
@@ -3829,7 +3829,7 @@ var DEFAULT_API_BASE;
|
|
|
3829
3829
|
var init_defaults = __esm({
|
|
3830
3830
|
"src/config/defaults.ts"() {
|
|
3831
3831
|
"use strict";
|
|
3832
|
-
DEFAULT_API_BASE = "https://tso-api.siluzan.com";
|
|
3832
|
+
DEFAULT_API_BASE = "https://tso-api-ci.siluzan.com";
|
|
3833
3833
|
}
|
|
3834
3834
|
});
|
|
3835
3835
|
|
|
@@ -106735,7 +106735,7 @@ async function writeReportAnalysisSnapshot(params) {
|
|
|
106735
106735
|
);
|
|
106736
106736
|
await fs8.writeFile(
|
|
106737
106737
|
path12.join(absDir, outlineFileName),
|
|
106738
|
-
`${formatOutlineFileBody(fileName, params.payload)}
|
|
106738
|
+
`${formatOutlineFileBody(fileName, params.payload, params.outlineExtraHints)}
|
|
106739
106739
|
`,
|
|
106740
106740
|
"utf8"
|
|
106741
106741
|
);
|
|
@@ -113676,17 +113676,120 @@ init_cli_table();
|
|
|
113676
113676
|
// src/commands/ad/campaign-batch-diff.ts
|
|
113677
113677
|
init_auth();
|
|
113678
113678
|
init_cli_json_snapshot();
|
|
113679
|
+
|
|
113680
|
+
// src/commands/ad/campaign-extension-remediate.ts
|
|
113681
|
+
function shellArg(value) {
|
|
113682
|
+
return /[\s"]/.test(value) ? `"${value.replace(/"/g, '\\"')}"` : value;
|
|
113683
|
+
}
|
|
113684
|
+
function pickString(...vals) {
|
|
113685
|
+
for (const v of vals) {
|
|
113686
|
+
if (typeof v === "string" && v.trim()) return v.trim();
|
|
113687
|
+
}
|
|
113688
|
+
return "";
|
|
113689
|
+
}
|
|
113679
113690
|
function asRecord2(v) {
|
|
113680
113691
|
return v && typeof v === "object" && !Array.isArray(v) ? v : null;
|
|
113681
113692
|
}
|
|
113682
|
-
function
|
|
113693
|
+
function extensionType2(ext) {
|
|
113694
|
+
return pickString(ext["typeV2"], ext["TypeV2"], ext["AssetFieldType"], ext["type"]).toUpperCase();
|
|
113695
|
+
}
|
|
113696
|
+
function extensionLevel(ext) {
|
|
113697
|
+
const level = pickString(ext["level"], ext["Level"]) || "Campaign";
|
|
113698
|
+
return level.charAt(0).toUpperCase() + level.slice(1).toLowerCase();
|
|
113699
|
+
}
|
|
113700
|
+
function buildExtensionRemediateCommandLine(opts) {
|
|
113701
|
+
const { accountId, campaignId, ext } = opts;
|
|
113702
|
+
const jsonOut = shellArg(opts.jsonOutDir ?? "./snap");
|
|
113703
|
+
const type = extensionType2(ext);
|
|
113704
|
+
const level = extensionLevel(ext);
|
|
113705
|
+
const props = asRecord2(ext["Properties"]) ?? {};
|
|
113706
|
+
const levelFlag = `--level ${level}`;
|
|
113707
|
+
const campaignFlag = level === "Campaign" ? `--campaign-id ${campaignId}` : "";
|
|
113708
|
+
const base = ["siluzan-tso", "ad", "extension"];
|
|
113709
|
+
if (type === "SITELINK") {
|
|
113710
|
+
const text = pickString(props["Text"], props["LinkText"]);
|
|
113711
|
+
const url = pickString(props["DestinationUrl"]);
|
|
113712
|
+
if (!text || !url) return null;
|
|
113713
|
+
const parts = [
|
|
113714
|
+
...base,
|
|
113715
|
+
"sitelink",
|
|
113716
|
+
`-a ${accountId}`,
|
|
113717
|
+
`--text ${shellArg(text)}`,
|
|
113718
|
+
`--url ${shellArg(url)}`
|
|
113719
|
+
];
|
|
113720
|
+
const line2 = pickString(props["Line2"], props["Description1"]);
|
|
113721
|
+
const line3 = pickString(props["Line3"], props["Description2"]);
|
|
113722
|
+
if (line2) parts.push(`--line2 ${shellArg(line2)}`);
|
|
113723
|
+
if (line3) parts.push(`--line3 ${shellArg(line3)}`);
|
|
113724
|
+
parts.push(levelFlag);
|
|
113725
|
+
if (campaignFlag) parts.push(campaignFlag);
|
|
113726
|
+
parts.push(`--json-out ${jsonOut}`);
|
|
113727
|
+
return parts.join(" ");
|
|
113728
|
+
}
|
|
113729
|
+
if (type === "CALLOUT") {
|
|
113730
|
+
const text = pickString(props["Text"], props["CalloutText"]);
|
|
113731
|
+
if (!text) return null;
|
|
113732
|
+
const parts = [
|
|
113733
|
+
...base,
|
|
113734
|
+
"callout",
|
|
113735
|
+
`-a ${accountId}`,
|
|
113736
|
+
`--text ${shellArg(text)}`,
|
|
113737
|
+
levelFlag
|
|
113738
|
+
];
|
|
113739
|
+
if (campaignFlag) parts.push(campaignFlag);
|
|
113740
|
+
parts.push(`--json-out ${jsonOut}`);
|
|
113741
|
+
return parts.join(" ");
|
|
113742
|
+
}
|
|
113743
|
+
if (type === "CALL") {
|
|
113744
|
+
const code = pickString(props["ContryCode"], props["CountryCode"]);
|
|
113745
|
+
const phone = pickString(props["PhoneNumber"], props["phoneNumber"]);
|
|
113746
|
+
if (!code || !phone) return null;
|
|
113747
|
+
const parts = [
|
|
113748
|
+
...base,
|
|
113749
|
+
"call",
|
|
113750
|
+
`-a ${accountId}`,
|
|
113751
|
+
`--country-code ${shellArg(code)}`,
|
|
113752
|
+
`--phone ${shellArg(phone)}`,
|
|
113753
|
+
levelFlag
|
|
113754
|
+
];
|
|
113755
|
+
if (campaignFlag) parts.push(campaignFlag);
|
|
113756
|
+
parts.push(`--json-out ${jsonOut}`);
|
|
113757
|
+
return parts.join(" ");
|
|
113758
|
+
}
|
|
113759
|
+
if (type === "STRUCTURED_SNIPPET") {
|
|
113760
|
+
const ssv = asRecord2(ext["StructuredSnippetHeaderValue"]);
|
|
113761
|
+
const header = pickString(ssv?.["Key"]);
|
|
113762
|
+
const values = ssv?.["Value"];
|
|
113763
|
+
if (!header || !Array.isArray(values) || values.length === 0) return null;
|
|
113764
|
+
const valueStr = values.filter((v) => typeof v === "string" && v.trim().length > 0).join(",");
|
|
113765
|
+
if (!valueStr) return null;
|
|
113766
|
+
const parts = [
|
|
113767
|
+
...base,
|
|
113768
|
+
"snippet",
|
|
113769
|
+
`-a ${accountId}`,
|
|
113770
|
+
`--header ${shellArg(header)}`,
|
|
113771
|
+
`--values ${shellArg(valueStr)}`,
|
|
113772
|
+
levelFlag
|
|
113773
|
+
];
|
|
113774
|
+
if (campaignFlag) parts.push(campaignFlag);
|
|
113775
|
+
parts.push(`--json-out ${jsonOut}`);
|
|
113776
|
+
return parts.join(" ");
|
|
113777
|
+
}
|
|
113778
|
+
return null;
|
|
113779
|
+
}
|
|
113780
|
+
|
|
113781
|
+
// src/commands/ad/campaign-batch-diff.ts
|
|
113782
|
+
function asRecord3(v) {
|
|
113783
|
+
return v && typeof v === "object" && !Array.isArray(v) ? v : null;
|
|
113784
|
+
}
|
|
113785
|
+
function pickString2(...vals) {
|
|
113683
113786
|
for (const v of vals) {
|
|
113684
113787
|
if (typeof v === "string" && v.trim()) return v.trim();
|
|
113685
113788
|
}
|
|
113686
113789
|
return "";
|
|
113687
113790
|
}
|
|
113688
113791
|
function normMatchType(v) {
|
|
113689
|
-
return
|
|
113792
|
+
return pickString2(v).toUpperCase() || "BROAD";
|
|
113690
113793
|
}
|
|
113691
113794
|
function keywordKey(core, matchType) {
|
|
113692
113795
|
return `${normMatchType(matchType)}:${unwrapKeywordDisplayTextForEdit(core).trim().toLowerCase()}`;
|
|
@@ -113696,19 +113799,19 @@ function liveKeywordTexts(item) {
|
|
|
113696
113799
|
if (Array.isArray(kt)) {
|
|
113697
113800
|
return kt.filter((t2) => typeof t2 === "string" && t2.trim().length > 0);
|
|
113698
113801
|
}
|
|
113699
|
-
const t =
|
|
113802
|
+
const t = pickString2(item["text"], kt);
|
|
113700
113803
|
return t ? [t] : [];
|
|
113701
113804
|
}
|
|
113702
113805
|
function belongsToCampaign(item, campaignId, campaignName) {
|
|
113703
|
-
const cid =
|
|
113704
|
-
const cname =
|
|
113806
|
+
const cid = pickString2(item["campaignId"], item["campaignid"], item["CampaignId"]);
|
|
113807
|
+
const cname = pickString2(item["campaignName"], item["campaign"], item["CampaignName"]);
|
|
113705
113808
|
if (campaignId && cid) return cid === campaignId;
|
|
113706
113809
|
if (campaignName && cname) return cname === campaignName;
|
|
113707
113810
|
return false;
|
|
113708
113811
|
}
|
|
113709
113812
|
function belongsToAdGroup(item, adGroupName, adGroupId) {
|
|
113710
|
-
const gid =
|
|
113711
|
-
const gname =
|
|
113813
|
+
const gid = pickString2(item["adGroupId"], item["adgroupId"], item["AdGroupId"]);
|
|
113814
|
+
const gname = pickString2(item["adGroupName"], item["adGroup"], item["AdGroupName"]);
|
|
113712
113815
|
if (adGroupId && gid) return gid === adGroupId;
|
|
113713
113816
|
return gname === adGroupName;
|
|
113714
113817
|
}
|
|
@@ -113729,10 +113832,10 @@ function liveAdHasHeadline(ad, headline) {
|
|
|
113729
113832
|
}
|
|
113730
113833
|
function formatExtensionPlanned(ext) {
|
|
113731
113834
|
if (!ext) return "\u2014";
|
|
113732
|
-
const type =
|
|
113733
|
-
const props =
|
|
113734
|
-
const phone = props ?
|
|
113735
|
-
const code = props ?
|
|
113835
|
+
const type = pickString2(ext["typeV2"], ext["AssetFieldType"], ext["type"]);
|
|
113836
|
+
const props = asRecord3(ext["Properties"]);
|
|
113837
|
+
const phone = props ? pickString2(props["PhoneNumber"], props["phoneNumber"]) : "";
|
|
113838
|
+
const code = props ? pickString2(props["ContryCode"], props["CountryCode"]) : "";
|
|
113736
113839
|
const parts = [type ? `\u7C7B\u578B: ${type}` : "", code || phone ? `\u7535\u8BDD: ${code}${phone}` : ""].filter(
|
|
113737
113840
|
Boolean
|
|
113738
113841
|
);
|
|
@@ -113743,15 +113846,15 @@ function listPlannedKeywords(campaign) {
|
|
|
113743
113846
|
if (!Array.isArray(groups)) return [];
|
|
113744
113847
|
const out = [];
|
|
113745
113848
|
for (let gi = 0; gi < groups.length; gi++) {
|
|
113746
|
-
const g =
|
|
113849
|
+
const g = asRecord3(groups[gi]);
|
|
113747
113850
|
if (!g) continue;
|
|
113748
|
-
const groupName =
|
|
113851
|
+
const groupName = pickString2(g["Name"], g["name"]) || `AdGroupsForBatchJob[${gi}]`;
|
|
113749
113852
|
const blocks = g["KeywordsForBatchJob"];
|
|
113750
113853
|
if (!Array.isArray(blocks)) continue;
|
|
113751
113854
|
for (let bi = 0; bi < blocks.length; bi++) {
|
|
113752
|
-
const block =
|
|
113855
|
+
const block = asRecord3(blocks[bi]);
|
|
113753
113856
|
if (!block) continue;
|
|
113754
|
-
const matchTypeV2 =
|
|
113857
|
+
const matchTypeV2 = pickString2(block["MatchTypeV2"], block["matchTypeV2"]) || "BROAD";
|
|
113755
113858
|
const texts = block["KeywordText"];
|
|
113756
113859
|
if (!Array.isArray(texts)) continue;
|
|
113757
113860
|
for (let ki = 0; ki < texts.length; ki++) {
|
|
@@ -113773,9 +113876,9 @@ function listPlannedNegativeKeywords(campaign) {
|
|
|
113773
113876
|
if (!Array.isArray(blocks)) return [];
|
|
113774
113877
|
const out = [];
|
|
113775
113878
|
for (let bi = 0; bi < blocks.length; bi++) {
|
|
113776
|
-
const block =
|
|
113879
|
+
const block = asRecord3(blocks[bi]);
|
|
113777
113880
|
if (!block) continue;
|
|
113778
|
-
const matchTypeV2 =
|
|
113881
|
+
const matchTypeV2 = pickString2(block["MatchTypeV2"], block["matchTypeV2"]) || "BROAD";
|
|
113779
113882
|
const texts = block["KeywordText"];
|
|
113780
113883
|
if (!Array.isArray(texts)) continue;
|
|
113781
113884
|
for (let ki = 0; ki < texts.length; ki++) {
|
|
@@ -113793,7 +113896,7 @@ function listPlannedNegativeKeywords(campaign) {
|
|
|
113793
113896
|
}
|
|
113794
113897
|
function compareCampaignCreateToLive(cfg, campaignId, live, meta) {
|
|
113795
113898
|
const campaign = cfg.campaign ?? {};
|
|
113796
|
-
const campaignName =
|
|
113899
|
+
const campaignName = pickString2(cfg.name, campaign["Name"], campaign["name"]);
|
|
113797
113900
|
const accountId = String(cfg.account ?? "");
|
|
113798
113901
|
const missing = [];
|
|
113799
113902
|
if (!live.campaignFound) {
|
|
@@ -113807,15 +113910,15 @@ function compareCampaignCreateToLive(cfg, campaignId, live, meta) {
|
|
|
113807
113910
|
}
|
|
113808
113911
|
const liveGroups = live.adGroups;
|
|
113809
113912
|
const liveGroupNames = new Set(
|
|
113810
|
-
liveGroups.map((g) =>
|
|
113913
|
+
liveGroups.map((g) => pickString2(g["name"], g["Name"]).toLowerCase()).filter(Boolean)
|
|
113811
113914
|
);
|
|
113812
113915
|
const groups = campaign["AdGroupsForBatchJob"];
|
|
113813
113916
|
const plannedGroupCount = Array.isArray(groups) ? groups.length : 0;
|
|
113814
113917
|
if (Array.isArray(groups)) {
|
|
113815
113918
|
for (let gi = 0; gi < groups.length; gi++) {
|
|
113816
|
-
const g =
|
|
113919
|
+
const g = asRecord3(groups[gi]);
|
|
113817
113920
|
if (!g) continue;
|
|
113818
|
-
const groupName =
|
|
113921
|
+
const groupName = pickString2(g["Name"], g["name"]) || `AdGroupsForBatchJob[${gi}]`;
|
|
113819
113922
|
const groupPath = `campaign.AdGroupsForBatchJob[${gi}]`;
|
|
113820
113923
|
if (!liveGroupNames.has(groupName.toLowerCase())) {
|
|
113821
113924
|
missing.push({
|
|
@@ -113829,9 +113932,9 @@ function compareCampaignCreateToLive(cfg, campaignId, live, meta) {
|
|
|
113829
113932
|
continue;
|
|
113830
113933
|
}
|
|
113831
113934
|
const liveGroup = liveGroups.find(
|
|
113832
|
-
(lg) =>
|
|
113935
|
+
(lg) => pickString2(lg["name"], lg["Name"]).toLowerCase() === groupName.toLowerCase()
|
|
113833
113936
|
);
|
|
113834
|
-
const liveGroupId = liveGroup ?
|
|
113937
|
+
const liveGroupId = liveGroup ? pickString2(liveGroup["id"], liveGroup["Id"]) : "";
|
|
113835
113938
|
const liveKwInGroup = live.keywords.filter(
|
|
113836
113939
|
(k) => belongsToCampaign(k, campaignId, campaignName) && belongsToAdGroup(k, groupName, liveGroupId)
|
|
113837
113940
|
);
|
|
@@ -113844,9 +113947,9 @@ function compareCampaignCreateToLive(cfg, campaignId, live, meta) {
|
|
|
113844
113947
|
const blocks = g["KeywordsForBatchJob"];
|
|
113845
113948
|
if (Array.isArray(blocks)) {
|
|
113846
113949
|
for (let bi = 0; bi < blocks.length; bi++) {
|
|
113847
|
-
const block =
|
|
113950
|
+
const block = asRecord3(blocks[bi]);
|
|
113848
113951
|
if (!block) continue;
|
|
113849
|
-
const matchTypeV2 =
|
|
113952
|
+
const matchTypeV2 = pickString2(block["MatchTypeV2"], block["matchTypeV2"]) || "BROAD";
|
|
113850
113953
|
const texts = block["KeywordText"];
|
|
113851
113954
|
if (!Array.isArray(texts)) continue;
|
|
113852
113955
|
for (let ki = 0; ki < texts.length; ki++) {
|
|
@@ -113873,10 +113976,10 @@ function compareCampaignCreateToLive(cfg, campaignId, live, meta) {
|
|
|
113873
113976
|
const ads = g["AdsForBatchJob"];
|
|
113874
113977
|
if (Array.isArray(ads)) {
|
|
113875
113978
|
for (let ai = 0; ai < ads.length; ai++) {
|
|
113876
|
-
const ad =
|
|
113979
|
+
const ad = asRecord3(ads[ai]);
|
|
113877
113980
|
if (!ad) continue;
|
|
113878
113981
|
const path34 = `${groupPath}.AdsForBatchJob[${ai}]`;
|
|
113879
|
-
const primary =
|
|
113982
|
+
const primary = pickString2(ad["headlinePart1"], ad["AdTitle"]);
|
|
113880
113983
|
if (!primary) {
|
|
113881
113984
|
missing.push({
|
|
113882
113985
|
layer: "ad",
|
|
@@ -113888,7 +113991,7 @@ function compareCampaignCreateToLive(cfg, campaignId, live, meta) {
|
|
|
113888
113991
|
});
|
|
113889
113992
|
continue;
|
|
113890
113993
|
}
|
|
113891
|
-
const finalUrl =
|
|
113994
|
+
const finalUrl = pickString2(ad["Finalurl"], ad["DestinationUrl"], ad["finalUrl"]);
|
|
113892
113995
|
const found = liveAdsInGroup.some((la) => liveAdHasHeadline(la, primary));
|
|
113893
113996
|
if (!found) {
|
|
113894
113997
|
missing.push({
|
|
@@ -113929,13 +114032,15 @@ function compareCampaignCreateToLive(cfg, campaignId, live, meta) {
|
|
|
113929
114032
|
).length;
|
|
113930
114033
|
if (plannedExt > liveExt && Array.isArray(extBlocks)) {
|
|
113931
114034
|
for (let ei = liveExt; ei < extBlocks.length; ei++) {
|
|
113932
|
-
const ext =
|
|
114035
|
+
const ext = asRecord3(extBlocks[ei]);
|
|
114036
|
+
const remediateCommand = ext && accountId && campaignId ? buildExtensionRemediateCommandLine({ accountId, campaignId, ext }) ?? void 0 : void 0;
|
|
113933
114037
|
missing.push({
|
|
113934
114038
|
layer: "extension",
|
|
113935
114039
|
path: `campaign.ExtensionsForBatchJob[${ei}]`,
|
|
113936
114040
|
plannedContent: formatExtensionPlanned(ext),
|
|
113937
114041
|
liveNote: `\u7CFB\u5217\u7EA7\u9644\u52A0\u4FE1\u606F\u8D26\u6237\u5185 ${liveExt}/${plannedExt} \u6761`,
|
|
113938
|
-
summary: `\u9644\u52A0\u4FE1\u606F\u672A\u521B\u5EFA\uFF1A${formatExtensionPlanned(ext)}
|
|
114042
|
+
summary: `\u9644\u52A0\u4FE1\u606F\u672A\u521B\u5EFA\uFF1A${formatExtensionPlanned(ext)}`,
|
|
114043
|
+
remediateCommand
|
|
113939
114044
|
});
|
|
113940
114045
|
}
|
|
113941
114046
|
}
|
|
@@ -113959,7 +114064,7 @@ function compareCampaignCreateToLive(cfg, campaignId, live, meta) {
|
|
|
113959
114064
|
plannedKeywords: plannedKeywords.length,
|
|
113960
114065
|
liveKeywords: liveKwCount,
|
|
113961
114066
|
plannedAds: Array.isArray(groups) ? groups.reduce((n, g) => {
|
|
113962
|
-
const gr =
|
|
114067
|
+
const gr = asRecord3(g);
|
|
113963
114068
|
const ads = gr?.["AdsForBatchJob"];
|
|
113964
114069
|
return n + (Array.isArray(ads) ? ads.length : 0);
|
|
113965
114070
|
}, 0) : 0,
|
|
@@ -114020,7 +114125,7 @@ async function fetchLiveSnapshotForCampaign(config, accountId, campaignId, campa
|
|
|
114020
114125
|
)
|
|
114021
114126
|
]);
|
|
114022
114127
|
const campaignFound = campaigns.some(
|
|
114023
|
-
(c) =>
|
|
114128
|
+
(c) => pickString2(c["id"], c["Id"]) === campaignId || pickString2(c["name"], c["Name"]) === campaignName
|
|
114024
114129
|
);
|
|
114025
114130
|
return {
|
|
114026
114131
|
campaignFound,
|
|
@@ -114035,8 +114140,8 @@ async function fetchLiveSnapshotForCampaign(config, accountId, campaignId, campa
|
|
|
114035
114140
|
};
|
|
114036
114141
|
}
|
|
114037
114142
|
function resolveCampaignIdFromBatch(record) {
|
|
114038
|
-
const campaign =
|
|
114039
|
-
return
|
|
114143
|
+
const campaign = asRecord3(record["campaign"]);
|
|
114144
|
+
return pickString2(campaign?.["Id"], campaign?.["id"], record["campaignId"]);
|
|
114040
114145
|
}
|
|
114041
114146
|
function shellArgPath(p) {
|
|
114042
114147
|
return /[\s"]/.test(p) ? `"${p.replace(/"/g, '\\"')}"` : p;
|
|
@@ -114083,7 +114188,9 @@ function printBatchDiffFollowUpHint(opts) {
|
|
|
114083
114188
|
if (opts.status === "Successfully") {
|
|
114084
114189
|
console.log(" \u6838\u5BF9\u8BA1\u5212\u4E0E\u8D26\u6237\u662F\u5426\u4E00\u81F4\uFF08\u65E0\u7F3A\u5931\u5E94\u663E\u793A \u2705\uFF09\u3002\n");
|
|
114085
114190
|
} else {
|
|
114086
|
-
console.log(
|
|
114191
|
+
console.log(
|
|
114192
|
+
" \u5217\u51FA\u7F3A\u5931\u9879\u540E\u81EA\u52A8\u8865\u5EFA\uFF08\u9644\u52A0\u4FE1\u606F\u7528 ad extension *\uFF1B\u7EC4/\u8BCD/RSA \u7528\u5BF9\u5E94 create \u547D\u4EE4\uFF09\u3002\n"
|
|
114193
|
+
);
|
|
114087
114194
|
}
|
|
114088
114195
|
console.log(` ${cmd}
|
|
114089
114196
|
`);
|
|
@@ -114111,6 +114218,9 @@ function printMissingEntry(configFile, item) {
|
|
|
114111
114218
|
if (item.liveNote) {
|
|
114112
114219
|
console.log(` \u5B9E\u51B5: ${item.liveNote}`);
|
|
114113
114220
|
}
|
|
114221
|
+
if (item.remediateCommand) {
|
|
114222
|
+
console.log(` \u8865\u5EFA: ${item.remediateCommand}`);
|
|
114223
|
+
}
|
|
114114
114224
|
}
|
|
114115
114225
|
function printMissingGroupByAdGroup(configFile, layer, items) {
|
|
114116
114226
|
const byGroup = /* @__PURE__ */ new Map();
|
|
@@ -114178,6 +114288,11 @@ function formatBatchDiffGrouped(result) {
|
|
|
114178
114288
|
for (const m of layerItems) {
|
|
114179
114289
|
printMissingEntry(configFile, m);
|
|
114180
114290
|
}
|
|
114291
|
+
if (layer === "extension" && layerItems.some((m) => m.remediateCommand)) {
|
|
114292
|
+
console.log(
|
|
114293
|
+
"\n \u{1F4A1} \u9644\u52A0\u4FE1\u606F BatchJob \u5E38\u5931\u8D25\uFF1AAgent \u5E94**\u76F4\u63A5\u6267\u884C**\u4E0A\u65B9\u300C\u8865\u5EFA\u300D\u547D\u4EE4\uFF08\u7528\u6237\u5DF2\u786E\u8BA4\u8FC7 JSON\uFF09\uFF0C\u52FF\u4EC5\u8BE2\u95EE\u662F\u5426\u8865\u5EFA\u3002"
|
|
114294
|
+
);
|
|
114295
|
+
}
|
|
114181
114296
|
}
|
|
114182
114297
|
}
|
|
114183
114298
|
console.log();
|
|
@@ -114190,14 +114305,14 @@ async function runCampaignBatchDiff(opts) {
|
|
|
114190
114305
|
`);
|
|
114191
114306
|
process.exit(1);
|
|
114192
114307
|
}
|
|
114193
|
-
const accountId =
|
|
114308
|
+
const accountId = pickString2(opts.account, cfg.account);
|
|
114194
114309
|
if (!accountId) {
|
|
114195
114310
|
console.error("\n\u274C \u7F3A\u5C11\u8D26\u6237 ID\uFF1A\u8BF7\u5728 JSON \u4E2D\u586B\u5199 account \u6216\u4F7F\u7528 -a\n");
|
|
114196
114311
|
process.exit(1);
|
|
114197
114312
|
}
|
|
114198
114313
|
cfg.account = accountId;
|
|
114199
114314
|
const config = loadConfig(opts.token);
|
|
114200
|
-
let campaignId =
|
|
114315
|
+
let campaignId = pickString2(opts.campaignId);
|
|
114201
114316
|
let batchStatus;
|
|
114202
114317
|
if (opts.batchId) {
|
|
114203
114318
|
const batchUrl = `${config.apiBaseUrl}/query/campaign-creation-record/campaign-batch-asyncs/${opts.batchId}`;
|
|
@@ -114210,7 +114325,7 @@ async function runCampaignBatchDiff(opts) {
|
|
|
114210
114325
|
`);
|
|
114211
114326
|
process.exit(1);
|
|
114212
114327
|
}
|
|
114213
|
-
batchStatus =
|
|
114328
|
+
batchStatus = pickString2(batch["status"]);
|
|
114214
114329
|
if (batchStatus === "Creating") {
|
|
114215
114330
|
console.error("\n\u274C \u4EFB\u52A1\u4ECD\u5728\u521B\u5EFA\u4E2D\uFF0C\u8BF7\u7A0D\u540E\u518D\u6267\u884C ad batch diff\n");
|
|
114216
114331
|
process.exit(1);
|
|
@@ -114228,7 +114343,7 @@ async function runCampaignBatchDiff(opts) {
|
|
|
114228
114343
|
}
|
|
114229
114344
|
}
|
|
114230
114345
|
const campaign = cfg.campaign ?? {};
|
|
114231
|
-
const campaignName =
|
|
114346
|
+
const campaignName = pickString2(cfg.name, campaign["Name"], campaign["name"]);
|
|
114232
114347
|
if (!campaignId && campaignName) {
|
|
114233
114348
|
const googleApiUrl = requireGoogleApi(config);
|
|
114234
114349
|
const params = new URLSearchParams();
|
|
@@ -114240,10 +114355,10 @@ async function runCampaignBatchDiff(opts) {
|
|
|
114240
114355
|
opts.verbose
|
|
114241
114356
|
);
|
|
114242
114357
|
const hit = campaigns.find(
|
|
114243
|
-
(c) =>
|
|
114358
|
+
(c) => pickString2(c["name"], c["Name"]).toLowerCase() === campaignName.toLowerCase()
|
|
114244
114359
|
);
|
|
114245
114360
|
if (hit) {
|
|
114246
|
-
campaignId =
|
|
114361
|
+
campaignId = pickString2(hit["id"], hit["Id"]);
|
|
114247
114362
|
}
|
|
114248
114363
|
}
|
|
114249
114364
|
if (!campaignId) {
|
|
@@ -123718,7 +123833,7 @@ function readJsonFile(filePath) {
|
|
|
123718
123833
|
}
|
|
123719
123834
|
});
|
|
123720
123835
|
}
|
|
123721
|
-
function
|
|
123836
|
+
function asRecord4(value) {
|
|
123722
123837
|
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
123723
123838
|
return value;
|
|
123724
123839
|
}
|
|
@@ -123744,10 +123859,10 @@ function injectReportData(html, payload) {
|
|
|
123744
123859
|
async function runWebsiteDiagnosisRender(opts) {
|
|
123745
123860
|
const dataPath = path17.resolve(opts.dataFile);
|
|
123746
123861
|
const dataRaw = await readJsonFile(dataPath);
|
|
123747
|
-
let data =
|
|
123862
|
+
let data = asRecord4(dataRaw);
|
|
123748
123863
|
if (opts.collectFile) {
|
|
123749
123864
|
const collectRaw = await readJsonFile(path17.resolve(opts.collectFile));
|
|
123750
|
-
data = mergeCollectLighthouse(data,
|
|
123865
|
+
data = mergeCollectLighthouse(data, asRecord4(collectRaw));
|
|
123751
123866
|
}
|
|
123752
123867
|
const templatePath = websiteDiagnosisReportTemplatePath();
|
|
123753
123868
|
let html;
|
|
@@ -124388,7 +124503,7 @@ function formatMarketReportContentErrors(result) {
|
|
|
124388
124503
|
}
|
|
124389
124504
|
|
|
124390
124505
|
// src/commands/market-analysis/render-report.ts
|
|
124391
|
-
function
|
|
124506
|
+
function asRecord5(value) {
|
|
124392
124507
|
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
124393
124508
|
return value;
|
|
124394
124509
|
}
|
|
@@ -124418,7 +124533,7 @@ async function runMarketAnalysisRender(opts) {
|
|
|
124418
124533
|
}
|
|
124419
124534
|
let html;
|
|
124420
124535
|
try {
|
|
124421
|
-
html = resolveMarketAnalysisHtml(
|
|
124536
|
+
html = resolveMarketAnalysisHtml(asRecord5(dataRaw));
|
|
124422
124537
|
} catch (err) {
|
|
124423
124538
|
const message = err instanceof Error ? err.message : String(err);
|
|
124424
124539
|
console.error(`
|
|
@@ -124430,7 +124545,7 @@ async function runMarketAnalysisRender(opts) {
|
|
|
124430
124545
|
console.error("\n\u274C \u62A5\u544A HTML \u5185\u5BB9\u4E3A\u7A7A\n");
|
|
124431
124546
|
process.exit(1);
|
|
124432
124547
|
}
|
|
124433
|
-
const reportData =
|
|
124548
|
+
const reportData = asRecord5(dataRaw);
|
|
124434
124549
|
const contentCheck = validateMarketReportContent(html, validationContextFromReport(reportData));
|
|
124435
124550
|
if (!contentCheck.ok) {
|
|
124436
124551
|
console.error(`
|
|
@@ -125453,21 +125568,21 @@ function buildGoogleAdsDiagnosisCollectPayload(opts) {
|
|
|
125453
125568
|
}
|
|
125454
125569
|
|
|
125455
125570
|
// src/commands/google-ads-diagnosis/collect-integrity.ts
|
|
125456
|
-
function
|
|
125571
|
+
function asRecord6(value) {
|
|
125457
125572
|
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
125458
125573
|
return value;
|
|
125459
125574
|
}
|
|
125460
125575
|
return null;
|
|
125461
125576
|
}
|
|
125462
125577
|
function itemCount(block) {
|
|
125463
|
-
const items =
|
|
125578
|
+
const items = asRecord6(block)?.items;
|
|
125464
125579
|
return Array.isArray(items) ? items.length : 0;
|
|
125465
125580
|
}
|
|
125466
125581
|
function getCollectIntegrityWarnings(reportData) {
|
|
125467
125582
|
const warnings = [];
|
|
125468
125583
|
const kwItems = itemCount(reportData.keywords);
|
|
125469
125584
|
const fullKwItems = itemCount(reportData.fullKeywords);
|
|
125470
|
-
const keywordCount = Number(
|
|
125585
|
+
const keywordCount = Number(asRecord6(reportData.structure)?.keywordCount ?? 0);
|
|
125471
125586
|
if (kwItems === 0 && fullKwItems > 0) {
|
|
125472
125587
|
warnings.push(
|
|
125473
125588
|
`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`
|
|
@@ -125478,7 +125593,7 @@ function getCollectIntegrityWarnings(reportData) {
|
|
|
125478
125593
|
);
|
|
125479
125594
|
}
|
|
125480
125595
|
const campaignItems = itemCount(reportData.campaigns);
|
|
125481
|
-
const campaignCount = Number(
|
|
125596
|
+
const campaignCount = Number(asRecord6(reportData.structure)?.campaignCount ?? 0);
|
|
125482
125597
|
if (campaignItems === 0 && campaignCount > 0) {
|
|
125483
125598
|
warnings.push(
|
|
125484
125599
|
`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`
|
|
@@ -125682,7 +125797,7 @@ import fs18 from "fs/promises";
|
|
|
125682
125797
|
import path23 from "path";
|
|
125683
125798
|
|
|
125684
125799
|
// src/commands/google-ads-diagnosis/report-content.ts
|
|
125685
|
-
function
|
|
125800
|
+
function asRecord7(value) {
|
|
125686
125801
|
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
125687
125802
|
return value;
|
|
125688
125803
|
}
|
|
@@ -125691,7 +125806,7 @@ function asRecord6(value) {
|
|
|
125691
125806
|
function getNestedValue(root, path34) {
|
|
125692
125807
|
let cur = root;
|
|
125693
125808
|
for (const key of path34) {
|
|
125694
|
-
const rec =
|
|
125809
|
+
const rec = asRecord7(cur);
|
|
125695
125810
|
if (!rec) return void 0;
|
|
125696
125811
|
cur = rec[key];
|
|
125697
125812
|
}
|
|
@@ -125708,7 +125823,7 @@ function validateBudgetCompetitivenessStrategies(data) {
|
|
|
125708
125823
|
}
|
|
125709
125824
|
const warnings = [];
|
|
125710
125825
|
for (let i = 0; i < items.length; i++) {
|
|
125711
|
-
const row =
|
|
125826
|
+
const row = asRecord7(items[i]);
|
|
125712
125827
|
const name2 = String(row?.name ?? row?.metric ?? i);
|
|
125713
125828
|
if (!hasNarrativeContent(row?.strategy)) {
|
|
125714
125829
|
warnings.push(`budgetCompetitiveness[${name2}].strategy \u4E3A\u7A7A\uFF08\u987B\u7531 Agent \u586B\u5199\uFF09`);
|
|
@@ -125717,13 +125832,13 @@ function validateBudgetCompetitivenessStrategies(data) {
|
|
|
125717
125832
|
return warnings;
|
|
125718
125833
|
}
|
|
125719
125834
|
function validateNewFeatureRecommendations(data) {
|
|
125720
|
-
const nf =
|
|
125835
|
+
const nf = asRecord7(data.newFeatures);
|
|
125721
125836
|
if (!nf) return [];
|
|
125722
125837
|
const items = nf.items;
|
|
125723
125838
|
if (!Array.isArray(items)) return [];
|
|
125724
125839
|
const warnings = [];
|
|
125725
125840
|
for (let i = 0; i < items.length; i++) {
|
|
125726
|
-
const row =
|
|
125841
|
+
const row = asRecord7(items[i]);
|
|
125727
125842
|
const name2 = String(row?.strategyName ?? row?.feature ?? row?.strategy ?? i);
|
|
125728
125843
|
if (!hasNarrativeContent(row?.optimizerRecommendation)) {
|
|
125729
125844
|
warnings.push(`newFeatures.items[${name2}].optimizerRecommendation \u4E3A\u7A7A\uFF08\u987B\u7531 Agent \u586B\u5199\uFF09`);
|
|
@@ -125732,13 +125847,13 @@ function validateNewFeatureRecommendations(data) {
|
|
|
125732
125847
|
return warnings;
|
|
125733
125848
|
}
|
|
125734
125849
|
function validateAdCreativeItemSuggestions(data) {
|
|
125735
|
-
const creative =
|
|
125850
|
+
const creative = asRecord7(data.adCreativeOptimization);
|
|
125736
125851
|
if (!creative) return [];
|
|
125737
125852
|
const items = creative.items;
|
|
125738
125853
|
if (!Array.isArray(items) || items.length === 0) return [];
|
|
125739
125854
|
const warnings = [];
|
|
125740
125855
|
for (let i = 0; i < items.length; i++) {
|
|
125741
|
-
const row =
|
|
125856
|
+
const row = asRecord7(items[i]);
|
|
125742
125857
|
const title = String(row?.adTitle ?? i);
|
|
125743
125858
|
if (!hasNarrativeContent(row?.suggestion ?? row?.suggestions)) {
|
|
125744
125859
|
warnings.push(
|
|
@@ -125753,7 +125868,7 @@ function validateGoogleAdsDiagnosisContent(data) {
|
|
|
125753
125868
|
(key) => data[key] === void 0
|
|
125754
125869
|
);
|
|
125755
125870
|
const warnings = [];
|
|
125756
|
-
const accountInfo =
|
|
125871
|
+
const accountInfo = asRecord7(data.accountInfo);
|
|
125757
125872
|
if (accountInfo && !accountInfo.companyName && !accountInfo.period) {
|
|
125758
125873
|
warnings.push("accountInfo \u7F3A\u5C11 companyName \u4E0E period\uFF0C\u9875\u7709\u53EF\u80FD\u4E3A\u7A7A");
|
|
125759
125874
|
}
|
|
@@ -125801,7 +125916,7 @@ var GOOGLE_ADS_DIAGNOSIS_RESTORE_ITEM_MODULES = [
|
|
|
125801
125916
|
GOOGLE_ADS_DIAGNOSIS_BIDDING_STRATEGY_MODULE,
|
|
125802
125917
|
GOOGLE_ADS_DIAGNOSIS_NEW_FEATURES_MODULE
|
|
125803
125918
|
];
|
|
125804
|
-
function
|
|
125919
|
+
function asRecord8(value) {
|
|
125805
125920
|
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
125806
125921
|
return value;
|
|
125807
125922
|
}
|
|
@@ -125812,7 +125927,7 @@ function isValidRateChange(value) {
|
|
|
125812
125927
|
return typeof value === "number" && Number.isFinite(value);
|
|
125813
125928
|
}
|
|
125814
125929
|
function isComparisonTableRowValid(row) {
|
|
125815
|
-
const rec =
|
|
125930
|
+
const rec = asRecord8(row);
|
|
125816
125931
|
if (!rec) return false;
|
|
125817
125932
|
if (!String(rec.title ?? "").trim()) return false;
|
|
125818
125933
|
if (!Number.isFinite(Number(rec.currentCost))) return false;
|
|
@@ -125824,12 +125939,12 @@ function isComparisonTableRowValid(row) {
|
|
|
125824
125939
|
return true;
|
|
125825
125940
|
}
|
|
125826
125941
|
function isNewFeatureRowValid(row) {
|
|
125827
|
-
const rec =
|
|
125942
|
+
const rec = asRecord8(row);
|
|
125828
125943
|
if (!rec) return false;
|
|
125829
125944
|
return Boolean(String(rec.strategyName ?? rec.feature ?? rec.strategy ?? "").trim());
|
|
125830
125945
|
}
|
|
125831
125946
|
function isBiddingStrategyRowValid(row) {
|
|
125832
|
-
const rec =
|
|
125947
|
+
const rec = asRecord8(row);
|
|
125833
125948
|
if (!rec) return false;
|
|
125834
125949
|
if (!String(rec.campaignName ?? "").trim()) return false;
|
|
125835
125950
|
if (!Number.isFinite(Number(rec.duration)) || Number(rec.duration) <= 0) return false;
|
|
@@ -125867,11 +125982,11 @@ function getFactItemsErrors(data) {
|
|
|
125867
125982
|
for (const mod of GOOGLE_ADS_DIAGNOSIS_RESTORE_ITEM_MODULES) {
|
|
125868
125983
|
const validator = MODULE_ITEM_VALIDATORS[mod];
|
|
125869
125984
|
const hint = MODULE_ITEM_ERROR_HINT[mod];
|
|
125870
|
-
const block =
|
|
125985
|
+
const block = asRecord8(data[mod]);
|
|
125871
125986
|
const items = block?.items;
|
|
125872
125987
|
if (!Array.isArray(items) || items.length === 0) {
|
|
125873
125988
|
if (mod === "keywords") {
|
|
125874
|
-
const fullKw =
|
|
125989
|
+
const fullKw = asRecord8(data.fullKeywords)?.items;
|
|
125875
125990
|
if (Array.isArray(fullKw) && fullKw.length > 0) {
|
|
125876
125991
|
errors.push(
|
|
125877
125992
|
"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"
|
|
@@ -125896,15 +126011,15 @@ var LANDING_PAGE_SPEED_KEYS = [
|
|
|
125896
126011
|
"speedIndex"
|
|
125897
126012
|
];
|
|
125898
126013
|
function isLandingPageSpeedValid(value) {
|
|
125899
|
-
const rec =
|
|
126014
|
+
const rec = asRecord8(value);
|
|
125900
126015
|
if (!rec) return false;
|
|
125901
126016
|
return LANDING_PAGE_SPEED_KEYS.every(
|
|
125902
126017
|
(key) => typeof rec[key] === "number" && Number.isFinite(rec[key])
|
|
125903
126018
|
);
|
|
125904
126019
|
}
|
|
125905
126020
|
function restoreLandingPageSpeedFromCollect(finalData, collectReportData) {
|
|
125906
|
-
const finalBlock =
|
|
125907
|
-
const collectBlock =
|
|
126021
|
+
const finalBlock = asRecord8(finalData[GOOGLE_ADS_DIAGNOSIS_LANDING_PAGE_MODULE]);
|
|
126022
|
+
const collectBlock = asRecord8(collectReportData[GOOGLE_ADS_DIAGNOSIS_LANDING_PAGE_MODULE]);
|
|
125908
126023
|
if (!collectBlock) return { data: finalData, restored: false };
|
|
125909
126024
|
const needsDesktop = !isLandingPageSpeedValid(finalBlock?.desktop) && isLandingPageSpeedValid(collectBlock.desktop);
|
|
125910
126025
|
const needsMobile = !isLandingPageSpeedValid(finalBlock?.mobile) && isLandingPageSpeedValid(collectBlock.mobile);
|
|
@@ -125922,8 +126037,8 @@ function mergeComparisonItemsFromCollect(finalData, collectReportData) {
|
|
|
125922
126037
|
const restoredModules = [];
|
|
125923
126038
|
for (const mod of GOOGLE_ADS_DIAGNOSIS_RESTORE_ITEM_MODULES) {
|
|
125924
126039
|
const validator = MODULE_ITEM_VALIDATORS[mod];
|
|
125925
|
-
const finalBlock =
|
|
125926
|
-
const collectBlock =
|
|
126040
|
+
const finalBlock = asRecord8(data[mod]);
|
|
126041
|
+
const collectBlock = asRecord8(collectReportData[mod]);
|
|
125927
126042
|
const finalItems = finalBlock?.items;
|
|
125928
126043
|
const collectItems = collectBlock?.items;
|
|
125929
126044
|
if (!Array.isArray(collectItems) || collectItems.length === 0) continue;
|
|
@@ -125939,7 +126054,7 @@ function mergeComparisonItemsFromCollect(finalData, collectReportData) {
|
|
|
125939
126054
|
}
|
|
125940
126055
|
|
|
125941
126056
|
// src/commands/google-ads-diagnosis/narrative-consistency-check.ts
|
|
125942
|
-
function
|
|
126057
|
+
function asRecord9(value) {
|
|
125943
126058
|
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
125944
126059
|
return value;
|
|
125945
126060
|
}
|
|
@@ -125947,14 +126062,14 @@ function asRecord8(value) {
|
|
|
125947
126062
|
}
|
|
125948
126063
|
function collectNarrativeTexts(data) {
|
|
125949
126064
|
const texts = [];
|
|
125950
|
-
const summary =
|
|
126065
|
+
const summary = asRecord9(data.summary);
|
|
125951
126066
|
if (Array.isArray(summary?.keyIssues)) {
|
|
125952
126067
|
for (const item of summary.keyIssues) texts.push(String(item ?? ""));
|
|
125953
126068
|
}
|
|
125954
|
-
const overview =
|
|
126069
|
+
const overview = asRecord9(data.diagnosisOverview);
|
|
125955
126070
|
if (Array.isArray(overview?.disadvantages)) {
|
|
125956
126071
|
for (const item of overview.disadvantages) {
|
|
125957
|
-
const rec =
|
|
126072
|
+
const rec = asRecord9(item);
|
|
125958
126073
|
if (rec) {
|
|
125959
126074
|
texts.push(String(rec.title ?? ""), String(rec.description ?? ""));
|
|
125960
126075
|
} else {
|
|
@@ -125965,16 +126080,16 @@ function collectNarrativeTexts(data) {
|
|
|
125965
126080
|
return texts.filter((t) => t.trim().length > 0);
|
|
125966
126081
|
}
|
|
125967
126082
|
function newFeatureAccountStatus(data, strategy) {
|
|
125968
|
-
const nf =
|
|
126083
|
+
const nf = asRecord9(data.newFeatures);
|
|
125969
126084
|
const items = nf?.items;
|
|
125970
126085
|
if (!Array.isArray(items)) return null;
|
|
125971
|
-
const row = items.find((it) =>
|
|
125972
|
-
const rec =
|
|
126086
|
+
const row = items.find((it) => asRecord9(it)?.strategy === strategy);
|
|
126087
|
+
const rec = asRecord9(row);
|
|
125973
126088
|
if (!rec || typeof rec.accountStatus !== "boolean") return null;
|
|
125974
126089
|
return rec.accountStatus;
|
|
125975
126090
|
}
|
|
125976
126091
|
function goldAccountFlag(data, key) {
|
|
125977
|
-
const gold =
|
|
126092
|
+
const gold = asRecord9(data.goldAccount);
|
|
125978
126093
|
const value = gold?.[key];
|
|
125979
126094
|
return typeof value === "boolean" ? value : null;
|
|
125980
126095
|
}
|
|
@@ -126005,7 +126120,7 @@ function getNarrativeConsistencyErrors(data) {
|
|
|
126005
126120
|
}
|
|
126006
126121
|
|
|
126007
126122
|
// src/commands/google-ads-diagnosis/render-report.ts
|
|
126008
|
-
function
|
|
126123
|
+
function asRecord10(value) {
|
|
126009
126124
|
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
126010
126125
|
return value;
|
|
126011
126126
|
}
|
|
@@ -126014,8 +126129,8 @@ function asRecord9(value) {
|
|
|
126014
126129
|
async function tryLoadCollectReportData(collectPath) {
|
|
126015
126130
|
try {
|
|
126016
126131
|
const raw = JSON.parse(await fs18.readFile(collectPath, "utf8"));
|
|
126017
|
-
const root =
|
|
126018
|
-
const reportData =
|
|
126132
|
+
const root = asRecord10(raw);
|
|
126133
|
+
const reportData = asRecord10(root?.reportData);
|
|
126019
126134
|
return reportData ?? root;
|
|
126020
126135
|
} catch {
|
|
126021
126136
|
return null;
|
|
@@ -126032,7 +126147,7 @@ async function runGoogleAdsDiagnosisRender(opts) {
|
|
|
126032
126147
|
`);
|
|
126033
126148
|
process.exit(1);
|
|
126034
126149
|
}
|
|
126035
|
-
let data =
|
|
126150
|
+
let data = asRecord10(dataRaw);
|
|
126036
126151
|
if (!opts.noMergeCollect) {
|
|
126037
126152
|
const collectPath = path23.resolve(
|
|
126038
126153
|
opts.collectFile ?? path23.join(path23.dirname(dataPath), GOOGLE_ADS_DIAGNOSIS_COLLECT_BASENAME)
|
|
@@ -126545,7 +126660,7 @@ function ensureScorecardFromSnapshot(healthDiagnosis, merged) {
|
|
|
126545
126660
|
}
|
|
126546
126661
|
|
|
126547
126662
|
// src/commands/facebook-analysis/merge-snapshot.ts
|
|
126548
|
-
function
|
|
126663
|
+
function asRecord11(value) {
|
|
126549
126664
|
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
126550
126665
|
return value;
|
|
126551
126666
|
}
|
|
@@ -126594,7 +126709,7 @@ async function readManifest(snapshotDir) {
|
|
|
126594
126709
|
async function loadSectionFile(snapshotDir, file) {
|
|
126595
126710
|
try {
|
|
126596
126711
|
const raw = await fs21.readFile(path28.join(snapshotDir, file), "utf8");
|
|
126597
|
-
return
|
|
126712
|
+
return asRecord11(JSON.parse(raw));
|
|
126598
126713
|
} catch {
|
|
126599
126714
|
return null;
|
|
126600
126715
|
}
|
|
@@ -126642,7 +126757,7 @@ async function mergeFacebookSnapshotIntoReport(payload, snapshotDir) {
|
|
|
126642
126757
|
if (data) sectionMap.set(art.section, data);
|
|
126643
126758
|
}
|
|
126644
126759
|
const overview = sectionMap.get("overview");
|
|
126645
|
-
const current =
|
|
126760
|
+
const current = asRecord11(overview?.currentPeriod);
|
|
126646
126761
|
const accountName = typeof overview?.accountName === "string" ? overview.accountName : void 0;
|
|
126647
126762
|
const accountId = manifest.accountId ?? (typeof overview?.accountId === "string" ? overview.accountId : void 0);
|
|
126648
126763
|
const kpis = { ...payload.kpis ?? {} };
|
|
@@ -126764,7 +126879,7 @@ function isFiniteNumber(value) {
|
|
|
126764
126879
|
function asArray2(value) {
|
|
126765
126880
|
return Array.isArray(value) ? value : [];
|
|
126766
126881
|
}
|
|
126767
|
-
function
|
|
126882
|
+
function asRecord12(value) {
|
|
126768
126883
|
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
126769
126884
|
return value;
|
|
126770
126885
|
}
|
|
@@ -126776,26 +126891,26 @@ function pushMissing(missing, id, chapter, dimension, hint) {
|
|
|
126776
126891
|
function countAdSetsWithSpend(tables) {
|
|
126777
126892
|
const rows = asArray2(tables?.adSets);
|
|
126778
126893
|
return rows.filter((row) => {
|
|
126779
|
-
const r =
|
|
126894
|
+
const r = asRecord12(row);
|
|
126780
126895
|
const spend = r?.spend;
|
|
126781
126896
|
return isFiniteNumber(spend) && spend > 0;
|
|
126782
126897
|
}).length;
|
|
126783
126898
|
}
|
|
126784
126899
|
function chartLabels(chart) {
|
|
126785
|
-
const c =
|
|
126900
|
+
const c = asRecord12(chart);
|
|
126786
126901
|
return asArray2(c?.labels).length;
|
|
126787
126902
|
}
|
|
126788
126903
|
function validateMetaPeriodReportContent(data) {
|
|
126789
126904
|
const missing = [];
|
|
126790
|
-
const meta =
|
|
126791
|
-
const kpis =
|
|
126792
|
-
const narrative =
|
|
126793
|
-
const tables =
|
|
126794
|
-
const charts =
|
|
126795
|
-
const sections =
|
|
126796
|
-
const health =
|
|
126797
|
-
const priorityPlan =
|
|
126798
|
-
const actionChecklist =
|
|
126905
|
+
const meta = asRecord12(data.meta) ?? {};
|
|
126906
|
+
const kpis = asRecord12(data.kpis) ?? {};
|
|
126907
|
+
const narrative = asRecord12(data.narrative) ?? {};
|
|
126908
|
+
const tables = asRecord12(data.tables);
|
|
126909
|
+
const charts = asRecord12(data.charts);
|
|
126910
|
+
const sections = asRecord12(data.sections);
|
|
126911
|
+
const health = asRecord12(data.healthDiagnosis);
|
|
126912
|
+
const priorityPlan = asRecord12(data.priorityPlan);
|
|
126913
|
+
const actionChecklist = asRecord12(data.actionChecklist);
|
|
126799
126914
|
const adSetsWithSpend2 = countAdSetsWithSpend(tables ?? void 0);
|
|
126800
126915
|
const regionalNarratives = asArray2(narrative.regional).length;
|
|
126801
126916
|
const recommendations = asArray2(narrative.recommendations);
|
|
@@ -126867,7 +126982,7 @@ function validateMetaPeriodReportContent(data) {
|
|
|
126867
126982
|
"charts.audience.labels \u81F3\u5C11 1 \u9879"
|
|
126868
126983
|
);
|
|
126869
126984
|
}
|
|
126870
|
-
const funnel =
|
|
126985
|
+
const funnel = asRecord12(charts?.funnel);
|
|
126871
126986
|
if (!isFiniteNumber(funnel?.reach)) {
|
|
126872
126987
|
pushMissing(
|
|
126873
126988
|
missing,
|
|
@@ -126931,7 +127046,7 @@ function validateMetaPeriodReportContent(data) {
|
|
|
126931
127046
|
);
|
|
126932
127047
|
} else {
|
|
126933
127048
|
for (let i = 0; i < regionalNarratives; i++) {
|
|
126934
|
-
const row =
|
|
127049
|
+
const row = asRecord12(asArray2(narrative.regional)[i]);
|
|
126935
127050
|
if (textLen2(row?.text) < 80) {
|
|
126936
127051
|
pushMissing(
|
|
126937
127052
|
missing,
|
|
@@ -126961,7 +127076,7 @@ function validateMetaPeriodReportContent(data) {
|
|
|
126961
127076
|
"\u5FC5\u987B\u6070\u597D 4 \u6761\uFF08\u7B80\u5316\u8868\u5355/\u533A\u57DF\u8C03\u6574/\u9884\u7B97\u91CD\u6784/\u7D20\u6750\u5EFA\u8BAE\uFF09"
|
|
126962
127077
|
);
|
|
126963
127078
|
} else {
|
|
126964
|
-
const titles = recommendations.map((r) =>
|
|
127079
|
+
const titles = recommendations.map((r) => asRecord12(r)?.title);
|
|
126965
127080
|
for (const title of RECOMMENDATION_TITLES) {
|
|
126966
127081
|
if (!titles.includes(title)) {
|
|
126967
127082
|
pushMissing(
|
|
@@ -126974,7 +127089,7 @@ function validateMetaPeriodReportContent(data) {
|
|
|
126974
127089
|
}
|
|
126975
127090
|
}
|
|
126976
127091
|
for (let i = 0; i < recommendations.length; i++) {
|
|
126977
|
-
const rec =
|
|
127092
|
+
const rec = asRecord12(recommendations[i]);
|
|
126978
127093
|
if (textLen2(rec?.content) < 150) {
|
|
126979
127094
|
pushMissing(
|
|
126980
127095
|
missing,
|
|
@@ -126996,7 +127111,7 @@ function validateMetaPeriodReportContent(data) {
|
|
|
126996
127111
|
);
|
|
126997
127112
|
} else {
|
|
126998
127113
|
for (let i = 0; i < supplementary.length; i++) {
|
|
126999
|
-
const row =
|
|
127114
|
+
const row = asRecord12(supplementary[i]);
|
|
127000
127115
|
if (!textLen2(row?.dimension) || !textLen2(row?.issue)) {
|
|
127001
127116
|
pushMissing(
|
|
127002
127117
|
missing,
|
|
@@ -127078,7 +127193,7 @@ function validateMetaPeriodReportContent(data) {
|
|
|
127078
127193
|
);
|
|
127079
127194
|
} else {
|
|
127080
127195
|
for (let i = 0; i < fourQuestions.length; i++) {
|
|
127081
|
-
const q =
|
|
127196
|
+
const q = asRecord12(fourQuestions[i]);
|
|
127082
127197
|
const evidence = asArray2(q?.evidence).filter((e) => textLen2(e) > 0);
|
|
127083
127198
|
if (evidence.length < 2) {
|
|
127084
127199
|
pushMissing(
|
|
@@ -127102,7 +127217,7 @@ function validateMetaPeriodReportContent(data) {
|
|
|
127102
127217
|
}
|
|
127103
127218
|
let scorecardValidRows = 0;
|
|
127104
127219
|
for (let i = 0; i < scorecard.length; i++) {
|
|
127105
|
-
const row =
|
|
127220
|
+
const row = asRecord12(scorecard[i]);
|
|
127106
127221
|
const itemOk = textLen2(row?.item) > 0;
|
|
127107
127222
|
const dataOk = textLen2(row?.data) > 0;
|
|
127108
127223
|
const signalOk = SCORECARD_SIGNALS.includes(row?.signal);
|
|
@@ -127144,7 +127259,7 @@ function validateMetaPeriodReportContent(data) {
|
|
|
127144
127259
|
);
|
|
127145
127260
|
}
|
|
127146
127261
|
for (const key of ["platform", "country", "adSets"]) {
|
|
127147
|
-
const insight = textLen2(
|
|
127262
|
+
const insight = textLen2(asRecord12(sections?.[key])?.insight);
|
|
127148
127263
|
if (insight < 200) {
|
|
127149
127264
|
pushMissing(
|
|
127150
127265
|
missing,
|
|
@@ -127155,7 +127270,7 @@ function validateMetaPeriodReportContent(data) {
|
|
|
127155
127270
|
);
|
|
127156
127271
|
}
|
|
127157
127272
|
}
|
|
127158
|
-
const audience =
|
|
127273
|
+
const audience = asRecord12(sections?.audience);
|
|
127159
127274
|
if (asArray2(audience?.goldenProfile).filter((s) => textLen2(s) > 0).length < 3) {
|
|
127160
127275
|
pushMissing(
|
|
127161
127276
|
missing,
|
|
@@ -127183,7 +127298,7 @@ function validateMetaPeriodReportContent(data) {
|
|
|
127183
127298
|
"\u2265150 \u5B57"
|
|
127184
127299
|
);
|
|
127185
127300
|
}
|
|
127186
|
-
const landingRows = asArray2(
|
|
127301
|
+
const landingRows = asArray2(asRecord12(sections?.landingPage)?.rows);
|
|
127187
127302
|
if (landingRows.length < 3) {
|
|
127188
127303
|
pushMissing(
|
|
127189
127304
|
missing,
|
|
@@ -127268,7 +127383,7 @@ async function readJsonFile2(filePath) {
|
|
|
127268
127383
|
throw new Error(`\u65E0\u6CD5\u89E3\u6790 JSON\uFF1A${filePath}`);
|
|
127269
127384
|
}
|
|
127270
127385
|
}
|
|
127271
|
-
function
|
|
127386
|
+
function asRecord13(value) {
|
|
127272
127387
|
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
127273
127388
|
return value;
|
|
127274
127389
|
}
|
|
@@ -127283,7 +127398,7 @@ function injectReportData2(html, payload) {
|
|
|
127283
127398
|
}
|
|
127284
127399
|
async function runFacebookAnalysisRender(opts) {
|
|
127285
127400
|
const dataPath = path29.resolve(opts.dataFile);
|
|
127286
|
-
let data =
|
|
127401
|
+
let data = asRecord13(await readJsonFile2(dataPath));
|
|
127287
127402
|
if (opts.snapshotDir?.trim()) {
|
|
127288
127403
|
data = await mergeFacebookSnapshotIntoReport(data, path29.resolve(opts.snapshotDir));
|
|
127289
127404
|
}
|
|
@@ -127692,6 +127807,55 @@ function syncOverviewBalanceFields(record) {
|
|
|
127692
127807
|
return out;
|
|
127693
127808
|
}
|
|
127694
127809
|
|
|
127810
|
+
// src/commands/bing-analysis/normalize-bing-rates.ts
|
|
127811
|
+
var RATE_FIELD_NAMES2 = /* @__PURE__ */ new Set(["ctr", "conversionRate"]);
|
|
127812
|
+
var BING_RATE_BEARING_SECTIONS = /* @__PURE__ */ new Set([
|
|
127813
|
+
"overview",
|
|
127814
|
+
"device",
|
|
127815
|
+
"geographic",
|
|
127816
|
+
"age-audience",
|
|
127817
|
+
"gender-audience",
|
|
127818
|
+
"audience-merged",
|
|
127819
|
+
"campaigns",
|
|
127820
|
+
"ad-groups",
|
|
127821
|
+
"ads",
|
|
127822
|
+
"keywords",
|
|
127823
|
+
"search-terms"
|
|
127824
|
+
]);
|
|
127825
|
+
function divIfFiniteNumber2(v) {
|
|
127826
|
+
return typeof v === "number" && Number.isFinite(v) ? v / 100 : v;
|
|
127827
|
+
}
|
|
127828
|
+
function divideRatesInPlace2(value, seen) {
|
|
127829
|
+
if (value === null || typeof value !== "object") return;
|
|
127830
|
+
if (seen.has(value)) return;
|
|
127831
|
+
seen.add(value);
|
|
127832
|
+
if (Array.isArray(value)) {
|
|
127833
|
+
for (const item of value) divideRatesInPlace2(item, seen);
|
|
127834
|
+
return;
|
|
127835
|
+
}
|
|
127836
|
+
const obj = value;
|
|
127837
|
+
for (const k of Object.keys(obj)) {
|
|
127838
|
+
if (RATE_FIELD_NAMES2.has(k)) {
|
|
127839
|
+
obj[k] = divIfFiniteNumber2(obj[k]);
|
|
127840
|
+
} else {
|
|
127841
|
+
divideRatesInPlace2(obj[k], seen);
|
|
127842
|
+
}
|
|
127843
|
+
}
|
|
127844
|
+
}
|
|
127845
|
+
function normalizeBingRateScales(payload, section) {
|
|
127846
|
+
if (!BING_RATE_BEARING_SECTIONS.has(section)) return payload;
|
|
127847
|
+
divideRatesInPlace2(payload, /* @__PURE__ */ new WeakSet());
|
|
127848
|
+
return payload;
|
|
127849
|
+
}
|
|
127850
|
+
var BING_RATE_NORMALIZED_OUTLINE_HINTS = [
|
|
127851
|
+
"// \u6982\u7387\u5B57\u6BB5\uFF1A`ctr` / `conversionRate` \u5DF2\u7531 bing-analysis CLI \u4ECE\u7F51\u5173\u767E\u5206\u6570\u523B\u5EA6\u5F52\u4E00\u4E3A **0~1 \u5C0F\u6570**\uFF08\u5982 `0.0074` = 0.74%\uFF09\u3002",
|
|
127852
|
+
"// \u5199\u300Cx%\u300D\u6587\u6848\uFF1A`(v * 100).toFixed(2) + '%'`\uFF1B**\u7981\u6B62**\u518D \xF7100\uFF0C\u4E5F**\u7981\u6B62**\u628A JSON \u6570\u503C\u5F53\u300C\u5DF2\u662F\u767E\u5206\u6570\u300D\u76F4\u63A5\u52A0 `%`\uFF08\u4F1A \xD7100 \u9519\u8BEF\uFF0C\u5982 0.74% \u663E\u793A\u6210 74%\uFF09\u3002"
|
|
127853
|
+
];
|
|
127854
|
+
function buildBingOutlineHints(section) {
|
|
127855
|
+
if (!BING_RATE_BEARING_SECTIONS.has(section)) return void 0;
|
|
127856
|
+
return BING_RATE_NORMALIZED_OUTLINE_HINTS;
|
|
127857
|
+
}
|
|
127858
|
+
|
|
127695
127859
|
// src/commands/bing-analysis/fetch.ts
|
|
127696
127860
|
var BING_KEYWORD_LIMIT_DEFAULT = 100;
|
|
127697
127861
|
function stripLocalDate(d) {
|
|
@@ -127804,7 +127968,10 @@ async function fetchBingOverviewPayload(config, id, startDate, endDate, verbose)
|
|
|
127804
127968
|
endDate,
|
|
127805
127969
|
verbose
|
|
127806
127970
|
);
|
|
127807
|
-
return
|
|
127971
|
+
return normalizeBingSectionPayload(
|
|
127972
|
+
syncOverviewBalanceFields(withPrevious),
|
|
127973
|
+
"overview"
|
|
127974
|
+
);
|
|
127808
127975
|
}
|
|
127809
127976
|
async function enrichBingOverviewPreviousPeriod(config, accountId, record, startDate, endDate, verbose) {
|
|
127810
127977
|
if (!isBingPeriodBlockEmpty(record.previousPeriod)) {
|
|
@@ -127848,13 +128015,16 @@ async function fetchBingAudienceMergedPayload(config, id, startDate, endDate, ve
|
|
|
127848
128015
|
fetchBingJson(config, ageUrl, verbose),
|
|
127849
128016
|
fetchBingJson(config, genderUrl, verbose)
|
|
127850
128017
|
]);
|
|
127851
|
-
return
|
|
127852
|
-
|
|
127853
|
-
|
|
127854
|
-
|
|
127855
|
-
|
|
127856
|
-
|
|
127857
|
-
|
|
128018
|
+
return normalizeBingSectionPayload(
|
|
128019
|
+
{
|
|
128020
|
+
mergedParts: ["AgeAudienceData", "GenderAudienceData"],
|
|
128021
|
+
startDate,
|
|
128022
|
+
endDate,
|
|
128023
|
+
account: id,
|
|
128024
|
+
data: { ageAudience: ageData, genderAudience: genderData }
|
|
128025
|
+
},
|
|
128026
|
+
"audience-merged"
|
|
128027
|
+
);
|
|
127858
128028
|
}
|
|
127859
128029
|
async function fetchBingSectionPayload(def, opts, config, id) {
|
|
127860
128030
|
const { startDate, endDate } = resolveBingDateRange(opts.start, opts.end);
|
|
@@ -127871,7 +128041,12 @@ async function fetchBingSectionPayload(def, opts, config, id) {
|
|
|
127871
128041
|
params.set("orderByCost", "true");
|
|
127872
128042
|
}
|
|
127873
128043
|
const url = reportingUrl(config, id, def.segment, params.toString());
|
|
127874
|
-
|
|
128044
|
+
const raw = await fetchBingJson(config, url, verbose);
|
|
128045
|
+
return normalizeBingRateScales(raw, def.name);
|
|
128046
|
+
}
|
|
128047
|
+
function normalizeBingSectionPayload(payload, section) {
|
|
128048
|
+
if (payload === null || typeof payload !== "object") return payload;
|
|
128049
|
+
return normalizeBingRateScales(payload, section);
|
|
127875
128050
|
}
|
|
127876
128051
|
function endpointHintForBingSection(def) {
|
|
127877
128052
|
return `GET \u2026/BingV2/{id}/${def.segment}`;
|
|
@@ -127930,7 +128105,8 @@ async function runAllBingSections(opts) {
|
|
|
127930
128105
|
accountId: id,
|
|
127931
128106
|
dateRange: { start: startDate, end: endDate },
|
|
127932
128107
|
payload,
|
|
127933
|
-
cliVersion
|
|
128108
|
+
cliVersion,
|
|
128109
|
+
outlineExtraHints: buildBingOutlineHints(def.name)
|
|
127934
128110
|
});
|
|
127935
128111
|
return {
|
|
127936
128112
|
section: def.name,
|