siluzan-tso-cli 1.1.13 → 1.1.14-beta.1
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 +118 -23
- package/dist/skill/SKILL.md +3 -0
- package/dist/skill/_meta.json +2 -2
- package/dist/skill/references/account-analytics.md +17 -0
- package/dist/skill/references/finance.md +5 -5
- package/dist/skill/references/google-ads.md +1 -1
- package/dist/skill/references/reporting.md +2 -2
- package/dist/skill/references/setup.md +5 -5
- package/dist/skill/references/tso-home.md +1 -1
- package/dist/skill/scripts/install.ps1 +2 -2
- package/dist/skill/scripts/install.sh +2 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -45,7 +45,7 @@ HTML 报告模板引用以下 CDN:`cdn.tailwindcss.com`、`cdnjs.cloudflare.co
|
|
|
45
45
|
在**用户的目标项目根目录**执行(根据用户使用的助手选择 `--ai`):
|
|
46
46
|
|
|
47
47
|
```bash
|
|
48
|
-
npm install -g siluzan-tso-cli
|
|
48
|
+
npm install -g siluzan-tso-cli@beta
|
|
49
49
|
siluzan-tso init --ai cursor # 写入 Cursor(默认)
|
|
50
50
|
siluzan-tso init --ai cursor,claude # 同时写入多个平台
|
|
51
51
|
siluzan-tso init --ai all # 写入所有支持的平台
|
|
@@ -53,6 +53,7 @@ siluzan-tso init -d /path/to/skills # 写入自定义目录
|
|
|
53
53
|
siluzan-tso init --force # 强制覆盖已存在文件
|
|
54
54
|
```
|
|
55
55
|
|
|
56
|
+
> **注意**:当前为测试版(1.1.14-beta.1),供内部测试使用。正式发布后安装命令将改为 `npm install -g siluzan-tso-cli`。
|
|
56
57
|
|
|
57
58
|
| 助手 | 建议 `--ai` |
|
|
58
59
|
| ----------------------- | ------------------------------------ |
|
package/dist/index.js
CHANGED
|
@@ -1962,7 +1962,7 @@ import { fileURLToPath as fileURLToPath4 } from "url";
|
|
|
1962
1962
|
import { Command } from "commander";
|
|
1963
1963
|
|
|
1964
1964
|
// src/config/defaults.ts
|
|
1965
|
-
var DEFAULT_API_BASE = "https://tso-api.siluzan.com";
|
|
1965
|
+
var DEFAULT_API_BASE = "https://tso-api-ci.siluzan.com";
|
|
1966
1966
|
|
|
1967
1967
|
// ../common/dist/index.js
|
|
1968
1968
|
import * as fs from "fs";
|
|
@@ -2441,19 +2441,19 @@ import * as fs3 from "fs/promises";
|
|
|
2441
2441
|
import * as path3 from "path";
|
|
2442
2442
|
async function getSkillFiles(skillDir) {
|
|
2443
2443
|
const out = {};
|
|
2444
|
-
async function
|
|
2444
|
+
async function walk2(dir, prefix) {
|
|
2445
2445
|
const entries = await fs3.readdir(dir, { withFileTypes: true });
|
|
2446
2446
|
for (const ent of entries) {
|
|
2447
2447
|
const rel = prefix ? `${prefix}/${ent.name}` : ent.name;
|
|
2448
2448
|
const full = path3.join(dir, ent.name);
|
|
2449
2449
|
if (ent.isDirectory()) {
|
|
2450
|
-
await
|
|
2450
|
+
await walk2(full, rel);
|
|
2451
2451
|
} else {
|
|
2452
2452
|
out[rel] = await fs3.readFile(full, "utf8");
|
|
2453
2453
|
}
|
|
2454
2454
|
}
|
|
2455
2455
|
}
|
|
2456
|
-
await
|
|
2456
|
+
await walk2(skillDir, "");
|
|
2457
2457
|
return out;
|
|
2458
2458
|
}
|
|
2459
2459
|
|
|
@@ -5070,6 +5070,37 @@ async function runReportPushReceiveEmails(opts) {
|
|
|
5070
5070
|
console.log();
|
|
5071
5071
|
}
|
|
5072
5072
|
|
|
5073
|
+
// src/utils/strip-legacy-google-fields.ts
|
|
5074
|
+
var LEGACY_WHEN_V2_PRESENT = [
|
|
5075
|
+
["status", "statusV2"],
|
|
5076
|
+
["channelType", "channelTypeV2"],
|
|
5077
|
+
["subChannelType", "subChannelTypeV2"],
|
|
5078
|
+
["biddingStrategyType", "biddingStrategyTypeV2"],
|
|
5079
|
+
["campaignStatus", "campaignStatusV2"],
|
|
5080
|
+
["adGroupStatus", "adGroupStatusV2"],
|
|
5081
|
+
["matchType", "matchTypeV2"],
|
|
5082
|
+
["type", "typeV2"]
|
|
5083
|
+
];
|
|
5084
|
+
function stripLegacyGoogleFieldsIfV2Present(value) {
|
|
5085
|
+
return walk(value);
|
|
5086
|
+
}
|
|
5087
|
+
function walk(obj) {
|
|
5088
|
+
if (Array.isArray(obj)) return obj.map(walk);
|
|
5089
|
+
if (obj !== null && typeof obj === "object") {
|
|
5090
|
+
const o = { ...obj };
|
|
5091
|
+
for (const [legacy, modern] of LEGACY_WHEN_V2_PRESENT) {
|
|
5092
|
+
if (modern in o && legacy in o) {
|
|
5093
|
+
delete o[legacy];
|
|
5094
|
+
}
|
|
5095
|
+
}
|
|
5096
|
+
for (const k of Object.keys(o)) {
|
|
5097
|
+
o[k] = walk(o[k]);
|
|
5098
|
+
}
|
|
5099
|
+
return o;
|
|
5100
|
+
}
|
|
5101
|
+
return obj;
|
|
5102
|
+
}
|
|
5103
|
+
|
|
5073
5104
|
// src/commands/google-analysis.ts
|
|
5074
5105
|
var SECTIONS = [
|
|
5075
5106
|
{
|
|
@@ -5292,7 +5323,7 @@ async function runOneSection(def, opts) {
|
|
|
5292
5323
|
]);
|
|
5293
5324
|
const merged = { images, videos };
|
|
5294
5325
|
if (opts.json) {
|
|
5295
|
-
console.log(JSON.stringify(merged, null, 2));
|
|
5326
|
+
console.log(JSON.stringify(stripLegacyGoogleFieldsIfV2Present(merged), null, 2));
|
|
5296
5327
|
return;
|
|
5297
5328
|
}
|
|
5298
5329
|
const iLen = Array.isArray(images) ? images.length : 0;
|
|
@@ -5307,7 +5338,7 @@ async function runOneSection(def, opts) {
|
|
|
5307
5338
|
}
|
|
5308
5339
|
const data = await fetchJson(config, fullPath, !!opts.verbose);
|
|
5309
5340
|
if (opts.json) {
|
|
5310
|
-
console.log(JSON.stringify(data, null, 2));
|
|
5341
|
+
console.log(JSON.stringify(stripLegacyGoogleFieldsIfV2Present(data), null, 2));
|
|
5311
5342
|
return;
|
|
5312
5343
|
}
|
|
5313
5344
|
console.log(
|
|
@@ -6806,6 +6837,39 @@ function requireGoogleApi(config) {
|
|
|
6806
6837
|
}
|
|
6807
6838
|
return config.googleApiUrl;
|
|
6808
6839
|
}
|
|
6840
|
+
function formatGoogleCampaignListStatus(row) {
|
|
6841
|
+
let result = "-";
|
|
6842
|
+
const raw = row.statusV2;
|
|
6843
|
+
if (raw == null || String(raw).trim() === "") return result;
|
|
6844
|
+
const statusV2 = String(raw).toUpperCase();
|
|
6845
|
+
const start = parseCampaignTimeMs(row.startTime);
|
|
6846
|
+
const end = parseCampaignTimeMs(row.endTime);
|
|
6847
|
+
if (start == null || end == null) return result;
|
|
6848
|
+
const now = Date.now();
|
|
6849
|
+
if (statusV2 === "PAUSED") {
|
|
6850
|
+
if (now > start && now < end) {
|
|
6851
|
+
result = "\u5DF2\u6682\u505C";
|
|
6852
|
+
} else if (now > end) {
|
|
6853
|
+
result = "\u5DF2\u7ED3\u675F\u4F7F\u7528";
|
|
6854
|
+
} else if (now < start) {
|
|
6855
|
+
result = "\u672A\u6295\u653E";
|
|
6856
|
+
}
|
|
6857
|
+
} else if (statusV2 === "ENABLED") {
|
|
6858
|
+
if (now > start && now < end) {
|
|
6859
|
+
result = "\u6709\u6548";
|
|
6860
|
+
} else if (now > end) {
|
|
6861
|
+
result = "\u5DF2\u7ED3\u675F\u4F7F\u7528";
|
|
6862
|
+
} else if (now < start) {
|
|
6863
|
+
result = "\u672A\u6295\u653E";
|
|
6864
|
+
}
|
|
6865
|
+
}
|
|
6866
|
+
return result;
|
|
6867
|
+
}
|
|
6868
|
+
function parseCampaignTimeMs(v) {
|
|
6869
|
+
if (v == null || v === "") return null;
|
|
6870
|
+
const t = new Date(v).getTime();
|
|
6871
|
+
return Number.isFinite(t) ? t : null;
|
|
6872
|
+
}
|
|
6809
6873
|
async function runAdCampaigns(opts) {
|
|
6810
6874
|
const config = loadConfig(opts.token);
|
|
6811
6875
|
const googleApiUrl = requireGoogleApi(config);
|
|
@@ -6828,18 +6892,19 @@ async function runAdCampaigns(opts) {
|
|
|
6828
6892
|
return {
|
|
6829
6893
|
...item,
|
|
6830
6894
|
budgetDisplay,
|
|
6831
|
-
budgetUnit: "display"
|
|
6895
|
+
budgetUnit: "display",
|
|
6896
|
+
statusDisplay: formatGoogleCampaignListStatus(item)
|
|
6832
6897
|
};
|
|
6833
6898
|
});
|
|
6834
6899
|
const n = items.length;
|
|
6835
6900
|
if (opts.json) {
|
|
6836
6901
|
console.log(
|
|
6837
6902
|
JSON.stringify(
|
|
6838
|
-
{
|
|
6903
|
+
stripLegacyGoogleFieldsIfV2Present({
|
|
6839
6904
|
...wrapListJson({ page: 1, pageSize: Math.max(n, 1), total: n, items }),
|
|
6840
6905
|
code: data.code ?? null,
|
|
6841
6906
|
message: data.message ?? null
|
|
6842
|
-
},
|
|
6907
|
+
}),
|
|
6843
6908
|
null,
|
|
6844
6909
|
2
|
|
6845
6910
|
)
|
|
@@ -6874,7 +6939,7 @@ async function runAdCampaigns(opts) {
|
|
|
6874
6939
|
const budget = item.budgetDisplay != null ? item.budgetDisplay.toFixed(2) : "\u2014";
|
|
6875
6940
|
return {
|
|
6876
6941
|
name: (item.name ?? "").slice(0, nameW),
|
|
6877
|
-
status: item.
|
|
6942
|
+
status: item.statusDisplay ?? formatGoogleCampaignListStatus(item),
|
|
6878
6943
|
channelType: item.channelTypeV2 ?? "",
|
|
6879
6944
|
bidding: String(item.biddingStrategyTypeV2 ?? ""),
|
|
6880
6945
|
budget,
|
|
@@ -6917,10 +6982,10 @@ async function runAdGroups(opts) {
|
|
|
6917
6982
|
if (opts.json) {
|
|
6918
6983
|
console.log(
|
|
6919
6984
|
JSON.stringify(
|
|
6920
|
-
{
|
|
6985
|
+
stripLegacyGoogleFieldsIfV2Present({
|
|
6921
6986
|
...wrapListJson({ page: 1, pageSize: Math.max(n, 1), total: n, items }),
|
|
6922
6987
|
code: data.code ?? null
|
|
6923
|
-
},
|
|
6988
|
+
}),
|
|
6924
6989
|
null,
|
|
6925
6990
|
2
|
|
6926
6991
|
)
|
|
@@ -6990,7 +7055,13 @@ async function runAdList(opts) {
|
|
|
6990
7055
|
const n = items.length;
|
|
6991
7056
|
if (opts.json) {
|
|
6992
7057
|
console.log(
|
|
6993
|
-
JSON.stringify(
|
|
7058
|
+
JSON.stringify(
|
|
7059
|
+
stripLegacyGoogleFieldsIfV2Present(
|
|
7060
|
+
wrapListJson({ page: 1, pageSize: Math.max(n, 1), total: n, items })
|
|
7061
|
+
),
|
|
7062
|
+
null,
|
|
7063
|
+
2
|
|
7064
|
+
)
|
|
6994
7065
|
);
|
|
6995
7066
|
return;
|
|
6996
7067
|
}
|
|
@@ -7027,7 +7098,7 @@ async function runAdList(opts) {
|
|
|
7027
7098
|
campaign: String(item["campaign"] ?? "").slice(0, campW),
|
|
7028
7099
|
adGroup: String(item["adGroup"] ?? "").slice(0, grpW),
|
|
7029
7100
|
status: String(item["statusV2"]),
|
|
7030
|
-
type: String(item["typeV2"] ??
|
|
7101
|
+
type: String(item["typeV2"] ?? ""),
|
|
7031
7102
|
impressions: String(item["impressions"] ?? 0),
|
|
7032
7103
|
clicks: String(item["clicks"] ?? 0),
|
|
7033
7104
|
ctr,
|
|
@@ -7066,7 +7137,13 @@ async function runAdKeywords(opts) {
|
|
|
7066
7137
|
const n = items.length;
|
|
7067
7138
|
if (opts.json) {
|
|
7068
7139
|
console.log(
|
|
7069
|
-
JSON.stringify(
|
|
7140
|
+
JSON.stringify(
|
|
7141
|
+
stripLegacyGoogleFieldsIfV2Present(
|
|
7142
|
+
wrapListJson({ page: 1, pageSize: Math.max(n, 1), total: n, items })
|
|
7143
|
+
),
|
|
7144
|
+
null,
|
|
7145
|
+
2
|
|
7146
|
+
)
|
|
7070
7147
|
);
|
|
7071
7148
|
return;
|
|
7072
7149
|
}
|
|
@@ -7082,7 +7159,7 @@ ${label}\uFF08\u8D26\u6237\uFF1A${opts.account}\uFF0C\u7B2C 1 \u9875\uFF0C\u672C
|
|
|
7082
7159
|
if (opts.negative) {
|
|
7083
7160
|
items.forEach((item) => {
|
|
7084
7161
|
const kwText = Array.isArray(item["keywordText"]) ? item["keywordText"].join(", ") : String(item["text"] ?? item["keywordText"] ?? item["id"] ?? "\u2014");
|
|
7085
|
-
const matchType = item["matchTypeV2"] ??
|
|
7162
|
+
const matchType = item["matchTypeV2"] ?? "\u2014";
|
|
7086
7163
|
console.log(` [${matchType}] ${kwText} id: ${String(item["id"] ?? "")}`);
|
|
7087
7164
|
});
|
|
7088
7165
|
} else {
|
|
@@ -7113,8 +7190,8 @@ ${label}\uFF08\u8D26\u6237\uFF1A${opts.account}\uFF0C\u7B2C 1 \u9875\uFF0C\u672C
|
|
|
7113
7190
|
const kwText = String(item["text"] ?? item["keywordText"] ?? "\u2014");
|
|
7114
7191
|
const campaign = String(item["campaignName"] ?? item["campaign"] ?? "");
|
|
7115
7192
|
const adGroup = String(item["adGroupName"] ?? item["adGroup"] ?? "");
|
|
7116
|
-
const status = String(item["
|
|
7117
|
-
const matchType = String(item["matchTypeV2"] ??
|
|
7193
|
+
const status = String(item["userStatus"] ?? "");
|
|
7194
|
+
const matchType = String(item["matchTypeV2"] ?? "");
|
|
7118
7195
|
const ctr = item["ctr"] != null ? (Number(item["ctr"]) * 100).toFixed(2) + "%" : "\u2014";
|
|
7119
7196
|
const spend = item["spend"] != null ? Number(item["spend"]).toFixed(2) : "\u2014";
|
|
7120
7197
|
return {
|
|
@@ -8145,7 +8222,13 @@ async function runAdExtensionList(opts) {
|
|
|
8145
8222
|
const n = items.length;
|
|
8146
8223
|
if (opts.json) {
|
|
8147
8224
|
console.log(
|
|
8148
|
-
JSON.stringify(
|
|
8225
|
+
JSON.stringify(
|
|
8226
|
+
stripLegacyGoogleFieldsIfV2Present(
|
|
8227
|
+
wrapListJson({ page: 1, pageSize: Math.max(n, 1), total: n, items })
|
|
8228
|
+
),
|
|
8229
|
+
null,
|
|
8230
|
+
2
|
|
8231
|
+
)
|
|
8149
8232
|
);
|
|
8150
8233
|
return;
|
|
8151
8234
|
}
|
|
@@ -8278,7 +8361,13 @@ async function runAdSearchTerms(opts) {
|
|
|
8278
8361
|
const n = items.length;
|
|
8279
8362
|
if (opts.json) {
|
|
8280
8363
|
console.log(
|
|
8281
|
-
JSON.stringify(
|
|
8364
|
+
JSON.stringify(
|
|
8365
|
+
stripLegacyGoogleFieldsIfV2Present(
|
|
8366
|
+
wrapListJson({ page: 1, pageSize: Math.max(n, 1), total: n, items })
|
|
8367
|
+
),
|
|
8368
|
+
null,
|
|
8369
|
+
2
|
|
8370
|
+
)
|
|
8282
8371
|
);
|
|
8283
8372
|
return;
|
|
8284
8373
|
}
|
|
@@ -8321,7 +8410,7 @@ async function runAdSearchTerms(opts) {
|
|
|
8321
8410
|
term: term.slice(0, termW),
|
|
8322
8411
|
campaign: String(item["campaignName"] ?? "").slice(0, campW),
|
|
8323
8412
|
adGroup: String(item["adGroupName"] ?? "").slice(0, grpW),
|
|
8324
|
-
matchType: String(item["
|
|
8413
|
+
matchType: String(item["matchTypeV2"] ?? ""),
|
|
8325
8414
|
impressions: String(item["impressions"] ?? 0),
|
|
8326
8415
|
clicks: String(item["clicks"] ?? 0),
|
|
8327
8416
|
ctr,
|
|
@@ -8355,7 +8444,7 @@ async function runAdGeoSearch(opts) {
|
|
|
8355
8444
|
for (const item of items) {
|
|
8356
8445
|
const id = String(item["id"] ?? "");
|
|
8357
8446
|
const name = String(item["locationName"] ?? item["canonicalName"] ?? item["name"] ?? "");
|
|
8358
|
-
const type = String(item["targetType"] ?? item["typeV2"] ??
|
|
8447
|
+
const type = String(item["targetType"] ?? item["typeV2"] ?? "");
|
|
8359
8448
|
console.log(` id:${id} ${name} [${type}]`);
|
|
8360
8449
|
}
|
|
8361
8450
|
console.log();
|
|
@@ -8391,7 +8480,13 @@ async function runAdGeoList(opts) {
|
|
|
8391
8480
|
const n = items.length;
|
|
8392
8481
|
if (opts.json) {
|
|
8393
8482
|
console.log(
|
|
8394
|
-
JSON.stringify(
|
|
8483
|
+
JSON.stringify(
|
|
8484
|
+
stripLegacyGoogleFieldsIfV2Present(
|
|
8485
|
+
wrapListJson({ page: 1, pageSize: Math.max(n, 1), total: n, items })
|
|
8486
|
+
),
|
|
8487
|
+
null,
|
|
8488
|
+
2
|
|
8489
|
+
)
|
|
8395
8490
|
);
|
|
8396
8491
|
return;
|
|
8397
8492
|
}
|
package/dist/skill/SKILL.md
CHANGED
|
@@ -75,6 +75,8 @@ allowed-tools: Bash(siluzan-tso:*) Read
|
|
|
75
75
|
- 这种报告你无法用它来做数据分析除非用户明确要求(Siluzan平台的优化报告)
|
|
76
76
|
- (推荐,默认生成这种报告)由你主动拉取数据,并按照skill给出的格式,输出给用户:详情请查看(`references/account-analytics.md`)
|
|
77
77
|
|
|
78
|
+
**写报告前必读(账户 vs 系列)**:`stats` / `balance` / `list-accounts` 里的账户 `status` 只表示**广告账户**是否可用,**不能**当作**广告系列**是否启用;系列是否投放须用 `ad campaigns`(及 CLI 派生的 `statusDisplay` 等)。详见 `references/account-analytics.md`「账户状态 ≠ 广告系列状态」。
|
|
79
|
+
|
|
78
80
|
### 广告账户相关
|
|
79
81
|
|
|
80
82
|
- 广告账户开户请阅读: `references/open-account-by-media.md`
|
|
@@ -121,6 +123,7 @@ allowed-tools: Bash(siluzan-tso:*) Read
|
|
|
121
123
|
|
|
122
124
|
### 硬规范
|
|
123
125
|
|
|
126
|
+
- **出报告时账户状态 ≠ 广告系列状态**:`stats` / `balance` / `list-accounts` 中的 `status`(如 Enabled)只表示**广告账户**关联/可用,**绝不能**据此填写或推断**各广告系列**是否「启用/在投」。已暂停或移除的系列若被写成启用,属于严重错误。系列是否启用**必须**来自 `ad campaigns`(或系列维报表中的系列状态)。详见 `references/account-analytics.md`「账户状态 ≠ 广告系列状态」。
|
|
124
127
|
- **不确定时读文档**:遇到不熟悉的命令,先读对应 references 文件或使用-h查看命令帮助,不要猜参数。
|
|
125
128
|
- **先查账户再操作**:对具体账户做操作前,先通过 `list-accounts -m [mediaType] -k [mediaCustomerId]` 确认。特别是不确定是Google/Bing/TikTok这些媒体平台中的哪一个的时候
|
|
126
129
|
- **使用 --json 处理数据**:需对返回数据做计算或筛选时,加 `--json`,再用 `node -e` 过滤提取(见 `references/tips.md`)。
|
package/dist/skill/_meta.json
CHANGED
|
@@ -22,6 +22,23 @@
|
|
|
22
22
|
|
|
23
23
|
## 报告中的硬约束(必须遵守)
|
|
24
24
|
|
|
25
|
+
### 账户状态 ≠ 广告系列状态(出报告最高优先级)
|
|
26
|
+
|
|
27
|
+
**常见错误**:Agent 看到 `stats`、`balance` 或账户列表里 `status: Enabled`(或「账户正常」),就在报告中把**广告系列**写成「启用」「在投」。这会导致**已暂停、已结束排期或已从投放层面移除**的系列被误标为启用。
|
|
28
|
+
|
|
29
|
+
| 含义 | 数据来源(示例) | `status` / 状态字段表示什么 |
|
|
30
|
+
|------|------------------|-----------------------------|
|
|
31
|
+
| **广告账户**是否在媒体侧关联可用、能否拉数 | `stats`、`balance`、`list-accounts` | 账户级启用/可用,**不描述单个系列是否在投** |
|
|
32
|
+
| **广告系列**是否暂停、是否在排期内可投放 | **`ad campaigns`**(含 `statusDisplay` 等)、系列维报表 | 系列级启停;**唯一**用于写「某系列启用/暂停」 |
|
|
33
|
+
|
|
34
|
+
**写报告时的硬性规则**:
|
|
35
|
+
|
|
36
|
+
1. **禁止**用 `stats` / `balance` / `list-accounts` 返回的账户 `status` 推断或概括**广告系列**是否启用;不得在报告正文写「根据账户状态,各系列均为启用」这类表述(除非已逐条用 `ad campaigns` 核对)。
|
|
37
|
+
2. **凡是**描述「某广告系列是否投放 / 启用 / 暂停 / 移除」,**必须**基于 `siluzan-tso ad campaigns -a <mediaCustomerId> --json`(或 `google-analysis` 中带系列粒度、含系列状态的数据)。账户总览里的消耗/点击可以与系列表并列,但**系列状态列不得来自账户接口**。
|
|
38
|
+
3. 若报告中有「账户概况」与「广告系列明细」两块:前者可用账户级接口;后者**系列状态列只能**来自系列级接口(如 `statusDisplay`),与账户 `status` **不得混为一谈**。
|
|
39
|
+
|
|
40
|
+
**一句话**:账户「能用 / Enabled」≠ 系列「在投」;系列是否启用只看系列级数据(首推 `ad campaigns`)。
|
|
41
|
+
|
|
25
42
|
### 品牌名 / 公司名来源
|
|
26
43
|
|
|
27
44
|
生成带品牌名、方案、邮件、广告文案的报告时,**严禁自行生成品牌名(包括中文译名、拼音、意译)**。品牌名必须来自以下来源之一,按优先级:
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
## invoice-info — 发票抬头管理
|
|
9
9
|
|
|
10
|
-
对应页面:`https://www.siluzan.com/v3/foreign_trade/settings/invoiceInformation`
|
|
10
|
+
对应页面:`https://www-ci.siluzan.com/v3/foreign_trade/settings/invoiceInformation`
|
|
11
11
|
|
|
12
12
|
发票抬头是开票申请时使用的公司/企业信息模板,支持三种类型:
|
|
13
13
|
|
|
@@ -131,10 +131,10 @@ siluzan-tso config show
|
|
|
131
131
|
**示例:**
|
|
132
132
|
|
|
133
133
|
```
|
|
134
|
-
- 现金充值(单笔):https://www.siluzan.com/recharge/pay
|
|
135
|
-
- 现金充值(批量):https://www.siluzan.com/recharge/pay_batch
|
|
136
|
-
- 月结充值: https://www.siluzan.com/recharge/accountBillingQuota
|
|
137
|
-
- 丝路赞钱包: https://www.siluzan.com/recharge/siluzanWallet
|
|
134
|
+
- 现金充值(单笔):https://www-ci.siluzan.com/recharge/pay
|
|
135
|
+
- 现金充值(批量):https://www-ci.siluzan.com/recharge/pay_batch
|
|
136
|
+
- 月结充值: https://www-ci.siluzan.com/recharge/accountBillingQuota
|
|
137
|
+
- 丝路赞钱包: https://www-ci.siluzan.com/recharge/siluzanWallet
|
|
138
138
|
```
|
|
139
139
|
|
|
140
140
|
---
|
|
@@ -124,7 +124,7 @@ siluzan-tso ad campaigns -a 6326027735 --start 2026-03-01 --end 2026-03-31
|
|
|
124
124
|
siluzan-tso ad campaigns -a 6326027735 --json
|
|
125
125
|
```
|
|
126
126
|
|
|
127
|
-
|
|
127
|
+
输出字段:名称、状态、类型、预算、点击数、展示数(具体字段名以 `--json` 为准;另有 CLI 派生的 `statusDisplay`、`budgetDisplay` 等便于阅读)。
|
|
128
128
|
|
|
129
129
|
---
|
|
130
130
|
|
|
@@ -212,8 +212,8 @@ siluzan-tso report list -m Google --json
|
|
|
212
212
|
|
|
213
213
|
# 第二步:查看 webUrl
|
|
214
214
|
siluzan-tso config show
|
|
215
|
-
# webUrl: https://www.siluzan.com
|
|
215
|
+
# webUrl: https://www-ci.siluzan.com
|
|
216
216
|
|
|
217
217
|
# 第三步:拼接链接(Google 日报)
|
|
218
|
-
# https://www.siluzan.com/media-report/publish/rpt_abc123?culture=zh-CN
|
|
218
|
+
# https://www-ci.siluzan.com/media-report/publish/rpt_abc123?culture=zh-CN
|
|
219
219
|
```
|
|
@@ -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
|
---
|
|
@@ -47,7 +47,7 @@ siluzan-tso config set --api-key <Key> # 或通过 config set 直接写入
|
|
|
47
47
|
siluzan-tso config set --token <Token> # 备用:设置 JWT Token
|
|
48
48
|
```
|
|
49
49
|
|
|
50
|
-
API Key 获取入口:`https://www.siluzan.com/v3/foreign_trade/settings/apiKeyManagement`
|
|
50
|
+
API Key 获取入口:`https://www-ci.siluzan.com/v3/foreign_trade/settings/apiKeyManagement`
|
|
51
51
|
|
|
52
52
|
### 通过环境变量传入凭据(CI/CD 推荐)
|
|
53
53
|
|
|
@@ -82,9 +82,9 @@ siluzan-tso config show
|
|
|
82
82
|
|
|
83
83
|
```
|
|
84
84
|
构建环境 : production
|
|
85
|
-
apiBaseUrl : https://tso-api.siluzan.com
|
|
86
|
-
googleApiUrl : https://googleapi.mysiluzan.com
|
|
87
|
-
webUrl : https://www.siluzan.com
|
|
85
|
+
apiBaseUrl : https://tso-api-ci.siluzan.com
|
|
86
|
+
googleApiUrl : https://googleapi-ci.mysiluzan.com
|
|
87
|
+
webUrl : https://www-ci.siluzan.com
|
|
88
88
|
apiKey : abcd****1234
|
|
89
89
|
```
|
|
90
90
|
|
|
@@ -10,8 +10,8 @@ $ErrorActionPreference = 'Stop'
|
|
|
10
10
|
$PKG_NAME = 'siluzan-tso-cli'
|
|
11
11
|
$CLI_BIN = 'siluzan-tso'
|
|
12
12
|
$SKILL_LABEL = 'Siluzan TSO'
|
|
13
|
-
$INSTALL_CMD = 'npm install -g siluzan-tso-cli'
|
|
14
|
-
$WEB_BASE = 'https://www.siluzan.com'
|
|
13
|
+
$INSTALL_CMD = 'npm install -g siluzan-tso-cli@beta'
|
|
14
|
+
$WEB_BASE = 'https://www-ci.siluzan.com'
|
|
15
15
|
|
|
16
16
|
# -- Constants ----------------------------------------------------------------
|
|
17
17
|
$NODE_MAJOR_MIN = 18
|
|
@@ -10,8 +10,8 @@ set -euo pipefail
|
|
|
10
10
|
readonly PKG_NAME="siluzan-tso-cli"
|
|
11
11
|
readonly CLI_BIN="siluzan-tso"
|
|
12
12
|
readonly SKILL_LABEL="Siluzan TSO"
|
|
13
|
-
readonly INSTALL_CMD="npm install -g siluzan-tso-cli"
|
|
14
|
-
readonly WEB_BASE="https://www.siluzan.com"
|
|
13
|
+
readonly INSTALL_CMD="npm install -g siluzan-tso-cli@beta"
|
|
14
|
+
readonly WEB_BASE="https://www-ci.siluzan.com"
|
|
15
15
|
|
|
16
16
|
# -- Constants ----------------------------------------------------------------
|
|
17
17
|
readonly NODE_MAJOR_MIN=18
|