siluzan-tso-cli 1.1.43-beta.1 → 1.1.43-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 +1 -1
- package/dist/index.js +150 -53
- package/dist/skill/_meta.json +2 -2
- package/dist/skill/assets/google-period-report.schema.json +2 -2
- 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.2),供内部测试使用。正式发布后安装命令将改为 `npm install -g siluzan-tso-cli`。
|
|
55
55
|
|
|
56
56
|
| 助手 | 建议 `--ai` |
|
|
57
57
|
| ----------------------- | ------------------------------------ |
|
package/dist/index.js
CHANGED
|
@@ -5544,6 +5544,32 @@ var init_daily_metrics_range = __esm({
|
|
|
5544
5544
|
}
|
|
5545
5545
|
});
|
|
5546
5546
|
|
|
5547
|
+
// src/utils/snapshot/async-lock.ts
|
|
5548
|
+
async function withAsyncLock(key, fn) {
|
|
5549
|
+
const prev = chains.get(key) ?? Promise.resolve();
|
|
5550
|
+
let release;
|
|
5551
|
+
const gate = new Promise((resolve21) => {
|
|
5552
|
+
release = resolve21;
|
|
5553
|
+
});
|
|
5554
|
+
chains.set(
|
|
5555
|
+
key,
|
|
5556
|
+
prev.catch(() => void 0).then(() => gate)
|
|
5557
|
+
);
|
|
5558
|
+
await prev.catch(() => void 0);
|
|
5559
|
+
try {
|
|
5560
|
+
return await fn();
|
|
5561
|
+
} finally {
|
|
5562
|
+
release();
|
|
5563
|
+
}
|
|
5564
|
+
}
|
|
5565
|
+
var chains;
|
|
5566
|
+
var init_async_lock = __esm({
|
|
5567
|
+
"src/utils/snapshot/async-lock.ts"() {
|
|
5568
|
+
"use strict";
|
|
5569
|
+
chains = /* @__PURE__ */ new Map();
|
|
5570
|
+
}
|
|
5571
|
+
});
|
|
5572
|
+
|
|
5547
5573
|
// src/commands/google-analysis/wrap-snapshot-envelope.ts
|
|
5548
5574
|
function coerceToRowArray(value) {
|
|
5549
5575
|
if (Array.isArray(value)) return value;
|
|
@@ -5766,26 +5792,29 @@ async function writeGoogleAnalysisSnapshot(params) {
|
|
|
5766
5792
|
const outlineBody = formatOutlineFileBody(fileName, normalizedWrapped, outlineExtra);
|
|
5767
5793
|
await fs7.writeFile(path11.join(absDir, outlineFileName), `${outlineBody}
|
|
5768
5794
|
`, "utf8");
|
|
5769
|
-
const existing = await readManifestIfExists(absDir, accountSlug || void 0);
|
|
5770
5795
|
const newArtifact = {
|
|
5771
5796
|
section: params.section,
|
|
5772
5797
|
file: relativeFile,
|
|
5773
5798
|
writtenAt,
|
|
5774
5799
|
...params.endpointHint !== void 0 ? { endpointHint: params.endpointHint } : {}
|
|
5775
5800
|
};
|
|
5776
|
-
const merged = mergeGoogleAnalysisManifest(existing, {
|
|
5777
|
-
accountId: params.accountId,
|
|
5778
|
-
dateRange: params.dateRange,
|
|
5779
|
-
cliVersion: params.cliVersion,
|
|
5780
|
-
newArtifact
|
|
5781
|
-
});
|
|
5782
5801
|
const manifestFile = googleAnalysisManifestFile(accountSlug || void 0);
|
|
5783
|
-
|
|
5784
|
-
|
|
5785
|
-
|
|
5802
|
+
const manifestPath = path11.join(absDir, manifestFile);
|
|
5803
|
+
await withAsyncLock(manifestPath, async () => {
|
|
5804
|
+
const existing = await readManifestIfExists(absDir, accountSlug || void 0);
|
|
5805
|
+
const merged = mergeGoogleAnalysisManifest(existing, {
|
|
5806
|
+
accountId: params.accountId,
|
|
5807
|
+
dateRange: params.dateRange,
|
|
5808
|
+
cliVersion: params.cliVersion,
|
|
5809
|
+
newArtifact
|
|
5810
|
+
});
|
|
5811
|
+
await fs7.writeFile(
|
|
5812
|
+
manifestPath,
|
|
5813
|
+
`${JSON.stringify(stringifyIdsDeep(merged), null, 2)}
|
|
5786
5814
|
`,
|
|
5787
|
-
|
|
5788
|
-
|
|
5815
|
+
"utf8"
|
|
5816
|
+
);
|
|
5817
|
+
});
|
|
5789
5818
|
return {
|
|
5790
5819
|
kind: "siluzan-tso-google-analysis-snapshot",
|
|
5791
5820
|
schemaVersion: SCHEMA_VERSION2,
|
|
@@ -5803,6 +5832,7 @@ var init_google_analysis = __esm({
|
|
|
5803
5832
|
"use strict";
|
|
5804
5833
|
init_cli_json();
|
|
5805
5834
|
init_wrap_snapshot_envelope();
|
|
5835
|
+
init_async_lock();
|
|
5806
5836
|
init_dir();
|
|
5807
5837
|
LEGACY_MANIFEST_FILE = "manifest.json";
|
|
5808
5838
|
CLI_PACKAGE = "siluzan-tso-cli";
|
|
@@ -102288,6 +102318,56 @@ async function loadSectionFile(snapshotDir, file) {
|
|
|
102288
102318
|
return null;
|
|
102289
102319
|
}
|
|
102290
102320
|
}
|
|
102321
|
+
function dateTagFromRange(dateRange) {
|
|
102322
|
+
if (!dateRange || dateRange.mode !== "range") return void 0;
|
|
102323
|
+
return `${dateRange.start.replace(/-/g, "")}-${dateRange.end.replace(/-/g, "")}`;
|
|
102324
|
+
}
|
|
102325
|
+
function dateTagFromMeta(meta) {
|
|
102326
|
+
const start = typeof meta?.startDate === "string" ? meta.startDate : "";
|
|
102327
|
+
const end = typeof meta?.endDate === "string" ? meta.endDate : "";
|
|
102328
|
+
if (!/^\d{4}-\d{2}-\d{2}$/.test(start) || !/^\d{4}-\d{2}-\d{2}$/.test(end)) return void 0;
|
|
102329
|
+
return `${start.replace(/-/g, "")}-${end.replace(/-/g, "")}`;
|
|
102330
|
+
}
|
|
102331
|
+
function pickSectionSnapshotFile(files, section, accountId, preferredDateTag) {
|
|
102332
|
+
const escapedSection = section.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
102333
|
+
const escapedId = accountId.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
102334
|
+
const re = new RegExp(`^${escapedSection}-${escapedId}(?:_(\\d{8}-\\d{8}))?\\.json$`);
|
|
102335
|
+
const matches = files.filter((f) => re.test(f));
|
|
102336
|
+
if (matches.length === 0) return null;
|
|
102337
|
+
if (preferredDateTag) {
|
|
102338
|
+
const preferred = matches.find((f) => f.endsWith(`_${preferredDateTag}.json`));
|
|
102339
|
+
if (preferred) return preferred;
|
|
102340
|
+
}
|
|
102341
|
+
return [...matches].sort().at(-1) ?? null;
|
|
102342
|
+
}
|
|
102343
|
+
async function recoverMissingReportSections(sectionMap, absDir, accountId, preferredDateTag) {
|
|
102344
|
+
if (!accountId) return;
|
|
102345
|
+
let entries;
|
|
102346
|
+
try {
|
|
102347
|
+
entries = await fs20.readdir(absDir);
|
|
102348
|
+
} catch {
|
|
102349
|
+
return;
|
|
102350
|
+
}
|
|
102351
|
+
for (const section of REPORT_MERGE_SECTIONS) {
|
|
102352
|
+
if (sectionMap.has(section)) continue;
|
|
102353
|
+
const file = pickSectionSnapshotFile(entries, section, accountId, preferredDateTag);
|
|
102354
|
+
if (!file) continue;
|
|
102355
|
+
const data = await loadSectionFile(absDir, file);
|
|
102356
|
+
if (data) sectionMap.set(section, data);
|
|
102357
|
+
}
|
|
102358
|
+
}
|
|
102359
|
+
function periodMetricsFromRow(row) {
|
|
102360
|
+
if (!row) return null;
|
|
102361
|
+
return {
|
|
102362
|
+
cost: num2(row.spend),
|
|
102363
|
+
impressions: num2(row.impressions),
|
|
102364
|
+
clicks: num2(row.clicks),
|
|
102365
|
+
conversions: num2(row.conversions),
|
|
102366
|
+
ctr: num2(row.ctr),
|
|
102367
|
+
averageCpc: num2(row.averageCpc),
|
|
102368
|
+
costPerConversion: num2(row.costPerConversion)
|
|
102369
|
+
};
|
|
102370
|
+
}
|
|
102291
102371
|
function items(envelope) {
|
|
102292
102372
|
if (!envelope || !Array.isArray(envelope.items)) return [];
|
|
102293
102373
|
return envelope.items.filter((row) => asRecord11(row) !== null);
|
|
@@ -102342,16 +102422,18 @@ async function mergeGoogleAnalysisSnapshotIntoReport(payload, snapshotDir) {
|
|
|
102342
102422
|
if (!manifest) {
|
|
102343
102423
|
throw new Error(`\u672A\u5728 ${absDir} \u627E\u5230 manifest*.json\uFF0C\u8BF7\u5148\u6267\u884C google-analysis --json-out`);
|
|
102344
102424
|
}
|
|
102425
|
+
const accountId = manifest.googleAnalysis?.accountId ?? metaAccountId;
|
|
102426
|
+
const dateRange = manifest.googleAnalysis?.dateRange;
|
|
102427
|
+
const preferredDateTag = dateTagFromRange(dateRange) ?? dateTagFromMeta(asRecord11(payload.meta) ?? void 0);
|
|
102345
102428
|
const sectionMap = /* @__PURE__ */ new Map();
|
|
102346
102429
|
for (const art of manifest.artifacts ?? []) {
|
|
102347
102430
|
const data = await loadSectionFile(absDir, art.file);
|
|
102348
102431
|
if (data) sectionMap.set(art.section, data);
|
|
102349
102432
|
}
|
|
102433
|
+
await recoverMissingReportSections(sectionMap, absDir, accountId, preferredDateTag);
|
|
102350
102434
|
const overviewRecord = record(sectionMap.get("overview"));
|
|
102351
102435
|
const currentPeriod = asRecord11(overviewRecord?.currentPeriod);
|
|
102352
102436
|
const previousPeriod = asRecord11(overviewRecord?.previousPeriod);
|
|
102353
|
-
const accountId = manifest.googleAnalysis?.accountId ?? metaAccountId;
|
|
102354
|
-
const dateRange = manifest.googleAnalysis?.dateRange;
|
|
102355
102437
|
const meta = { ...payload.meta ?? {} };
|
|
102356
102438
|
if (!meta.accountId && accountId) meta.accountId = accountId;
|
|
102357
102439
|
if (!meta.currency) {
|
|
@@ -102363,16 +102445,9 @@ async function mergeGoogleAnalysisSnapshotIntoReport(payload, snapshotDir) {
|
|
|
102363
102445
|
if (!meta.generatedAt) meta.generatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
102364
102446
|
const campaignRows2 = items(sectionMap.get("campaigns"));
|
|
102365
102447
|
const campaignKpis = sumCampaignRowsToPeriodKpis(campaignRows2);
|
|
102448
|
+
const dimensionSummaryRecord = record(sectionMap.get("dimension-summary"));
|
|
102366
102449
|
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);
|
|
102450
|
+
const currentKpiSource = campaignKpis ?? periodMetricsFromRow(currentPeriod) ?? periodMetricsFromRow(dimensionSummaryRecord);
|
|
102376
102451
|
if (currentKpiSource) {
|
|
102377
102452
|
if (currentKpiSource.cost != null) kpis.cost = currentKpiSource.cost;
|
|
102378
102453
|
if (currentKpiSource.impressions != null) kpis.impressions = currentKpiSource.impressions;
|
|
@@ -102411,22 +102486,21 @@ async function mergeGoogleAnalysisSnapshotIntoReport(payload, snapshotDir) {
|
|
|
102411
102486
|
}
|
|
102412
102487
|
}
|
|
102413
102488
|
const tables = { ...payload.tables ?? {} };
|
|
102414
|
-
{
|
|
102415
|
-
|
|
102416
|
-
|
|
102417
|
-
|
|
102418
|
-
|
|
102419
|
-
|
|
102420
|
-
|
|
102421
|
-
|
|
102422
|
-
|
|
102423
|
-
|
|
102424
|
-
|
|
102425
|
-
|
|
102426
|
-
|
|
102427
|
-
|
|
102428
|
-
|
|
102429
|
-
}
|
|
102489
|
+
if (dimensionSummaryRecord) {
|
|
102490
|
+
tables.dimensionSummary = {
|
|
102491
|
+
cost: num2(dimensionSummaryRecord.spend),
|
|
102492
|
+
impressions: num2(dimensionSummaryRecord.impressions),
|
|
102493
|
+
clicks: num2(dimensionSummaryRecord.clicks),
|
|
102494
|
+
conversions: num2(dimensionSummaryRecord.conversions),
|
|
102495
|
+
ctr: num2(dimensionSummaryRecord.ctr),
|
|
102496
|
+
averageCpc: num2(dimensionSummaryRecord.averageCpc),
|
|
102497
|
+
costPerConversion: num2(dimensionSummaryRecord.costPerConversion),
|
|
102498
|
+
searchImpressionShare: num2(dimensionSummaryRecord.searchImpressionShare),
|
|
102499
|
+
searchBudgetLostImpressionShare: num2(
|
|
102500
|
+
dimensionSummaryRecord.searchBudgetLostImpressionShare
|
|
102501
|
+
),
|
|
102502
|
+
searchRankLostImpressionShare: num2(dimensionSummaryRecord.searchRankLostImpressionShare)
|
|
102503
|
+
};
|
|
102430
102504
|
}
|
|
102431
102505
|
if (campaignRows2.length > 0) {
|
|
102432
102506
|
tables.campaigns = campaignRows2.map((r) => ({
|
|
@@ -102464,10 +102538,20 @@ async function mergeGoogleAnalysisSnapshotIntoReport(payload, snapshotDir) {
|
|
|
102464
102538
|
tables
|
|
102465
102539
|
};
|
|
102466
102540
|
}
|
|
102541
|
+
var REPORT_MERGE_SECTIONS;
|
|
102467
102542
|
var init_merge_snapshot = __esm({
|
|
102468
102543
|
"src/commands/google-analysis/merge-snapshot.ts"() {
|
|
102469
102544
|
"use strict";
|
|
102470
102545
|
init_google_analysis();
|
|
102546
|
+
REPORT_MERGE_SECTIONS = [
|
|
102547
|
+
"overview",
|
|
102548
|
+
"daily-metrics",
|
|
102549
|
+
"dimension-summary",
|
|
102550
|
+
"campaigns",
|
|
102551
|
+
"devices",
|
|
102552
|
+
"geographic",
|
|
102553
|
+
"keywords"
|
|
102554
|
+
];
|
|
102471
102555
|
}
|
|
102472
102556
|
});
|
|
102473
102557
|
|
|
@@ -102511,6 +102595,19 @@ function validateGooglePeriodReportContent(data) {
|
|
|
102511
102595
|
"\u22651 \u6BB5\u6267\u884C\u6458\u8981\uFF0C\u6982\u8FF0\u672C\u671F\u6D88\u8017/\u8F6C\u5316/CTR/CPA \u8D70\u52BF"
|
|
102512
102596
|
);
|
|
102513
102597
|
}
|
|
102598
|
+
const kpis = asRecord12(data.kpis);
|
|
102599
|
+
const hasCoreKpi = ["cost", "impressions", "clicks"].some(
|
|
102600
|
+
(key) => Number.isFinite(Number(kpis?.[key]))
|
|
102601
|
+
);
|
|
102602
|
+
if (!hasCoreKpi) {
|
|
102603
|
+
pushMissing(
|
|
102604
|
+
missing,
|
|
102605
|
+
"kpis-core-metrics",
|
|
102606
|
+
"\u6267\u884C\u6458\u8981 KPI",
|
|
102607
|
+
"kpis.cost / kpis.impressions / kpis.clicks",
|
|
102608
|
+
"\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"
|
|
102609
|
+
);
|
|
102610
|
+
}
|
|
102514
102611
|
const sectionAnalysisCounts = {};
|
|
102515
102612
|
const sectionSuggestionCounts = {};
|
|
102516
102613
|
for (const key of SECTION_KEYS) {
|
|
@@ -102571,7 +102668,7 @@ function formatGooglePeriodReportErrors(result) {
|
|
|
102571
102668
|
...lines,
|
|
102572
102669
|
"",
|
|
102573
102670
|
"\u8BF7\u8865\u5168 narrative.sections.<dimension>.{analysis,suggestions} \u4E0E narrative.recommendations \u540E\u91CD\u65B0\u6267\u884C render\u3002",
|
|
102574
|
-
"
|
|
102671
|
+
"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
102672
|
].join("\n");
|
|
102576
102673
|
}
|
|
102577
102674
|
var SECTION_KEYS, SECTION_LABELS;
|
|
@@ -108342,6 +108439,7 @@ init_cli_table();
|
|
|
108342
108439
|
init_cli_json_snapshot();
|
|
108343
108440
|
|
|
108344
108441
|
// src/utils/snapshot/report-analysis.ts
|
|
108442
|
+
init_async_lock();
|
|
108345
108443
|
init_google_analysis();
|
|
108346
108444
|
init_cli_json();
|
|
108347
108445
|
import * as fs8 from "fs/promises";
|
|
@@ -108415,25 +108513,24 @@ async function writeReportAnalysisSnapshot(params) {
|
|
|
108415
108513
|
`,
|
|
108416
108514
|
"utf8"
|
|
108417
108515
|
);
|
|
108418
|
-
const existing = await readReportManifestIfExists(absDir, accountSlug || void 0);
|
|
108419
108516
|
const newArtifact = {
|
|
108420
108517
|
section: params.section,
|
|
108421
108518
|
file: fileName,
|
|
108422
108519
|
writtenAt
|
|
108423
108520
|
};
|
|
108424
|
-
const merged = mergeReportAnalysisManifest(existing, {
|
|
108425
|
-
accountId: params.accountId,
|
|
108426
|
-
dateRange: params.dateRange,
|
|
108427
|
-
cliVersion: params.cliVersion,
|
|
108428
|
-
newArtifact
|
|
108429
|
-
});
|
|
108430
108521
|
const manifestFile = reportManifestFile(accountSlug || void 0);
|
|
108431
|
-
|
|
108432
|
-
|
|
108433
|
-
|
|
108434
|
-
|
|
108435
|
-
|
|
108436
|
-
|
|
108522
|
+
const manifestPath = path12.join(absDir, manifestFile);
|
|
108523
|
+
await withAsyncLock(manifestPath, async () => {
|
|
108524
|
+
const existing = await readReportManifestIfExists(absDir, accountSlug || void 0);
|
|
108525
|
+
const merged = mergeReportAnalysisManifest(existing, {
|
|
108526
|
+
accountId: params.accountId,
|
|
108527
|
+
dateRange: params.dateRange,
|
|
108528
|
+
cliVersion: params.cliVersion,
|
|
108529
|
+
newArtifact
|
|
108530
|
+
});
|
|
108531
|
+
await fs8.writeFile(manifestPath, `${JSON.stringify(merged, null, 2)}
|
|
108532
|
+
`, "utf8");
|
|
108533
|
+
});
|
|
108437
108534
|
return {
|
|
108438
108535
|
kind: "siluzan-tso-report-analysis-snapshot",
|
|
108439
108536
|
schemaVersion: SCHEMA_VERSION3,
|
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" },
|
|
@@ -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.2'
|
|
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.2"
|
|
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"
|