siluzan-tso-cli 1.1.56 → 1.1.57-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 +334 -26
- package/dist/skill/SKILL.md +2 -1
- package/dist/skill/_meta.json +2 -2
- package/dist/skill/references/accounts/accounts-balance-stats.md +23 -0
- package/dist/skill/references/accounts/accounts-list.md +1 -1
- package/dist/skill/references/accounts/accounts-permissions.md +2 -2
- package/dist/skill/references/accounts/finance.md +1 -1
- package/dist/skill/references/accounts/open-account-by-media.md +1 -1
- package/dist/skill/references/accounts/open-account-google-ui.md +1 -1
- package/dist/skill/references/analytics/rag.md +1 -1
- package/dist/skill/references/analytics/reporting.md +5 -5
- package/dist/skill/references/core/agent-conventions.md +4 -1
- package/dist/skill/references/core/playbooks.md +5 -2
- package/dist/skill/references/core/setup.md +5 -5
- package/dist/skill/references/core/workflows.md +1 -1
- package/dist/skill/references/misc/tso-home.md +2 -2
- package/dist/skill/references/report-templates/bing-period-report.md +1 -1
- package/dist/skill/references/report-templates/google-period-report.md +1 -1
- package/dist/skill/references/report-templates/google-quarterly-plan.md +2 -0
- package/dist/skill/references/report-templates/google-quarterly-report.md +2 -0
- package/dist/skill/references/report-templates/meta-period-report.md +1 -1
- package/dist/skill/references/report-templates/tiktok-period-report.md +1 -1
- package/dist/skill/references/report-templates/yandex-period-report.md +1 -1
- package/dist/skill/report-templates/bing-period-report.html +14 -2
- package/dist/skill/report-templates/bing-period-report.md +1 -1
- package/dist/skill/report-templates/google-period-report.html +14 -2
- package/dist/skill/report-templates/google-period-report.md +1 -1
- package/dist/skill/report-templates/google-quarterly-plan.md +2 -0
- package/dist/skill/report-templates/google-quarterly-report.html +20 -10
- package/dist/skill/report-templates/google-quarterly-report.md +2 -0
- package/dist/skill/report-templates/meta-period-report.html +6 -2
- package/dist/skill/report-templates/meta-period-report.md +1 -1
- package/dist/skill/report-templates/tiktok-period-report.html +14 -2
- package/dist/skill/report-templates/tiktok-period-report.md +1 -1
- package/dist/skill/report-templates/yandex-period-report.html +15 -1
- package/dist/skill/report-templates/yandex-period-report.md +1 -1
- package/dist/skill/scripts/install.ps1 +3 -3
- package/dist/skill/scripts/install.sh +3 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -43,7 +43,7 @@ HTML 报告模板外部资源:**ECharts** 统一使用 `https://staticpn.siluz
|
|
|
43
43
|
在**用户的目标项目根目录**执行(根据用户使用的助手选择 `--ai`):
|
|
44
44
|
|
|
45
45
|
```bash
|
|
46
|
-
npm install -g siluzan-tso-cli
|
|
46
|
+
npm install -g siluzan-tso-cli@beta
|
|
47
47
|
siluzan-tso init --ai cursor # 写入 Cursor(默认)
|
|
48
48
|
siluzan-tso init --ai cursor,claude # 同时写入多个平台
|
|
49
49
|
siluzan-tso init --ai all # 写入所有支持的平台
|
|
@@ -51,6 +51,7 @@ siluzan-tso init -d /path/to/skills # 写入自定义目录
|
|
|
51
51
|
siluzan-tso init --force # 强制覆盖已存在文件
|
|
52
52
|
```
|
|
53
53
|
|
|
54
|
+
> **注意**:当前为测试版(1.1.57-beta.2),供内部测试使用。正式发布后安装命令将改为 `npm install -g siluzan-tso-cli`。
|
|
54
55
|
|
|
55
56
|
| 助手 | 建议 `--ai` |
|
|
56
57
|
| ----------------------- | ------------------------------------ |
|
package/dist/index.js
CHANGED
|
@@ -4049,7 +4049,7 @@ var DEFAULT_API_BASE;
|
|
|
4049
4049
|
var init_defaults = __esm({
|
|
4050
4050
|
"src/config/defaults.ts"() {
|
|
4051
4051
|
"use strict";
|
|
4052
|
-
DEFAULT_API_BASE = "https://tso-api.siluzan.com";
|
|
4052
|
+
DEFAULT_API_BASE = "https://tso-api-ci.siluzan.com";
|
|
4053
4053
|
}
|
|
4054
4054
|
});
|
|
4055
4055
|
|
|
@@ -5376,6 +5376,119 @@ var init_brief = __esm({
|
|
|
5376
5376
|
}
|
|
5377
5377
|
});
|
|
5378
5378
|
|
|
5379
|
+
// src/commands/accounts-digest/campaign-types.ts
|
|
5380
|
+
function presenceCount(value) {
|
|
5381
|
+
if (value == null || value === false || value === "") return 0;
|
|
5382
|
+
if (typeof value === "number") return Number.isFinite(value) ? value : 0;
|
|
5383
|
+
if (typeof value === "string") {
|
|
5384
|
+
const n = Number(value);
|
|
5385
|
+
if (value.trim() !== "" && Number.isFinite(n)) return n;
|
|
5386
|
+
return 0;
|
|
5387
|
+
}
|
|
5388
|
+
if (Array.isArray(value)) return value.length;
|
|
5389
|
+
if (typeof value === "object") {
|
|
5390
|
+
const obj = value;
|
|
5391
|
+
for (const key of COUNT_KEYS) {
|
|
5392
|
+
if (key in obj) return presenceCount(obj[key]);
|
|
5393
|
+
}
|
|
5394
|
+
return Object.keys(obj).length > 0 ? 1 : 0;
|
|
5395
|
+
}
|
|
5396
|
+
return 0;
|
|
5397
|
+
}
|
|
5398
|
+
function summarizeCampaignTypes(record2, depth = 0) {
|
|
5399
|
+
const empty = {
|
|
5400
|
+
campaignTypesStatus: "ok",
|
|
5401
|
+
campaignTypes: [],
|
|
5402
|
+
hasDisplay: false,
|
|
5403
|
+
hasPmax: false,
|
|
5404
|
+
campaignTypesError: null
|
|
5405
|
+
};
|
|
5406
|
+
if (record2 == null || typeof record2 !== "object" || Array.isArray(record2)) return empty;
|
|
5407
|
+
const bag = record2;
|
|
5408
|
+
const types = [];
|
|
5409
|
+
let hasDisplay = false;
|
|
5410
|
+
let hasPmax = false;
|
|
5411
|
+
for (const [key, value] of Object.entries(bag)) {
|
|
5412
|
+
if (key === "ok" || key === "meta" || key.startsWith("_")) continue;
|
|
5413
|
+
if (NESTED_KEYS.includes(key)) continue;
|
|
5414
|
+
if (presenceCount(value) <= 0) continue;
|
|
5415
|
+
types.push(key);
|
|
5416
|
+
if (DISPLAY_KEY.test(key)) hasDisplay = true;
|
|
5417
|
+
if (PMAX_KEY.test(key)) hasPmax = true;
|
|
5418
|
+
}
|
|
5419
|
+
if (types.length === 0 && depth === 0) {
|
|
5420
|
+
for (const nest of NESTED_KEYS) {
|
|
5421
|
+
const inner = bag[nest];
|
|
5422
|
+
if (inner && typeof inner === "object") return summarizeCampaignTypes(inner, 1);
|
|
5423
|
+
}
|
|
5424
|
+
}
|
|
5425
|
+
return {
|
|
5426
|
+
campaignTypesStatus: "ok",
|
|
5427
|
+
campaignTypes: types,
|
|
5428
|
+
hasDisplay,
|
|
5429
|
+
hasPmax,
|
|
5430
|
+
campaignTypesError: null
|
|
5431
|
+
};
|
|
5432
|
+
}
|
|
5433
|
+
function skippedNoSpendCampaignTypes() {
|
|
5434
|
+
return {
|
|
5435
|
+
campaignTypesStatus: "skipped-no-spend",
|
|
5436
|
+
campaignTypes: null,
|
|
5437
|
+
hasDisplay: null,
|
|
5438
|
+
hasPmax: null,
|
|
5439
|
+
campaignTypesError: null
|
|
5440
|
+
};
|
|
5441
|
+
}
|
|
5442
|
+
function errorCampaignTypes(message) {
|
|
5443
|
+
const text2 = message.replace(/\s+/g, " ").trim().slice(0, 180);
|
|
5444
|
+
return {
|
|
5445
|
+
campaignTypesStatus: "error",
|
|
5446
|
+
campaignTypes: null,
|
|
5447
|
+
hasDisplay: null,
|
|
5448
|
+
hasPmax: null,
|
|
5449
|
+
campaignTypesError: text2 || "unknown error"
|
|
5450
|
+
};
|
|
5451
|
+
}
|
|
5452
|
+
function errorMessage(err) {
|
|
5453
|
+
if (err instanceof Error) return err.message;
|
|
5454
|
+
return String(err);
|
|
5455
|
+
}
|
|
5456
|
+
async function fetchCampaignTypeSummaries(accountIds, fetchOne, opts) {
|
|
5457
|
+
const ids = [...new Set(accountIds.filter((id) => id.trim() !== ""))];
|
|
5458
|
+
const out = /* @__PURE__ */ new Map();
|
|
5459
|
+
if (ids.length === 0) return out;
|
|
5460
|
+
const concurrency = Math.max(1, Math.min(opts?.concurrency ?? 8, 16, ids.length));
|
|
5461
|
+
let cursor = 0;
|
|
5462
|
+
let done = 0;
|
|
5463
|
+
const workers = new Array(concurrency).fill(0).map(async () => {
|
|
5464
|
+
while (true) {
|
|
5465
|
+
const index = cursor++;
|
|
5466
|
+
if (index >= ids.length) return;
|
|
5467
|
+
const id = ids[index];
|
|
5468
|
+
try {
|
|
5469
|
+
const record2 = await fetchOne(id);
|
|
5470
|
+
out.set(id, summarizeCampaignTypes(record2));
|
|
5471
|
+
} catch (err) {
|
|
5472
|
+
out.set(id, errorCampaignTypes(errorMessage(err)));
|
|
5473
|
+
}
|
|
5474
|
+
done += 1;
|
|
5475
|
+
opts?.onProgress?.(done, ids.length);
|
|
5476
|
+
}
|
|
5477
|
+
});
|
|
5478
|
+
await Promise.all(workers);
|
|
5479
|
+
return out;
|
|
5480
|
+
}
|
|
5481
|
+
var DISPLAY_KEY, PMAX_KEY, NESTED_KEYS, COUNT_KEYS;
|
|
5482
|
+
var init_campaign_types = __esm({
|
|
5483
|
+
"src/commands/accounts-digest/campaign-types.ts"() {
|
|
5484
|
+
"use strict";
|
|
5485
|
+
DISPLAY_KEY = /^(display|responsive_display|display_network)$/i;
|
|
5486
|
+
PMAX_KEY = /^(performance_?max|performancemax)$/i;
|
|
5487
|
+
NESTED_KEYS = ["data", "record", "types", "typesSummary"];
|
|
5488
|
+
COUNT_KEYS = ["count", "total", "campaignCount", "value", "campaigns"];
|
|
5489
|
+
}
|
|
5490
|
+
});
|
|
5491
|
+
|
|
5379
5492
|
// src/commands/accounts-digest/filters.ts
|
|
5380
5493
|
function keepDigestRow(row, opts) {
|
|
5381
5494
|
const { maxCpa, zeroConversions } = opts;
|
|
@@ -5404,6 +5517,18 @@ var init_filters = __esm({
|
|
|
5404
5517
|
});
|
|
5405
5518
|
|
|
5406
5519
|
// src/commands/accounts-digest/run.ts
|
|
5520
|
+
function formatCampaignTypesCell(row) {
|
|
5521
|
+
if (row.campaignTypesStatus === "ok") {
|
|
5522
|
+
const flags = [
|
|
5523
|
+
row.hasDisplay ? "Display" : "\u65E0Display",
|
|
5524
|
+
row.hasPmax ? "PMax" : "\u65E0PMax"
|
|
5525
|
+
];
|
|
5526
|
+
return flags.join(" ");
|
|
5527
|
+
}
|
|
5528
|
+
if (row.campaignTypesStatus === "skipped-no-spend") return "\u672A\u67E5(\u65E0\u6D88\u8017)";
|
|
5529
|
+
if (row.campaignTypesStatus === "error") return "\u67E5\u8BE2\u5931\u8D25";
|
|
5530
|
+
return "-";
|
|
5531
|
+
}
|
|
5407
5532
|
function round2(n) {
|
|
5408
5533
|
if (n == null) return null;
|
|
5409
5534
|
const v = Number(n);
|
|
@@ -5422,6 +5547,16 @@ async function runAccountsDigest(opts) {
|
|
|
5422
5547
|
process.exit(1);
|
|
5423
5548
|
}
|
|
5424
5549
|
const media = opts.media;
|
|
5550
|
+
if (opts.withCampaignTypes && media !== "Google") {
|
|
5551
|
+
console.error(
|
|
5552
|
+
"\n\u274C --with-campaign-types \u53EA\u652F\u6301 -m Google\uFF08\u7CFB\u5217\u7C7B\u578B\u6765\u81EA Google campaigns/types-summary\uFF09\u3002\n"
|
|
5553
|
+
);
|
|
5554
|
+
process.exit(1);
|
|
5555
|
+
}
|
|
5556
|
+
if (opts.withCampaignTypes && !config.googleApiUrl) {
|
|
5557
|
+
console.error("\n\u274C \u5F53\u524D\u914D\u7F6E\u6CA1\u6709 googleApiUrl\uFF0C\u65E0\u6CD5\u62C9\u53D6\u5E7F\u544A\u7CFB\u5217\u7C7B\u578B\u3002\n");
|
|
5558
|
+
process.exit(1);
|
|
5559
|
+
}
|
|
5425
5560
|
const filterIds = (opts.accounts ?? "").split(",").map((s) => s.trim()).filter(Boolean);
|
|
5426
5561
|
const filterSet = new Set(filterIds);
|
|
5427
5562
|
const minSpend = opts.minSpend ?? 0;
|
|
@@ -5565,6 +5700,56 @@ async function runAccountsDigest(opts) {
|
|
|
5565
5700
|
rows.push(row);
|
|
5566
5701
|
}
|
|
5567
5702
|
rows.sort((a, b) => (b.spend ?? 0) - (a.spend ?? 0));
|
|
5703
|
+
let campaignTypeMeta = null;
|
|
5704
|
+
if (opts.withCampaignTypes) {
|
|
5705
|
+
const concurrency = Math.max(1, Math.min(opts.campaignTypeConcurrency ?? 8, 16));
|
|
5706
|
+
const spendIds = rows.filter((row) => (row.spend ?? 0) > 0).map((row) => row.mediaCustomerId);
|
|
5707
|
+
process.stderr.write(
|
|
5708
|
+
`\u23F3 [accounts-digest] \u7CFB\u5217\u7C7B\u578B\uFF1A${spendIds.length} \u4E2A\u6709\u6D88\u8017\u8D26\u6237\uFF0C\u5E76\u53D1 ${concurrency}\uFF08\u6D88\u8017\u4E3A 0 \u7684\u4E0D\u8BF7\u6C42\uFF09\u3002
|
|
5709
|
+
`
|
|
5710
|
+
);
|
|
5711
|
+
const summaries = await fetchCampaignTypeSummaries(
|
|
5712
|
+
spendIds,
|
|
5713
|
+
(accountId) => {
|
|
5714
|
+
const url = `${config.googleApiUrl}/reporting/media-account/${encodeURIComponent(accountId)}/campaigns/types-summary`;
|
|
5715
|
+
return apiFetch2(url, config, {}, !!opts.verbose);
|
|
5716
|
+
},
|
|
5717
|
+
{
|
|
5718
|
+
concurrency,
|
|
5719
|
+
onProgress: (done, total) => {
|
|
5720
|
+
if (done === total || done % 50 === 0) {
|
|
5721
|
+
process.stderr.write(` \u2713 [\u7CFB\u5217\u7C7B\u578B] ${done}/${total}
|
|
5722
|
+
`);
|
|
5723
|
+
}
|
|
5724
|
+
}
|
|
5725
|
+
}
|
|
5726
|
+
);
|
|
5727
|
+
let ok = 0;
|
|
5728
|
+
let failed = 0;
|
|
5729
|
+
let skippedNoSpend = 0;
|
|
5730
|
+
for (const row of rows) {
|
|
5731
|
+
const fields = (row.spend ?? 0) > 0 ? summaries.get(row.mediaCustomerId) ?? errorCampaignTypes("\u672A\u8FD4\u56DE\u8BE5\u8D26\u6237\u7684\u7CFB\u5217\u7C7B\u578B") : skippedNoSpendCampaignTypes();
|
|
5732
|
+
row.campaignTypesStatus = fields.campaignTypesStatus;
|
|
5733
|
+
row.campaignTypes = fields.campaignTypes;
|
|
5734
|
+
row.hasDisplay = fields.hasDisplay;
|
|
5735
|
+
row.hasPmax = fields.hasPmax;
|
|
5736
|
+
row.campaignTypesError = fields.campaignTypesError;
|
|
5737
|
+
if (fields.campaignTypesStatus === "ok") ok += 1;
|
|
5738
|
+
else if (fields.campaignTypesStatus === "error") failed += 1;
|
|
5739
|
+
else skippedNoSpend += 1;
|
|
5740
|
+
}
|
|
5741
|
+
campaignTypeMeta = {
|
|
5742
|
+
requested: spendIds.length,
|
|
5743
|
+
ok,
|
|
5744
|
+
failed,
|
|
5745
|
+
skippedNoSpend,
|
|
5746
|
+
concurrency
|
|
5747
|
+
};
|
|
5748
|
+
process.stderr.write(
|
|
5749
|
+
` \u2713 [\u7CFB\u5217\u7C7B\u578B] \u5B8C\u6210\uFF1A\u6210\u529F ${ok}\uFF0C\u5931\u8D25 ${failed}\uFF0C\u672A\u67E5\uFF08\u65E0\u6D88\u8017\uFF09${skippedNoSpend}
|
|
5750
|
+
`
|
|
5751
|
+
);
|
|
5752
|
+
}
|
|
5568
5753
|
const totals = rows.reduce(
|
|
5569
5754
|
(acc, r) => {
|
|
5570
5755
|
acc.spend += r.spend ?? 0;
|
|
@@ -5606,7 +5791,9 @@ async function runAccountsDigest(opts) {
|
|
|
5606
5791
|
},
|
|
5607
5792
|
totalsScope: "returned",
|
|
5608
5793
|
currencyNote: "\u5404\u8D26\u6237\u4E3B\u5E01\u79CD\u53EF\u80FD\u4E0D\u540C\uFF1Btotals \u53EA\u5408\u8BA1 returned\uFF08\u547D\u4E2D\uFF09\u884C\uFF0C\u7981\u6B62\u5199\u6210\u5168\u90E8\u8D26\u6237\u3002\u8DE8\u5E01\u79CD\u8BF7\u6309 currencyCode \u5206\u8868",
|
|
5609
|
-
generatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
5794
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
5795
|
+
/** 未传 --with-campaign-types 时为 null。失败户的 hasDisplay 是 null,不能当成「无展示」。 */
|
|
5796
|
+
campaignTypes: campaignTypeMeta
|
|
5610
5797
|
};
|
|
5611
5798
|
const agentBrief = buildDigestAgentBrief({
|
|
5612
5799
|
rows,
|
|
@@ -5649,6 +5836,9 @@ ${media} \u8D26\u6237\u6295\u653E\u753B\u50CF\uFF1A\u8FD4\u56DE ${rows.length} \
|
|
|
5649
5836
|
{ key: "cpa", header: "CPA" },
|
|
5650
5837
|
{ key: "balance", header: "\u4F59\u989D" }
|
|
5651
5838
|
];
|
|
5839
|
+
if (opts.withCampaignTypes) {
|
|
5840
|
+
cols.push({ key: "types", header: "\u7CFB\u5217\u7C7B\u578B" });
|
|
5841
|
+
}
|
|
5652
5842
|
const tableRows = rows.map((r) => ({
|
|
5653
5843
|
id: r.mediaCustomerId,
|
|
5654
5844
|
company: r.advertiserName ?? "-",
|
|
@@ -5660,7 +5850,8 @@ ${media} \u8D26\u6237\u6295\u653E\u753B\u50CF\uFF1A\u8FD4\u56DE ${rows.length} \
|
|
|
5660
5850
|
ctr: r.ctr != null ? r.ctr.toFixed(2) : "-",
|
|
5661
5851
|
cpc: formatMoneyDisplay(r.cpc, r.currencyCode),
|
|
5662
5852
|
cpa: formatMoneyDisplay(r.cpa, r.currencyCode),
|
|
5663
|
-
balance: formatMoneyDisplay(r.balance, r.currencyCode)
|
|
5853
|
+
balance: formatMoneyDisplay(r.balance, r.currencyCode),
|
|
5854
|
+
types: formatCampaignTypesCell(r)
|
|
5664
5855
|
}));
|
|
5665
5856
|
printCliTable(tableRows, cols);
|
|
5666
5857
|
if (meta.nullConversionCount > 0) {
|
|
@@ -5693,6 +5884,7 @@ var init_run = __esm({
|
|
|
5693
5884
|
init_list_fetch();
|
|
5694
5885
|
init_login_customer_id();
|
|
5695
5886
|
init_brief();
|
|
5887
|
+
init_campaign_types();
|
|
5696
5888
|
init_filters();
|
|
5697
5889
|
}
|
|
5698
5890
|
});
|
|
@@ -5716,7 +5908,15 @@ function register10(program2) {
|
|
|
5716
5908
|
"--skip-balance",
|
|
5717
5909
|
"\u53EA\u62C9\u6D88\u8017\u3001\u4E0D\u62C9\u4F59\u989D\uFF08\u8F6C\u5316\u6210\u672C/CPA \u5DE1\u68C0\u63A8\u8350\uFF1B\u53EF\u5C11\u4E00\u534A\u8BF7\u6C42\u65F6\u95F4\uFF09",
|
|
5718
5910
|
false
|
|
5719
|
-
).option("--page-size <n>", "\u8D26\u6237\u6E05\u5355\u5206\u9875\u5927\u5C0F\uFF08\u9ED8\u8BA4 200\uFF0C\u4E0A\u9650 500\uFF09", (v) => parseInt(v, 10)).option("--login-customer-id <id>", LOGIN_CUSTOMER_ID_OPTION_HELP).option(
|
|
5911
|
+
).option("--page-size <n>", "\u8D26\u6237\u6E05\u5355\u5206\u9875\u5927\u5C0F\uFF08\u9ED8\u8BA4 200\uFF0C\u4E0A\u9650 500\uFF09", (v) => parseInt(v, 10)).option("--login-customer-id <id>", LOGIN_CUSTOMER_ID_OPTION_HELP).option(
|
|
5912
|
+
"--with-campaign-types",
|
|
5913
|
+
"\u4EC5 Google\uFF1A\u5BF9\u533A\u95F4\u6D88\u8017 > 0 \u7684\u8D26\u6237\u5E76\u53D1\u62C9\u53D6 campaigns/types-summary\uFF0C\u5728\u6BCF\u884C\u5199\u5165 hasDisplay / hasPmax\u3002\u7528\u6765\u505A\u300C\u65E0\u5C55\u793A\u6216\u65E0 PMax\u300D\u5DE1\u68C0\uFF0C\u4E0D\u8981\u518D\u9010\u6237\u8C03 ad campaigns\u3002",
|
|
5914
|
+
false
|
|
5915
|
+
).option(
|
|
5916
|
+
"--campaign-type-concurrency <n>",
|
|
5917
|
+
"\u7CFB\u5217\u7C7B\u578B\u8BF7\u6C42\u5E76\u53D1\uFF081\u201316\uFF0C\u9ED8\u8BA4 8\uFF09",
|
|
5918
|
+
(v) => parseInt(v, 10)
|
|
5919
|
+
).option("--max-pages <n>", "\u6700\u591A\u626B\u63CF\u9875\u6570\uFF08\u9ED8\u8BA4 20\uFF0C\u4E0A\u9650 200\uFF09", (v) => parseInt(v, 10)).option("-t, --token <token>", "Token\uFF08\u53EF\u9009\uFF1B\u4F18\u5148\u4E8E ~/.siluzan/config.json\uFF09").option(
|
|
5720
5920
|
"--json-out <path>",
|
|
5721
5921
|
"\u843D\u76D8\uFF08\u76EE\u5F55\u6216 *.json \u6587\u4EF6\u8DEF\u5F84\uFF09\u5E76\u66F4\u65B0 cli-manifest[-<\u67E5\u8BE2id>].json\uFF1B\u76EE\u5F55\u6A21\u5F0F\u6587\u4EF6\u540D\u4E3A `<section>[-<\u67E5\u8BE2id>].json`\uFF1Bstdout \u4E00\u884C\u6458\u8981 JSON\uFF0C\u542B outlineFile\uFF08TS \u5F0F\u7C7B\u578B\u5728 `*.outline.txt`\uFF09",
|
|
5722
5922
|
void 0
|
|
@@ -5737,7 +5937,9 @@ function register10(program2) {
|
|
|
5737
5937
|
jsonOut: opts.jsonOut,
|
|
5738
5938
|
refreshDp: opts.refreshDp,
|
|
5739
5939
|
verbose: opts.verbose,
|
|
5740
|
-
loginCustomerId: opts.loginCustomerId
|
|
5940
|
+
loginCustomerId: opts.loginCustomerId,
|
|
5941
|
+
withCampaignTypes: opts.withCampaignTypes,
|
|
5942
|
+
campaignTypeConcurrency: opts.campaignTypeConcurrency
|
|
5741
5943
|
});
|
|
5742
5944
|
}
|
|
5743
5945
|
);
|
|
@@ -7039,6 +7241,80 @@ var init_geo_en2zh = __esm({
|
|
|
7039
7241
|
}
|
|
7040
7242
|
});
|
|
7041
7243
|
|
|
7244
|
+
// src/commands/report-period.ts
|
|
7245
|
+
function classifyReportPeriod(current, previous) {
|
|
7246
|
+
const start = tryParse(current?.start);
|
|
7247
|
+
const end = tryParse(current?.end);
|
|
7248
|
+
if (!start || !end) return customPeriod();
|
|
7249
|
+
if (isFullCalendarQuarter(start, end)) {
|
|
7250
|
+
const tag = quarterTagFromMonth(start.m);
|
|
7251
|
+
const prev = tryParse(previous?.start);
|
|
7252
|
+
const prevEnd = tryParse(previous?.end);
|
|
7253
|
+
return {
|
|
7254
|
+
current: tag,
|
|
7255
|
+
previous: prev && prevEnd && isFullCalendarQuarter(prev, prevEnd) ? quarterTagFromMonth(prev.m) : "\u4E0A\u671F",
|
|
7256
|
+
next: tag === "Q4" ? "Q1" : `Q${Number(tag.slice(1)) + 1}`,
|
|
7257
|
+
unit: "\u5B63\u5EA6"
|
|
7258
|
+
};
|
|
7259
|
+
}
|
|
7260
|
+
if (start.d === 1 && start.y === end.y && start.m === end.m) {
|
|
7261
|
+
const prev = tryParse(previous?.start);
|
|
7262
|
+
const prevEnd = tryParse(previous?.end);
|
|
7263
|
+
const prevSameMonth = prev && prevEnd && prev.y === prevEnd.y && prev.m === prevEnd.m;
|
|
7264
|
+
const nextMonth = start.m === 12 ? 1 : start.m + 1;
|
|
7265
|
+
return {
|
|
7266
|
+
current: `${start.m}\u6708`,
|
|
7267
|
+
previous: prevSameMonth ? `${prev.m}\u6708` : "\u4E0A\u671F",
|
|
7268
|
+
next: `${nextMonth}\u6708`,
|
|
7269
|
+
unit: "\u6708\u5EA6"
|
|
7270
|
+
};
|
|
7271
|
+
}
|
|
7272
|
+
return customPeriod();
|
|
7273
|
+
}
|
|
7274
|
+
function rangeFromMeta(meta) {
|
|
7275
|
+
if (!meta || typeof meta !== "object") return null;
|
|
7276
|
+
const record2 = meta;
|
|
7277
|
+
if (typeof record2.startDate !== "string" || typeof record2.endDate !== "string") return null;
|
|
7278
|
+
return { start: record2.startDate, end: record2.endDate };
|
|
7279
|
+
}
|
|
7280
|
+
function attachPeriodChrome(payload) {
|
|
7281
|
+
return { ...payload, periodChrome: classifyReportPeriod(rangeFromMeta(payload.meta)) };
|
|
7282
|
+
}
|
|
7283
|
+
function periodWord(unit, neutral) {
|
|
7284
|
+
return unit || neutral;
|
|
7285
|
+
}
|
|
7286
|
+
function customPeriod() {
|
|
7287
|
+
return { current: "\u672C\u671F", previous: "\u4E0A\u671F", next: "\u4E0B\u671F", unit: "" };
|
|
7288
|
+
}
|
|
7289
|
+
function tryParse(iso) {
|
|
7290
|
+
if (!iso) return null;
|
|
7291
|
+
const match = DATE_RE2.exec(iso.trim());
|
|
7292
|
+
if (!match) return null;
|
|
7293
|
+
const y = Number(match[1]);
|
|
7294
|
+
const m = Number(match[2]);
|
|
7295
|
+
const d = Number(match[3]);
|
|
7296
|
+
if (m < 1 || m > 12 || d < 1 || d > 31) return null;
|
|
7297
|
+
return { y, m, d };
|
|
7298
|
+
}
|
|
7299
|
+
function lastDayOfMonth(y, m) {
|
|
7300
|
+
return new Date(Date.UTC(y, m, 0)).getUTCDate();
|
|
7301
|
+
}
|
|
7302
|
+
function isFullCalendarQuarter(start, end) {
|
|
7303
|
+
if (start.y !== end.y || start.d !== 1) return false;
|
|
7304
|
+
if (![1, 4, 7, 10].includes(start.m)) return false;
|
|
7305
|
+
return end.m === start.m + 2 && end.d === lastDayOfMonth(end.y, end.m);
|
|
7306
|
+
}
|
|
7307
|
+
function quarterTagFromMonth(month) {
|
|
7308
|
+
return `Q${Math.ceil(month / 3)}`;
|
|
7309
|
+
}
|
|
7310
|
+
var DATE_RE2;
|
|
7311
|
+
var init_report_period = __esm({
|
|
7312
|
+
"src/commands/report-period.ts"() {
|
|
7313
|
+
"use strict";
|
|
7314
|
+
DATE_RE2 = /^(\d{4})-(\d{2})-(\d{2})/;
|
|
7315
|
+
}
|
|
7316
|
+
});
|
|
7317
|
+
|
|
7042
7318
|
// src/commands/google-analysis/sections.ts
|
|
7043
7319
|
var SECTIONS;
|
|
7044
7320
|
var init_sections = __esm({
|
|
@@ -104454,7 +104730,7 @@ function asRecord23(value) {
|
|
|
104454
104730
|
throw new Error("\u62A5\u544A\u6570\u636E\u987B\u4E3A JSON \u5BF9\u8C61");
|
|
104455
104731
|
}
|
|
104456
104732
|
function injectReportData2(html, payload) {
|
|
104457
|
-
const json = JSON.stringify(payload).replace(/</g, "\\u003c");
|
|
104733
|
+
const json = JSON.stringify(attachPeriodChrome(payload)).replace(/</g, "\\u003c");
|
|
104458
104734
|
if (!html.includes(INJECT_MARKER)) {
|
|
104459
104735
|
throw new Error("\u62A5\u544A\u6A21\u677F\u7F3A\u5C11\u6570\u636E\u6CE8\u5165\u951A\u70B9\uFF0C\u8BF7\u66F4\u65B0 google-period-report.html");
|
|
104460
104736
|
}
|
|
@@ -104497,8 +104773,9 @@ async function runGoogleAnalysisRender(opts) {
|
|
|
104497
104773
|
await fsPromises2.mkdir(outDir, { recursive: true });
|
|
104498
104774
|
await fsPromises2.writeFile(outPath, injectReportData2(html, data), "utf8");
|
|
104499
104775
|
const { stats } = contentCheck;
|
|
104776
|
+
const cover = periodWord(attachPeriodChrome(data).periodChrome.unit, "\u5468\u671F");
|
|
104500
104777
|
console.log(`
|
|
104501
|
-
\u2705 Google Ads \
|
|
104778
|
+
\u2705 Google Ads ${cover}\u5206\u6790 HTML \u62A5\u544A\u5DF2\u751F\u6210\uFF1A${outPath}
|
|
104502
104779
|
`);
|
|
104503
104780
|
console.log(
|
|
104504
104781
|
` \u5FC5\u542B\u5B57\u6BB5\u5DF2\u5168\u90E8\u8986\u76D6\uFF5C\u6267\u884C\u6458\u8981 ${stats.executiveSummaryCount} \u6BB5\uFF5C\u4F18\u5316\u5EFA\u8BAE ${stats.recommendationCount} \u6761\uFF5C8 \u4E2A\u6570\u636E\u7EF4\u5206\u6790+\u5EFA\u8BAE\u5747\u5DF2\u9F50\u5168
|
|
@@ -104514,6 +104791,7 @@ var init_render_report = __esm({
|
|
|
104514
104791
|
"use strict";
|
|
104515
104792
|
init_merge_snapshot();
|
|
104516
104793
|
init_report_content();
|
|
104794
|
+
init_report_period();
|
|
104517
104795
|
TEMPLATE_BASENAME = "google-period-report.html";
|
|
104518
104796
|
INJECT_MARKER = "window.__GOOGLE_PERIOD_REPORT__ = window.__GOOGLE_PERIOD_REPORT__ || null;";
|
|
104519
104797
|
}
|
|
@@ -136875,9 +137153,10 @@ async function fetchYoutubeChannel(opts) {
|
|
|
136875
137153
|
}
|
|
136876
137154
|
|
|
136877
137155
|
// src/commands/google-quarterly/windows.ts
|
|
136878
|
-
|
|
137156
|
+
init_report_period();
|
|
137157
|
+
var DATE_RE3 = /^(\d{4})-(\d{2})-(\d{2})$/;
|
|
136879
137158
|
function parseIsoDate(iso) {
|
|
136880
|
-
const match =
|
|
137159
|
+
const match = DATE_RE3.exec(iso.trim());
|
|
136881
137160
|
if (!match) throw new Error(`\u65E5\u671F\u987B\u4E3A YYYY-MM-DD\uFF1A${iso}`);
|
|
136882
137161
|
const y = Number(match[1]);
|
|
136883
137162
|
const m = Number(match[2]);
|
|
@@ -136891,7 +137170,7 @@ function pad(n) {
|
|
|
136891
137170
|
function isoDate(y, m, d) {
|
|
136892
137171
|
return `${y}-${pad(m)}-${pad(d)}`;
|
|
136893
137172
|
}
|
|
136894
|
-
function
|
|
137173
|
+
function lastDayOfMonth2(y, m) {
|
|
136895
137174
|
return new Date(Date.UTC(y, m, 0)).getUTCDate();
|
|
136896
137175
|
}
|
|
136897
137176
|
function listMonths(start, end) {
|
|
@@ -136902,7 +137181,7 @@ function listMonths(start, end) {
|
|
|
136902
137181
|
let m = s.m;
|
|
136903
137182
|
while (y < e.y || y === e.y && m <= e.m) {
|
|
136904
137183
|
const monthStart = y === s.y && m === s.m ? start : isoDate(y, m, 1);
|
|
136905
|
-
const monthEnd = y === e.y && m === e.m ? end : isoDate(y, m,
|
|
137184
|
+
const monthEnd = y === e.y && m === e.m ? end : isoDate(y, m, lastDayOfMonth2(y, m));
|
|
136906
137185
|
months.push({
|
|
136907
137186
|
start: monthStart,
|
|
136908
137187
|
end: monthEnd,
|
|
@@ -136937,14 +137216,14 @@ function previousCalendarQuarter(start, end) {
|
|
|
136937
137216
|
const e = parseIsoDate(end);
|
|
136938
137217
|
const quarterStartMonths = [1, 4, 7, 10];
|
|
136939
137218
|
if (s.d !== 1 || !quarterStartMonths.includes(s.m)) return null;
|
|
136940
|
-
if (e.m !== s.m + 2 || e.d !==
|
|
137219
|
+
if (e.m !== s.m + 2 || e.d !== lastDayOfMonth2(e.y, e.m)) return null;
|
|
136941
137220
|
const prevEndMonth = s.m - 1;
|
|
136942
137221
|
const year = prevEndMonth < 1 ? s.y - 1 : s.y;
|
|
136943
137222
|
const endM = prevEndMonth < 1 ? 12 : prevEndMonth;
|
|
136944
137223
|
const startM = endM - 2;
|
|
136945
137224
|
return {
|
|
136946
137225
|
start: isoDate(year, startM, 1),
|
|
136947
|
-
end: isoDate(year, endM,
|
|
137226
|
+
end: isoDate(year, endM, lastDayOfMonth2(year, endM))
|
|
136948
137227
|
};
|
|
136949
137228
|
}
|
|
136950
137229
|
function yearOverYearRange(start, end) {
|
|
@@ -136957,13 +137236,23 @@ function yearOverYearRange(start, end) {
|
|
|
136957
137236
|
}
|
|
136958
137237
|
function shiftYear(p, delta) {
|
|
136959
137238
|
const y = p.y + delta;
|
|
136960
|
-
const last =
|
|
137239
|
+
const last = lastDayOfMonth2(y, p.m);
|
|
136961
137240
|
return isoDate(y, p.m, Math.min(p.d, last));
|
|
136962
137241
|
}
|
|
136963
137242
|
function utcToIso(ms) {
|
|
136964
137243
|
const d = new Date(ms);
|
|
136965
137244
|
return isoDate(d.getUTCFullYear(), d.getUTCMonth() + 1, d.getUTCDate());
|
|
136966
137245
|
}
|
|
137246
|
+
function reportPeriodChrome(current, previous) {
|
|
137247
|
+
const kind = classifyReportPeriod(current, previous);
|
|
137248
|
+
if (kind.unit === "\u5B63\u5EA6") {
|
|
137249
|
+
return { ...kind, reportTitle: "Google \u5B63\u5EA6\u7ECF\u8425\u62A5\u544A", coverEn: "QUARTERLY REPORT" };
|
|
137250
|
+
}
|
|
137251
|
+
if (kind.unit === "\u6708\u5EA6") {
|
|
137252
|
+
return { ...kind, reportTitle: "Google \u6708\u5EA6\u7ECF\u8425\u62A5\u544A", coverEn: "MONTHLY REPORT" };
|
|
137253
|
+
}
|
|
137254
|
+
return { ...kind, reportTitle: "Google \u7ECF\u8425\u62A5\u544A", coverEn: "PERFORMANCE REPORT" };
|
|
137255
|
+
}
|
|
136967
137256
|
function lastCompleteQuarter(now = /* @__PURE__ */ new Date()) {
|
|
136968
137257
|
const parts = new Intl.DateTimeFormat("en-CA", {
|
|
136969
137258
|
timeZone: "Asia/Shanghai",
|
|
@@ -136978,7 +137267,7 @@ function lastCompleteQuarter(now = /* @__PURE__ */ new Date()) {
|
|
|
136978
137267
|
const year = m <= 3 ? y - 1 : y;
|
|
136979
137268
|
return {
|
|
136980
137269
|
start: isoDate(year, qStartMonth, 1),
|
|
136981
|
-
end: isoDate(year, qEndMonth,
|
|
137270
|
+
end: isoDate(year, qEndMonth, lastDayOfMonth2(year, qEndMonth))
|
|
136982
137271
|
};
|
|
136983
137272
|
}
|
|
136984
137273
|
|
|
@@ -137851,17 +138140,28 @@ async function runGoogleQuarterlyRender(opts) {
|
|
|
137851
138140
|
}
|
|
137852
138141
|
const outPath = path31.resolve(opts.out ?? path31.join(path31.dirname(dataPath), GOOGLE_QUARTERLY_HTML_BASENAME));
|
|
137853
138142
|
await fs26.mkdir(path31.dirname(outPath), { recursive: true });
|
|
137854
|
-
const
|
|
138143
|
+
const periodChrome = reportPeriodChrome(readMetaRange(merged, "current"), readMetaRange(merged, "previous"));
|
|
138144
|
+
const outHtml = injectQuarterlyReport(html, { ...merged, periodChrome });
|
|
137855
138145
|
if (!outHtml.includes("homeCDN/echarts.min.js") || /new Chart\s*\(/.test(outHtml)) {
|
|
137856
138146
|
console.error("\n\u274C \u6A21\u677F\u5FC5\u987B\u4F7F\u7528 ECharts\uFF0C\u7981\u6B62 Chart.js\u3002\n");
|
|
137857
138147
|
process.exit(1);
|
|
137858
138148
|
}
|
|
137859
138149
|
await fs26.writeFile(outPath, outHtml, "utf8");
|
|
137860
138150
|
console.log(`
|
|
137861
|
-
\u2705
|
|
138151
|
+
\u2705 ${periodChrome.reportTitle}\u5DF2\u751F\u6210\uFF1A${outPath}
|
|
137862
138152
|
`);
|
|
137863
138153
|
console.log("\u5728\u6D4F\u89C8\u5668\u4E2D\u6253\u5F00\u5373\u53EF\u67E5\u770B\uFF08ECharts\uFF1Astaticpn.siluzan.com/homeCDN/echarts.min.js\uFF09\u3002\n");
|
|
137864
138154
|
}
|
|
138155
|
+
function readMetaRange(payload, key) {
|
|
138156
|
+
const meta = payload.meta;
|
|
138157
|
+
if (!meta || typeof meta !== "object") return null;
|
|
138158
|
+
const block = meta[key];
|
|
138159
|
+
if (!block || typeof block !== "object") return null;
|
|
138160
|
+
const start = block.start;
|
|
138161
|
+
const end = block.end;
|
|
138162
|
+
if (typeof start !== "string" || typeof end !== "string") return null;
|
|
138163
|
+
return { start, end };
|
|
138164
|
+
}
|
|
137865
138165
|
|
|
137866
138166
|
// src/commands/google-quarterly/register.ts
|
|
137867
138167
|
function registerGoogleQuarterlyCommands(program2) {
|
|
@@ -138809,6 +139109,7 @@ function formatMetaPeriodReportErrors(result) {
|
|
|
138809
139109
|
}
|
|
138810
139110
|
|
|
138811
139111
|
// src/commands/facebook-analysis/render-report.ts
|
|
139112
|
+
init_report_period();
|
|
138812
139113
|
var TEMPLATE_BASENAMES2 = {
|
|
138813
139114
|
html: "meta-period-report.html"
|
|
138814
139115
|
};
|
|
@@ -138858,7 +139159,7 @@ function asRecord27(value) {
|
|
|
138858
139159
|
throw new Error("\u62A5\u544A\u6570\u636E\u987B\u4E3A JSON \u5BF9\u8C61");
|
|
138859
139160
|
}
|
|
138860
139161
|
function injectReportData3(html, payload) {
|
|
138861
|
-
const json = JSON.stringify(payload).replace(/</g, "\\u003c");
|
|
139162
|
+
const json = JSON.stringify(attachPeriodChrome(payload)).replace(/</g, "\\u003c");
|
|
138862
139163
|
if (!html.includes(INJECT_MARKER2)) {
|
|
138863
139164
|
throw new Error("\u62A5\u544A\u6A21\u677F\u7F3A\u5C11\u6570\u636E\u6CE8\u5165\u951A\u70B9\uFF0C\u8BF7\u66F4\u65B0 meta-period-report.html");
|
|
138864
139165
|
}
|
|
@@ -138921,8 +139222,9 @@ async function runFacebookAnalysisRender(opts) {
|
|
|
138921
139222
|
written.push(outPath);
|
|
138922
139223
|
}
|
|
138923
139224
|
const { stats } = contentCheck;
|
|
139225
|
+
const cover = periodWord(attachPeriodChrome(data).periodChrome.unit, "\u5468\u671F");
|
|
138924
139226
|
console.log(`
|
|
138925
|
-
\u2705 Meta/Facebook \
|
|
139227
|
+
\u2705 Meta/Facebook ${cover}\u5206\u6790\u62A5\u544A\u5DF2\u751F\u6210\uFF1A${written.join(" \u3001 ")}
|
|
138926
139228
|
`);
|
|
138927
139229
|
console.log(
|
|
138928
139230
|
` \u5FC5\u542B\u5B57\u6BB5\u5DF2\u5168\u90E8\u8986\u76D6\uFF5C\u5EFA\u8BAE ${stats.recommendationCount}\uFF5C\u56DB\u95EE ${stats.fourQuestionsCount}\uFF5C\u65E5 ${stats.dailyRows}\uFF5C\u56FD\u5BB6 ${stats.countryRows}\uFF5C\u7CFB\u5217 ${stats.campaignRows}\uFF5C\u53D7\u4F17 ${stats.audienceRows}
|
|
@@ -141417,6 +141719,7 @@ function formatBingPeriodReportErrors(result) {
|
|
|
141417
141719
|
}
|
|
141418
141720
|
|
|
141419
141721
|
// src/commands/bing-analysis/render-report.ts
|
|
141722
|
+
init_report_period();
|
|
141420
141723
|
var TEMPLATE_BASENAME3 = "bing-period-report.html";
|
|
141421
141724
|
var INJECT_MARKER4 = "window.__BING_PERIOD_REPORT__ = window.__BING_PERIOD_REPORT__ || null;";
|
|
141422
141725
|
function resolveSkillTemplatePath6(basename13) {
|
|
@@ -141449,7 +141752,7 @@ function asRecord34(value) {
|
|
|
141449
141752
|
throw new Error("\u62A5\u544A\u6570\u636E\u987B\u4E3A JSON \u5BF9\u8C61");
|
|
141450
141753
|
}
|
|
141451
141754
|
function injectReportData5(html, payload) {
|
|
141452
|
-
const json = JSON.stringify(payload).replace(/</g, "\\u003c");
|
|
141755
|
+
const json = JSON.stringify(attachPeriodChrome(payload)).replace(/</g, "\\u003c");
|
|
141453
141756
|
if (!html.includes(INJECT_MARKER4)) {
|
|
141454
141757
|
throw new Error("\u62A5\u544A\u6A21\u677F\u7F3A\u5C11\u6570\u636E\u6CE8\u5165\u951A\u70B9\uFF0C\u8BF7\u66F4\u65B0 bing-period-report.html");
|
|
141455
141758
|
}
|
|
@@ -141492,8 +141795,9 @@ async function runBingAnalysisRender(opts) {
|
|
|
141492
141795
|
await fsPromises5.mkdir(outDir, { recursive: true });
|
|
141493
141796
|
await fsPromises5.writeFile(outPath, injectReportData5(html, data), "utf8");
|
|
141494
141797
|
const { stats } = contentCheck;
|
|
141798
|
+
const cover = periodWord(attachPeriodChrome(data).periodChrome.unit, "\u8D26\u6237");
|
|
141495
141799
|
console.log(`
|
|
141496
|
-
\u2705 Bing \u5E7F\u544A\
|
|
141800
|
+
\u2705 Bing \u5E7F\u544A${cover}\u5206\u6790 HTML \u62A5\u544A\u5DF2\u751F\u6210\uFF1A${outPath}
|
|
141497
141801
|
`);
|
|
141498
141802
|
console.log(
|
|
141499
141803
|
` \u5FC5\u542B\u5B57\u6BB5\u5DF2\u5168\u90E8\u8986\u76D6\uFF5C\u6267\u884C\u6458\u8981 ${stats.executiveSummaryCount} \u6BB5\uFF5C\u4F18\u5316\u5EFA\u8BAE ${stats.recommendationCount} \u6761\uFF5C9 \u4E2A\u5206\u6790\u5C0F\u8282\u5747\u5DF2\u9F50\u5168
|
|
@@ -142314,6 +142618,7 @@ function formatTikTokPeriodReportErrors(result) {
|
|
|
142314
142618
|
}
|
|
142315
142619
|
|
|
142316
142620
|
// src/commands/tiktok-analysis/render-report.ts
|
|
142621
|
+
init_report_period();
|
|
142317
142622
|
var TEMPLATE_BASENAME4 = "tiktok-period-report.html";
|
|
142318
142623
|
var INJECT_MARKER5 = "window.__TIKTOK_PERIOD_REPORT__ = window.__TIKTOK_PERIOD_REPORT__ || null;";
|
|
142319
142624
|
function resolveSkillTemplatePath7(basename13) {
|
|
@@ -142346,7 +142651,7 @@ function asRecord37(value) {
|
|
|
142346
142651
|
throw new Error("\u62A5\u544A\u6570\u636E\u987B\u4E3A JSON \u5BF9\u8C61");
|
|
142347
142652
|
}
|
|
142348
142653
|
function injectReportData6(html, payload) {
|
|
142349
|
-
const json = JSON.stringify(payload).replace(/</g, "\\u003c");
|
|
142654
|
+
const json = JSON.stringify(attachPeriodChrome(payload)).replace(/</g, "\\u003c");
|
|
142350
142655
|
if (!html.includes(INJECT_MARKER5)) {
|
|
142351
142656
|
throw new Error("\u62A5\u544A\u6A21\u677F\u7F3A\u5C11\u6570\u636E\u6CE8\u5165\u951A\u70B9\uFF0C\u8BF7\u66F4\u65B0 tiktok-period-report.html");
|
|
142352
142657
|
}
|
|
@@ -142389,8 +142694,9 @@ async function runTikTokAnalysisRender(opts) {
|
|
|
142389
142694
|
await fsPromises6.mkdir(outDir, { recursive: true });
|
|
142390
142695
|
await fsPromises6.writeFile(outPath, injectReportData6(html, data), "utf8");
|
|
142391
142696
|
const { stats } = contentCheck;
|
|
142697
|
+
const cover = periodWord(attachPeriodChrome(data).periodChrome.unit, "\u8D26\u6237");
|
|
142392
142698
|
console.log(`
|
|
142393
|
-
\u2705 TikTok \u5E7F\u544A\u4E3B\
|
|
142699
|
+
\u2705 TikTok \u5E7F\u544A\u4E3B${cover}\u5206\u6790 HTML \u62A5\u544A\u5DF2\u751F\u6210\uFF1A${outPath}
|
|
142394
142700
|
`);
|
|
142395
142701
|
console.log(
|
|
142396
142702
|
` \u5FC5\u542B\u5B57\u6BB5\u5DF2\u5168\u90E8\u8986\u76D6\uFF5C\u6267\u884C\u6458\u8981 ${stats.executiveSummaryCount} \u6BB5\uFF5C\u4F18\u5316\u5EFA\u8BAE ${stats.recommendationCount} \u6761\uFF5C\u5206\u6790\u5C0F\u8282\u5DF2\u6309\u6709\u6570\u636E\u7684\u7EF4\u5EA6\u6821\u9A8C
|
|
@@ -144088,6 +144394,7 @@ function formatYandexPeriodReportErrors(result) {
|
|
|
144088
144394
|
}
|
|
144089
144395
|
|
|
144090
144396
|
// src/commands/yandex-analysis/render-report.ts
|
|
144397
|
+
init_report_period();
|
|
144091
144398
|
var TEMPLATE_BASENAME5 = "yandex-period-report.html";
|
|
144092
144399
|
var INJECT_MARKER6 = "window.__YANDEX_PERIOD_REPORT__ = window.__YANDEX_PERIOD_REPORT__ || null;";
|
|
144093
144400
|
function resolveSkillTemplatePath8(basename13) {
|
|
@@ -144133,7 +144440,7 @@ function refreshWeeklyFromDaily(data) {
|
|
|
144133
144440
|
};
|
|
144134
144441
|
}
|
|
144135
144442
|
function injectReportData7(html, payload) {
|
|
144136
|
-
const json = JSON.stringify(payload).replace(/</g, "\\u003c");
|
|
144443
|
+
const json = JSON.stringify(attachPeriodChrome(payload)).replace(/</g, "\\u003c");
|
|
144137
144444
|
if (!html.includes(INJECT_MARKER6)) {
|
|
144138
144445
|
throw new Error("\u62A5\u544A\u6A21\u677F\u7F3A\u5C11\u6570\u636E\u6CE8\u5165\u951A\u70B9\uFF0C\u8BF7\u66F4\u65B0 yandex-period-report.html");
|
|
144139
144446
|
}
|
|
@@ -144175,8 +144482,9 @@ async function runYandexAnalysisRender(opts) {
|
|
|
144175
144482
|
await fsPromises7.mkdir(outDir, { recursive: true });
|
|
144176
144483
|
await fsPromises7.writeFile(outPath, injectReportData7(html, data), "utf8");
|
|
144177
144484
|
const { stats } = contentCheck;
|
|
144485
|
+
const cover = periodWord(attachPeriodChrome(data).periodChrome.unit, "\u8D26\u6237");
|
|
144178
144486
|
console.log(`
|
|
144179
|
-
\u2705 Yandex \u5E7F\u544A\
|
|
144487
|
+
\u2705 Yandex \u5E7F\u544A${cover}\u5206\u6790 HTML \u62A5\u544A\u5DF2\u751F\u6210\uFF1A${outPath}
|
|
144180
144488
|
`);
|
|
144181
144489
|
console.log(
|
|
144182
144490
|
` \u5FC5\u542B\u5B57\u6BB5\u5DF2\u5168\u90E8\u8986\u76D6\uFF5C\u4F18\u5316\u5EFA\u8BAE ${stats.recommendationCount} \u6761\uFF5C8 \u4E2A\u5206\u6790\u7AE0\u8282\u5747\u5DF2\u9F50\u5168
|
|
@@ -144781,7 +145089,7 @@ function playbookCodeFatal(cmd) {
|
|
|
144781
145089
|
const known = {
|
|
144782
145090
|
W1: "siluzan-tso list-accounts [-m Google] --json-out <dir>",
|
|
144783
145091
|
P2: "siluzan-tso balance-scan -m Google --json-out <dir>",
|
|
144784
|
-
P3: "siluzan-tso accounts-digest -m Google --start <S> --end <D> --json-out <dir
|
|
145092
|
+
P3: "siluzan-tso accounts-digest -m Google --start <S> --end <D> --json-out <dir>\uFF08\u8981\u5224\u65AD Display/PMax \u518D\u52A0 --with-campaign-types\uFF0C\u4E0D\u8981\u9010\u6237 ad campaigns\uFF09"
|
|
144785
145093
|
};
|
|
144786
145094
|
const replacement = known[key];
|
|
144787
145095
|
if (replacement) {
|
package/dist/skill/SKILL.md
CHANGED
|
@@ -46,6 +46,7 @@ siluzan-tso -h
|
|
|
46
46
|
- **Google 广告类型边界**:CLI/Skill **仅支持** Google **搜索广告(Search)** 与 **Performance Max(PMax)** 的方案、创建与精细管理;**不支持展示广告(Display)**(含自适应展示广告 RDA、独立 Display 系列)。用户要做展示广告时须明确告知不支持,可改推 Search / PMax;**禁止**用 `campaign-create` / `ad-create` 伪装创建 Display。
|
|
47
47
|
- **删系列 / 测试系列须先问(硬性)**:「尽快创建 / 按方案搭建」不含删除授权。创建失败(政策违规、名称占用、半成品)时**禁止**自行建测试系列逐地域排查、**禁止**自行 `ad campaign-delete`(含 Agent 自建系列);先报告原因与选项,用户同意后才带 `--i-confirm --commit`。也**禁止**为让创建通过而整类删减方案内容(否词/排除地域/投放国家):按 `batch get` 的 `failedOperations[]` 只改报错项。见 `google-ads-write.md`「创建失败时禁止的做法」。
|
|
48
48
|
- **创建完成后交付(硬性)**:`ad batch diff` 的 stdout 含 `BEGIN_USER_DELIVERY_MARKDOWN`…`END_USER_DELIVERY_MARKDOWN`——**立刻**把中间全文原样发给用户(可先于补建);禁止只摘要、禁止只说「创建成功/详情已交付/未发现缺失」、禁止等全部补建结束再交付。
|
|
49
|
+
- **报告 render 成功即交付(硬性)**:`google-quarterly render`、`google-analysis render`、`google-ads-diagnosis render`、`website-diagnosis render`、`market-analysis render`,以及 Bing / TikTok / Yandex / Meta 的 `render`,stdout 给出 HTML 路径后**立刻**把该文件交给用户。P10 与各媒体周期报告封面的「季度 / 月度」已按日期写好(其余仍是「周期」或「账户」)。**禁止**再打开、grep 或修改这份 HTML,**禁止**为此再 render。用户没要 PDF 就不要 `html_to_pdf`;要了也只转一次,**禁止**再用 pdftotext、PyMuPDF、截图或视觉模型读 PDF/页面。细则见 `agent-conventions.md` §七。
|
|
49
50
|
- `list-accounts` 无余额/消耗;列全部用 `--page-size 999`。查单户必须 `-k`;`itemCount < total` 时**禁止**判定账户未绑定。
|
|
50
51
|
- 多户余额用 `balance-scan`(P2),多户消耗 / **转化成本·CPA 巡检**用 `accounts-digest`(P3,`--max-cpa`;**没有** `cpa-risk-scan`)。禁止外层 for-loop 逐户拉数。
|
|
51
52
|
- `stats` 的 `spend` = **区间合计**,不是日消耗。
|
|
@@ -122,7 +123,7 @@ siluzan-tso -h
|
|
|
122
123
|
| ------------------ | ------ | -------- |
|
|
123
124
|
| Google 广告账户诊断 / 健康检查(**含账户 ID**) | **P1** | `references/analytics/account-analytics.md` + `report-templates/google-ads-diagnosis.md`(可选对照 `report-templates/google-account-diagnosis-report.md`) |
|
|
124
125
|
| Google 账户周期 **数据汇总**(只要花费/转化/9 章/Excel;**无**规划/客户版/大族) | **P4** | `report-templates/google-period-report.md` + `references/analytics/account-analytics.md`;Excel 加 `google-period-report-excel.md` |
|
|
125
|
-
| Google/谷歌 **月度报告** / **月总结与规划** / **季度报告** / **Q3 汇总** / **Q4 规划** / **季度汇报** / **投放季报** / 客户版 / 大族模板 / 下月规划(效率+区域询盘+国家明细) | **P10** | `report-templates/google-quarterly-report.md` + **`report-templates/google-quarterly-plan.md`**(**未带询盘表须先问并等待**,禁止不问就 collect;有表须先转 JSON 再 `--inquiries`,禁止把 xlsx 丢给 collect;`collect`+`render
|
|
126
|
+
| Google/谷歌 **月度报告** / **月总结与规划** / **季度报告** / **Q3 汇总** / **Q4 规划** / **季度汇报** / **投放季报** / 客户版 / 大族模板 / 下月规划(效率+区域询盘+国家明细) | **P10** | `report-templates/google-quarterly-report.md` + **`report-templates/google-quarterly-plan.md`**(**未带询盘表须先问并等待**,禁止不问就 collect;有表须先转 JSON 再 `--inquiries`,禁止把 xlsx 丢给 collect;`collect`+`render`;规划九章;花费三档基于本期、不是已批预算;**不做**行业数据;**render 成功立刻交付 HTML**,禁止改 HTML / 转 PDF / 截图验收) |
|
|
126
127
|
| Meta/Facebook 周期或诊断 | **P4-FB** | `report-templates/meta-period-report.md` + `assets/meta-period-report-rules.md` + `references/analytics/facebook-analysis-guide.md` |
|
|
127
128
|
| TikTok / Bing 周期报告 | **P4** | `references/analytics/account-analytics.md` + 对应 `report-templates/*-period-report.md` |
|
|
128
129
|
| Yandex 账户分析 / 周期 / 月报 | **P4** | `report-templates/yandex-period-report.md` + `references/analytics/account-analytics.md`;Excel 加 `yandex-period-report-excel.md` |
|
package/dist/skill/_meta.json
CHANGED