siluzan-tso-cli 1.1.36-beta.1 → 1.1.36-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md 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.36-beta.1),供内部测试使用。正式发布后安装命令将改为 `npm install -g siluzan-tso-cli`。
54
+ > **注意**:当前为测试版(1.1.36-beta.2),供内部测试使用。正式发布后安装命令将改为 `npm install -g siluzan-tso-cli`。
55
55
 
56
56
  | 助手 | 建议 `--ai` |
57
57
  | ----------------------- | ------------------------------------ |
package/dist/index.js CHANGED
@@ -106735,7 +106735,7 @@ async function writeReportAnalysisSnapshot(params) {
106735
106735
  );
106736
106736
  await fs8.writeFile(
106737
106737
  path12.join(absDir, outlineFileName),
106738
- `${formatOutlineFileBody(fileName, params.payload)}
106738
+ `${formatOutlineFileBody(fileName, params.payload, params.outlineExtraHints)}
106739
106739
  `,
106740
106740
  "utf8"
106741
106741
  );
@@ -127807,6 +127807,55 @@ function syncOverviewBalanceFields(record) {
127807
127807
  return out;
127808
127808
  }
127809
127809
 
127810
+ // src/commands/bing-analysis/normalize-bing-rates.ts
127811
+ var RATE_FIELD_NAMES2 = /* @__PURE__ */ new Set(["ctr", "conversionRate"]);
127812
+ var BING_RATE_BEARING_SECTIONS = /* @__PURE__ */ new Set([
127813
+ "overview",
127814
+ "device",
127815
+ "geographic",
127816
+ "age-audience",
127817
+ "gender-audience",
127818
+ "audience-merged",
127819
+ "campaigns",
127820
+ "ad-groups",
127821
+ "ads",
127822
+ "keywords",
127823
+ "search-terms"
127824
+ ]);
127825
+ function divIfFiniteNumber2(v) {
127826
+ return typeof v === "number" && Number.isFinite(v) ? v / 100 : v;
127827
+ }
127828
+ function divideRatesInPlace2(value, seen) {
127829
+ if (value === null || typeof value !== "object") return;
127830
+ if (seen.has(value)) return;
127831
+ seen.add(value);
127832
+ if (Array.isArray(value)) {
127833
+ for (const item of value) divideRatesInPlace2(item, seen);
127834
+ return;
127835
+ }
127836
+ const obj = value;
127837
+ for (const k of Object.keys(obj)) {
127838
+ if (RATE_FIELD_NAMES2.has(k)) {
127839
+ obj[k] = divIfFiniteNumber2(obj[k]);
127840
+ } else {
127841
+ divideRatesInPlace2(obj[k], seen);
127842
+ }
127843
+ }
127844
+ }
127845
+ function normalizeBingRateScales(payload, section) {
127846
+ if (!BING_RATE_BEARING_SECTIONS.has(section)) return payload;
127847
+ divideRatesInPlace2(payload, /* @__PURE__ */ new WeakSet());
127848
+ return payload;
127849
+ }
127850
+ var BING_RATE_NORMALIZED_OUTLINE_HINTS = [
127851
+ "// \u6982\u7387\u5B57\u6BB5\uFF1A`ctr` / `conversionRate` \u5DF2\u7531 bing-analysis CLI \u4ECE\u7F51\u5173\u767E\u5206\u6570\u523B\u5EA6\u5F52\u4E00\u4E3A **0~1 \u5C0F\u6570**\uFF08\u5982 `0.0074` = 0.74%\uFF09\u3002",
127852
+ "// \u5199\u300Cx%\u300D\u6587\u6848\uFF1A`(v * 100).toFixed(2) + '%'`\uFF1B**\u7981\u6B62**\u518D \xF7100\uFF0C\u4E5F**\u7981\u6B62**\u628A JSON \u6570\u503C\u5F53\u300C\u5DF2\u662F\u767E\u5206\u6570\u300D\u76F4\u63A5\u52A0 `%`\uFF08\u4F1A \xD7100 \u9519\u8BEF\uFF0C\u5982 0.74% \u663E\u793A\u6210 74%\uFF09\u3002"
127853
+ ];
127854
+ function buildBingOutlineHints(section) {
127855
+ if (!BING_RATE_BEARING_SECTIONS.has(section)) return void 0;
127856
+ return BING_RATE_NORMALIZED_OUTLINE_HINTS;
127857
+ }
127858
+
127810
127859
  // src/commands/bing-analysis/fetch.ts
127811
127860
  var BING_KEYWORD_LIMIT_DEFAULT = 100;
127812
127861
  function stripLocalDate(d) {
@@ -127919,7 +127968,10 @@ async function fetchBingOverviewPayload(config, id, startDate, endDate, verbose)
127919
127968
  endDate,
127920
127969
  verbose
127921
127970
  );
127922
- return syncOverviewBalanceFields(withPrevious);
127971
+ return normalizeBingSectionPayload(
127972
+ syncOverviewBalanceFields(withPrevious),
127973
+ "overview"
127974
+ );
127923
127975
  }
127924
127976
  async function enrichBingOverviewPreviousPeriod(config, accountId, record, startDate, endDate, verbose) {
127925
127977
  if (!isBingPeriodBlockEmpty(record.previousPeriod)) {
@@ -127963,13 +128015,16 @@ async function fetchBingAudienceMergedPayload(config, id, startDate, endDate, ve
127963
128015
  fetchBingJson(config, ageUrl, verbose),
127964
128016
  fetchBingJson(config, genderUrl, verbose)
127965
128017
  ]);
127966
- return {
127967
- mergedParts: ["AgeAudienceData", "GenderAudienceData"],
127968
- startDate,
127969
- endDate,
127970
- account: id,
127971
- data: { ageAudience: ageData, genderAudience: genderData }
127972
- };
128018
+ return normalizeBingSectionPayload(
128019
+ {
128020
+ mergedParts: ["AgeAudienceData", "GenderAudienceData"],
128021
+ startDate,
128022
+ endDate,
128023
+ account: id,
128024
+ data: { ageAudience: ageData, genderAudience: genderData }
128025
+ },
128026
+ "audience-merged"
128027
+ );
127973
128028
  }
127974
128029
  async function fetchBingSectionPayload(def, opts, config, id) {
127975
128030
  const { startDate, endDate } = resolveBingDateRange(opts.start, opts.end);
@@ -127986,7 +128041,12 @@ async function fetchBingSectionPayload(def, opts, config, id) {
127986
128041
  params.set("orderByCost", "true");
127987
128042
  }
127988
128043
  const url = reportingUrl(config, id, def.segment, params.toString());
127989
- return fetchBingJson(config, url, verbose);
128044
+ const raw = await fetchBingJson(config, url, verbose);
128045
+ return normalizeBingRateScales(raw, def.name);
128046
+ }
128047
+ function normalizeBingSectionPayload(payload, section) {
128048
+ if (payload === null || typeof payload !== "object") return payload;
128049
+ return normalizeBingRateScales(payload, section);
127990
128050
  }
127991
128051
  function endpointHintForBingSection(def) {
127992
128052
  return `GET \u2026/BingV2/{id}/${def.segment}`;
@@ -128045,7 +128105,8 @@ async function runAllBingSections(opts) {
128045
128105
  accountId: id,
128046
128106
  dateRange: { start: startDate, end: endDate },
128047
128107
  payload,
128048
- cliVersion
128108
+ cliVersion,
128109
+ outlineExtraHints: buildBingOutlineHints(def.name)
128049
128110
  });
128050
128111
  return {
128051
128112
  section: def.name,
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "slug": "siluzan-tso",
3
- "version": "1.1.36-beta.1",
4
- "publishedAt": 1783409684521
3
+ "version": "1.1.36-beta.2",
4
+ "publishedAt": 1783560342359
5
5
  }
@@ -319,7 +319,7 @@ siluzan-tso tiktok-analysis -a 1234567890 --start 2026-03-01 --end 2026-03-31 \
319
319
 
320
320
  ## Bing(BingV2)账户分析
321
321
 
322
- > **日期限制**:Bing 报表不能包含今天或昨天。省略时 CLI 默认截至前天的近 7 天。
322
+ > **日期限制**:Bing 报表不能包含今天或昨天。省略时 CLI 默认截至前天的近 7 天。用户要**已结束的完整自然月**(如「6月份」)→ `--start` 当月 1 日、`--end` 当月最后一天(6 月 = `06-30`,**禁止**减 1 天),见 `report-templates/bing-period-report.md` §日期规则。
323
323
 
324
324
  与 `google-analysis` / `facebook-analysis` 同架构:`bing-analysis -a … --json-out …`(省略子命令名默认执行 `run`,批量拉取全部维度或 `--sections` 指定子集)。
325
325
 
@@ -343,6 +343,18 @@ siluzan-tso bing-analysis -a <mediaCustomerId> --start 2026-03-01 --end 2026-03-
343
343
 
344
344
  报告模板:`report-templates/bing-period-report.md`(**每个 section 须写「分析」**;**余额**:`overview` section 落盘 JSON 常自动校正 0 快照,读 `balance`/`remainingAccountBudget` 或 `balance` 命令;**环比**:网关 `previousPeriod` 常空,CLI 二次拉上一周期回填)。
345
345
 
346
+ ### 点击率 / 转化率口径(必读)
347
+
348
+ Bing 的 `ctr` / `conversionRate` 为 0~1 小数**(`0.007434` = 0.74%),与 Google `schemaVersion ≥ 2` 一致。
349
+
350
+ | 场景 | 正确写法 | 错误写法 |
351
+ | ---- | -------- | -------- |
352
+ | 报告 HTML 展示 | `(ctr * 100).toFixed(2) + '%'` | `ctr.toFixed(2) + '%'`(会显示 74% 而非 0.74%) |
353
+ | Excel 0~1 列 | 直接写 JSON 数值 | 再 ÷100 |
354
+ | 脚本校验 | `clicks / impressions` 与 `ctr` 应一致(允许四舍五入) | 心算或凭印象 |
355
+
356
+ 各 `overview-*.outline.txt` 等文件头部含 `//` 硬提示,Agent **须先读 outline** 再写报告。
357
+
346
358
  ---
347
359
 
348
360
  ## 报告模板
@@ -149,6 +149,11 @@
149
149
  | Google 关键词/系列分析 | `now - 30d` ~ `now - 1d` |
150
150
  | MetaAd 账户分析 | 不得默认,必须问 |
151
151
 
152
+ **完整自然月口径**(用户说「X 月 / X月份 / 月报」且该月已结束,或上表「月报」默认):
153
+
154
+ - `--start` = 当月 1 日,`--end` = 当月最后一天(6 月 → `06-01` ~ `06-30`,**禁止** `06-29`)。
155
+ - **Bing** `bing-analysis`:「不能含今天/昨天」仅当区间碰到**执行日**的今天/昨天时才需把结束日压到前天;**已结束的历史自然月必须用该月最后一天**,细则见 `report-templates/bing-period-report.md` §日期规则。交付前核对 `overview` 的 `activeDays` = 该月日历天数。
156
+
152
157
  ---
153
158
 
154
159
  ## 六、币种与金额
@@ -12,7 +12,25 @@
12
12
 
13
13
  - Bing 接口**无法**拉取过新的数据;时间范围内**任意一天**若为**今天**或**昨天**,请求会失败。
14
14
  - **CLI**:`--start` / `--end` 须**同传或同省略**;省略时默认**截至前天**的近 7 天(与 `bing-analysis` 实现一致)。
15
- - 自选区间时请确保结束日**前天**。
15
+ - **自选区间且靠近当前日期**时,结束日须**前天**(仅当区间会碰到今天/昨天才需要压到前天;**不是**把历史自然月最后一天减 1 天)。
16
+
17
+ ### 用户说「X 月 / X月份 / 月报」(已结束的完整自然月)
18
+
19
+ 用户指定某一月份(如「6月份报告」「6月 Bing 月报」)且该月**已结束**时:
20
+
21
+ | 项 | 规则 |
22
+ | --- | --- |
23
+ | `--start` | 当月 **1 日**(如 `2026-06-01`) |
24
+ | `--end` | 当月**最后一天**(如 6 月 → `2026-06-30`,**禁止**写成 `06-29`) |
25
+ | 报告首行 | `统计区间:YYYY-MM-01 ~ YYYY-MM-<末>` |
26
+
27
+ 示例(7 月 8 日要 6 月报告):
28
+
29
+ ```bash
30
+ siluzan-tso bing-analysis -a <id> --start 2026-06-01 --end 2026-06-30 --json-out ./snap-bing
31
+ ```
32
+
33
+ **禁止**因「Bing 不能含今天/昨天」而对**已结束的历史自然月**把 `--end` 减 1 天——该限制只针对区间内的日历日是否等于**执行当天的今天或昨天**;6 月 30 日在 7 月 8 日拉数完全合法。拉数后核对 `overview` 落盘里的 `activeDays` 应等于该月日历天数(6 月 = 30)。
16
34
 
17
35
  ---
18
36
 
@@ -35,7 +53,7 @@ siluzan-tso bing-analysis -a <mediaCustomerId> --start <S> --end <E> \
35
53
 
36
54
  - 写脚本前先读各 `bing-*-<id>.outline.txt`,再读 `.json`(见 `references/core/agent-conventions.md` §三)。
37
55
  - TopN、排序、汇总均在脚本内完成,禁止心算。
38
- - 金额字段已为**元**;`ctr` / `conversionRate` 等为 **0~1 小数**,展示百分比用 `(v * 100).toFixed(2) + '%'`。
56
+ - 金额字段已为**元**;`ctr` / `conversionRate` 等:**`bing-analysis` 落盘前已从网关百分数刻度归一为 0~1 小数**(如 `0.0074` = 0.74%),展示百分比用 `(v * 100).toFixed(2) + '%'`;**禁止**把 JSON 数值当「已是百分数」直接加 `%`(会重复 ×100,如 0.74% 显示成 74%)。
39
57
 
40
58
  ---
41
59
 
@@ -12,7 +12,25 @@
12
12
 
13
13
  - Bing 接口**无法**拉取过新的数据;时间范围内**任意一天**若为**今天**或**昨天**,请求会失败。
14
14
  - **CLI**:`--start` / `--end` 须**同传或同省略**;省略时默认**截至前天**的近 7 天(与 `bing-analysis` 实现一致)。
15
- - 自选区间时请确保结束日**前天**。
15
+ - **自选区间且靠近当前日期**时,结束日须**前天**(仅当区间会碰到今天/昨天才需要压到前天;**不是**把历史自然月最后一天减 1 天)。
16
+
17
+ ### 用户说「X 月 / X月份 / 月报」(已结束的完整自然月)
18
+
19
+ 用户指定某一月份(如「6月份报告」「6月 Bing 月报」)且该月**已结束**时:
20
+
21
+ | 项 | 规则 |
22
+ | --- | --- |
23
+ | `--start` | 当月 **1 日**(如 `2026-06-01`) |
24
+ | `--end` | 当月**最后一天**(如 6 月 → `2026-06-30`,**禁止**写成 `06-29`) |
25
+ | 报告首行 | `统计区间:YYYY-MM-01 ~ YYYY-MM-<末>` |
26
+
27
+ 示例(7 月 8 日要 6 月报告):
28
+
29
+ ```bash
30
+ siluzan-tso bing-analysis -a <id> --start 2026-06-01 --end 2026-06-30 --json-out ./snap-bing
31
+ ```
32
+
33
+ **禁止**因「Bing 不能含今天/昨天」而对**已结束的历史自然月**把 `--end` 减 1 天——该限制只针对区间内的日历日是否等于**执行当天的今天或昨天**;6 月 30 日在 7 月 8 日拉数完全合法。拉数后核对 `overview` 落盘里的 `activeDays` 应等于该月日历天数(6 月 = 30)。
16
34
 
17
35
  ---
18
36
 
@@ -35,7 +53,7 @@ siluzan-tso bing-analysis -a <mediaCustomerId> --start <S> --end <E> \
35
53
 
36
54
  - 写脚本前先读各 `bing-*-<id>.outline.txt`,再读 `.json`(见 `references/core/agent-conventions.md` §三)。
37
55
  - TopN、排序、汇总均在脚本内完成,禁止心算。
38
- - 金额字段已为**元**;`ctr` / `conversionRate` 等为 **0~1 小数**,展示百分比用 `(v * 100).toFixed(2) + '%'`。
56
+ - 金额字段已为**元**;`ctr` / `conversionRate` 等:**`bing-analysis` 落盘前已从网关百分数刻度归一为 0~1 小数**(如 `0.0074` = 0.74%),展示百分比用 `(v * 100).toFixed(2) + '%'`;**禁止**把 JSON 数值当「已是百分数」直接加 `%`(会重复 ×100,如 0.74% 显示成 74%)。
39
57
 
40
58
  ---
41
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.36-beta.1'
12
+ $PKG_VERSION = '1.1.36-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.36-beta.1"
12
+ readonly PKG_VERSION="1.1.36-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"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "siluzan-tso-cli",
3
- "version": "1.1.36-beta.1",
3
+ "version": "1.1.36-beta.2",
4
4
  "description": "Siluzan 广告账户管理 CLI — 查询账户、余额、消耗数据,管理绑定关系与充值。",
5
5
  "keywords": [
6
6
  "ad-account",