siluzan-tso-cli 1.1.43-beta.1 → 1.1.43-beta.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/index.js +239 -77
- package/dist/skill/SKILL.md +3 -1
- package/dist/skill/_meta.json +2 -2
- package/dist/skill/assets/google-period-report.schema.json +2 -2
- package/dist/skill/references/accounts/accounts-permissions.md +3 -1
- package/dist/skill/references/accounts/accounts.md +3 -1
- package/dist/skill/references/core/agent-conventions.md +3 -2
- package/dist/skill/references/google-ads/google-ads-read.md +3 -1
- package/dist/skill/references/google-ads/google-ads-write.md +1 -1
- package/dist/skill/references/google-ads/google-ads.md +4 -0
- package/dist/skill/references/report-templates/google-period-report.md +3 -3
- package/dist/skill/report-templates/google-period-report.md +3 -3
- package/dist/skill/scripts/install.ps1 +1 -1
- package/dist/skill/scripts/install.sh +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -51,7 +51,7 @@ siluzan-tso init -d /path/to/skills # 写入自定义目录
|
|
|
51
51
|
siluzan-tso init --force # 强制覆盖已存在文件
|
|
52
52
|
```
|
|
53
53
|
|
|
54
|
-
> **注意**:当前为测试版(1.1.43-beta.
|
|
54
|
+
> **注意**:当前为测试版(1.1.43-beta.3),供内部测试使用。正式发布后安装命令将改为 `npm install -g siluzan-tso-cli`。
|
|
55
55
|
|
|
56
56
|
| 助手 | 建议 `--ai` |
|
|
57
57
|
| ----------------------- | ------------------------------------ |
|
package/dist/index.js
CHANGED
|
@@ -4816,6 +4816,19 @@ function normalizeNumericMediaCustomerId(raw) {
|
|
|
4816
4816
|
if (!looksLikeNumericMediaCustomerId(raw)) return null;
|
|
4817
4817
|
return stripMediaCustomerIdDecorators(raw);
|
|
4818
4818
|
}
|
|
4819
|
+
function requireNumericMediaCustomerId(raw, label = "-a, --account") {
|
|
4820
|
+
const trimmed = raw?.toString().trim() ?? "";
|
|
4821
|
+
if (!trimmed) {
|
|
4822
|
+
throw new Error(`\u8BF7\u6307\u5B9A\u5A92\u4F53\u8D26\u6237 ID\uFF08${label}\uFF09`);
|
|
4823
|
+
}
|
|
4824
|
+
const id = normalizeNumericMediaCustomerId(trimmed);
|
|
4825
|
+
if (!id) {
|
|
4826
|
+
throw new Error(
|
|
4827
|
+
`\u5A92\u4F53\u8D26\u6237 ID \u5FC5\u987B\u4E3A\u7EAF\u6570\u5B57\uFF08Google CID \u53EF\u542B\u8FDE\u5B57\u7B26\uFF0C\u5982 123-456-7890\uFF1B\u52FF\u4F20 entityId/UUID\uFF09\uFF1A${trimmed}`
|
|
4828
|
+
);
|
|
4829
|
+
}
|
|
4830
|
+
return id;
|
|
4831
|
+
}
|
|
4819
4832
|
function looksLikeEntityIdUuid(raw) {
|
|
4820
4833
|
return ENTITY_ID_UUID_RE.test(raw.trim());
|
|
4821
4834
|
}
|
|
@@ -5544,6 +5557,32 @@ var init_daily_metrics_range = __esm({
|
|
|
5544
5557
|
}
|
|
5545
5558
|
});
|
|
5546
5559
|
|
|
5560
|
+
// src/utils/snapshot/async-lock.ts
|
|
5561
|
+
async function withAsyncLock(key, fn) {
|
|
5562
|
+
const prev = chains.get(key) ?? Promise.resolve();
|
|
5563
|
+
let release;
|
|
5564
|
+
const gate = new Promise((resolve21) => {
|
|
5565
|
+
release = resolve21;
|
|
5566
|
+
});
|
|
5567
|
+
chains.set(
|
|
5568
|
+
key,
|
|
5569
|
+
prev.catch(() => void 0).then(() => gate)
|
|
5570
|
+
);
|
|
5571
|
+
await prev.catch(() => void 0);
|
|
5572
|
+
try {
|
|
5573
|
+
return await fn();
|
|
5574
|
+
} finally {
|
|
5575
|
+
release();
|
|
5576
|
+
}
|
|
5577
|
+
}
|
|
5578
|
+
var chains;
|
|
5579
|
+
var init_async_lock = __esm({
|
|
5580
|
+
"src/utils/snapshot/async-lock.ts"() {
|
|
5581
|
+
"use strict";
|
|
5582
|
+
chains = /* @__PURE__ */ new Map();
|
|
5583
|
+
}
|
|
5584
|
+
});
|
|
5585
|
+
|
|
5547
5586
|
// src/commands/google-analysis/wrap-snapshot-envelope.ts
|
|
5548
5587
|
function coerceToRowArray(value) {
|
|
5549
5588
|
if (Array.isArray(value)) return value;
|
|
@@ -5766,26 +5805,29 @@ async function writeGoogleAnalysisSnapshot(params) {
|
|
|
5766
5805
|
const outlineBody = formatOutlineFileBody(fileName, normalizedWrapped, outlineExtra);
|
|
5767
5806
|
await fs7.writeFile(path11.join(absDir, outlineFileName), `${outlineBody}
|
|
5768
5807
|
`, "utf8");
|
|
5769
|
-
const existing = await readManifestIfExists(absDir, accountSlug || void 0);
|
|
5770
5808
|
const newArtifact = {
|
|
5771
5809
|
section: params.section,
|
|
5772
5810
|
file: relativeFile,
|
|
5773
5811
|
writtenAt,
|
|
5774
5812
|
...params.endpointHint !== void 0 ? { endpointHint: params.endpointHint } : {}
|
|
5775
5813
|
};
|
|
5776
|
-
const merged = mergeGoogleAnalysisManifest(existing, {
|
|
5777
|
-
accountId: params.accountId,
|
|
5778
|
-
dateRange: params.dateRange,
|
|
5779
|
-
cliVersion: params.cliVersion,
|
|
5780
|
-
newArtifact
|
|
5781
|
-
});
|
|
5782
5814
|
const manifestFile = googleAnalysisManifestFile(accountSlug || void 0);
|
|
5783
|
-
|
|
5784
|
-
|
|
5785
|
-
|
|
5815
|
+
const manifestPath = path11.join(absDir, manifestFile);
|
|
5816
|
+
await withAsyncLock(manifestPath, async () => {
|
|
5817
|
+
const existing = await readManifestIfExists(absDir, accountSlug || void 0);
|
|
5818
|
+
const merged = mergeGoogleAnalysisManifest(existing, {
|
|
5819
|
+
accountId: params.accountId,
|
|
5820
|
+
dateRange: params.dateRange,
|
|
5821
|
+
cliVersion: params.cliVersion,
|
|
5822
|
+
newArtifact
|
|
5823
|
+
});
|
|
5824
|
+
await fs7.writeFile(
|
|
5825
|
+
manifestPath,
|
|
5826
|
+
`${JSON.stringify(stringifyIdsDeep(merged), null, 2)}
|
|
5786
5827
|
`,
|
|
5787
|
-
|
|
5788
|
-
|
|
5828
|
+
"utf8"
|
|
5829
|
+
);
|
|
5830
|
+
});
|
|
5789
5831
|
return {
|
|
5790
5832
|
kind: "siluzan-tso-google-analysis-snapshot",
|
|
5791
5833
|
schemaVersion: SCHEMA_VERSION2,
|
|
@@ -5803,6 +5845,7 @@ var init_google_analysis = __esm({
|
|
|
5803
5845
|
"use strict";
|
|
5804
5846
|
init_cli_json();
|
|
5805
5847
|
init_wrap_snapshot_envelope();
|
|
5848
|
+
init_async_lock();
|
|
5806
5849
|
init_dir();
|
|
5807
5850
|
LEGACY_MANIFEST_FILE = "manifest.json";
|
|
5808
5851
|
CLI_PACKAGE = "siluzan-tso-cli";
|
|
@@ -102288,6 +102331,56 @@ async function loadSectionFile(snapshotDir, file) {
|
|
|
102288
102331
|
return null;
|
|
102289
102332
|
}
|
|
102290
102333
|
}
|
|
102334
|
+
function dateTagFromRange(dateRange) {
|
|
102335
|
+
if (!dateRange || dateRange.mode !== "range") return void 0;
|
|
102336
|
+
return `${dateRange.start.replace(/-/g, "")}-${dateRange.end.replace(/-/g, "")}`;
|
|
102337
|
+
}
|
|
102338
|
+
function dateTagFromMeta(meta) {
|
|
102339
|
+
const start = typeof meta?.startDate === "string" ? meta.startDate : "";
|
|
102340
|
+
const end = typeof meta?.endDate === "string" ? meta.endDate : "";
|
|
102341
|
+
if (!/^\d{4}-\d{2}-\d{2}$/.test(start) || !/^\d{4}-\d{2}-\d{2}$/.test(end)) return void 0;
|
|
102342
|
+
return `${start.replace(/-/g, "")}-${end.replace(/-/g, "")}`;
|
|
102343
|
+
}
|
|
102344
|
+
function pickSectionSnapshotFile(files, section, accountId, preferredDateTag) {
|
|
102345
|
+
const escapedSection = section.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
102346
|
+
const escapedId = accountId.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
102347
|
+
const re = new RegExp(`^${escapedSection}-${escapedId}(?:_(\\d{8}-\\d{8}))?\\.json$`);
|
|
102348
|
+
const matches = files.filter((f) => re.test(f));
|
|
102349
|
+
if (matches.length === 0) return null;
|
|
102350
|
+
if (preferredDateTag) {
|
|
102351
|
+
const preferred = matches.find((f) => f.endsWith(`_${preferredDateTag}.json`));
|
|
102352
|
+
if (preferred) return preferred;
|
|
102353
|
+
}
|
|
102354
|
+
return [...matches].sort().at(-1) ?? null;
|
|
102355
|
+
}
|
|
102356
|
+
async function recoverMissingReportSections(sectionMap, absDir, accountId, preferredDateTag) {
|
|
102357
|
+
if (!accountId) return;
|
|
102358
|
+
let entries;
|
|
102359
|
+
try {
|
|
102360
|
+
entries = await fs20.readdir(absDir);
|
|
102361
|
+
} catch {
|
|
102362
|
+
return;
|
|
102363
|
+
}
|
|
102364
|
+
for (const section of REPORT_MERGE_SECTIONS) {
|
|
102365
|
+
if (sectionMap.has(section)) continue;
|
|
102366
|
+
const file = pickSectionSnapshotFile(entries, section, accountId, preferredDateTag);
|
|
102367
|
+
if (!file) continue;
|
|
102368
|
+
const data = await loadSectionFile(absDir, file);
|
|
102369
|
+
if (data) sectionMap.set(section, data);
|
|
102370
|
+
}
|
|
102371
|
+
}
|
|
102372
|
+
function periodMetricsFromRow(row) {
|
|
102373
|
+
if (!row) return null;
|
|
102374
|
+
return {
|
|
102375
|
+
cost: num2(row.spend),
|
|
102376
|
+
impressions: num2(row.impressions),
|
|
102377
|
+
clicks: num2(row.clicks),
|
|
102378
|
+
conversions: num2(row.conversions),
|
|
102379
|
+
ctr: num2(row.ctr),
|
|
102380
|
+
averageCpc: num2(row.averageCpc),
|
|
102381
|
+
costPerConversion: num2(row.costPerConversion)
|
|
102382
|
+
};
|
|
102383
|
+
}
|
|
102291
102384
|
function items(envelope) {
|
|
102292
102385
|
if (!envelope || !Array.isArray(envelope.items)) return [];
|
|
102293
102386
|
return envelope.items.filter((row) => asRecord11(row) !== null);
|
|
@@ -102342,16 +102435,18 @@ async function mergeGoogleAnalysisSnapshotIntoReport(payload, snapshotDir) {
|
|
|
102342
102435
|
if (!manifest) {
|
|
102343
102436
|
throw new Error(`\u672A\u5728 ${absDir} \u627E\u5230 manifest*.json\uFF0C\u8BF7\u5148\u6267\u884C google-analysis --json-out`);
|
|
102344
102437
|
}
|
|
102438
|
+
const accountId = manifest.googleAnalysis?.accountId ?? metaAccountId;
|
|
102439
|
+
const dateRange = manifest.googleAnalysis?.dateRange;
|
|
102440
|
+
const preferredDateTag = dateTagFromRange(dateRange) ?? dateTagFromMeta(asRecord11(payload.meta) ?? void 0);
|
|
102345
102441
|
const sectionMap = /* @__PURE__ */ new Map();
|
|
102346
102442
|
for (const art of manifest.artifacts ?? []) {
|
|
102347
102443
|
const data = await loadSectionFile(absDir, art.file);
|
|
102348
102444
|
if (data) sectionMap.set(art.section, data);
|
|
102349
102445
|
}
|
|
102446
|
+
await recoverMissingReportSections(sectionMap, absDir, accountId, preferredDateTag);
|
|
102350
102447
|
const overviewRecord = record(sectionMap.get("overview"));
|
|
102351
102448
|
const currentPeriod = asRecord11(overviewRecord?.currentPeriod);
|
|
102352
102449
|
const previousPeriod = asRecord11(overviewRecord?.previousPeriod);
|
|
102353
|
-
const accountId = manifest.googleAnalysis?.accountId ?? metaAccountId;
|
|
102354
|
-
const dateRange = manifest.googleAnalysis?.dateRange;
|
|
102355
102450
|
const meta = { ...payload.meta ?? {} };
|
|
102356
102451
|
if (!meta.accountId && accountId) meta.accountId = accountId;
|
|
102357
102452
|
if (!meta.currency) {
|
|
@@ -102363,16 +102458,9 @@ async function mergeGoogleAnalysisSnapshotIntoReport(payload, snapshotDir) {
|
|
|
102363
102458
|
if (!meta.generatedAt) meta.generatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
102364
102459
|
const campaignRows2 = items(sectionMap.get("campaigns"));
|
|
102365
102460
|
const campaignKpis = sumCampaignRowsToPeriodKpis(campaignRows2);
|
|
102461
|
+
const dimensionSummaryRecord = record(sectionMap.get("dimension-summary"));
|
|
102366
102462
|
const kpis = { ...payload.kpis ?? {} };
|
|
102367
|
-
const currentKpiSource = campaignKpis ?? (currentPeriod
|
|
102368
|
-
cost: num2(currentPeriod.spend),
|
|
102369
|
-
impressions: num2(currentPeriod.impressions),
|
|
102370
|
-
clicks: num2(currentPeriod.clicks),
|
|
102371
|
-
conversions: num2(currentPeriod.conversions),
|
|
102372
|
-
ctr: num2(currentPeriod.ctr),
|
|
102373
|
-
averageCpc: num2(currentPeriod.averageCpc),
|
|
102374
|
-
costPerConversion: num2(currentPeriod.costPerConversion)
|
|
102375
|
-
} : null);
|
|
102463
|
+
const currentKpiSource = campaignKpis ?? periodMetricsFromRow(currentPeriod) ?? periodMetricsFromRow(dimensionSummaryRecord);
|
|
102376
102464
|
if (currentKpiSource) {
|
|
102377
102465
|
if (currentKpiSource.cost != null) kpis.cost = currentKpiSource.cost;
|
|
102378
102466
|
if (currentKpiSource.impressions != null) kpis.impressions = currentKpiSource.impressions;
|
|
@@ -102411,22 +102499,21 @@ async function mergeGoogleAnalysisSnapshotIntoReport(payload, snapshotDir) {
|
|
|
102411
102499
|
}
|
|
102412
102500
|
}
|
|
102413
102501
|
const tables = { ...payload.tables ?? {} };
|
|
102414
|
-
{
|
|
102415
|
-
|
|
102416
|
-
|
|
102417
|
-
|
|
102418
|
-
|
|
102419
|
-
|
|
102420
|
-
|
|
102421
|
-
|
|
102422
|
-
|
|
102423
|
-
|
|
102424
|
-
|
|
102425
|
-
|
|
102426
|
-
|
|
102427
|
-
|
|
102428
|
-
|
|
102429
|
-
}
|
|
102502
|
+
if (dimensionSummaryRecord) {
|
|
102503
|
+
tables.dimensionSummary = {
|
|
102504
|
+
cost: num2(dimensionSummaryRecord.spend),
|
|
102505
|
+
impressions: num2(dimensionSummaryRecord.impressions),
|
|
102506
|
+
clicks: num2(dimensionSummaryRecord.clicks),
|
|
102507
|
+
conversions: num2(dimensionSummaryRecord.conversions),
|
|
102508
|
+
ctr: num2(dimensionSummaryRecord.ctr),
|
|
102509
|
+
averageCpc: num2(dimensionSummaryRecord.averageCpc),
|
|
102510
|
+
costPerConversion: num2(dimensionSummaryRecord.costPerConversion),
|
|
102511
|
+
searchImpressionShare: num2(dimensionSummaryRecord.searchImpressionShare),
|
|
102512
|
+
searchBudgetLostImpressionShare: num2(
|
|
102513
|
+
dimensionSummaryRecord.searchBudgetLostImpressionShare
|
|
102514
|
+
),
|
|
102515
|
+
searchRankLostImpressionShare: num2(dimensionSummaryRecord.searchRankLostImpressionShare)
|
|
102516
|
+
};
|
|
102430
102517
|
}
|
|
102431
102518
|
if (campaignRows2.length > 0) {
|
|
102432
102519
|
tables.campaigns = campaignRows2.map((r) => ({
|
|
@@ -102464,10 +102551,20 @@ async function mergeGoogleAnalysisSnapshotIntoReport(payload, snapshotDir) {
|
|
|
102464
102551
|
tables
|
|
102465
102552
|
};
|
|
102466
102553
|
}
|
|
102554
|
+
var REPORT_MERGE_SECTIONS;
|
|
102467
102555
|
var init_merge_snapshot = __esm({
|
|
102468
102556
|
"src/commands/google-analysis/merge-snapshot.ts"() {
|
|
102469
102557
|
"use strict";
|
|
102470
102558
|
init_google_analysis();
|
|
102559
|
+
REPORT_MERGE_SECTIONS = [
|
|
102560
|
+
"overview",
|
|
102561
|
+
"daily-metrics",
|
|
102562
|
+
"dimension-summary",
|
|
102563
|
+
"campaigns",
|
|
102564
|
+
"devices",
|
|
102565
|
+
"geographic",
|
|
102566
|
+
"keywords"
|
|
102567
|
+
];
|
|
102471
102568
|
}
|
|
102472
102569
|
});
|
|
102473
102570
|
|
|
@@ -102511,6 +102608,19 @@ function validateGooglePeriodReportContent(data) {
|
|
|
102511
102608
|
"\u22651 \u6BB5\u6267\u884C\u6458\u8981\uFF0C\u6982\u8FF0\u672C\u671F\u6D88\u8017/\u8F6C\u5316/CTR/CPA \u8D70\u52BF"
|
|
102512
102609
|
);
|
|
102513
102610
|
}
|
|
102611
|
+
const kpis = asRecord12(data.kpis);
|
|
102612
|
+
const hasCoreKpi = ["cost", "impressions", "clicks"].some(
|
|
102613
|
+
(key) => Number.isFinite(Number(kpis?.[key]))
|
|
102614
|
+
);
|
|
102615
|
+
if (!hasCoreKpi) {
|
|
102616
|
+
pushMissing(
|
|
102617
|
+
missing,
|
|
102618
|
+
"kpis-core-metrics",
|
|
102619
|
+
"\u6267\u884C\u6458\u8981 KPI",
|
|
102620
|
+
"kpis.cost / kpis.impressions / kpis.clicks",
|
|
102621
|
+
"\u81F3\u5C11\u4E00\u9879\u987B\u4E3A\u6709\u6548\u6570\u5B57\uFF1B\u8BF7\u52A0 --snapshot-dir \u4ECE google-analysis \u5FEB\u7167\u5408\u5E76\uFF08\u52FF\u624B\u586B HTML\uFF09"
|
|
102622
|
+
);
|
|
102623
|
+
}
|
|
102514
102624
|
const sectionAnalysisCounts = {};
|
|
102515
102625
|
const sectionSuggestionCounts = {};
|
|
102516
102626
|
for (const key of SECTION_KEYS) {
|
|
@@ -102571,7 +102681,7 @@ function formatGooglePeriodReportErrors(result) {
|
|
|
102571
102681
|
...lines,
|
|
102572
102682
|
"",
|
|
102573
102683
|
"\u8BF7\u8865\u5168 narrative.sections.<dimension>.{analysis,suggestions} \u4E0E narrative.recommendations \u540E\u91CD\u65B0\u6267\u884C render\u3002",
|
|
102574
|
-
"
|
|
102684
|
+
"kpis/charts/tables \u987B\u7531 --snapshot-dir \u4ECE google-analysis \u5FEB\u7167\u81EA\u52A8\u5408\u5E76\uFF08Agent \u53EA\u5199 narrative\uFF09\uFF1B\u5408\u5E76\u540E kpis \u4ECD\u4E3A\u7A7A\u65F6\u68C0\u67E5\u5FEB\u7167\u76EE\u5F55 manifest \u662F\u5426\u542B overview/campaigns/dimension-summary\u3002\u7981\u6B62\u624B\u5199 HTML\u3002"
|
|
102575
102685
|
].join("\n");
|
|
102576
102686
|
}
|
|
102577
102687
|
var SECTION_KEYS, SECTION_LABELS;
|
|
@@ -107168,6 +107278,20 @@ init_list_fetch();
|
|
|
107168
107278
|
|
|
107169
107279
|
// src/commands/ad/_shared.ts
|
|
107170
107280
|
init_auth();
|
|
107281
|
+
init_media_customer_id();
|
|
107282
|
+
function resolveAdAccountId(account, label = "-a, --account") {
|
|
107283
|
+
try {
|
|
107284
|
+
return requireNumericMediaCustomerId(account, label);
|
|
107285
|
+
} catch (e) {
|
|
107286
|
+
console.error(`
|
|
107287
|
+
\u274C ${e instanceof Error ? e.message : String(e)}
|
|
107288
|
+
`);
|
|
107289
|
+
process.exit(1);
|
|
107290
|
+
}
|
|
107291
|
+
}
|
|
107292
|
+
function withResolvedAdAccount(opts) {
|
|
107293
|
+
return { ...opts, account: resolveAdAccountId(opts.account) };
|
|
107294
|
+
}
|
|
107171
107295
|
async function findItemInList(listUrl, config, id, verbose) {
|
|
107172
107296
|
const res = await apiFetch2(listUrl, config, {}, verbose);
|
|
107173
107297
|
const item = (res.data ?? []).find((x) => x["id"] === id);
|
|
@@ -108342,6 +108466,7 @@ init_cli_table();
|
|
|
108342
108466
|
init_cli_json_snapshot();
|
|
108343
108467
|
|
|
108344
108468
|
// src/utils/snapshot/report-analysis.ts
|
|
108469
|
+
init_async_lock();
|
|
108345
108470
|
init_google_analysis();
|
|
108346
108471
|
init_cli_json();
|
|
108347
108472
|
import * as fs8 from "fs/promises";
|
|
@@ -108415,25 +108540,24 @@ async function writeReportAnalysisSnapshot(params) {
|
|
|
108415
108540
|
`,
|
|
108416
108541
|
"utf8"
|
|
108417
108542
|
);
|
|
108418
|
-
const existing = await readReportManifestIfExists(absDir, accountSlug || void 0);
|
|
108419
108543
|
const newArtifact = {
|
|
108420
108544
|
section: params.section,
|
|
108421
108545
|
file: fileName,
|
|
108422
108546
|
writtenAt
|
|
108423
108547
|
};
|
|
108424
|
-
const merged = mergeReportAnalysisManifest(existing, {
|
|
108425
|
-
accountId: params.accountId,
|
|
108426
|
-
dateRange: params.dateRange,
|
|
108427
|
-
cliVersion: params.cliVersion,
|
|
108428
|
-
newArtifact
|
|
108429
|
-
});
|
|
108430
108548
|
const manifestFile = reportManifestFile(accountSlug || void 0);
|
|
108431
|
-
|
|
108432
|
-
|
|
108433
|
-
|
|
108434
|
-
|
|
108435
|
-
|
|
108436
|
-
|
|
108549
|
+
const manifestPath = path12.join(absDir, manifestFile);
|
|
108550
|
+
await withAsyncLock(manifestPath, async () => {
|
|
108551
|
+
const existing = await readReportManifestIfExists(absDir, accountSlug || void 0);
|
|
108552
|
+
const merged = mergeReportAnalysisManifest(existing, {
|
|
108553
|
+
accountId: params.accountId,
|
|
108554
|
+
dateRange: params.dateRange,
|
|
108555
|
+
cliVersion: params.cliVersion,
|
|
108556
|
+
newArtifact
|
|
108557
|
+
});
|
|
108558
|
+
await fs8.writeFile(manifestPath, `${JSON.stringify(merged, null, 2)}
|
|
108559
|
+
`, "utf8");
|
|
108560
|
+
});
|
|
108437
108561
|
return {
|
|
108438
108562
|
kind: "siluzan-tso-report-analysis-snapshot",
|
|
108439
108563
|
schemaVersion: SCHEMA_VERSION3,
|
|
@@ -111297,6 +111421,9 @@ init_cli_json_snapshot();
|
|
|
111297
111421
|
init_strip_legacy_google_fields();
|
|
111298
111422
|
init_cli_table();
|
|
111299
111423
|
|
|
111424
|
+
// src/commands/ad/campaign-create-validate.ts
|
|
111425
|
+
init_media_customer_id();
|
|
111426
|
+
|
|
111300
111427
|
// src/commands/ad/campaign-length-violations.ts
|
|
111301
111428
|
function pushLengthViolation(violations, item) {
|
|
111302
111429
|
violations.push({ ...item, excess: item.actual - item.limit });
|
|
@@ -111694,6 +111821,7 @@ function keywordRowToYuan(item) {
|
|
|
111694
111821
|
return { ...rest, maxCPCYuan: toDisplayMoney(rawMaxCpc) };
|
|
111695
111822
|
}
|
|
111696
111823
|
async function runAdKeywords(opts) {
|
|
111824
|
+
opts = withResolvedAdAccount(opts);
|
|
111697
111825
|
const config = loadConfig(opts.token);
|
|
111698
111826
|
const googleApiUrl = requireGoogleApi(config);
|
|
111699
111827
|
const params = new URLSearchParams();
|
|
@@ -111797,6 +111925,7 @@ ${label}\uFF08\u8D26\u6237\uFF1A${opts.account}\uFF0C\u7B2C 1 \u9875\uFF0C\u672C
|
|
|
111797
111925
|
console.log();
|
|
111798
111926
|
}
|
|
111799
111927
|
async function runAdKeywordCreate(opts) {
|
|
111928
|
+
opts = withResolvedAdAccount(opts);
|
|
111800
111929
|
const config = loadConfig(opts.token);
|
|
111801
111930
|
const googleApiUrl = requireGoogleApi(config);
|
|
111802
111931
|
let keywords;
|
|
@@ -111849,6 +111978,7 @@ async function runAdKeywordCreate(opts) {
|
|
|
111849
111978
|
`);
|
|
111850
111979
|
}
|
|
111851
111980
|
async function runAdNegativeKeywordCreate(opts) {
|
|
111981
|
+
opts = withResolvedAdAccount(opts);
|
|
111852
111982
|
const config = loadConfig(opts.token);
|
|
111853
111983
|
const googleApiUrl = requireGoogleApi(config);
|
|
111854
111984
|
let keywords;
|
|
@@ -111888,6 +112018,7 @@ async function runAdNegativeKeywordCreate(opts) {
|
|
|
111888
112018
|
`);
|
|
111889
112019
|
}
|
|
111890
112020
|
async function runAdKeywordDelete(opts) {
|
|
112021
|
+
opts = withResolvedAdAccount(opts);
|
|
111891
112022
|
const config = await ensureDataPermission(loadConfig(opts.token));
|
|
111892
112023
|
const googleApiUrl = requireGoogleApi(config);
|
|
111893
112024
|
const body = [{ adGroupId: opts.adgroupId, id: opts.id }];
|
|
@@ -111905,6 +112036,7 @@ async function runAdKeywordDelete(opts) {
|
|
|
111905
112036
|
`);
|
|
111906
112037
|
}
|
|
111907
112038
|
async function runAdKeywordEdit(opts) {
|
|
112039
|
+
opts = withResolvedAdAccount(opts);
|
|
111908
112040
|
const config = await ensureDataPermission(loadConfig(opts.token));
|
|
111909
112041
|
const googleApiUrl = requireGoogleApi(config);
|
|
111910
112042
|
const hasChange = opts.text !== void 0 || opts.matchType !== void 0 || opts.maxCpc !== void 0 || opts.finalUrl !== void 0 || opts.status !== void 0;
|
|
@@ -111970,6 +112102,7 @@ async function runAdKeywordEdit(opts) {
|
|
|
111970
112102
|
`);
|
|
111971
112103
|
}
|
|
111972
112104
|
async function runAdKeywordStatus(opts) {
|
|
112105
|
+
opts = withResolvedAdAccount(opts);
|
|
111973
112106
|
const config = await ensureDataPermission(loadConfig(opts.token));
|
|
111974
112107
|
const googleApiUrl = requireGoogleApi(config);
|
|
111975
112108
|
const params = new URLSearchParams();
|
|
@@ -112005,6 +112138,7 @@ async function runAdKeywordStatus(opts) {
|
|
|
112005
112138
|
`);
|
|
112006
112139
|
}
|
|
112007
112140
|
async function runAdNegativeKeywordEdit(opts) {
|
|
112141
|
+
opts = withResolvedAdAccount(opts);
|
|
112008
112142
|
const config = loadConfig(opts.token);
|
|
112009
112143
|
const googleApiUrl = requireGoogleApi(config);
|
|
112010
112144
|
const dateParams = new URLSearchParams();
|
|
@@ -112046,6 +112180,7 @@ async function runAdNegativeKeywordEdit(opts) {
|
|
|
112046
112180
|
`);
|
|
112047
112181
|
}
|
|
112048
112182
|
async function runAdNegativeKeywordDelete(opts) {
|
|
112183
|
+
opts = withResolvedAdAccount(opts);
|
|
112049
112184
|
const config = loadConfig(opts.token);
|
|
112050
112185
|
const googleApiUrl = requireGoogleApi(config);
|
|
112051
112186
|
const dateParams = new URLSearchParams();
|
|
@@ -112888,9 +113023,13 @@ function validateCampaignCreateConfigCore(cfg) {
|
|
|
112888
113023
|
"customerName \u4E3A\u7A7A\uFF1A\u63D0\u4EA4 campaign-create \u65F6\u5C06\u6309 account \u4ECE list-accounts \u81EA\u52A8\u586B\u5145 mediaCustomerName"
|
|
112889
113024
|
);
|
|
112890
113025
|
}
|
|
112891
|
-
const
|
|
112892
|
-
|
|
112893
|
-
|
|
113026
|
+
const normalizedAccount = normalizeNumericMediaCustomerId(String(cfg.account ?? "").trim());
|
|
113027
|
+
const accountNum = normalizedAccount ? Number(normalizedAccount) : NaN;
|
|
113028
|
+
if (!normalizedAccount || !Number.isFinite(accountNum) || accountNum <= 0) {
|
|
113029
|
+
pushErr2(
|
|
113030
|
+
errors,
|
|
113031
|
+
"account \u5FC5\u987B\u662F\u53EF\u89E3\u6790\u4E3A\u6B63\u6570\u7684\u5A92\u4F53\u8D26\u6237 ID\uFF08\u53EF\u542B\u8FDE\u5B57\u7B26\uFF1B\u63D0\u4EA4\u65F6 customerId \u4E3A number\uFF09"
|
|
113032
|
+
);
|
|
112894
113033
|
}
|
|
112895
113034
|
if (!cfg.campaign || typeof cfg.campaign !== "object" || Array.isArray(cfg.campaign)) {
|
|
112896
113035
|
pushErr2(errors, "campaign \u5BF9\u8C61\u4E0D\u80FD\u4E3A\u7A7A\uFF08\u540E\u7AEF\uFF1Acampaign is null\uFF09");
|
|
@@ -113256,6 +113395,7 @@ function parseCampaignTimeMs(v) {
|
|
|
113256
113395
|
return Number.isFinite(t) ? t : null;
|
|
113257
113396
|
}
|
|
113258
113397
|
async function runAdCampaigns(opts) {
|
|
113398
|
+
opts = withResolvedAdAccount(opts);
|
|
113259
113399
|
const config = loadConfig(opts.token);
|
|
113260
113400
|
const googleApiUrl = requireGoogleApi(config);
|
|
113261
113401
|
const params = new URLSearchParams();
|
|
@@ -113338,6 +113478,7 @@ async function runAdCampaigns(opts) {
|
|
|
113338
113478
|
console.log();
|
|
113339
113479
|
}
|
|
113340
113480
|
async function runAdCampaignStatus(opts) {
|
|
113481
|
+
opts = withResolvedAdAccount(opts);
|
|
113341
113482
|
const config = loadConfig(opts.token);
|
|
113342
113483
|
const googleApiUrl = requireGoogleApi(config);
|
|
113343
113484
|
const detailUrl = `${googleApiUrl}/campaignmanagement/campaign/${opts.account}/${opts.id}`;
|
|
@@ -113372,6 +113513,7 @@ async function runAdCampaignStatus(opts) {
|
|
|
113372
113513
|
`);
|
|
113373
113514
|
}
|
|
113374
113515
|
async function runAdCampaignDelete(opts) {
|
|
113516
|
+
opts = withResolvedAdAccount(opts);
|
|
113375
113517
|
const config = loadConfig(opts.token);
|
|
113376
113518
|
const googleApiUrl = requireGoogleApi(config);
|
|
113377
113519
|
const detailUrl = `${googleApiUrl}/campaignmanagement/campaign/${opts.account}/${opts.id}`;
|
|
@@ -113409,6 +113551,7 @@ async function runAdCampaignDelete(opts) {
|
|
|
113409
113551
|
}
|
|
113410
113552
|
async function runAdCampaignCreate(opts) {
|
|
113411
113553
|
const cfg = loadCampaignCreateConfig(opts.configFile);
|
|
113554
|
+
cfg.account = resolveAdAccountId(cfg.account, "account");
|
|
113412
113555
|
const { errors, warnings } = runCampaignCreateValidation(cfg);
|
|
113413
113556
|
if (warnings.length > 0) {
|
|
113414
113557
|
console.warn("\n\u26A0\uFE0F \u6295\u653E\u914D\u7F6E\u8B66\u544A\uFF08\u4E0D\u963B\u65AD\u63D0\u4EA4\uFF0C\u4F46\u5EFA\u8BAE\u4FEE\u6B63\uFF09\uFF1A");
|
|
@@ -113615,6 +113758,7 @@ function validateCampaignEditBidding(opts) {
|
|
|
113615
113758
|
}
|
|
113616
113759
|
}
|
|
113617
113760
|
async function runAdCampaignEdit(opts) {
|
|
113761
|
+
opts = withResolvedAdAccount(opts);
|
|
113618
113762
|
validateCampaignEditBidding(opts);
|
|
113619
113763
|
const config = loadConfig(opts.token);
|
|
113620
113764
|
const googleApiUrl = requireGoogleApi(config);
|
|
@@ -113684,6 +113828,7 @@ init_cli_json_snapshot();
|
|
|
113684
113828
|
init_strip_legacy_google_fields();
|
|
113685
113829
|
init_cli_table();
|
|
113686
113830
|
async function runAdGroups(opts) {
|
|
113831
|
+
opts = withResolvedAdAccount(opts);
|
|
113687
113832
|
const config = loadConfig(opts.token);
|
|
113688
113833
|
const googleApiUrl = requireGoogleApi(config);
|
|
113689
113834
|
const params = new URLSearchParams();
|
|
@@ -113775,6 +113920,7 @@ function adGroupRowToYuan(item) {
|
|
|
113775
113920
|
};
|
|
113776
113921
|
}
|
|
113777
113922
|
async function runAdGroupCreate(opts) {
|
|
113923
|
+
opts = withResolvedAdAccount(opts);
|
|
113778
113924
|
const config = loadConfig(opts.token);
|
|
113779
113925
|
const googleApiUrl = requireGoogleApi(config);
|
|
113780
113926
|
const body = {
|
|
@@ -113826,6 +113972,7 @@ async function runAdGroupCreate(opts) {
|
|
|
113826
113972
|
}
|
|
113827
113973
|
}
|
|
113828
113974
|
async function runAdGroupStatus(opts) {
|
|
113975
|
+
opts = withResolvedAdAccount(opts);
|
|
113829
113976
|
const config = loadConfig(opts.token);
|
|
113830
113977
|
const googleApiUrl = requireGoogleApi(config);
|
|
113831
113978
|
const listUrl = adgroupListUrl(googleApiUrl, opts.account, opts.startDate, opts.endDate);
|
|
@@ -113853,6 +114000,7 @@ async function runAdGroupStatus(opts) {
|
|
|
113853
114000
|
`);
|
|
113854
114001
|
}
|
|
113855
114002
|
async function runAdGroupDelete(opts) {
|
|
114003
|
+
opts = withResolvedAdAccount(opts);
|
|
113856
114004
|
const config = loadConfig(opts.token);
|
|
113857
114005
|
const googleApiUrl = requireGoogleApi(config);
|
|
113858
114006
|
const listUrl = adgroupListUrl(googleApiUrl, opts.account, opts.startDate, opts.endDate);
|
|
@@ -113879,6 +114027,7 @@ async function runAdGroupDelete(opts) {
|
|
|
113879
114027
|
`);
|
|
113880
114028
|
}
|
|
113881
114029
|
async function runAdGroupRename(opts) {
|
|
114030
|
+
opts = withResolvedAdAccount(opts);
|
|
113882
114031
|
const config = loadConfig(opts.token);
|
|
113883
114032
|
const googleApiUrl = requireGoogleApi(config);
|
|
113884
114033
|
const listUrl = adgroupListUrl(googleApiUrl, opts.account, opts.startDate, opts.endDate);
|
|
@@ -113906,6 +114055,7 @@ async function runAdGroupRename(opts) {
|
|
|
113906
114055
|
`);
|
|
113907
114056
|
}
|
|
113908
114057
|
async function runAdGroupEdit(opts) {
|
|
114058
|
+
opts = withResolvedAdAccount(opts);
|
|
113909
114059
|
const hasName = opts.name !== void 0 && opts.name.trim() !== "";
|
|
113910
114060
|
const hasMax = opts.maxCPCAmount !== void 0;
|
|
113911
114061
|
const hasTcpa = opts.targetCpaAmount !== void 0;
|
|
@@ -114028,6 +114178,7 @@ function formatRsaCliValidationError(errors) {
|
|
|
114028
114178
|
|
|
114029
114179
|
// src/commands/ad/ad-creative.ts
|
|
114030
114180
|
async function runAdList(opts) {
|
|
114181
|
+
opts = withResolvedAdAccount(opts);
|
|
114031
114182
|
const config = loadConfig(opts.token);
|
|
114032
114183
|
const googleApiUrl = requireGoogleApi(config);
|
|
114033
114184
|
const params = new URLSearchParams();
|
|
@@ -114104,6 +114255,7 @@ async function runAdList(opts) {
|
|
|
114104
114255
|
console.log();
|
|
114105
114256
|
}
|
|
114106
114257
|
async function runAdCreate(opts) {
|
|
114258
|
+
opts = withResolvedAdAccount(opts);
|
|
114107
114259
|
const config = loadConfig(opts.token);
|
|
114108
114260
|
const googleApiUrl = requireGoogleApi(config);
|
|
114109
114261
|
const rsaErrors = validateRsaCliCopy({
|
|
@@ -114170,6 +114322,7 @@ async function runAdCreate(opts) {
|
|
|
114170
114322
|
}
|
|
114171
114323
|
}
|
|
114172
114324
|
async function runAdStatus(opts) {
|
|
114325
|
+
opts = withResolvedAdAccount(opts);
|
|
114173
114326
|
const config = loadConfig(opts.token);
|
|
114174
114327
|
const googleApiUrl = requireGoogleApi(config);
|
|
114175
114328
|
const params = new URLSearchParams();
|
|
@@ -114200,6 +114353,7 @@ async function runAdStatus(opts) {
|
|
|
114200
114353
|
`);
|
|
114201
114354
|
}
|
|
114202
114355
|
async function runAdDelete(opts) {
|
|
114356
|
+
opts = withResolvedAdAccount(opts);
|
|
114203
114357
|
const config = loadConfig(opts.token);
|
|
114204
114358
|
const googleApiUrl = requireGoogleApi(config);
|
|
114205
114359
|
const params = new URLSearchParams();
|
|
@@ -114229,6 +114383,7 @@ async function runAdDelete(opts) {
|
|
|
114229
114383
|
`);
|
|
114230
114384
|
}
|
|
114231
114385
|
async function runAdEdit(opts) {
|
|
114386
|
+
opts = withResolvedAdAccount(opts);
|
|
114232
114387
|
const config = await ensureDataPermission(loadConfig(opts.token));
|
|
114233
114388
|
const googleApiUrl = requireGoogleApi(config);
|
|
114234
114389
|
const hasTextOrUrl = opts.headlines || opts.descriptions || opts.finalUrl !== void 0 || opts.path1 !== void 0 || opts.path2 !== void 0;
|
|
@@ -114532,6 +114687,7 @@ function buildBusinessMessageBodyForCampaign(accountId, campaignId, bm) {
|
|
|
114532
114687
|
|
|
114533
114688
|
// src/commands/ad/extension.ts
|
|
114534
114689
|
async function runAdExtensionList(opts) {
|
|
114690
|
+
opts = withResolvedAdAccount(opts);
|
|
114535
114691
|
const config = loadConfig(opts.token);
|
|
114536
114692
|
const googleApiUrl = requireGoogleApi(config);
|
|
114537
114693
|
const url = `${googleApiUrl}/extensionmanagement/v2/list/${opts.account}`;
|
|
@@ -114608,6 +114764,7 @@ async function runAdExtensionList(opts) {
|
|
|
114608
114764
|
console.log();
|
|
114609
114765
|
}
|
|
114610
114766
|
async function runAdExtensionCreate(opts) {
|
|
114767
|
+
opts = withResolvedAdAccount(opts);
|
|
114611
114768
|
const config = loadConfig(opts.token);
|
|
114612
114769
|
const googleApiUrl = requireGoogleApi(config);
|
|
114613
114770
|
const body = {
|
|
@@ -114685,6 +114842,7 @@ async function runAdExtensionCreate(opts) {
|
|
|
114685
114842
|
}
|
|
114686
114843
|
}
|
|
114687
114844
|
async function runAdExtensionDelete(opts) {
|
|
114845
|
+
opts = withResolvedAdAccount(opts);
|
|
114688
114846
|
const config = loadConfig(opts.token);
|
|
114689
114847
|
const googleApiUrl = requireGoogleApi(config);
|
|
114690
114848
|
const listUrl = `${googleApiUrl}/extensionmanagement/v2/list/${opts.account}`;
|
|
@@ -114786,6 +114944,7 @@ async function runAdExtensionSnippetHeaders(opts) {
|
|
|
114786
114944
|
console.log();
|
|
114787
114945
|
}
|
|
114788
114946
|
async function runAdExtensionLeadFormCreate(opts) {
|
|
114947
|
+
opts = withResolvedAdAccount(opts);
|
|
114789
114948
|
const config = loadConfig(opts.token);
|
|
114790
114949
|
const googleApiUrl = requireGoogleApi(config);
|
|
114791
114950
|
let cfg;
|
|
@@ -114844,6 +115003,7 @@ async function runAdExtensionLeadFormCreate(opts) {
|
|
|
114844
115003
|
);
|
|
114845
115004
|
}
|
|
114846
115005
|
async function runAdExtensionWhatsappCreate(opts) {
|
|
115006
|
+
opts = withResolvedAdAccount(opts);
|
|
114847
115007
|
const config = loadConfig(opts.token);
|
|
114848
115008
|
const googleApiUrl = requireGoogleApi(config);
|
|
114849
115009
|
if (!opts.configFile) {
|
|
@@ -114906,6 +115066,7 @@ function detectExtensionUpdateKind(raw) {
|
|
|
114906
115066
|
process.exit(1);
|
|
114907
115067
|
}
|
|
114908
115068
|
async function runAdExtensionUpdate(opts) {
|
|
115069
|
+
opts = withResolvedAdAccount(opts);
|
|
114909
115070
|
const config = loadConfig(opts.token);
|
|
114910
115071
|
const googleApiUrl = requireGoogleApi(config);
|
|
114911
115072
|
let raw;
|
|
@@ -114980,6 +115141,7 @@ init_cli_json_snapshot();
|
|
|
114980
115141
|
init_strip_legacy_google_fields();
|
|
114981
115142
|
init_cli_table();
|
|
114982
115143
|
async function runAdSearchTerms(opts) {
|
|
115144
|
+
opts = withResolvedAdAccount(opts);
|
|
114983
115145
|
const config = loadConfig(opts.token);
|
|
114984
115146
|
const googleApiUrl = requireGoogleApi(config);
|
|
114985
115147
|
const params = new URLSearchParams();
|
|
@@ -115108,6 +115270,7 @@ function resolveCriterionId(rows, opts) {
|
|
|
115108
115270
|
return String(match.id);
|
|
115109
115271
|
}
|
|
115110
115272
|
async function runAdDeviceBidList(opts) {
|
|
115273
|
+
opts = withResolvedAdAccount(opts);
|
|
115111
115274
|
const config = loadConfig(opts.token);
|
|
115112
115275
|
const googleApiUrl = requireGoogleApi(config);
|
|
115113
115276
|
const level = opts.level ?? "campaign";
|
|
@@ -115204,6 +115367,7 @@ async function runAdDeviceBidList(opts) {
|
|
|
115204
115367
|
console.log();
|
|
115205
115368
|
}
|
|
115206
115369
|
async function runAdDeviceBidSet(opts) {
|
|
115370
|
+
opts = withResolvedAdAccount(opts);
|
|
115207
115371
|
const config = loadConfig(opts.token);
|
|
115208
115372
|
const googleApiUrl = requireGoogleApi(config);
|
|
115209
115373
|
const level = opts.level ?? "campaign";
|
|
@@ -115334,6 +115498,7 @@ function pickGeoResolveCandidate(query, items2) {
|
|
|
115334
115498
|
return items2[0] ?? null;
|
|
115335
115499
|
}
|
|
115336
115500
|
async function runAdGeoSearch(opts) {
|
|
115501
|
+
opts = withResolvedAdAccount(opts);
|
|
115337
115502
|
const config = loadConfig(opts.token);
|
|
115338
115503
|
const googleApiUrl = requireGoogleApi(config);
|
|
115339
115504
|
let items2;
|
|
@@ -115406,6 +115571,7 @@ function parseGeoResolveQueries(opts) {
|
|
|
115406
115571
|
return unique;
|
|
115407
115572
|
}
|
|
115408
115573
|
async function runAdGeoResolve(opts) {
|
|
115574
|
+
opts = withResolvedAdAccount(opts);
|
|
115409
115575
|
let queries;
|
|
115410
115576
|
try {
|
|
115411
115577
|
queries = parseGeoResolveQueries(opts);
|
|
@@ -115517,6 +115683,7 @@ async function runAdGeoResolve(opts) {
|
|
|
115517
115683
|
}
|
|
115518
115684
|
}
|
|
115519
115685
|
async function runAdGeoList(opts) {
|
|
115686
|
+
opts = withResolvedAdAccount(opts);
|
|
115520
115687
|
const config = loadConfig(opts.token);
|
|
115521
115688
|
const googleApiUrl = requireGoogleApi(config);
|
|
115522
115689
|
let url;
|
|
@@ -115580,6 +115747,7 @@ async function runAdGeoList(opts) {
|
|
|
115580
115747
|
console.log();
|
|
115581
115748
|
}
|
|
115582
115749
|
async function runAdGeoAdd(opts) {
|
|
115750
|
+
opts = withResolvedAdAccount(opts);
|
|
115583
115751
|
const config = loadConfig(opts.token);
|
|
115584
115752
|
const googleApiUrl = requireGoogleApi(config);
|
|
115585
115753
|
const body = {
|
|
@@ -115606,6 +115774,7 @@ async function runAdGeoAdd(opts) {
|
|
|
115606
115774
|
`);
|
|
115607
115775
|
}
|
|
115608
115776
|
async function runAdGeoRemove(opts) {
|
|
115777
|
+
opts = withResolvedAdAccount(opts);
|
|
115609
115778
|
const config = loadConfig(opts.token);
|
|
115610
115779
|
const googleApiUrl = requireGoogleApi(config);
|
|
115611
115780
|
const body = { campaignId: opts.campaignId, id: opts.locationId };
|
|
@@ -115646,6 +115815,7 @@ function resolveGeoCriterionId(rows, opts) {
|
|
|
115646
115815
|
return String(match["id"]);
|
|
115647
115816
|
}
|
|
115648
115817
|
async function runAdGeoSetBid(opts) {
|
|
115818
|
+
opts = withResolvedAdAccount(opts);
|
|
115649
115819
|
const config = loadConfig(opts.token);
|
|
115650
115820
|
const googleApiUrl = requireGoogleApi(config);
|
|
115651
115821
|
if (opts.bidModifier < 0) {
|
|
@@ -116502,11 +116672,12 @@ async function runCampaignBatchDiff(opts) {
|
|
|
116502
116672
|
`);
|
|
116503
116673
|
process.exit(1);
|
|
116504
116674
|
}
|
|
116505
|
-
const
|
|
116506
|
-
if (!
|
|
116675
|
+
const rawAccountId = pickString2(opts.account, cfg.account);
|
|
116676
|
+
if (!rawAccountId) {
|
|
116507
116677
|
console.error("\n\u274C \u7F3A\u5C11\u8D26\u6237 ID\uFF1A\u8BF7\u5728 JSON \u4E2D\u586B\u5199 account \u6216\u4F7F\u7528 -a\n");
|
|
116508
116678
|
process.exit(1);
|
|
116509
116679
|
}
|
|
116680
|
+
const accountId = resolveAdAccountId(rawAccountId, "account / -a");
|
|
116510
116681
|
cfg.account = accountId;
|
|
116511
116682
|
const config = loadConfig(opts.token);
|
|
116512
116683
|
let campaignId = pickString2(opts.campaignId);
|
|
@@ -118527,14 +118698,7 @@ async function emitPmaxResult(opts, section, commandLabel, payload, idSuffix) {
|
|
|
118527
118698
|
});
|
|
118528
118699
|
}
|
|
118529
118700
|
function requireAccountId(account, label = "-a, --account") {
|
|
118530
|
-
|
|
118531
|
-
if (!id) {
|
|
118532
|
-
console.error(`
|
|
118533
|
-
\u274C \u8BF7\u6307\u5B9A\u5A92\u4F53\u8D26\u6237 ID\uFF08${label}\uFF09
|
|
118534
|
-
`);
|
|
118535
|
-
process.exit(1);
|
|
118536
|
-
}
|
|
118537
|
-
return id;
|
|
118701
|
+
return resolveAdAccountId(account, label);
|
|
118538
118702
|
}
|
|
118539
118703
|
|
|
118540
118704
|
// src/commands/ad/pmax-video-upload.ts
|
|
@@ -118897,7 +119061,8 @@ async function runAdPmaxCreate(opts) {
|
|
|
118897
119061
|
}
|
|
118898
119062
|
const config = await ensureDataPermission(loadConfig(opts.token));
|
|
118899
119063
|
const googleApiUrl = requireGoogleApi(config);
|
|
118900
|
-
const accountId = cfg.account.toString()
|
|
119064
|
+
const accountId = resolveAdAccountId(cfg.account.toString(), "account");
|
|
119065
|
+
cfg.account = accountId;
|
|
118901
119066
|
let imageSlots;
|
|
118902
119067
|
try {
|
|
118903
119068
|
imageSlots = await resolvePmaxImageSlots(
|
|
@@ -122706,6 +122871,7 @@ async function runAccountMe(opts) {
|
|
|
122706
122871
|
init_dist();
|
|
122707
122872
|
init_auth();
|
|
122708
122873
|
init_cli_json_snapshot();
|
|
122874
|
+
init_media_customer_id();
|
|
122709
122875
|
|
|
122710
122876
|
// src/commands/account-manage/google-mcc.ts
|
|
122711
122877
|
init_auth();
|
|
@@ -122881,18 +123047,14 @@ function hintGoogleOAuth(kind) {
|
|
|
122881
123047
|
return "\u6267\u884C siluzan-tso account auth -m Google \u5B8C\u6210 OAuth \u7ED1\u5B9A\u3002";
|
|
122882
123048
|
}
|
|
122883
123049
|
function assertGoogleMediaCustomerId(raw) {
|
|
122884
|
-
|
|
122885
|
-
|
|
122886
|
-
|
|
122887
|
-
|
|
122888
|
-
|
|
122889
|
-
|
|
122890
|
-
console.error(
|
|
122891
|
-
"\n\u274C --account \u987B\u4E3A Google mediaCustomerId\uFF08\u7EAF\u6570\u5B57\uFF0C\u4E0E list-accounts -m Google \u4E00\u81F4\uFF09\u3002\n"
|
|
122892
|
-
);
|
|
123050
|
+
try {
|
|
123051
|
+
return requireNumericMediaCustomerId(raw, "--account");
|
|
123052
|
+
} catch (e) {
|
|
123053
|
+
console.error(`
|
|
123054
|
+
\u274C ${e instanceof Error ? e.message : String(e)}
|
|
123055
|
+
`);
|
|
122893
123056
|
process.exit(1);
|
|
122894
123057
|
}
|
|
122895
|
-
return id;
|
|
122896
123058
|
}
|
|
122897
123059
|
function parseCustomerAccessPermissionBody(text) {
|
|
122898
123060
|
const trimmed = text.trim();
|
package/dist/skill/SKILL.md
CHANGED
|
@@ -38,7 +38,9 @@ siluzan-tso -h
|
|
|
38
38
|
|
|
39
39
|
## 即时规范
|
|
40
40
|
|
|
41
|
-
- `entityId`(UUID)≠ `mediaCustomerId`(`list-accounts` 的 `ma.mediaCustomerId`:Google/TikTok/Bing 多为数字;**Yandex=`porg-…`**;Meta 常带 `act_`)。`stats`/`balance`/`accounts-digest
|
|
41
|
+
- `entityId`(UUID)≠ `mediaCustomerId`(`list-accounts` 的 `ma.mediaCustomerId`:Google/TikTok/Bing 多为数字;**Yandex=`porg-…`**;Meta 常带 `act_`)。`stats`/`balance`/`accounts-digest`/`ad *` 的 `-a` **只传 mediaCustomerId**;空结果或 verbose 打出 403 时**先核验 ID**,禁止把 UUID/`entityId` 传给 `-a`,也**禁止**据此直接 `reauth`。
|
|
42
|
+
- **Google CID 特殊性**:UI 常写 `123-456-7890`(带连字符),平台 `mediaCustomerId` 为纯数字 `1234567890`。CLI 会自动去连字符;**勿**把带横杠 403 误判为 OAuth 失效。优先用 `list-accounts` 的纯数字 ID。
|
|
43
|
+
- **禁止臆测授权过期**:见 403/`HTTP 403`/拉数失败/「可能 OAuth 失效」时,**禁止**口头推断授权过期或直接 `reauth`。须先核验 ID(及 Google 连字符),再跑 `account check-access -a <mediaCustomerId>`(套餐已激活前提下),以返回的 `accessible` / `reauth_required` / `no_permission` 为准;细则见 `accounts-permissions.md`。
|
|
42
44
|
- `list-accounts` 无余额/消耗;列全部用 `--page-size 999`。
|
|
43
45
|
- 多户余额用 `balance-scan`(P2),多户消耗用 `accounts-digest`(P3);禁止外层 for-loop 逐户拉数。
|
|
44
46
|
- `stats` 的 `spend` = **区间合计**,不是日消耗。
|
package/dist/skill/_meta.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
3
|
"$id": "siluzan-tso/google-period-report.schema.json",
|
|
4
4
|
"title": "GooglePeriodReport",
|
|
5
|
-
"description": "Google Ads 周期分析报告 JSON(对齐 google-period-report.md 8 默认维度;供 google-analysis render 注入 HTML)。Agent 只需撰写 narrative
|
|
5
|
+
"description": "Google Ads 周期分析报告 JSON(对齐 google-period-report.md 8 默认维度;供 google-analysis render 注入 HTML)。Agent 只需撰写 narrative;kpis/charts/tables 由 --snapshot-dir 从 google-analysis 快照合并。render 校验要求合并后 kpis.cost/impressions/clicks 至少一项为有效数字,否则拒绝出 HTML(避免执行摘要卡片全「—」)。",
|
|
6
6
|
"type": "object",
|
|
7
7
|
"required": ["meta", "narrative"],
|
|
8
8
|
"properties": {
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
},
|
|
22
22
|
"kpis": {
|
|
23
23
|
"type": "object",
|
|
24
|
-
"description": "账户级 KPI(执行摘要 KPI
|
|
24
|
+
"description": "账户级 KPI(执行摘要 KPI 网格);Agent 可省略,但 render 时须经 --snapshot-dir 合并出 cost/impressions/clicks",
|
|
25
25
|
"properties": {
|
|
26
26
|
"cost": { "type": "number" },
|
|
27
27
|
"impressions": { "type": "number" },
|
|
@@ -35,7 +35,9 @@ siluzan-tso account me --check-phone 15130150466 --json-out ./snap-me
|
|
|
35
35
|
|
|
36
36
|
校验当前丝路赞凭据是否对指定 **Google** 广告账户有访问权限。应在拉数/诊断前调用,避免误用他户 ID。
|
|
37
37
|
|
|
38
|
-
|
|
38
|
+
> **硬约束**:**禁止**凭 403、空结果或经验臆测「授权/OAuth 过期」。要对用户下「授权不可用 / 需重授权」结论前,**必须**先跑本命令(并满足下方套餐已激活前提),以 CLI 返回的 `status` 为准;**禁止**跳过本命令直接 `reauth` 或口头推断。
|
|
39
|
+
|
|
40
|
+
**前提(查授权是否过期时)**:须先确认该户 **套餐已激活**(`list-accounts` 的 `ma.scopeActivatedSources` 有未过期条目,或 Google 表格「套餐激活」= ✅ 已激活)。在已激活前提下,**必须**用本命令检查授权是否可用:
|
|
39
41
|
|
|
40
42
|
| 结果 | 含义(套餐已激活时) |
|
|
41
43
|
| ---- | -------------------- |
|
|
@@ -19,6 +19,8 @@
|
|
|
19
19
|
- `list-accounts` **不含**余额/消耗;列全部账户用 `--page-size 999`,禁止默认 20 再翻页。
|
|
20
20
|
- `entityId`(UUID,分享/delink/账单/`reauth`)≠ `mediaCustomerId`(`balance`/`stats`/`accounts-digest`/`ad` 的 `-a`)。**Yandex 的 mediaCustomerId 形如 `porg-…`,不是 UUID。**
|
|
21
21
|
- MetaAd OAuth 户的 `mediaCustomerId` **须带 `act_` 前缀**。
|
|
22
|
-
- `stats`/`balance
|
|
22
|
+
- **Google CID**:广告后台展示常为 `XXX-XXX-XXXX`(连字符),`list-accounts` / 网关要的是**纯数字**(无横杠)。`stats`/`balance`/`ad *`/`google-analysis` 的 `-a` 可带连字符(CLI 会去掉);若仍见 `HTTP 403:123-456-7890` 这类**回显带横杠 ID**,优先改成纯数字重试,**不要**直接当 OAuth 失效去 `reauth`。
|
|
23
|
+
- **禁止臆测授权过期**:403 / 拉数失败时**禁止**口头说「可能授权过期」。须 ID 核验后,对 Google 在套餐已激活前提下执行 `account check-access -a <mediaCustomerId>`,以返回 `status` 为准(见 [`accounts-permissions.md`](accounts-permissions.md));非 Google 看 `list-accounts` 的 `invalidOAuthToken`。
|
|
24
|
+
- `stats`/`balance` 空结果 + verbose `HTTP 403`:先确认 `-a` 是否为 `ma.mediaCustomerId`(且 Google 已去连字符);**禁止**把 `entityId` / tokenId 当 `-a`;未跑 `account check-access`(或未见 `invalidOAuthToken=true`)**禁止** `reauth`。
|
|
23
25
|
- 多账户余额预警用 `balance-scan`(P2),多账户消耗汇总用 `accounts-digest`(P3);**禁止**外层 for-loop 逐户 `balance`/`stats`。
|
|
24
26
|
- `stats` 默认 `spend` = **区间合计**,不是日消耗;日均看 `balance-scan.dailySpend` 或合计÷天数。
|
|
@@ -126,7 +126,8 @@
|
|
|
126
126
|
|
|
127
127
|
- **账户状态 ≠ 系列状态**:`stats` / `balance` / `list-accounts` 的 `status` 只表示账户是否可用;系列状态必须来自 `ad campaigns`。
|
|
128
128
|
- **数据时效性**:涉及「今天/当天/今日消耗」「实时消耗排行」前,必读 `references/analytics/account-analytics.md` 顶部「数据时效性」表。TikTok / Yandex / BingV2 / Kwai 是 `accountsoverview` 同步昨天数据,**不能查今天**。
|
|
129
|
-
- **先查账户再操作**(拉数 / 改账户 / 报告 / **创建广告**):`list-accounts -m [mediaType] -k [mediaCustomerId]`;用户给出的 `mediaCustomerId` 必须 `-k` 核验,无结果则告知用户并停止,**禁止**翻页 grep 自行换 ID(会导致报告错户);拉数、脚本、报告文件名全链路用同一 ID(以 stdout `accountId`
|
|
129
|
+
- **先查账户再操作**(拉数 / 改账户 / 报告 / **创建广告**):`list-accounts -m [mediaType] -k [mediaCustomerId]`;用户给出的 `mediaCustomerId` 必须 `-k` 核验,无结果则告知用户并停止,**禁止**翻页 grep 自行换 ID(会导致报告错户);拉数、脚本、报告文件名全链路用同一 ID(以 stdout `accountId` 为准)。
|
|
130
|
+
- **禁止臆测授权过期(硬约束)**:任何 403、空结果、「可能授权/OAuth 过期」话术,**禁止**凭感觉下结论或直接 `reauth`。Google:在 `list-accounts` 确认套餐**已激活**后,**必须**执行 `account check-access -a <mediaCustomerId>`,仅当结果为 `reauth_required`(或列表 `invalidOAuthToken=true` 与之交叉确认)才谈重授权;`no_permission` 也可能是账户不在当前丝路赞账号下。**未激活套餐时勿用 check-access 判断授权过期**。非 Google 媒体以 `list-accounts` 的 `invalidOAuthToken` 为准,同样禁止臆测。详见 `accounts-permissions.md`。
|
|
130
131
|
- **一律走 CLI,禁止自拼网关请求**:查余额 / 拉数 / 写操作只用 `siluzan-tso …`。**禁止**用 curl、自写脚本或改请求头直连 TSO/Google 网关「另辟蹊径」取数;`balance` 等无数据时按 CLI/`list-accounts` 字段向用户说明(如套餐未激活、授权失效),**禁止**尝试任何绕过平台门禁的取数方式。
|
|
131
132
|
- **W3 仅出方案例外(覆盖上条)**:用户只要「投放方案 / 规划 / 表格 / 先别创建·开户·投钱」,或未给账户且未要求创建/发布时——**禁止**把「请先提供 Google 广告账户」当作第一步;按 `google-ads-campaign-plan.md` §「仅出方案 vs 创建」落盘 JSON 后 **写代码**投影完整审查稿(默认 MD;用户指定则 Excel 等;`account`=`[PENDING_ACCOUNT]`),跳过 `list-accounts` / `geo resolve` / validate / create。**禁止**只交概览表。用户确认要创建后再要账户并续跑创建流水线。
|
|
132
133
|
- **W3 审查稿(搜索与 PMax)**:JSON 落盘后、创建前,必须 **写代码**读取 JSON,按 `google-ads-launch-plan-template.md`(搜索)或 `google-ads-pmax-launch-plan-template.md`(PMax)生成完整审查文件交给用户;须含全部关键词/RSA 或全部 PMax 文案与附加资产。**禁止**用「方案总结」条数勾选代替。用户要求其他格式时改脚本输出,数据仍只从 JSON 来。
|
|
@@ -276,7 +277,7 @@
|
|
|
276
277
|
|
|
277
278
|
- **400**:参数错误,查看对应 reference 或 `-h`
|
|
278
279
|
- **401 / OAuth 失效**:仅当 `list-accounts` 的 `invalidOAuthToken=true`(或表格「授权状态」为失效)且用户确认后——Siluzan Agent **优先**用平台重新授权工具(如 `present_reauth`);备选 `account reauth -m <媒体> --id <entityId> --i-confirm --commit "…"`(内置 delink→OAuth,见 W9 / `accounts-permissions.md`)。走 CLI 时**必须把 stdout 中的完整授权 URL 原样贴给用户**,禁止只说「链接已生成」,也**禁止**自行改写/补全该 URL。解绑后若列表已无该户,恢复用平台授权工具或 `account auth -m <媒体>`。**丝路赞登录凭据失效** → `send-login-code` + `login --phone --code`,见 `references/core/setup.md`
|
|
279
|
-
- **403
|
|
280
|
+
- **403(拉数空结果 / ad 网关)**:① **优先核验 `-a` 是否为 `ma.mediaCustomerId`**(勿传 `entityId`/UUID;Google 注意连字符 CID;Yandex 形如 `porg-…`);② Google 看套餐是否激活(`scopeActivatedSources`),未激活则说明需先激活,**禁止**非 CLI 绕过取数;③ **禁止臆测授权过期**:套餐已激活时**必须**跑 `account check-access -a <mediaCustomerId>`,以 `status` 为准后再决定是否 `reauth`;**禁止**仅凭 403 文案对用户说「授权过期」。
|
|
280
281
|
- **500**:服务可能正在部署/升级,建议反馈 Siluzan 相关人员
|
|
281
282
|
|
|
282
283
|
---
|
|
@@ -26,12 +26,14 @@
|
|
|
26
26
|
|
|
27
27
|
| 需要的 ID | 获取命令 |
|
|
28
28
|
| ------------------- | ----------------------------------------------------------------------- |
|
|
29
|
-
| `accountId`(`-a`) | `siluzan-tso list-accounts --json-out ./snap` → `mediaCustomerId`
|
|
29
|
+
| `accountId`(`-a`) | `siluzan-tso list-accounts -m Google --json-out ./snap` → `mediaCustomerId`(**纯数字**;UI 的 `123-456-7890` 可原样传入,CLI 去连字符) |
|
|
30
30
|
| 广告系列 `id` | `siluzan-tso ad campaigns -a <accountId> --json-out ./snap` → `id` |
|
|
31
31
|
| 广告组 `id`、`name` | `siluzan-tso ad groups -a <accountId> --json-out ./snap` → `id`、`name` |
|
|
32
32
|
| 广告 `id` | `siluzan-tso ad list -a <accountId> --json-out ./snap` → `id` |
|
|
33
33
|
| 关键词 `id` | `siluzan-tso ad keywords -a <accountId> --json-out ./snap` → `id` |
|
|
34
34
|
|
|
35
|
+
> **Google CID**:带连字符进网关曾导致 `HTTP 403:123-456-7890`;现 CLI 已归一化。仍失败时用纯数字重试;**禁止臆测授权过期**,须 `account check-access -a <mediaCustomerId>`(详见 [google-ads.md](google-ads.md) Gotchas / `accounts-permissions.md`)。
|
|
36
|
+
|
|
35
37
|
---
|
|
36
38
|
|
|
37
39
|
## ad campaigns — 广告系列管理
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
|
|
33
33
|
| 需要的 ID | 获取命令 |
|
|
34
34
|
| ------------------- | ----------------------------------------------------------------------- |
|
|
35
|
-
| `accountId`(`-a`) | `
|
|
35
|
+
| `accountId`(`-a`) / JSON `account` | `list-accounts -m Google` → `mediaCustomerId`(纯数字;UI 带连字符 CID 可传入,CLI 去横杠,见 [google-ads.md](google-ads.md) Gotchas) |
|
|
36
36
|
| 广告系列 `id` | `siluzan-tso ad campaigns -a <accountId> --json-out ./snap` → `id` |
|
|
37
37
|
| 广告组 `id`、`name` | `siluzan-tso ad groups -a <accountId> --json-out ./snap` → `id`、`name` |
|
|
38
38
|
| 广告 `id` | `siluzan-tso ad list -a <accountId> --json-out ./snap` → `id` |
|
|
@@ -15,6 +15,10 @@
|
|
|
15
15
|
|
|
16
16
|
## Gotchas
|
|
17
17
|
|
|
18
|
+
- **Google 客户 ID(CID)带连字符**:UI / 用户口述常为 `123-456-7890`,平台 `mediaCustomerId` 与 Google 网关路径需要 `1234567890`。`ad campaigns` / `ad groups` / `ad list` 等 `-a` 与 JSON 顶层 `account` **可带连字符**(CLI 自动去掉)。若错误为 `HTTP 403:123-456-7890`(回显带横杠),先换纯数字重试,**禁止**据此直接判断 OAuth 过期或 `reauth`。
|
|
19
|
+
- **禁止臆测授权过期**:`ad *` / 拉数出现 403 或失败时,**禁止**对用户说「授权可能过期」。须核验 ID 后,在套餐已激活前提下执行 `account check-access -a <mediaCustomerId>`,仅当 `reauth_required`(或与 `invalidOAuthToken=true` 交叉确认)才走重授权。见 [`accounts-permissions.md`](../accounts/accounts-permissions.md)。
|
|
20
|
+
- `-a` 必须是 `list-accounts -m Google` 的 `ma.mediaCustomerId`,**禁止**传 `entityId`(UUID)。
|
|
21
|
+
|
|
18
22
|
## 金额单位(全局重要)
|
|
19
23
|
|
|
20
24
|
> **所有 CLI 金额参数均按「主币种金额」传入**(如 `1.5` = ¥1.50 / $1.50);CLI 写入网关前对「分」字段 ×100(含 `ad keyword-edit --max-cpc` → `maxCPC`)。
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
| ---------------- | ------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
16
16
|
| **1. 拉数** | Agent 调 CLI | `google-analysis -a <id> --start <s> --end <e> --json-out ./snap-google --sections <见下方默认维度对应 CLI>` |
|
|
17
17
|
| **2. 分析** | Agent | 用 **node/python 脚本**读落盘 JSON(勿用 Read 打开业务 `*.json`),完成筛选、聚合、排序与洞察 |
|
|
18
|
-
| **3. 写 JSON** | Agent | 按 `assets/google-period-report.schema.json` 撰写 `google-period-report.json
|
|
18
|
+
| **3. 写 JSON** | Agent | 按 `assets/google-period-report.schema.json` 撰写 `google-period-report.json`:只写 `meta` + `narrative`(8 维度叙事);`kpis`/`charts`/`tables` 留给步骤 4 的 `--snapshot-dir` 合并(合并后缺 KPI 会拒绝出 HTML) |
|
|
19
19
|
| **4. 渲染 HTML** | CLI | `google-analysis render` — **校验 narrative 8 维度必含字段**,缺项报错不生成 HTML;**禁止** Agent 手写/拼接 HTML |
|
|
20
20
|
|
|
21
21
|
```bash
|
|
@@ -33,7 +33,7 @@ siluzan-tso google-analysis render \
|
|
|
33
33
|
`google-period-report.json` 顶层结构:
|
|
34
34
|
|
|
35
35
|
- `meta`:`accountId`(必填)/ `accountName` / `currency` / `startDate` / `endDate`;可省略由 `--snapshot-dir` 从 `overview` + manifest 自动合并。
|
|
36
|
-
- `kpis`:账户级 KPI(消耗/展示/点击/转化/CTR/CPC/CPA,含可选 `previousPeriod`
|
|
36
|
+
- `kpis`:账户级 KPI(消耗/展示/点击/转化/CTR/CPC/CPA,含可选 `previousPeriod` 环比);Agent 可省略,但 **render 合并后** `cost`/`impressions`/`clicks` 至少一项须为有效数字,否则拒绝出 HTML(避免执行摘要卡片全「—」)。本期 KPI 由 CLI 优先对 `campaigns` 全系列累加并重算比率;无 campaigns 时回退 `overview.currentPeriod`,再回退 `dimension-summary`。环比仍来自 `overview.previousPeriod`。
|
|
37
37
|
- `charts.dailyTrend`:每日趋势(`dates`/`cost`/`conversions`/`cpa`);可省略由 `daily-metrics` 自动合并。
|
|
38
38
|
- `tables.{dimensionSummary,campaigns,devices,geographic,keywords}`:对应 §3~§7 表格数据;可省略由同名 `google-analysis` section 自动合并。
|
|
39
39
|
- `narrative`(**Agent 必填,唯一由 Agent 撰写的内容**):
|
|
@@ -53,7 +53,7 @@ siluzan-tso google-analysis render \
|
|
|
53
53
|
模板源码:`report-templates/google-period-report.html`(结构与样式,ECharts 渲染逻辑已内联)。
|
|
54
54
|
|
|
55
55
|
`render` 会向输出目录写入 HTML,并注入 `window.__GOOGLE_PERIOD_REPORT__`。
|
|
56
|
-
传 `--snapshot-dir` 时 CLI 自动从快照合并 KPI、每日趋势图表、周期汇总/系列/设备/地域/关键词表格:**数值一律以 CLI 快照覆盖**(`meta` 仅补空)。本期 KPI 优先 `campaigns`
|
|
56
|
+
传 `--snapshot-dir` 时 CLI 自动从快照合并 KPI、每日趋势图表、周期汇总/系列/设备/地域/关键词表格:**数值一律以 CLI 快照覆盖**(`meta` 仅补空)。本期 KPI 优先 `campaigns` 累加。若 manifest 缺条目但磁盘上仍有对应 `*.json`,CLI 会按文件名自愈补齐。Agent 只写 `narrative`,勿自填 `kpis`/`tables`。
|
|
57
57
|
|
|
58
58
|
---
|
|
59
59
|
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
| ---------------- | ------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
16
16
|
| **1. 拉数** | Agent 调 CLI | `google-analysis -a <id> --start <s> --end <e> --json-out ./snap-google --sections <见下方默认维度对应 CLI>` |
|
|
17
17
|
| **2. 分析** | Agent | 用 **node/python 脚本**读落盘 JSON(勿用 Read 打开业务 `*.json`),完成筛选、聚合、排序与洞察 |
|
|
18
|
-
| **3. 写 JSON** | Agent | 按 `assets/google-period-report.schema.json` 撰写 `google-period-report.json
|
|
18
|
+
| **3. 写 JSON** | Agent | 按 `assets/google-period-report.schema.json` 撰写 `google-period-report.json`:只写 `meta` + `narrative`(8 维度叙事);`kpis`/`charts`/`tables` 留给步骤 4 的 `--snapshot-dir` 合并(合并后缺 KPI 会拒绝出 HTML) |
|
|
19
19
|
| **4. 渲染 HTML** | CLI | `google-analysis render` — **校验 narrative 8 维度必含字段**,缺项报错不生成 HTML;**禁止** Agent 手写/拼接 HTML |
|
|
20
20
|
|
|
21
21
|
```bash
|
|
@@ -33,7 +33,7 @@ siluzan-tso google-analysis render \
|
|
|
33
33
|
`google-period-report.json` 顶层结构:
|
|
34
34
|
|
|
35
35
|
- `meta`:`accountId`(必填)/ `accountName` / `currency` / `startDate` / `endDate`;可省略由 `--snapshot-dir` 从 `overview` + manifest 自动合并。
|
|
36
|
-
- `kpis`:账户级 KPI(消耗/展示/点击/转化/CTR/CPC/CPA,含可选 `previousPeriod`
|
|
36
|
+
- `kpis`:账户级 KPI(消耗/展示/点击/转化/CTR/CPC/CPA,含可选 `previousPeriod` 环比);Agent 可省略,但 **render 合并后** `cost`/`impressions`/`clicks` 至少一项须为有效数字,否则拒绝出 HTML(避免执行摘要卡片全「—」)。本期 KPI 由 CLI 优先对 `campaigns` 全系列累加并重算比率;无 campaigns 时回退 `overview.currentPeriod`,再回退 `dimension-summary`。环比仍来自 `overview.previousPeriod`。
|
|
37
37
|
- `charts.dailyTrend`:每日趋势(`dates`/`cost`/`conversions`/`cpa`);可省略由 `daily-metrics` 自动合并。
|
|
38
38
|
- `tables.{dimensionSummary,campaigns,devices,geographic,keywords}`:对应 §3~§7 表格数据;可省略由同名 `google-analysis` section 自动合并。
|
|
39
39
|
- `narrative`(**Agent 必填,唯一由 Agent 撰写的内容**):
|
|
@@ -53,7 +53,7 @@ siluzan-tso google-analysis render \
|
|
|
53
53
|
模板源码:`report-templates/google-period-report.html`(结构与样式,ECharts 渲染逻辑已内联)。
|
|
54
54
|
|
|
55
55
|
`render` 会向输出目录写入 HTML,并注入 `window.__GOOGLE_PERIOD_REPORT__`。
|
|
56
|
-
传 `--snapshot-dir` 时 CLI 自动从快照合并 KPI、每日趋势图表、周期汇总/系列/设备/地域/关键词表格:**数值一律以 CLI 快照覆盖**(`meta` 仅补空)。本期 KPI 优先 `campaigns`
|
|
56
|
+
传 `--snapshot-dir` 时 CLI 自动从快照合并 KPI、每日趋势图表、周期汇总/系列/设备/地域/关键词表格:**数值一律以 CLI 快照覆盖**(`meta` 仅补空)。本期 KPI 优先 `campaigns` 累加。若 manifest 缺条目但磁盘上仍有对应 `*.json`,CLI 会按文件名自愈补齐。Agent 只写 `narrative`,勿自填 `kpis`/`tables`。
|
|
57
57
|
|
|
58
58
|
---
|
|
59
59
|
|
|
@@ -9,7 +9,7 @@ $ErrorActionPreference = 'Stop'
|
|
|
9
9
|
# -- Package info (injected at build time) ------------------------------------
|
|
10
10
|
$PKG_NAME = 'siluzan-tso-cli'
|
|
11
11
|
# PKG_VERSION 锁定到与本脚本同批构建产物一致的版本,避免与 dist/skill 错位
|
|
12
|
-
$PKG_VERSION = '1.1.43-beta.
|
|
12
|
+
$PKG_VERSION = '1.1.43-beta.3'
|
|
13
13
|
$CLI_BIN = 'siluzan-tso'
|
|
14
14
|
$SKILL_LABEL = 'Siluzan TSO'
|
|
15
15
|
$INSTALL_CMD = 'npm install -g siluzan-tso-cli@beta'
|
|
@@ -9,7 +9,7 @@ set -euo pipefail
|
|
|
9
9
|
# -- Package info (injected at build time) ------------------------------------
|
|
10
10
|
readonly PKG_NAME="siluzan-tso-cli"
|
|
11
11
|
# PKG_VERSION 锁定到与本脚本同批构建产物一致的版本,避免与 dist/skill 错位
|
|
12
|
-
readonly PKG_VERSION="1.1.43-beta.
|
|
12
|
+
readonly PKG_VERSION="1.1.43-beta.3"
|
|
13
13
|
readonly CLI_BIN="siluzan-tso"
|
|
14
14
|
readonly SKILL_LABEL="Siluzan TSO"
|
|
15
15
|
readonly INSTALL_CMD="npm install -g siluzan-tso-cli@beta"
|