siluzan-tso-cli 1.1.62-beta.1 → 1.1.62-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.62-beta.1),供内部测试使用。正式发布后安装命令将改为 `npm install -g siluzan-tso-cli`。
54
+ > **注意**:当前为测试版(1.1.62-beta.2),供内部测试使用。正式发布后安装命令将改为 `npm install -g siluzan-tso-cli`。
55
55
 
56
56
  | 助手 | 建议 `--ai` |
57
57
  | ----------------------- | ------------------------------------ |
package/dist/index.js CHANGED
@@ -6529,9 +6529,13 @@ function buildOutlineHints(section) {
6529
6529
  if (section === "campaigns") {
6530
6530
  hints.push(
6531
6531
  ...GOOGLE_ANALYSIS_CAMPAIGNS_OUTLINE_BUDGET_HINTS,
6532
- ...GOOGLE_ANALYSIS_CAMPAIGNS_COMPETITIVE_METRICS_HINTS
6532
+ ...GOOGLE_ANALYSIS_CAMPAIGNS_COMPETITIVE_METRICS_HINTS,
6533
+ ...GOOGLE_ANALYSIS_CONVERSIONS_VALUE_HINTS
6533
6534
  );
6534
6535
  }
6536
+ if (section === "ads" || section === "keywords" || section === "search-terms") {
6537
+ hints.push(...GOOGLE_ANALYSIS_CONVERSIONS_VALUE_HINTS);
6538
+ }
6535
6539
  if (section === "ads") {
6536
6540
  hints.push(...GOOGLE_ANALYSIS_ADS_OUTLINE_BUDGET_HINTS);
6537
6541
  }
@@ -6651,7 +6655,7 @@ async function writeGoogleAnalysisSnapshot(params) {
6651
6655
  agentHint: OUTLINE_AGENT_HINT
6652
6656
  };
6653
6657
  }
