siluzan-tso-cli 1.1.46-beta.5 → 1.1.46-beta.6

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.46-beta.5),供内部测试使用。正式发布后安装命令将改为 `npm install -g siluzan-tso-cli`。
54
+ > **注意**:当前为测试版(1.1.46-beta.6),供内部测试使用。正式发布后安装命令将改为 `npm install -g siluzan-tso-cli`。
55
55
 
56
56
  | 助手 | 建议 `--ai` |
57
57
  | ----------------------- | ------------------------------------ |
package/dist/index.js CHANGED
@@ -132169,7 +132169,14 @@ function parseLocalYmd(ymd) {
132169
132169
  return /* @__PURE__ */ new Date(NaN);
132170
132170
  }
132171
132171
  const [y, m, day] = parts;
132172
- return new Date(y, m - 1, day);
132172
+ if (m < 1 || m > 12 || day < 1 || day > 31) {
132173
+ return /* @__PURE__ */ new Date(NaN);
132174
+ }
132175
+ const d = new Date(y, m - 1, day);
132176
+ if (d.getFullYear() !== y || d.getMonth() !== m - 1 || d.getDate() !== day) {
132177
+ return /* @__PURE__ */ new Date(NaN);
132178
+ }
132179
+ return d;
132173
132180
  }
132174
132181
  function formatLocalYmd(d) {
132175
132182
  const y = d.getFullYear();
@@ -134371,7 +134378,14 @@ function parseLocalYmd2(ymd) {
134371
134378
  return /* @__PURE__ */ new Date(NaN);
134372
134379
  }
134373
134380
  const [y, m, day] = parts;
134374
- return new Date(y, m - 1, day);
134381
+ if (m < 1 || m > 12 || day < 1 || day > 31) {
134382
+ return /* @__PURE__ */ new Date(NaN);
134383
+ }
134384
+ const d = new Date(y, m - 1, day);
134385
+ if (d.getFullYear() !== y || d.getMonth() !== m - 1 || d.getDate() !== day) {
134386
+ return /* @__PURE__ */ new Date(NaN);
134387
+ }
134388
+ return d;
134375
134389
  }
