siluzan-tso-cli 1.1.25 → 1.1.26-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 +171 -30
- package/dist/skill/SKILL.md +5 -3
- package/dist/skill/_meta.json +2 -2
- package/dist/skill/references/README.md +2 -1
- package/dist/skill/references/accounts/accounts.md +5 -5
- package/dist/skill/references/accounts/finance.md +32 -32
- package/dist/skill/references/accounts/open-account-by-media.md +4 -2
- package/dist/skill/references/accounts/open-account-google-ui.md +1 -1
- package/dist/skill/references/analytics/account-analytics.md +2 -2
- 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 +20 -5
- package/dist/skill/references/core/deliverable-preflight.md +107 -0
- package/dist/skill/references/core/playbooks.md +4 -2
- package/dist/skill/references/core/setup.md +5 -5
- package/dist/skill/references/core/skill-authoring.md +1 -1
- package/dist/skill/references/core/subagent-orchestration.md +1 -1
- package/dist/skill/references/core/tips.md +14 -11
- package/dist/skill/references/core/workflows.md +6 -6
- package/dist/skill/references/misc/tso-home.md +2 -2
- package/dist/skill/report-templates/REPORT-WORKFLOW.md +8 -0
- package/dist/skill/scripts/check-deliverable-snap.mjs +170 -0
- package/dist/skill/scripts/install.ps1 +3 -3
- package/dist/skill/scripts/install.sh +3 -3
- package/eval/stub-fixtures/meta-overview.json +1 -1
- package/package.json +1 -1
|
@@ -24,7 +24,8 @@
|
|
|
24
24
|
|
|
25
25
|
- **禁止**「上次已经读过 `accounts/accounts.md` / `google-ads/google-ads.md`,本任务直接 `siluzan-tso …`」
|
|
26
26
|
- **禁止**用对话记忆中的示例 ID、金额、命令 flags 代替当次 Read 文档
|
|
27
|
-
- **禁止**跳过 `references/core/tips.md` 里的 outline
|
|
27
|
+
- **禁止**跳过 `references/core/tips.md` 里的 outline→脚本读 JSON 顺序
|
|
28
|
+
- **禁止**用 Read / `cat` / `type` 打开 `--json-out` 落盘的**业务数据** `*.json`(`campaigns-*.json`、`list-accounts-*.json` 等);只允许 Read 体积小的 `*.outline.txt` 与 `references/` 文档
|
|
28
29
|
|
|
29
30
|
---
|
|
30
31
|
|
|
@@ -36,7 +37,8 @@
|
|
|
36
37
|
2. 涉及写入/修改/删除的操作必须与用户确认
|
|
37
38
|
3. 按计划执行,说明每步意图
|
|
38
39
|
4. 用成对的读命令复核写入结果;异步任务每 5s 轮询直到完成
|
|
39
|
-
5.
|
|
40
|
+
5. **交付前**(报告/Excel/含金额话术):Read `references/core/deliverable-preflight.md`,运行 `node scripts/check-deliverable-snap.mjs`;币种须来自 `list-accounts` / `overview` 的 `currencyCode`
|
|
41
|
+
6. 全部完成后预测用户下一步操作
|
|
40
42
|
|
|
41
43
|
### 执行模式速查
|
|
42
44
|
|
|
@@ -77,15 +79,27 @@
|
|
|
77
79
|
|
|
78
80
|
本 Skill 下**所有**业务数据均以 CLI `--json-out`(或用户提供的同构 JSON)落盘为唯一真相源。Agent **不得**在对话或脚本里「代替」磁盘数据。
|
|
79
81
|
|
|
82
|
+
### 禁止 Read 完整业务 JSON(最高优先级)
|
|
83
|
+
|
|
84
|
+
`--json-out` 落盘的 `writtenFiles[0]`(及同目录 `campaigns-*.json`、`keywords-*.json` 等)往往为 **MB 级**。Agent **永远不得**用宿主 **Read** 工具、`cat`、`type`、`Get-Content` 等方式把整文件读进对话上下文。
|
|
85
|
+
|
|
86
|
+
| 允许 Read 的文件 | 必须用代码读取的文件 |
|
|
87
|
+
| ---------------- | -------------------- |
|
|
88
|
+
| `references/**/*.md`、`assets/**/*.md`(Skill 文档) | 所有 `--json-out` 业务 `*.json` |
|
|
89
|
+
| 当次 `*.outline.txt`(通常小于 2KB,仅 schema) | `cli-manifest*.json` / `manifest-*.json` 中的路径索引(应用脚本 `JSON.parse` 读,勿 Read 整份 manifest 若很大) |
|
|
90
|
+
| stdout **一行**摘要 JSON(命令回显,体积小) | 用户提供的同构大 JSON 文件 |
|
|
91
|
+
|
|
92
|
+
**正确流程**:解析 stdout 摘要 → Read **仅** `outlineFile` 了解字段类型 → **编写并执行** `node -e` / `node script.mjs` / `python`:`require` 或 `readFileSync` 读 JSON → `console.log` / 写 xlsx/html **只输出所需字段** → 用脚本打印结果回答用户。
|
|
93
|
+
|
|
80
94
|
| 必须 | 禁止 |
|
|
81
95
|
| ---- | ---- |
|
|
82
|
-
| 编写并**执行** Node/Python
|
|
83
|
-
| **先 outline
|
|
96
|
+
| 编写并**执行** Node/Python 脚本读落盘 JSON → 计算 → 写出 | 用 **Read** 打开 `writtenFiles` 业务 JSON;在回复里手填、改数、心算汇总 |
|
|
97
|
+
| **先 outline 后脚本读 JSON**:`outlineFile` → 代码读 `writtenFiles[0]` | 跳过 outline 猜字段名;把 outline 当数据贴给用户 |
|
|
84
98
|
| 字段路径以 `outlineFile` + 当次 `writtenFiles` / manifest 为准 | 把国家名、ID、金额、词表等**业务值**写成源码字面量 |
|
|
85
99
|
| 映射表 / 模板契约在运行时加载(`analytics/geo-continents.json`、`campaign-create-template.json`) | 复制配置条目进源码常量 |
|
|
86
100
|
| 脚本输出与落盘 JSON 可复核 | 用「上次对话记住的数」「占位数」写入交付物 |
|
|
87
101
|
|
|
88
|
-
**允许的字面量**:输出目录、Sheet/列标题、技术格式、`focusCountries` 等**用户当轮明确给出**的配置(建议落盘为 `config.json`
|
|
102
|
+
**允许的字面量**:输出目录、Sheet/列标题、技术格式、`focusCountries` 等**用户当轮明确给出**的配置(建议落盘为 `config.json` 再**用脚本**读)。
|
|
89
103
|
|
|
90
104
|
完整协议见 `references/core/tips.md` § 处理顺序、§ 已有 JSON 时。
|
|
91
105
|
|
|
@@ -122,6 +136,7 @@
|
|
|
122
136
|
- **符号**:`CNY` → **¥**;`USD` → **$**。多账户按 `currencyCode` 分表,**禁止**跨币种求和。
|
|
123
137
|
- **金额单位统一为「元」**:报告保留 2 位小数。
|
|
124
138
|
- **Google 广告诊断报告**:`report-templates/google-ads-diagnosis.md` § 撰写硬约束 — 每日趋势金额/CPA 必须 2 位小数;每个模块除表格外须有「分析」+「建议」,禁止只贴数据。
|
|
139
|
+
- **交付前自检**:报告/Excel 交付前运行 `scripts/check-deliverable-snap.mjs`(见 `deliverable-preflight.md`);自检失败禁止交付。
|
|
125
140
|
- **品牌名优先级**:(1) 用户明确提供 → (2) `list-accounts.mag.advertiserName` → (3) 网址域名占位 `[待确认品牌名]`。**严禁**把英文域名翻译为虚构中文品牌。
|
|
126
141
|
|
|
127
142
|
---
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
# 交付前自检(币种 + 数据完整)
|
|
2
|
+
|
|
3
|
+
> 用户反馈 Agent 产出物常见两类问题:**币种错误**(Google 默认美金、符号混用)、**报告缺章/缺数**(少拉维度、Read JSON 截断后手填)。本节给出**可执行**防线。
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 根因(简要)
|
|
8
|
+
|
|
9
|
+
| 问题 | 典型原因 |
|
|
10
|
+
| ---- | -------- |
|
|
11
|
+
| 币种错误 | 未先 `list-accounts` 读 `currencyCode`;报告脚本写死 `USD`;CNY 用 `$` |
|
|
12
|
+
| 数据缺失 | 未按模板拉全 `--sections`;未读 `manifest` 就写报告;Read 大 JSON 截断后编造;多账户漏 ID |
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## 强制流程(报告/Excel 交付前)
|
|
17
|
+
|
|
18
|
+
与 `references/core/agent-conventions.md`「计划 → 确认 → 执行 → **验证**」一致,**交付前增加验证步**:
|
|
19
|
+
|
|
20
|
+
### 1. 币种(第一步就做)
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
siluzan-tso list-accounts -m Google -k <mediaCustomerId> --json-out ./snap
|
|
24
|
+
node -e "
|
|
25
|
+
const d=require('./snap/list-accounts-google.json');
|
|
26
|
+
const ma=(d.items||[]).map(x=>x.ma||x).find(m=>String(m.mediaCustomerId)===process.argv[1]);
|
|
27
|
+
if(!ma){console.error('账户未找到');process.exit(1);}
|
|
28
|
+
console.log(ma.currencyCode, ma.mediaCustomerName);
|
|
29
|
+
" <mediaCustomerId>
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
- 将输出的 **`currencyCode`** 写入报告生成脚本(从 JSON 读,勿写死)。
|
|
33
|
+
- 首行必须有:`统计区间:YYYY-MM-DD ~ YYYY-MM-DD(货币:CNY|USD)`。
|
|
34
|
+
- 符号:**CNY → ¥**,**USD → $**。详见 `references/accounts/currency.md`。
|
|
35
|
+
|
|
36
|
+
### 2. 拉数完整(对照模板 + manifest)
|
|
37
|
+
|
|
38
|
+
- 周期报告:按 `report-templates/google-period-report.md` 默认 7 维(或用户追加维)一次 `google-analysis --sections … --json-out`。
|
|
39
|
+
- 拉数后打开 **`manifest-<accountId>.json`**(用脚本 `JSON.parse`,勿 Read 整份若很大),核对 `artifacts[].section` 与模板一致。
|
|
40
|
+
|
|
41
|
+
### 3. 写报告只用脚本读 JSON
|
|
42
|
+
|
|
43
|
+
见 `references/core/tips.md`:**禁止** Read 业务 `*.json`;用 `node`/Python 聚合后写 HTML/xlsx。
|
|
44
|
+
|
|
45
|
+
### 4. 交付前运行自检脚本(必做)
|
|
46
|
+
|
|
47
|
+
Skill 自带脚本(安装后在 skill 根目录):
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
node scripts/check-deliverable-snap.mjs ./snap --preset p4-google
|
|
51
|
+
# 或自定义:--required overview,campaigns,geographic
|
|
52
|
+
# 已知账户为 CNY 时:--expect-currency CNY
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
| 参数 | 说明 |
|
|
56
|
+
| ---- | ---- |
|
|
57
|
+
| `<snapDir>` | `--json-out` 目录 |
|
|
58
|
+
| `--preset p4-google` | 对齐 Google 周期报告默认 7 维 |
|
|
59
|
+
| `--preset p1-google` | 单账户画像:list-accounts + stats + ad-campaigns |
|
|
60
|
+
| `--required a,b` | 额外必填 section(逗号分隔,前缀匹配) |
|
|
61
|
+
| `--expect-currency CNY\|USD` | 与 `list-accounts` / `overview` 中 `currencyCode` 对照 |
|
|
62
|
+
|
|
63
|
+
**退出码 1** → 补拉缺失维度或修币种后再交付。**禁止**在自检失败时仍把报告发给用户。
|
|
64
|
+
|
|
65
|
+
### 5. 报告正文抽检(脚本输出对照)
|
|
66
|
+
|
|
67
|
+
交付前用脚本打印 3 个汇总(示例):
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
node -e "
|
|
71
|
+
const o=require('./snap/overview-<id>_<start>-<end>.json');
|
|
72
|
+
const r=o.record||{};
|
|
73
|
+
const p=r.currentPeriod||{};
|
|
74
|
+
console.log('spend',p.spend,'clicks',p.clicks,'currency',r.currencyCode);
|
|
75
|
+
"
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
报告中的总消耗、币种须与上述一致(允许四舍五入,**不允许数量级或币种错误**)。
|
|
79
|
+
|
|
80
|
+
---
|
|
81
|
+
|
|
82
|
+
## Playbook 挂钩
|
|
83
|
+
|
|
84
|
+
| Playbook | 自检命令 |
|
|
85
|
+
| -------- | -------- |
|
|
86
|
+
| P1 | `--preset p1-google` + `--expect-currency`(来自 list-accounts) |
|
|
87
|
+
| P4 | `--preset p4-google` |
|
|
88
|
+
| P6 / P7 | 按 `okki-weekly-google-client.md` / `google-inquiry-analysis.md` 自建 `--required`(含各月 `campaigns`、`geographic` 等) |
|
|
89
|
+
|
|
90
|
+
---
|
|
91
|
+
|
|
92
|
+
## 长期改进(可选)
|
|
93
|
+
|
|
94
|
+
| 手段 | 说明 |
|
|
95
|
+
| ---- | ---- |
|
|
96
|
+
| **skill-eval 场景** | 断言 Agent 先 `list-accounts` 且报告含 `(货币:XXX)` |
|
|
97
|
+
| **CLI `snap check`** | 将本脚本收编为 `siluzan-tso snap check`(待开发) |
|
|
98
|
+
| **报告模板内嵌币种占位** | 脚本写 `{currencyCode}` 而非手写符号 |
|
|
99
|
+
|
|
100
|
+
---
|
|
101
|
+
|
|
102
|
+
## 相关文档
|
|
103
|
+
|
|
104
|
+
- `references/accounts/currency.md`
|
|
105
|
+
- `report-templates/REPORT-WORKFLOW.md` § 步骤 4–5
|
|
106
|
+
- `references/core/tips.md`
|
|
107
|
+
- `references/core/playbooks.md`
|
|
@@ -4,10 +4,10 @@
|
|
|
4
4
|
|
|
5
5
|
| 编号 | 场景 | 必读 references |
|
|
6
6
|
| ---- | ---- | --------------- |
|
|
7
|
-
| P1 | 单账户投放画像 | `analytics/account-analytics.md`、`accounts/currency.md`、`core/tips.md` |
|
|
7
|
+
| P1 | 单账户投放画像 | `analytics/account-analytics.md`、`accounts/currency.md`、`core/tips.md`、`core/deliverable-preflight.md` |
|
|
8
8
|
| P2 | 多账户余额扫描 | `accounts/accounts.md`(`balance-scan`)、`accounts/currency.md` |
|
|
9
9
|
| P3 | 多账户投放画像汇总 | `accounts/accounts.md` § accounts-digest、`accounts/currency.md`、`core/tips.md` |
|
|
10
|
-
| P4 | Google 账户周期报告 | `report-templates/google-period-report.md`、`analytics/account-analytics.md`、`core/tips.md` |
|
|
10
|
+
| P4 | Google 账户周期报告 | `report-templates/google-period-report.md`、`analytics/account-analytics.md`、`core/tips.md`、`core/deliverable-preflight.md` |
|
|
11
11
|
| P4-FB | Meta/Facebook 周期报告 | `report-templates/meta-period-report.md`、`analytics/facebook-analysis-guide.md`、`analytics/account-analytics.md`、`core/tips.md` |
|
|
12
12
|
| P5 | 多账户多维度报告 | `analytics/google-analysis-batch.md`、`analytics/account-analytics.md`、`core/tips.md`;可选 `core/subagent-orchestration.md` |
|
|
13
13
|
| P6 | OKKI 周报 | `report-templates/okki-weekly-google-client.md`(全文)、`analytics/account-analytics.md`、`core/tips.md`;可选 `core/subagent-orchestration.md` |
|
|
@@ -24,6 +24,7 @@
|
|
|
24
24
|
4. `ad campaigns -a <mediaCustomerId> --start <S> --end <D> --json-out ./snap-p1`
|
|
25
25
|
5. 诊断/画像报告:Read `report-templates/google-ads-diagnosis.md`(**硬约束:每日趋势 2 位小数、每模块必有分析**)+ `google-account-diagnosis-report.md`(章节与 CLI);`google-analysis --json-out` 须含 `daily-metrics`(按日趋势)。
|
|
26
26
|
6. 首行标注统计区间和货币;HTML 可对齐 `report-template.html` + `section-*` 区块。
|
|
27
|
+
7. 交付前:`node scripts/check-deliverable-snap.mjs ./snap-p1 --preset p1-google --expect-currency <来自 list-accounts>`。
|
|
27
28
|
|
|
28
29
|
---
|
|
29
30
|
|
|
@@ -66,6 +67,7 @@ siluzan-tso accounts-digest -m Google -a id1,id2,... --start <S> --end <D> --jso
|
|
|
66
67
|
1. 确认时间范围;区间 > 3 个月时分段(季度/月)。
|
|
67
68
|
2. 按 `google-period-report.md` 默认维度执行 `google-analysis --json-out <dir>`。
|
|
68
69
|
3. 报告须含:账户概览、投放趋势、Top 关键词/系列/地区分布、优化建议。
|
|
70
|
+
4. 交付前:`node scripts/check-deliverable-snap.mjs <dir> --preset p4-google --expect-currency <currencyCode>`。
|
|
69
71
|
|
|
70
72
|
---
|
|
71
73
|
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
## 安装 CLI
|
|
11
11
|
|
|
12
12
|
```bash
|
|
13
|
-
npm install -g siluzan-tso-cli
|
|
13
|
+
npm install -g siluzan-tso-cli@beta
|
|
14
14
|
```
|
|
15
15
|
|
|
16
16
|
---
|
|
@@ -64,7 +64,7 @@ siluzan-tso config set --api-key <Key> # 或 config 直接写入
|
|
|
64
64
|
siluzan-tso config set --token <Token> # 备用:设置 JWT Token
|
|
65
65
|
```
|
|
66
66
|
|
|
67
|
-
API Key 获取入口:`https://www.siluzan.com/v3/foreign_trade/settings/apiKeyManagement`
|
|
67
|
+
API Key 获取入口:`https://www-ci.siluzan.com/v3/foreign_trade/settings/apiKeyManagement`
|
|
68
68
|
|
|
69
69
|
```bash
|
|
70
70
|
# 第 1 步:让用户报出手机号后,立刻发码(命令立即返回,不会等待输入)
|
|
@@ -129,9 +129,9 @@ siluzan-tso config show
|
|
|
129
129
|
|
|
130
130
|
```
|
|
131
131
|
构建环境 : production
|
|
132
|
-
apiBaseUrl : https://tso-api.siluzan.com
|
|
133
|
-
googleApiUrl : https://googleapi.mysiluzan.com
|
|
134
|
-
webUrl : https://www.siluzan.com
|
|
132
|
+
apiBaseUrl : https://tso-api-ci.siluzan.com
|
|
133
|
+
googleApiUrl : https://googleapi-ci.mysiluzan.com
|
|
134
|
+
webUrl : https://www-ci.siluzan.com
|
|
135
135
|
apiKey : abcd****1234
|
|
136
136
|
```
|
|
137
137
|
|
|
@@ -53,7 +53,7 @@ siluzan-tso/ # 安装后目录名
|
|
|
53
53
|
|
|
54
54
|
- 从 SKILL.md **只链接一层**(`references/foo.md`),避免 A→B→C 链式引用。
|
|
55
55
|
- 路径用正斜杠,不用 Windows 反斜杠。
|
|
56
|
-
- 占位符 `https://www.siluzan.com`、`https://tso-api.siluzan.com`、`npm install -g siluzan-tso-cli` 由构建脚本替换,文档中禁止硬编码环境 URL。
|
|
56
|
+
- 占位符 `https://www-ci.siluzan.com`、`https://tso-api-ci.siluzan.com`、`npm install -g siluzan-tso-cli@beta` 由构建脚本替换,文档中禁止硬编码环境 URL。
|
|
57
57
|
|
|
58
58
|
---
|
|
59
59
|
|
|
@@ -58,7 +58,7 @@
|
|
|
58
58
|
| `mediaCustomerId` | 来自当次 `list-accounts`,禁止猜 |
|
|
59
59
|
| `dateRange` | `start` / `end`(YYYY-MM-DD);P7 另附 `m1`/`m2`/`m3` |
|
|
60
60
|
| `commands[]` | 可复制 bash 块(来自 playbook 或 report-template) |
|
|
61
|
-
| `forbidden[]` | 至少包含:禁止 Read
|
|
61
|
+
| `forbidden[]` | 至少包含:禁止 Read/cat 打开落盘业务 `*.json`(须 node/python 脚本读盘);禁止把 JSON 贴进回复;禁止编造 ID/金额;禁止 batch 重新 `run`(仅 `resume`) |
|
|
62
62
|
| `returnSchema` | 子会话**只**回传下列内容 |
|
|
63
63
|
|
|
64
64
|
### returnSchema(子会话结束时的回复格式)
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
> **文档重读(与落盘无关,但同样硬)**:**每个新的用户任务**或**同对话内换话题**时,须按 `references/core/agent-conventions.md` § 文档加载纪律 **重新 Read** 当次任务对应的 `references/*.md` / `report-templates/*.md`,再执行 CLI。
|
|
4
4
|
|
|
5
|
-
> **AI 助手(本 Skill
|
|
5
|
+
> **AI 助手(本 Skill)**:凡需结构化数据,一律 **`--json-out <路径>`** 落盘,再用 **Node/Python 脚本**(`require` / `readFileSync`)读 JSON 做筛选与聚合。**禁止**用宿主 **Read** 工具打开落盘业务 `*.json`(会占满上下文且易截断)。命令成功时 **stdout 仅一行摘要 JSON**(体积小);**`outlineFile`**(`*.outline.txt`,通常小于 2KB)是唯一允许用 Read 查看的「数据结构」文件——最后一行是 TS 类型字面量。业务数据**只**通过脚本访问,脚本 stdout 打印你需要的行/汇总即可。
|
|
6
6
|
>
|
|
7
7
|
> **文件命名规则(重要)**:当一条命令具备明确的"查询 id"(mediaCustomerId / entityId / ruleId / auditId 等)时,目录模式落盘的 `*.json`、`*.outline.txt` 与对应 manifest **都会带上 `-<查询id>` 后缀**:
|
|
8
8
|
>
|
|
@@ -21,23 +21,26 @@
|
|
|
21
21
|
|
|
22
22
|
---
|
|
23
23
|
|
|
24
|
-
## 处理顺序(**Agent
|
|
24
|
+
## 处理顺序(**Agent 必读**):摘要 → outline(schema)→ **脚本**读 JSON
|
|
25
25
|
|
|
26
26
|
每条 `--json-out` 命令成功后,**必须**按以下顺序处理,不要跳步、不要把 outline 当数据:
|
|
27
27
|
|
|
28
|
-
1.
|
|
29
|
-
2.
|
|
28
|
+
1. **解析 stdout 一行摘要 JSON**(禁止 Read 打开 `writtenFiles[0]` 的 `.json`):拿到 `outlineFile`、`writtenFiles[0]`、`manifestFile`、`agentHint`。**不要硬编码 `<section>.json` 文件名**。
|
|
29
|
+
2. **了解字段结构**(二选一,推荐 A):
|
|
30
|
+
- **A**:宿主 **Read** 仅 `outlineFile`(`*.outline.txt`,体积小);或
|
|
31
|
+
- **B**:`node -e "const fs=require('fs'); const o=fs.readFileSync(process.argv[1],'utf8').trimEnd().split('\\n').filter(l=>!l.startsWith('//')); console.log(o.pop())" <outlineFile>`
|
|
30
32
|
- 第 1 行(注释):`// outline of \`<xxx>.json\` — schema-only, NOT the data.` —— **明确告诉你它是"数据结构描述文件"**,不要把它当业务数据使用。
|
|
31
33
|
- 第 2 行(注释):中文用法说明。
|
|
32
34
|
- 第 3 行(注释):类型推断口径(数组取前 8 项去重并集;环或重复对象记为 any)。
|
|
33
35
|
- 其后**可能**还有若干行 `//` 字段提示(例如 `google-analysis` 的 **`campaigns-*.outline.txt`** 会插入金额字段已统一为元的说明)。
|
|
34
36
|
- **类型字面量**:**最后一个不以 `//` 开头的行**(单行 TS,如 `{ a: number; items: { ... }[] }`)—— 写筛选/聚合脚本时取这一行即可。
|
|
35
37
|
- 提取写法:`outlineRaw.trimEnd().split('\n').filter(l => !l.startsWith('//')).pop()`(**不要**用 `lines[lines.length - 1]`:当最后一行是空行时会错)。
|
|
36
|
-
3.
|
|
37
|
-
-
|
|
38
|
-
-
|
|
39
|
-
-
|
|
40
|
-
|
|
38
|
+
3. **必须用 Bash 执行脚本**读 `writtenFiles[0]`(及 manifest 中的路径)做筛选、聚合、计算——脚本内 `require` / `readFileSync`,**禁止**宿主 Read / cat / type 打开该 `.json`。**禁止**:
|
|
39
|
+
- 用 **Read** 读 `campaigns-*.json`、`list-accounts-*.json` 等落盘业务 JSON;
|
|
40
|
+
- 把 outline 当 JSON `require`(它是 `.txt`);
|
|
41
|
+
- 跳过 outline 猜字段名;
|
|
42
|
+
- 把 outline 或整段 JSON 贴进对话当结论。
|
|
43
|
+
4. **交付物**用代码写出(HTML / Excel / PDF / PPT / Markdown 等);向用户展示的数字须来自**脚本 stdout**,不要在对话里手填数。
|
|
41
44
|
|
|
42
45
|
> 这个顺序对所有三类快照统一生效:通用业务命令(`cli-manifest[-<id>].json`)、`google-analysis`(`manifest-<accountId>.json`)、`report …` 分析(`report-manifest[-<accountId>].json`)。三种 summary 都带 `outlineFile`、`agentHint` 字段。
|
|
43
46
|
|
|
@@ -324,8 +327,8 @@ console.log('可用字段:', first && Object.keys(first));
|
|
|
324
327
|
|
|
325
328
|
## AI Agent 使用规范
|
|
326
329
|
|
|
327
|
-
- **优先用 `--json-out` 与清单文件**:同一运行目录、同一"查询 id"的多次拉数会合并到 `cli-manifest-<id>.json` 的 `artifacts[]`;不同账户/实体写入各自的 manifest
|
|
328
|
-
-
|
|
330
|
+
- **优先用 `--json-out` 与清单文件**:同一运行目录、同一"查询 id"的多次拉数会合并到 `cli-manifest-<id>.json` 的 `artifacts[]`;不同账户/实体写入各自的 manifest,互不覆盖。读具体数据**只**在脚本里 `require(writtenFiles[0])`,**禁止 Read 工具**打开该 json。
|
|
331
|
+
- **中间结果一律落盘**:跨步骤数据不要靠对话记忆,写到工作目录并在后续 **`node`/`python` 脚本**中读盘。
|
|
329
332
|
- **Windows 上避免直接管道 JSON**:PowerShell 管道可能改变编码;优先 `--json-out` + `node -e` 读文件。
|
|
330
333
|
- **用 `process.stdout.write` 而不是 `console.log` 提取单个值**:前者不带换行符,方便直接赋给 shell 变量。
|
|
331
334
|
- **节点代码复杂时拆分写法**:不要写超过 10 行的 `node -e` 单行,改用 `node -e "$(cat <<'EOF'\n...\nEOF\n)"` 或多行 `.mjs` 脚本文件。
|
|
@@ -70,7 +70,7 @@ siluzan-tso account-history -m Google
|
|
|
70
70
|
siluzan-tso list-accounts -m Google
|
|
71
71
|
|
|
72
72
|
# 第四步:充值激活(必须网页完成)
|
|
73
|
-
# siluzan-tso config show 取 webUrl,打开:
|
|
73
|
+
# siluzan-tso config show 取 webUrl,打开:https://www-ci.siluzan.com/v3/foreign_trade/tso/recharge/pay?mediaType=Google
|
|
74
74
|
# 美元账户最低约 100 USD,人民币账户约 700 CNY
|
|
75
75
|
```
|
|
76
76
|
|
|
@@ -135,7 +135,7 @@ siluzan-tso account-history -m Yandex
|
|
|
135
135
|
**必填清单**:见 `open-account-by-media.md` § BingV2(含 `--pattern`、`--name-remark-list`、`--postcode`)。
|
|
136
136
|
|
|
137
137
|
```bash
|
|
138
|
-
#
|
|
138
|
+
# 前置:查询行业(将 bing-industries 输出的 id 传给 --trade-id,勿猜行业名)
|
|
139
139
|
siluzan-tso open-account bing-industries --keyword "科技"
|
|
140
140
|
|
|
141
141
|
# 提交开户(CLI 自动按公司名创建/关联广告主组,无需 magKey)
|
|
@@ -149,7 +149,7 @@ siluzan-tso open-account bing \
|
|
|
149
149
|
--address "南山区科技园XX路XX号XX大厦" \
|
|
150
150
|
--postcode "518000" \
|
|
151
151
|
--promotion-link "https://www.brand-a.com" \
|
|
152
|
-
--trade-id "
|
|
152
|
+
--trade-id "<bing-industries 输出的 id>" \
|
|
153
153
|
--license-file "/path/to/license.jpg"
|
|
154
154
|
|
|
155
155
|
# 轮询审核进度
|
|
@@ -790,7 +790,7 @@ siluzan-tso balance -m Google -a <mediaCustomerId>
|
|
|
790
790
|
siluzan-tso config show # 取 webUrl
|
|
791
791
|
|
|
792
792
|
# 诊断入口(网页 AICreation 向导内嵌):
|
|
793
|
-
#
|
|
793
|
+
# https://www-ci.siluzan.com/v3/foreign_trade/tso/advertising/AICreation
|
|
794
794
|
# → 输入推广链接时,系统会自动触发网站诊断
|
|
795
795
|
```
|
|
796
796
|
|
|
@@ -851,7 +851,7 @@ siluzan-tso account bm-bind \
|
|
|
851
851
|
--bm-id <businessManagerId>
|
|
852
852
|
|
|
853
853
|
# 第三步:确认绑定状态(在网页确认)
|
|
854
|
-
#
|
|
854
|
+
# https://www-ci.siluzan.com/v3/foreign_trade/tso/manageAccounts → 找到对应账户查看 BM 状态
|
|
855
855
|
```
|
|
856
856
|
|
|
857
857
|
### 场景 C:TikTok BC 绑定 / 解绑
|
|
@@ -875,7 +875,7 @@ siluzan-tso account bc-unbind --customers <mediaCustomerId> --bc-id <bcId>
|
|
|
875
875
|
siluzan-tso config show # 取 webUrl
|
|
876
876
|
|
|
877
877
|
# 打开账户管理页:
|
|
878
|
-
#
|
|
878
|
+
# https://www-ci.siluzan.com/v3/foreign_trade/tso/manageAccounts
|
|
879
879
|
# → 选中 Google 账户 → 点击「激活账户」→ 选择激活方式
|
|
880
880
|
```
|
|
881
881
|
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
siluzan-tso config show # 取 webUrl
|
|
7
7
|
```
|
|
8
8
|
|
|
9
|
-
首页地址:`
|
|
9
|
+
首页地址:`https://www-ci.siluzan.com/v3/foreign_trade/tso/home`
|
|
10
10
|
|
|
11
11
|
---
|
|
12
12
|
|
|
@@ -25,7 +25,7 @@ siluzan-tso config show # 取 webUrl
|
|
|
25
25
|
|
|
26
26
|
## 推荐话术
|
|
27
27
|
|
|
28
|
-
1. **「和首页一样的总览」** → 打开 `
|
|
28
|
+
1. **「和首页一样的总览」** → 打开 `https://www-ci.siluzan.com/v3/foreign_trade/tso/home`。
|
|
29
29
|
2. **「某个 Google 账户昨天花了多少」** → `list-accounts -m Google` + `stats -m Google -a <id>`。
|
|
30
30
|
3. **「有待充值账户」** → 说明聚合数据在首页;CLI 可 `list-accounts` + `balance` 逐户排查,或引导充值页。
|
|
31
31
|
|
|
@@ -51,6 +51,14 @@
|
|
|
51
51
|
|
|
52
52
|
---
|
|
53
53
|
|
|
54
|
+
### 步骤 4b:交付前自检(**必做**)
|
|
55
|
+
|
|
56
|
+
- Read `references/core/deliverable-preflight.md`。
|
|
57
|
+
- 运行 `node scripts/check-deliverable-snap.mjs <snapDir> --preset p4-google`(或按模板 `--required` / `--expect-currency`)。
|
|
58
|
+
- 退出码非 0:**补拉数据或修正币种**,不得交付。
|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
|
|
54
62
|
### 步骤 5:撰写报告
|
|
55
63
|
|
|
56
64
|
- 按 `*.md` 章节结构组织内容。
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* 交付前快照自检(币种 + 缺文件/空数据)
|
|
4
|
+
*
|
|
5
|
+
* 用法:
|
|
6
|
+
* node scripts/check-deliverable-snap.mjs <snapDir> [--preset p4-google] [--required a,b]
|
|
7
|
+
* node scripts/check-deliverable-snap.mjs ./snap --expect-currency CNY
|
|
8
|
+
*
|
|
9
|
+
* 退出码:0 通过;1 有 error;2 用法错误
|
|
10
|
+
*/
|
|
11
|
+
import fs from "node:fs";
|
|
12
|
+
import path from "node:path";
|
|
13
|
+
|
|
14
|
+
const PRESETS = {
|
|
15
|
+
"p4-google": [
|
|
16
|
+
"overview",
|
|
17
|
+
"daily-metrics",
|
|
18
|
+
"dimension-summary",
|
|
19
|
+
"campaigns",
|
|
20
|
+
"devices",
|
|
21
|
+
"geographic",
|
|
22
|
+
"keywords",
|
|
23
|
+
],
|
|
24
|
+
"p1-google": ["list-accounts", "stats", "ad-campaigns"],
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
function parseArgs(argv) {
|
|
28
|
+
const snapDir = argv[2];
|
|
29
|
+
if (!snapDir) return { error: "缺少 snapDir" };
|
|
30
|
+
let preset;
|
|
31
|
+
let required = [];
|
|
32
|
+
let expectCurrency;
|
|
33
|
+
for (let i = 3; i < argv.length; i++) {
|
|
34
|
+
if (argv[i] === "--preset" && argv[i + 1]) {
|
|
35
|
+
preset = argv[++i];
|
|
36
|
+
} else if (argv[i] === "--required" && argv[i + 1]) {
|
|
37
|
+
required = argv[++i].split(",").map((s) => s.trim()).filter(Boolean);
|
|
38
|
+
} else if (argv[i] === "--expect-currency" && argv[i + 1]) {
|
|
39
|
+
expectCurrency = argv[++i].toUpperCase();
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
if (preset) {
|
|
43
|
+
const p = PRESETS[preset];
|
|
44
|
+
if (!p) return { error: `未知 preset:${preset},可选:${Object.keys(PRESETS).join(", ")}` };
|
|
45
|
+
required = [...new Set([...required, ...p])];
|
|
46
|
+
}
|
|
47
|
+
return { snapDir: path.resolve(snapDir), required, expectCurrency };
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function readJson(filePath) {
|
|
51
|
+
return JSON.parse(fs.readFileSync(filePath, "utf8"));
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function listManifests(dir) {
|
|
55
|
+
return fs.readdirSync(dir).filter((f) => /^manifest(-.*)?\.json$/.test(f) || /^report-manifest(-.*)?\.json$/.test(f) || /^cli-manifest(-.*)?\.json$/.test(f));
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function sectionMatches(artifactSection, required) {
|
|
59
|
+
return required.some((r) => artifactSection === r || artifactSection.startsWith(`${r}-`) || artifactSection.includes(r));
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function itemCount(payload) {
|
|
63
|
+
if (!payload || typeof payload !== "object") return null;
|
|
64
|
+
if (Array.isArray(payload.items)) return payload.items.length;
|
|
65
|
+
if (Array.isArray(payload.data?.items)) return payload.data.items.length;
|
|
66
|
+
if (payload.record && typeof payload.record === "object") return 1;
|
|
67
|
+
if (Array.isArray(payload.data)) return payload.data.length;
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function extractCurrencyFromPayload(payload, section) {
|
|
72
|
+
if (!payload || typeof payload !== "object") return null;
|
|
73
|
+
const rec = payload.record ?? payload.data?.record;
|
|
74
|
+
if (rec?.currencyCode) return String(rec.currencyCode);
|
|
75
|
+
const items = payload.items ?? payload.data?.items;
|
|
76
|
+
if (Array.isArray(items) && items.length > 0) {
|
|
77
|
+
const ma = items[0].ma ?? items[0];
|
|
78
|
+
if (ma?.currencyCode) return String(ma.currencyCode);
|
|
79
|
+
if (items[0].currencyCode) return String(items[0].currencyCode);
|
|
80
|
+
}
|
|
81
|
+
if (section.includes("list-accounts") && Array.isArray(items) && items[0]?.ma?.currencyCode) {
|
|
82
|
+
return String(items[0].ma.currencyCode);
|
|
83
|
+
}
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function main() {
|
|
88
|
+
const parsed = parseArgs(process.argv);
|
|
89
|
+
if (parsed.error) {
|
|
90
|
+
console.error(parsed.error);
|
|
91
|
+
process.exit(2);
|
|
92
|
+
}
|
|
93
|
+
const { snapDir, required, expectCurrency } = parsed;
|
|
94
|
+
if (!fs.existsSync(snapDir)) {
|
|
95
|
+
console.error(`目录不存在:${snapDir}`);
|
|
96
|
+
process.exit(2);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const manifests = listManifests(snapDir);
|
|
100
|
+
if (manifests.length === 0) {
|
|
101
|
+
console.error(`未找到 manifest / report-manifest / cli-manifest:${snapDir}`);
|
|
102
|
+
process.exit(1);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const errors = [];
|
|
106
|
+
const warnings = [];
|
|
107
|
+
let currencySeen = null;
|
|
108
|
+
|
|
109
|
+
for (const mf of manifests) {
|
|
110
|
+
const man = readJson(path.join(snapDir, mf));
|
|
111
|
+
const arts = man.artifacts ?? [];
|
|
112
|
+
console.log(`\n📋 ${mf}(${arts.length} 个 artifact)`);
|
|
113
|
+
|
|
114
|
+
if (required.length > 0) {
|
|
115
|
+
for (const req of required) {
|
|
116
|
+
const hit = arts.some((a) => sectionMatches(String(a.section ?? ""), [req]));
|
|
117
|
+
if (!hit) errors.push(`[${mf}] 缺少维度/命令:${req}`);
|
|
118
|
+
else console.log(` ✓ 已拉取:${req}`);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
for (const art of arts) {
|
|
123
|
+
const fp = path.join(snapDir, art.file);
|
|
124
|
+
if (!fs.existsSync(fp)) {
|
|
125
|
+
errors.push(`[${mf}] 文件缺失:${art.file}`);
|
|
126
|
+
continue;
|
|
127
|
+
}
|
|
128
|
+
let payload;
|
|
129
|
+
try {
|
|
130
|
+
payload = readJson(fp);
|
|
131
|
+
} catch (e) {
|
|
132
|
+
errors.push(`[${mf}] JSON 解析失败:${art.file}`);
|
|
133
|
+
continue;
|
|
134
|
+
}
|
|
135
|
+
const n = itemCount(payload);
|
|
136
|
+
const sec = String(art.section ?? "");
|
|
137
|
+
if (n === 0) warnings.push(`[${mf}] ${art.file} items 为空(章节可能写「无数据」)`);
|
|
138
|
+
const ccy = extractCurrencyFromPayload(payload, sec);
|
|
139
|
+
if (ccy) {
|
|
140
|
+
if (!currencySeen) currencySeen = ccy;
|
|
141
|
+
else if (currencySeen !== ccy) warnings.push(`[${mf}] 币种不一致:${ccy} vs 先前 ${currencySeen}`);
|
|
142
|
+
if (sec.includes("overview") || sec.includes("list-accounts")) {
|
|
143
|
+
console.log(` 💱 币种来源 ${art.file}:${ccy}`);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
if (expectCurrency) {
|
|
150
|
+
if (!currencySeen) warnings.push(`未从快照解析到 currencyCode,无法对照 --expect-currency ${expectCurrency}`);
|
|
151
|
+
else if (currencySeen !== expectCurrency) {
|
|
152
|
+
errors.push(`币种不符:快照为 ${currencySeen},期望 ${expectCurrency}(报告须用 ¥/ $ 与代码一致)`);
|
|
153
|
+
} else console.log(`\n✓ 币种与期望一致:${expectCurrency}`);
|
|
154
|
+
} else if (currencySeen) {
|
|
155
|
+
console.log(`\n💱 建议在报告首行写:统计区间:…(货币:${currencySeen})`);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
if (warnings.length) {
|
|
159
|
+
console.log("\n⚠️ 警告:");
|
|
160
|
+
warnings.forEach((w) => console.log(` - ${w}`));
|
|
161
|
+
}
|
|
162
|
+
if (errors.length) {
|
|
163
|
+
console.log("\n❌ 错误:");
|
|
164
|
+
errors.forEach((e) => console.log(` - ${e}`));
|
|
165
|
+
process.exit(1);
|
|
166
|
+
}
|
|
167
|
+
console.log("\n✅ 交付前自检通过(仍须确认报告正文金额来自脚本、非手填)\n");
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
main();
|
|
@@ -9,11 +9,11 @@ $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.
|
|
12
|
+
$PKG_VERSION = '1.1.26-beta.2'
|
|
13
13
|
$CLI_BIN = 'siluzan-tso'
|
|
14
14
|
$SKILL_LABEL = 'Siluzan TSO'
|
|
15
|
-
$INSTALL_CMD = 'npm install -g siluzan-tso-cli'
|
|
16
|
-
$WEB_BASE = 'https://www.siluzan.com'
|
|
15
|
+
$INSTALL_CMD = 'npm install -g siluzan-tso-cli@beta'
|
|
16
|
+
$WEB_BASE = 'https://www-ci.siluzan.com'
|
|
17
17
|
|
|
18
18
|
# -- Constants ----------------------------------------------------------------
|
|
19
19
|
$NODE_MAJOR_MIN = 18
|
|
@@ -9,11 +9,11 @@ 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.
|
|
12
|
+
readonly PKG_VERSION="1.1.26-beta.2"
|
|
13
13
|
readonly CLI_BIN="siluzan-tso"
|
|
14
14
|
readonly SKILL_LABEL="Siluzan TSO"
|
|
15
|
-
readonly INSTALL_CMD="npm install -g siluzan-tso-cli"
|
|
16
|
-
readonly WEB_BASE="https://www.siluzan.com"
|
|
15
|
+
readonly INSTALL_CMD="npm install -g siluzan-tso-cli@beta"
|
|
16
|
+
readonly WEB_BASE="https://www-ci.siluzan.com"
|
|
17
17
|
|
|
18
18
|
# -- Constants ----------------------------------------------------------------
|
|
19
19
|
readonly NODE_MAJOR_MIN=18
|
|
@@ -6,5 +6,5 @@
|
|
|
6
6
|
"writtenFiles": ["meta-overview-3995107670753101.json", "meta-overview-3995107670753101.outline.txt"],
|
|
7
7
|
"section": "meta-overview",
|
|
8
8
|
"outlineFile": "meta-overview-3995107670753101.outline.txt",
|
|
9
|
-
"agentHint": "Read outline
|
|
9
|
+
"agentHint": "禁止 Read 业务 *.json;先 outline schema,再用脚本读 JSON。"
|
|
10
10
|
}
|