6654
- var LEGACY_MANIFEST_FILE, CLI_PACKAGE, SCHEMA_VERSION2, DEFAULT_FIELD_GUIDE, GOOGLE_ANALYSIS_CAMPAIGNS_OUTLINE_BUDGET_HINTS, GOOGLE_ANALYSIS_ADS_OUTLINE_BUDGET_HINTS, GOOGLE_ANALYSIS_CAMPAIGNS_COMPETITIVE_METRICS_HINTS, RATE_BEARING_SECTIONS, GOOGLE_ANALYSIS_RATE_NORMALIZED_OUTLINE_HINTS, GOOGLE_ANALYSIS_ENVELOPE_OUTLINE_HINTS, GOOGLE_ANALYSIS_OVERVIEW_ACTIVE_DAYS_HINTS, GOOGLE_ANALYSIS_ZH_FIELD_HINTS_BY_SECTION;
6658
+ var LEGACY_MANIFEST_FILE, CLI_PACKAGE, SCHEMA_VERSION2, DEFAULT_FIELD_GUIDE, GOOGLE_ANALYSIS_CAMPAIGNS_OUTLINE_BUDGET_HINTS, GOOGLE_ANALYSIS_ADS_OUTLINE_BUDGET_HINTS, GOOGLE_ANALYSIS_CONVERSIONS_VALUE_HINTS, GOOGLE_ANALYSIS_CAMPAIGNS_COMPETITIVE_METRICS_HINTS, RATE_BEARING_SECTIONS, GOOGLE_ANALYSIS_RATE_NORMALIZED_OUTLINE_HINTS, GOOGLE_ANALYSIS_ENVELOPE_OUTLINE_HINTS, GOOGLE_ANALYSIS_OVERVIEW_ACTIVE_DAYS_HINTS, GOOGLE_ANALYSIS_ZH_FIELD_HINTS_BY_SECTION;
6655
6659
  var init_google_analysis = __esm({
6656
6660
  "src/utils/snapshot/google-analysis.ts"() {
6657
6661
  "use strict";
@@ -6674,6 +6678,9 @@ var init_google_analysis = __esm({
6674
6678
  GOOGLE_ANALYSIS_ADS_OUTLINE_BUDGET_HINTS = [
6675
6679
  "// \u91D1\u989D\uFF1A`ads[].spend` / `averageCpc` / `costPerConversion` \u843D\u76D8\u524D\u5DF2\u7EDF\u4E00\u4E3A**\u5143**\uFF08`costPerConversion` \u7531\u7F51\u5173\u5FAE\u5143 \xF71e6\uFF09\uFF1B**\u7981\u6B62**\u518D\u5BF9 `costPerConversion` \u505A \xF71_000_000\u3002"
6676
6680
  ];
6681
+ GOOGLE_ANALYSIS_CONVERSIONS_VALUE_HINTS = [
6682
+ "// \u8F6C\u5316\u4EF7\u503C\uFF1A`conversionsValue` \u662F\u8D26\u6237\u5E01\u79CD\u91D1\u989D\uFF08Google\u300C\u8F6C\u5316\u4EF7\u503C\u300D\uFF09\uFF0C\u4E0D\u662F `conversions`\uFF08\u6B21\u6570\uFF09\u3002\u62A5\u544A\u5199\u8F6C\u5316\u4EF7\u503C\u6216 ROAS \u65F6\u7528\u5B83\uFF1AROAS = conversionsValue / spend\uFF08spend \u4E3A 0 \u65F6\u8BE5\u683C\u5199\u300C\u2014\u300D\uFF09\u3002\u7981\u6B62\u7528\u6D88\u8017\u3001\u70B9\u51FB\u3001\u8F6C\u5316\u6B21\u6570\u81EA\u884C\u4F30\u7B97\u3002\u884C\u4E0A\u6CA1\u6709\u8BE5\u5B57\u6BB5\u65F6\u5199\u300C\u672A\u8FD4\u56DE\u300D\uFF0C\u7981\u6B62\u628A\u7CFB\u5217\u4EF7\u503C\u644A\u5230\u8BCD\u6216\u641C\u7D22\u5B57\u8BCD\u4E0A\u3002"
6683
+ ];
6677
6684
  GOOGLE_ANALYSIS_CAMPAIGNS_COMPETITIVE_METRICS_HINTS = [
6678
6685
  "// \u7ADE\u4E89\u6307\u6807\uFF1A`campaigns[].competitiveMetrics` \u4E3A **0~1 \u5C0F\u6570**\uFF08\u5982 `0.0999` = 9.99%\uFF09\uFF0C\u542B Top/AbsoluteTop/Content/ClickShare \u7B49\uFF1B\u65E0\u6570\u636E\u65F6\u4E3A `null`\u3002",
6679
6686
  "// \u884C\u9876 `searchImpressionShare` / `searchBudgetLostImpressionShare` / `searchRankLostImpressionShare` \u843D\u76D8\u524D\u5DF2\u5F52\u4E00\u4E3A **0~1 \u5C0F\u6570**\uFF08\u4E0E `competitiveMetrics` \u540C\u540D\u9879\u4E00\u81F4\uFF09\uFF1B\u5C55\u793A\u4E3A % \u65F6 \xD7100\u3002",
@@ -115352,7 +115359,8 @@ ${label2}\uFF08\u8D26\u6237\uFF1A${opts.account}\uFF0C\u7B2C 1 \u9875\uFF0C\u672
115352
115359
  { key: "impressions", header: "\u5C55\u793A" },
115353
115360
  { key: "clicks", header: "\u70B9\u51FB" },
115354
115361
  { key: "ctr", header: "\u70B9\u51FB\u7387" },
115355
- { key: "spend", header: "\u8D39\u7528" }
115362
+ { key: "spend", header: "\u8D39\u7528" },
115363
+ { key: "conversionsValue", header: "\u8F6C\u5316\u4EF7\u503C" }
115356
115364
  ];
115357
115365
  const rows = items3.map((item) => {
115358
115366
  const kwText = String(item["text"] ?? item["keywordText"] ?? "\u2014");
@@ -115362,6 +115370,8 @@ ${label2}\uFF08\u8D26\u6237\uFF1A${opts.account}\uFF0C\u7B2C 1 \u9875\uFF0C\u672
115362
115370
  const matchType = String(item["matchTypeV2"] ?? "");
115363
115371
  const ctr = item["ctr"] != null ? (Number(item["ctr"]) * 100).toFixed(2) + "%" : "\u2014";
115364
115372
  const spend = item["spend"] != null ? Number(item["spend"]).toFixed(2) : "\u2014";
115373
+ const valueRaw = item["conversionsValue"];
115374
+ const conversionsValue = typeof valueRaw === "number" && Number.isFinite(valueRaw) ? valueRaw.toFixed(2) : "\u2014";
115365
115375
  return {
115366
115376
  keyword: kwText.slice(0, kwW),
115367
115377
  campaign: campaign.slice(0, campW),
@@ -115371,7 +115381,8 @@ ${label2}\uFF08\u8D26\u6237\uFF1A${opts.account}\uFF0C\u7B2C 1 \u9875\uFF0C\u672
115371
115381
  impressions: String(item["impressions"] ?? 0),
115372
115382
  clicks: String(item["clicks"] ?? 0),
115373
115383
  ctr,
115374
- spend
115384
+ spend,
115385
+ conversionsValue
115375
115386
  };
115376
115387
  });
115377
115388
  printCliTable(rows, cols);
@@ -119283,12 +119294,15 @@ async function runAdSearchTerms(opts) {
119283
119294
  { key: "impressions", header: "\u5C55\u793A" },
119284
119295
  { key: "clicks", header: "\u70B9\u51FB" },
119285
119296
  { key: "ctr", header: "\u70B9\u51FB\u7387" },
119286
- { key: "cost", header: "\u8D39\u7528" }
119297
+ { key: "cost", header: "\u8D39\u7528" },
119298
+ { key: "conversionsValue", header: "\u8F6C\u5316\u4EF7\u503C" }
119287
119299
  ];
119288
119300
  const rows = items3.map((item) => {
119289
119301
  const term = String(item["searchTermText"] ?? "");
119290
119302
  const ctr = item["ctr"] != null ? (Number(item["ctr"]) * 100).toFixed(2) + "%" : "\u2014";
119291
119303
  const cost = item["cost"] != null ? Number(item["cost"]).toFixed(2) : "\u2014";
119304
+ const valueRaw = item["conversionsValue"];
119305
+ const conversionsValue = typeof valueRaw === "number" && Number.isFinite(valueRaw) ? valueRaw.toFixed(2) : "\u2014";
119292
119306
  return {
119293
119307
  term: term.slice(0, termW),
119294
119308
  campaign: String(item["campaignName"] ?? "").slice(0, campW),
@@ -119297,7 +119311,8 @@ async function runAdSearchTerms(opts) {
119297
119311
  impressions: String(item["impressions"] ?? 0),
119298
119312
  clicks: String(item["clicks"] ?? 0),
119299
119313
  ctr,
119300
- cost
119314
+ cost,
119315
+ conversionsValue
119301
119316
  };
119302
119317
  });
119303
119318
  printCliTable(rows, cols);
@@ -139129,7 +139144,7 @@ function matchConversionAction(actions, name2) {
139129
139144
  return label2.includes(needle);
139130
139145
  });
139131
139146
  if (!hit) return null;
139132
- const n = num2(hit.conversions ?? hit.conversionsValue ?? hit.allConversions);
139147
+ const n = num2(hit.conversions ?? hit.allConversions);
139133
139148
  return n;
139134
139149
  }
139135
139150
  function buildQuarterlyFacts(input) {
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "slug": "siluzan-tso",
3
- "version": "1.1.62-beta.1",
4
- "publishedAt": 1793275471119
3
+ "version": "1.1.62-beta.2",
4
+ "publishedAt": 1793277242701
5
5
  }
@@ -202,12 +202,15 @@ const data = d.record ?? d.items; // record 非空=汇总维度;否则=列表
202
202
  | 消耗 | `spend` |
203
203
  | 展示 | `impressions` |
204
204
  | 点击 | `clicks` |
205
- | 转化 | `conversions` |
205
+ | 转化次数 | `conversions` |
206
+ | 转化价值 | `conversionsValue` |
206
207
  | 点击率 | `ctr` |
207
208
  | 转化率 | `conversionRate` |
208
209
  | 平均点击成本 | `averageCpc` |
209
210
  | 转化成本 | `costPerConversion` |
210
211
 
212
+ `conversionsValue` 是账户币种的 Google「转化价值」,和 `conversions`(次数)不是一回事。带 `startDate` / `endDate` 时,`keywords`、`search-terms`、`campaigns`、`ads`,以及 `ad keywords` / `ad search-terms` / `ad campaigns` / `ad groups` / `ad list` 的行上会有这个字段。报告要写转化价值或 ROAS 时用它:`ROAS = conversionsValue / spend`(`spend` 为 0 时该格写「—」)。禁止用消耗、点击、转化次数自己算。行上没有该字段(设备、每日趋势,或当次快照未返回)写「未返回」,禁止把系列价值摊到关键词或搜索字词上。账户级三格仍只汇总 `campaigns` 的 `items[]`。
213
+
211
214
  ### CampaignSectionData 关键字段
212
215
 
213
216
  > `campaigns` 维度的系列行在 `items[]`(`schemaVersion 3` 起统一信封;本节下文的 `campaigns[]` 即指 `items[]`)。
@@ -81,7 +81,7 @@
81
81
  - **步骤**:
82
82
  1. **账户核验**:`list-accounts -m Google -k <mediaCustomerId> --json-out <dir>`;无记录则停止并告知用户。
83
83
  2. 确认时间范围;区间 > 3 个月时分段(季度/月)。
84
- 3. **拉数**:用户已指定 Sheet/维度 → 只拉对应 `--sections`(见 `google-period-report-excel.md` 映射表);未指定 → 按 `google-period-report.md` 默认 9 个拉数维(含 `audience`、`negative-keywords`),并可并行追问可选追加。终稿要「转化价值 / 单转化价值 / ROAS」的上期时,再按 excel 规格拉等长上期 `--sections campaigns` 后汇总,禁止把这三格留成「—」。
84
+ 3. **拉数**:用户已指定 Sheet/维度 → 只拉对应 `--sections`(见 `google-period-report-excel.md` 映射表);未指定 → 按 `google-period-report.md` 默认 9 个拉数维(含 `audience`、`negative-keywords`),并可并行追问可选追加。终稿要「转化价值 / 单转化价值 / ROAS」的上期时,再按 excel 规格拉等长上期 `--sections campaigns` 后汇总,禁止把这三格留成「—」。关键词 / 搜索字词表用各自行的 `conversionsValue`(ROAS = 该值 / `spend`),不要用系列汇总值摊下去。
85
85
  4. **门禁**:对每个 section Read `<section>-<accountId>_*.outline.txt` 后再写脚本。
86
86
  5. 脚本读盘写 `google-period-report.json`(`meta` + `narrative`,`meta.lang` 跟本轮语言);用户要 **Excel** → Agent 脚本写 xlsx(**禁止**宿主第三方 xlsx Skill;**无** CLI excel 子命令;表头跟 `meta.lang`)。
87
87
  6. 默认 HTML:`google-analysis render --data ./google-period-report.json --snapshot-dir ./snap-google --out ./google-period-report.html --lang <zh|en>`。须含账户概览、投放趋势、Top 关键词/系列/地区/受众分布、当前否词名单、优化建议。
@@ -50,7 +50,7 @@ siluzan-tso ad campaigns -a <accountId> [--start <YYYY-MM-DD>] [--end <YYYY-MM-D
50
50
 
51
51
  命令名:列表是 **`ad campaigns`**(兼容别名 `campaign-list`),**没有** `google-ads` 命名空间。`--json-out` **必须带路径**(落盘,勿管道 stdout)。
52
52
 
53
- 带 `--start` / `--end` 时,同行的 **`spend` / `impressions` / `clicks` / `conversions` 为该闭区间合计**(**不是**日消耗)。
53
+ 带 `--start` / `--end` 时,同行的 **`spend` / `impressions` / `clicks` / `conversions` / `conversionsValue` 为该闭区间合计**(**不是**日消耗)。`conversionsValue` 是账户币种的转化价值,不是转化次数。报告写转化价值或 ROAS 时用它(ROAS = `conversionsValue` / `spend`)。`ad groups`、`ad list`、`ad keywords`、`ad search-terms` 在同一日期窗口下也返回该字段。
54
54
  与日预算比较「是否超预算」时,**必须** `start=end=统计日`;多日窗口须先 ÷ 天数或改用 `balance-scan.dailySpend` / `overview.averageDailyCost` 再谈「日均」。
55
55
 
56
56
  > **缓存**:网关侧此列表按 `账户 + 日期区间` 缓存 60 秒(Redis)。若刚 `campaign-create` / `campaign-edit` / `campaign-status` / `campaign-delete` 后立刻查询想看到最新结果,加 `--fresh` 跳过缓存(网关 `newest=true`);日常巡检/报表不需要,留空更省配额。
@@ -185,6 +185,8 @@ siluzan-tso ad ad-delete -a <accountId> --id <adId>
185
185
  siluzan-tso ad keywords -a <accountId> [--negative] [--start/--end <date>] [--json-out ./snap]
186
186
  ```
187
187
 
188
+ 带日期时行上有 `conversionsValue`(账户币种的转化价值,不是 `conversions` 次数)。`--json-out` 里直接读这个字段;终端表「转化价值」列在字段缺失时为「—」。否定关键词名单没有这个指标。
189
+
188
190
  ### 添加关键词
189
191
 
190
192
  ```bash
@@ -263,6 +265,8 @@ siluzan-tso keyword geo-list [--country-code <US,CN,...>] [--name-contains <text
263
265
  siluzan-tso ad search-terms -a <accountId> [--start/--end YYYY-MM-DD] [--json-out ./snap]
264
266
  ```
265
267
 
268
+ 行上 `conversionsValue` 是该窗口的转化价值(账户币种)。ROAS = `conversionsValue` / `spend`(或同行费用字段)。不要用点击或转化次数换算。
269
+
266
270
  ---
267
271
 
268
272
  ## ad geo — 地理位置定向管理
@@ -228,8 +228,8 @@ siluzan-tso ad campaigns -a <CID> --json-out ./snap
228
228
 
229
229
  ### 10.2 预算分配合理性
230
230
 
231
- 1. 从 `campaigns` 获取每个系列的 spend、conversions、costPerConversion
232
- 2. 计算每个系列的 ROAS 或 CPA
231
+ 1. 从 `campaigns` 获取每个系列的 spend、conversions、costPerConversion、conversionsValue
232
+ 2. ROAS 用该行 `conversionsValue / spend`(`conversionsValuePerCost` 已是这一比值,不要再平均);CPA 用 `costPerConversion`
233
233
  3. 检查以下不合理分配:
234
234
 
235
235
  | 问题 | 检测方式 | 处理 |
@@ -165,7 +165,7 @@ Account
165
165
  **设定初始 tROAS:**
166
166
 
167
167
  1. 先用 Max Conversion Value(不设目标)运行 2-4 周
168
- 2. 计算平均 ROAS
168
+ 2. 平均 ROAS 直接用系列行的 `conversionsValue / spend`(或行上的 `conversionsValuePerCost`),不要用转化次数估算价值
169
169
  3. 设 tROAS = 平均 ROAS 或低 10-20%(给算法空间)
170
170
  4. 每 2 周提高不超过 15-20%
171
171
 
@@ -118,7 +118,7 @@ function valueKpis(items) {
118
118
 
119
119
  禁止对行上的 `conversionsValuePerCost` 再平均(那是行级「转化价值/费用」,不是账户 ROAS)。
120
120
 
121
- 关键词、搜索词、设备、每日趋势的 outline 若没有 `conversionsValue`,该列写「该维度未返回」,禁止把系列转化价值摊进去。上期 `campaigns` 拉数失败时,这三格写明原因,不要留空。
121
+ 关键词、搜索字词表的转化价值读各自行的 `conversionsValue`(账户币种),ROAS = 该值 / 该行 `spend`。禁止把系列汇总摊到词上,也禁止用转化次数估算。设备、每日趋势的 outline 若没有 `conversionsValue`,该列写「该维度未返回」。关键词或搜索字词当次 outline 也没有该字段时同样写「未返回」。上期 `campaigns` 拉数失败时,账户级三格写明原因,不要留空。
122
122
 
123
123
  英文终稿表头:转化价值 → Conversion value,单转化价值 → Value / conversion,ROAS 保持 ROAS。
124
124
 
@@ -118,7 +118,7 @@ function valueKpis(items) {
118
118
 
119
119
  禁止对行上的 `conversionsValuePerCost` 再平均(那是行级「转化价值/费用」,不是账户 ROAS)。
120
120
 
121
- 关键词、搜索词、设备、每日趋势的 outline 若没有 `conversionsValue`,该列写「该维度未返回」,禁止把系列转化价值摊进去。上期 `campaigns` 拉数失败时,这三格写明原因,不要留空。
121
+ 关键词、搜索字词表的转化价值读各自行的 `conversionsValue`(账户币种),ROAS = 该值 / 该行 `spend`。禁止把系列汇总摊到词上,也禁止用转化次数估算。设备、每日趋势的 outline 若没有 `conversionsValue`,该列写「该维度未返回」。关键词或搜索字词当次 outline 也没有该字段时同样写「未返回」。上期 `campaigns` 拉数失败时,账户级三格写明原因,不要留空。
122
122
 
123
123
  英文终稿表头:转化价值 → Conversion value,单转化价值 → Value / conversion,ROAS 保持 ROAS。
124
124
 
@@ -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.62-beta.1'
12
+ $PKG_VERSION = '1.1.62-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.62-beta.1"
12
+ readonly PKG_VERSION="1.1.62-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.62-beta.1",
3
+ "version": "1.1.62-beta.2",
4
4
  "description": "Siluzan 广告账户管理 CLI — 查询账户、余额、消耗数据,管理绑定关系与充值。",
5
5
  "keywords": [
6
6
  "ad-account",