134376
134390
  function formatLocalYmd2(d) {
134377
134391
  const y = d.getFullYear();
@@ -134502,6 +134516,11 @@ function num10(value) {
134502
134516
  function round25(value) {
134503
134517
  return value == null ? void 0 : Math.round(value * 100) / 100;
134504
134518
  }
134519
+ function normalizeYandexRoi(value) {
134520
+ if (typeof value !== "number" && typeof value !== "string") return void 0;
134521
+ const r = num10(value);
134522
+ return r === -1 ? void 0 : r;
134523
+ }
134505
134524
  function asItems(payload) {
134506
134525
  const obj = asRecord26(payload);
134507
134526
  if (!obj) return [];
@@ -134523,7 +134542,7 @@ function pickMetrics2(row, extras = []) {
134523
134542
  averageCpc: num10(row.averageCpc) ?? (clicks > 0 ? round25(spend / clicks) : void 0),
134524
134543
  conversionRate: num10(row.conversionRate) ?? (clicks > 0 ? conversions / clicks : void 0),
134525
134544
  costPerConversion: num10(row.costPerConversion) ?? (conversions > 0 ? round25(spend / conversions) : void 0),
134526
- roi: num10(row.roi)
134545
+ roi: normalizeYandexRoi(row.roi)
134527
134546
  };
134528
134547
  for (const f of extras) {
134529
134548
  if (row[f] !== void 0 && row[f] !== null) out[f] = row[f];
@@ -134568,8 +134587,14 @@ function parseYmd2(value) {
134568
134587
  const s = normalizeYandexDailyDate(value);
134569
134588
  const m = /^(\d{4})-(\d{2})-(\d{2})$/.exec(s);
134570
134589
  if (!m) return null;
134571
- const d = new Date(Number(m[1]), Number(m[2]) - 1, Number(m[3]));
134572
- return Number.isNaN(d.getTime()) ? null : d;
134590
+ const y = Number(m[1]);
134591
+ const month = Number(m[2]);
134592
+ const day = Number(m[3]);
134593
+ const d = new Date(y, month - 1, day);
134594
+ if (Number.isNaN(d.getTime()) || d.getFullYear() !== y || d.getMonth() !== month - 1 || d.getDate() !== day) {
134595
+ return null;
134596
+ }
134597
+ return d;
134573
134598
  }
134574
134599
  function formatYmd2(d) {
134575
134600
  const y = d.getFullYear();
@@ -134648,9 +134673,12 @@ var YANDEX_DAILY_OUTLINE_HINTS = [
134648
134673
  "// `items[].date` \u5DF2\u89C4\u8303\u4E3A YYYY-MM-DD\u3002\u63A5\u53E3\u53EF\u80FD\u8FD4\u56DE ISO \u65F6\u95F4\u6233\uFF08\u5982 2026-07-01T00:00:00+00:00\uFF09\uFF0CCLI \u5DF2\u622A\u65AD\uFF1B\u7981\u6B62\u518D\u62FC\u63A5 T00:00:00Z\uFF0C\u5426\u5219 new Date() \u4F1A Invalid Date\uFF0C\u5468\u6807\u7B7E\u53D8\u6210 NaN\u3002",
134649
134674
  "// \u5468\u8D8B\u52BF\u8BFB\u672C\u6587\u4EF6 `weekly[]`\uFF08ISO \u5468\u4E00~\u5468\u65E5\uFF1B`days` \u4E3A\u6709\u6570\u636E\u7684\u5929\u6570\uFF09\u3002\u7981\u6B62\u81EA\u884C\u6309 date \u805A\u5468\u3002"
134650
134675
  ];
134676
+ var YANDEX_ROI_OUTLINE_HINTS = [
134677
+ "// `roi` = (Revenue-Spend)/Spend\uFF0C\u5DF2\u662F\u500D\u6570\uFF083.72 \u2248 \u5229\u6DA6 372%\uFF09\uFF0C\u4E0D\u662F 0~1\u3002\u7981\u6B62\u518D \xD7100\u3002\u65E0\u6536\u76CA\u65F6\u5E38\u4E3A -1\uFF0C\u6309\u7F3A\u5931\u5904\u7406\u3002"
134678
+ ];
134651
134679
  function buildYandexOutlineHints(section) {
134652
- if (section === "daily") return YANDEX_DAILY_OUTLINE_HINTS;
134653
- return void 0;
134680
+ if (section === "daily") return [...YANDEX_DAILY_OUTLINE_HINTS, ...YANDEX_ROI_OUTLINE_HINTS];
134681
+ return YANDEX_ROI_OUTLINE_HINTS;
134654
134682
  }
134655
134683
  function mapDailyRows(payload) {
134656
134684
  return asItems(payload).map((r) => {
@@ -134737,7 +134765,8 @@ async function mergeYandexAnalysisSnapshotIntoReport(payload, snapshotDir) {
134737
134765
  kpis.costPerConversion = round25(num10(totals.costPerConversion));
134738
134766
  }
134739
134767
  if (num10(totals.conversionRate) != null) kpis.conversionRate = num10(totals.conversionRate);
134740
- if (num10(totals.roi) != null) kpis.roi = num10(totals.roi);
134768
+ const overviewRoi = normalizeYandexRoi(totals.roi);
134769
+ if (overviewRoi != null) kpis.roi = overviewRoi;
134741
134770
  }
134742
134771
  if (overview) {
134743
134772
  if (num10(overview.balance) != null) kpis.balance = round25(num10(overview.balance));
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "slug": "siluzan-tso",
3
- "version": "1.1.46-beta.5",
4
- "publishedAt": 1787024164836
3
+ "version": "1.1.46-beta.6",
4
+ "publishedAt": 1787040608858
5
5
  }
@@ -442,7 +442,7 @@ siluzan-tso bing-analysis run -a <mediaCustomerId> --json-out ./snap --sections
442
442
  >
443
443
  > **账户 ID**:Yandex Client-Login / mediaCustomerId(如 `porg-xxx`,与 `list-accounts -m Yandex` 一致),**不是**纯数字。
444
444
  >
445
- > **金额**:已从 micros 转为账户币种小数(`/1e6`)。`ctr` / `conversionRate` 为 **0~1 小数**(先读 outline)。
445
+ > **金额**:已从 micros 转为账户币种小数(`/1e6`)。`ctr` / `conversionRate` 为 **0~1 小数**(先读 outline)。`roi` 为 **(收益-消耗)/消耗** 倍数,禁止再 ×100。
446
446
 
447
447
  与 `bing-analysis` 同架构:`yandex-analysis -a … --json-out …`(省略子命令名默认执行 `run`);周期报告默认 **HTML**:`yandex-analysis render`(见 `report-templates/yandex-period-report.md`)。用户要 Excel → `yandex-period-report-excel.md`(Agent 脚本,无 excel 子命令)。
448
448
 
@@ -19,7 +19,7 @@
19
19
  | `geo` | `{ …, items: GeoRow[] }` | → `tables.geo[]` |
20
20
  | `devices` | `{ …, items: DeviceRow[] }` | → `tables.devices[]` |
21
21
 
22
- > 金额已为账户币种小数;`ctr` / `conversionRate` 为 **0~1 小数**。写叙事前先读各维 `*.outline.txt`。
22
+ > 金额已为账户币种小数;`ctr` / `conversionRate` 为 **0~1 小数**。`roi` 为 **(收益-消耗)/消耗** 的倍数(如 `3.72` 表示利润约 372%),**不是** 0~1,禁止再 ×100;无收益时常为 `-1`(按缺失)。写叙事前先读各维 `*.outline.txt`。
23
23
 
24
24
  ---
25
25
 
@@ -492,6 +492,17 @@
492
492
  return (n * 100).toFixed(2) + "%";
493
493
  }
494
494
 
495
+ /**
496
+ * 后端 roi = (Revenue - Spend) / Spend,已是倍数(如 3.72 = 372% 利润),
497
+ * **不是** CTR 那种 0~1。禁止走 pct(),否则 371.96 会显示成 37195.98%。
498
+ * 无收益时网关算成 -1,展示为 —。
499
+ */
500
+ function fmtRoi(v) {
501
+ const n = toNum(v);
502
+ if (n === undefined || n === -1) return "—";
503
+ return n.toFixed(2) + "%";
504
+ }
505
+
495
506
  function num2(v) {
496
507
  const n = toNum(v);
497
508
  return n === undefined ? "—" : n.toFixed(2);
@@ -1158,7 +1169,7 @@
1158
1169
  '</td><td class="num">' +
1159
1170
  money(r.costPerConversion, currency) +
1160
1171
  '</td><td class="num">' +
1161
- pct(r.roi) +
1172
+ fmtRoi(r.roi) +
1162
1173
  '</td><td class="num">' +
1163
1174
  pct(r.ctr) +
1164
1175
  "</td><td>" +
@@ -19,7 +19,7 @@
19
19
  | `geo` | `{ …, items: GeoRow[] }` | → `tables.geo[]` |
20
20
  | `devices` | `{ …, items: DeviceRow[] }` | → `tables.devices[]` |
21
21
 
22
- > 金额已为账户币种小数;`ctr` / `conversionRate` 为 **0~1 小数**。写叙事前先读各维 `*.outline.txt`。
22
+ > 金额已为账户币种小数;`ctr` / `conversionRate` 为 **0~1 小数**。`roi` 为 **(收益-消耗)/消耗** 的倍数(如 `3.72` 表示利润约 372%),**不是** 0~1,禁止再 ×100;无收益时常为 `-1`(按缺失)。写叙事前先读各维 `*.outline.txt`。
23
23
 
24
24
  ---
25
25
 
@@ -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.46-beta.5'
12
+ $PKG_VERSION = '1.1.46-beta.6'
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.46-beta.5"
12
+ readonly PKG_VERSION="1.1.46-beta.6"
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.46-beta.5",
3
+ "version": "1.1.46-beta.6",
4
4
  "description": "Siluzan 广告账户管理 CLI — 查询账户、余额、消耗数据,管理绑定关系与充值。",
5
5
  "keywords": [
6
6
  "ad-account",