siluzan-tso-cli 1.0.0-beta.52 → 1.0.0-beta.53
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 +1 -1
- package/dist/index.js +172 -40
- package/dist/skill/_meta.json +2 -2
- package/dist/skill/references/google-ads.md +206 -34
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -20,7 +20,7 @@ siluzan-tso init -d /path/to/skills # 写入自定义目录
|
|
|
20
20
|
siluzan-tso init --force # 强制覆盖已存在文件
|
|
21
21
|
```
|
|
22
22
|
|
|
23
|
-
> **注意**:当前为测试版(1.0.0-beta.
|
|
23
|
+
> **注意**:当前为测试版(1.0.0-beta.53),供内部测试使用。正式发布后安装命令将改为 `npm install -g siluzan-tso-cli`。
|
|
24
24
|
|
|
25
25
|
| 助手 | 建议 `--ai` |
|
|
26
26
|
|------|-------------|
|
package/dist/index.js
CHANGED
|
@@ -4557,6 +4557,7 @@ async function runInvoiceInfoDelete(opts) {
|
|
|
4557
4557
|
|
|
4558
4558
|
// src/commands/ad.ts
|
|
4559
4559
|
import { randomUUID } from "crypto";
|
|
4560
|
+
import { readFileSync as readFileSync6 } from "fs";
|
|
4560
4561
|
async function findItemInList(listUrl, config, id, verbose) {
|
|
4561
4562
|
const res = await apiFetch2(listUrl, config, {}, verbose);
|
|
4562
4563
|
const item = (res.data ?? []).find((x) => x["id"] === id);
|
|
@@ -5200,8 +5201,59 @@ function buildAdsForBatchJob(opts) {
|
|
|
5200
5201
|
return [ad];
|
|
5201
5202
|
}
|
|
5202
5203
|
async function runAdCampaignCreate(opts) {
|
|
5203
|
-
|
|
5204
|
-
|
|
5204
|
+
if (opts.configFile) {
|
|
5205
|
+
let fileConfig;
|
|
5206
|
+
try {
|
|
5207
|
+
fileConfig = JSON.parse(readFileSync6(opts.configFile, "utf8"));
|
|
5208
|
+
} catch (err) {
|
|
5209
|
+
console.error(`
|
|
5210
|
+
\u274C \u8BFB\u53D6\u914D\u7F6E\u6587\u4EF6\u5931\u8D25\uFF08${opts.configFile}\uFF09\uFF1A${err instanceof Error ? err.message : String(err)}
|
|
5211
|
+
`);
|
|
5212
|
+
process.exit(1);
|
|
5213
|
+
}
|
|
5214
|
+
const fromFile = {
|
|
5215
|
+
account: fileConfig.account,
|
|
5216
|
+
customerName: fileConfig.customerName,
|
|
5217
|
+
name: fileConfig.name,
|
|
5218
|
+
budget: fileConfig.budget,
|
|
5219
|
+
// CampaignCreateConfig.bidding → AdCampaignCreateOptions.biddingStrategy
|
|
5220
|
+
biddingStrategy: fileConfig.bidding,
|
|
5221
|
+
bidCeiling: fileConfig.bidCeiling,
|
|
5222
|
+
targetCpa: fileConfig.targetCpa,
|
|
5223
|
+
targetRoas: fileConfig.targetRoas,
|
|
5224
|
+
locationIds: fileConfig.locationIds,
|
|
5225
|
+
languageIds: fileConfig.languageIds,
|
|
5226
|
+
startDate: fileConfig.startDate,
|
|
5227
|
+
endDate: fileConfig.endDate,
|
|
5228
|
+
url: fileConfig.url,
|
|
5229
|
+
status: fileConfig.status,
|
|
5230
|
+
adgroupName: fileConfig.adgroupName,
|
|
5231
|
+
maxCpc: fileConfig.maxCpc,
|
|
5232
|
+
matchType: fileConfig.matchType,
|
|
5233
|
+
keywords: fileConfig.keywords,
|
|
5234
|
+
headlines: fileConfig.headlines,
|
|
5235
|
+
descriptions: fileConfig.descriptions,
|
|
5236
|
+
finalUrl: fileConfig.finalUrl,
|
|
5237
|
+
path1: fileConfig.path1,
|
|
5238
|
+
path2: fileConfig.path2,
|
|
5239
|
+
productWords: fileConfig.productWords,
|
|
5240
|
+
negativeKeywords: fileConfig.negativeKeywords,
|
|
5241
|
+
// CampaignCreateConfig.extensions/extraAdGroups → extensionsJson/extraAdGroupsJson
|
|
5242
|
+
extensionsJson: fileConfig.extensions,
|
|
5243
|
+
extraAdGroupsJson: fileConfig.extraAdGroups,
|
|
5244
|
+
draft: fileConfig.draft,
|
|
5245
|
+
targetSearchNetwork: fileConfig.targetSearchNetwork,
|
|
5246
|
+
targetContentNetwork: fileConfig.targetContentNetwork
|
|
5247
|
+
};
|
|
5248
|
+
opts = {
|
|
5249
|
+
...fromFile,
|
|
5250
|
+
...Object.fromEntries(
|
|
5251
|
+
Object.entries(opts).filter(([, v]) => v !== void 0 && v !== "" && !(Array.isArray(v) && v.length === 0))
|
|
5252
|
+
),
|
|
5253
|
+
configFile: void 0
|
|
5254
|
+
};
|
|
5255
|
+
}
|
|
5256
|
+
const config = await ensureDataPermission(loadConfig(opts.token));
|
|
5205
5257
|
if (!config.apiBaseUrl) {
|
|
5206
5258
|
console.error("\n\u274C \u672A\u914D\u7F6E apiBaseUrl\uFF0C\u8BF7\u6267\u884C\uFF1Asiluzan-tso config set --api-base <URL>\n");
|
|
5207
5259
|
process.exit(1);
|
|
@@ -5209,9 +5261,12 @@ async function runAdCampaignCreate(opts) {
|
|
|
5209
5261
|
const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
5210
5262
|
const defaultEnd = "2037-12-30";
|
|
5211
5263
|
const budgetBatch = Math.round(opts.budget * 100);
|
|
5212
|
-
const maxCpcBatch =
|
|
5264
|
+
const maxCpcBatch = Math.round(opts.maxCpc * 100);
|
|
5213
5265
|
const campaign = {
|
|
5266
|
+
// 预算共享(Web 默认 false = 独立预算)
|
|
5267
|
+
BudgetShared: false,
|
|
5214
5268
|
Budget: budgetBatch,
|
|
5269
|
+
BudgetId: 0,
|
|
5215
5270
|
Name: opts.name,
|
|
5216
5271
|
StatusV2: opts.status ?? "Enabled",
|
|
5217
5272
|
ChannelTypeV2: "SEARCH",
|
|
@@ -5222,22 +5277,51 @@ async function runAdCampaignCreate(opts) {
|
|
|
5222
5277
|
...opts.targetRoas ? { TargetRoas: opts.targetRoas } : {},
|
|
5223
5278
|
TargetGoogleSearch: true,
|
|
5224
5279
|
TargetSearchNetwork: opts.targetSearchNetwork ?? true,
|
|
5280
|
+
// 地理位置定向类型(0 = DONT_CARE,与 Web 一致)
|
|
5281
|
+
NegativeGeoTargetType: 0,
|
|
5282
|
+
PositiveGeoTargetType: 0,
|
|
5283
|
+
// DSA 动态搜索广告配置(搜索广告系列保持默认空值即可)
|
|
5284
|
+
DSADomainName: "",
|
|
5285
|
+
DSALanguageCode: "en",
|
|
5225
5286
|
TargetContentNetwork: opts.targetContentNetwork ?? false,
|
|
5226
5287
|
StartTime: opts.startDate ?? today,
|
|
5227
5288
|
EndTime: opts.endDate ?? defaultEnd,
|
|
5228
|
-
//
|
|
5289
|
+
// 否定关键词(传入则写入第一条记录;不传则空数组,后续可用 ad negative-keyword-create 追加)
|
|
5290
|
+
NegativeKeywordsForBatchJob: opts.negativeKeywords && opts.negativeKeywords.length > 0 ? [{
|
|
5291
|
+
KeywordText: opts.negativeKeywords,
|
|
5292
|
+
MatchTypeV2: "BROAD",
|
|
5293
|
+
FinalURL: ""
|
|
5294
|
+
}] : [],
|
|
5295
|
+
// 广告附加功能(CALL / STRUCTURED_SNIPPET / SITELINK 等,不传则空数组)
|
|
5296
|
+
ExtensionsForBatchJob: opts.extensionsJson ?? [],
|
|
5297
|
+
// 第一个广告组(关键词与广告:不传则为空,后续用 ad keyword-create / ad ad-create 补充)
|
|
5298
|
+
AdGroupsForBatchJob: [
|
|
5299
|
+
{
|
|
5300
|
+
Name: opts.adgroupName,
|
|
5301
|
+
StatusV2: "Enabled",
|
|
5302
|
+
TypeV2: "SEARCH_STANDARD",
|
|
5303
|
+
RotationModeV2: "Unspecified",
|
|
5304
|
+
MaxCPCAmount: maxCpcBatch,
|
|
5305
|
+
AdsForBatchJob: buildAdsForBatchJob(opts),
|
|
5306
|
+
KeywordsForBatchJob: buildKeywordsForBatchJob(opts)
|
|
5307
|
+
},
|
|
5308
|
+
// 可通过 --extra-adgroups-json 追加更多广告组
|
|
5309
|
+
...opts.extraAdGroupsJson ?? []
|
|
5310
|
+
],
|
|
5311
|
+
// 地理位置定向(id 保持字符串类型,与 Web API 一致)
|
|
5229
5312
|
targetedLocations: opts.locationIds.map((id) => ({
|
|
5313
|
+
id: String(id),
|
|
5230
5314
|
bidModifier: 0,
|
|
5231
|
-
bidModifierSpecified: false
|
|
5232
|
-
id: Number(id)
|
|
5315
|
+
bidModifierSpecified: false
|
|
5233
5316
|
})),
|
|
5234
5317
|
excludedLocations: [],
|
|
5235
5318
|
// 语言(默认英语 1000)
|
|
5236
5319
|
targetedLanguages: (opts.languageIds ?? ["1000"]).map((id) => ({ id: Number(id) })),
|
|
5237
|
-
//
|
|
5320
|
+
// 设备:30000=全部,30001=移动,30002=平板(与 Web 默认三端投放一致)
|
|
5238
5321
|
targetedPlatforms: [
|
|
5239
|
-
{ id:
|
|
5240
|
-
{ id:
|
|
5322
|
+
{ id: 30001, bidModifier: 0 },
|
|
5323
|
+
{ id: 30002, bidModifier: 0 },
|
|
5324
|
+
{ id: 3e4, bidModifier: 0 }
|
|
5241
5325
|
],
|
|
5242
5326
|
// 全周全天投放
|
|
5243
5327
|
adSchedules: [2, 3, 4, 5, 6, 7, 8].map((day) => ({
|
|
@@ -5246,19 +5330,7 @@ async function runAdCampaignCreate(opts) {
|
|
|
5246
5330
|
StartMinuteV2: 2,
|
|
5247
5331
|
endHour: 24,
|
|
5248
5332
|
EndMinuteV2: 2
|
|
5249
|
-
}))
|
|
5250
|
-
NegativeKeywordsForBatchJob: [],
|
|
5251
|
-
ExtensionsForBatchJob: [],
|
|
5252
|
-
// 第一个广告组(关键词与广告:不传则为空,后续用 ad keyword-create / ad ad-create 补充)
|
|
5253
|
-
AdGroupsForBatchJob: [{
|
|
5254
|
-
Name: opts.adgroupName,
|
|
5255
|
-
StatusV2: "Enabled",
|
|
5256
|
-
TypeV2: "SEARCH_STANDARD",
|
|
5257
|
-
RotationModeV2: "Unspecified",
|
|
5258
|
-
MaxCPCAmount: maxCpcBatch,
|
|
5259
|
-
AdsForBatchJob: buildAdsForBatchJob(opts),
|
|
5260
|
-
KeywordsForBatchJob: buildKeywordsForBatchJob(opts)
|
|
5261
|
-
}]
|
|
5333
|
+
}))
|
|
5262
5334
|
};
|
|
5263
5335
|
const adGroupsForBatchJob = campaign.AdGroupsForBatchJob;
|
|
5264
5336
|
const keywordRecommendations = (adGroupsForBatchJob ?? []).filter((g) => typeof g.Name === "string" && g.Name.trim().length > 0).map((g) => ({
|
|
@@ -5270,9 +5342,10 @@ async function runAdCampaignCreate(opts) {
|
|
|
5270
5342
|
customerName: opts.customerName,
|
|
5271
5343
|
campaignName: opts.name,
|
|
5272
5344
|
url: opts.url ?? "",
|
|
5273
|
-
//
|
|
5345
|
+
// locations 为地理位置名称的展示字符串列表(前端只读,后端不做校验,可留空)
|
|
5274
5346
|
locations: [],
|
|
5275
|
-
productWords
|
|
5347
|
+
// productWords 为产品核心词,用于 AI 生成关键词推荐,可通过 --product-words 传入
|
|
5348
|
+
productWords: opts.productWords ?? [],
|
|
5276
5349
|
// Web 端异步创建在走「立即发布」时会使用非空的 GoogleDataRecordId(UUID)且 DraftStatus 为 Published;
|
|
5277
5350
|
// 这里保持一致,使用随机 UUID 作为唯一标识。是否直接发布由 draft 开关控制:
|
|
5278
5351
|
// - draft=true → DraftStatus = Draft(仅创建草稿,需后续 ad batch publish 发布)
|
|
@@ -9901,20 +9974,20 @@ adCmd.command("keyword-negative-delete").description("\u5220\u9664\u5426\u5B9A\u
|
|
|
9901
9974
|
});
|
|
9902
9975
|
adCmd.command("campaign-create").description(
|
|
9903
9976
|
'\u65B0\u5EFA\u641C\u7D22\u5E7F\u544A\u7CFB\u5217\uFF08\u542B\u7B2C\u4E00\u4E2A\u5E7F\u544A\u7EC4\uFF0C\u5F02\u6B65\u63D0\u4EA4\uFF1B\u9ED8\u8BA4\u7ACB\u5373\u53D1\u5E03\uFF0C\u53EF\u52A0 --draft \u4EC5\u4FDD\u5B58\u4E3A\u8349\u7A3F\uFF09\n\n \u524D\u7F6E\u6B65\u9AA4\uFF1A\n 1. siluzan-tso ad geo search -a <accountId> -q "United States" # \u83B7\u53D6 locationId\n 2. \u586B\u5165 --location-ids\uFF0C\u521B\u5EFA\u5B8C\u6BD5\u540E\u7528 ad batch get --id <taskId> \u8DDF\u8FDB\u72B6\u6001'
|
|
9904
|
-
).
|
|
9977
|
+
).option("-a, --account <id>", "Google \u8D26\u6237 mediaCustomerId\uFF08\u4F7F\u7528 --config-file \u65F6\u53EF\u7701\u7565\uFF0C\u4ECE\u6587\u4EF6\u8BFB\u53D6\uFF09").option("--customer-name <name>", "\u8D26\u6237\u540D\u79F0\uFF08\u6765\u81EA list-accounts --json \u7684 mediaAccountName \u5B57\u6BB5\uFF09").option("--name <name>", "\u5E7F\u544A\u7CFB\u5217\u540D\u79F0").option(
|
|
9905
9978
|
"--budget <amount>",
|
|
9906
|
-
"\u65E5\u9884\u7B97\uFF1A\u8D26\u6237\u4E3B\u5E01\u79CD\u91D1\u989D\uFF08\u5982 100 = \u6BCF\u5929 100 USD/CNY\uFF1B\
|
|
9979
|
+
"\u65E5\u9884\u7B97\uFF1A\u8D26\u6237\u4E3B\u5E01\u79CD\u91D1\u989D\uFF08\u5982 100 = \u6BCF\u5929 100 USD/CNY\uFF1B\u5185\u90E8 \xD7100 \u5199\u5165\u63A5\u53E3\uFF09",
|
|
9907
9980
|
parseFloat
|
|
9908
|
-
).
|
|
9981
|
+
).option(
|
|
9909
9982
|
"--bidding <strategy>",
|
|
9910
9983
|
"\u51FA\u4EF7\u7B56\u7565\uFF1ATARGET_SPEND | MANUAL_CPC | TARGET_CPA | TARGET_ROAS"
|
|
9911
|
-
).
|
|
9984
|
+
).option(
|
|
9912
9985
|
"--location-ids <ids>",
|
|
9913
9986
|
"\u6295\u653E\u5730\u7406\u4F4D\u7F6E ID\uFF0C\u9017\u53F7\u5206\u9694\uFF08\u7528 ad geo search \u83B7\u53D6\uFF0C\u5982 2840=\u7F8E\u56FD\uFF09",
|
|
9914
9987
|
(v) => v.split(",").map((s) => s.trim())
|
|
9915
|
-
).
|
|
9988
|
+
).option("--adgroup-name <name>", "\u7B2C\u4E00\u4E2A\u5E7F\u544A\u7EC4\u540D\u79F0").option(
|
|
9916
9989
|
"--max-cpc <amount>",
|
|
9917
|
-
"\u7B2C\u4E00\u4E2A\u5E7F\u544A\u7EC4\u6700\u9AD8 CPC\uFF1A\u4E3B\u5E01\u79CD\u91D1\u989D\uFF08\u5982 1.5 = \u6BCF\u6B21\u70B9\u51FB 1.50\uFF1B\
|
|
9990
|
+
"\u7B2C\u4E00\u4E2A\u5E7F\u544A\u7EC4\u6700\u9AD8 CPC\uFF1A\u4E3B\u5E01\u79CD\u91D1\u989D\uFF08\u5982 1.5 = \u6BCF\u6B21\u70B9\u51FB 1.50\uFF1B\u5185\u90E8 \xD7100 \u5199\u5165 MaxCPCAmount\uFF09",
|
|
9918
9991
|
parseFloat
|
|
9919
9992
|
).option(
|
|
9920
9993
|
"--bid-ceiling <amount>",
|
|
@@ -9936,9 +10009,44 @@ adCmd.command("campaign-create").description(
|
|
|
9936
10009
|
"--descriptions <descs>",
|
|
9937
10010
|
"\u5E7F\u544A\u63CF\u8FF0\uFF0C\u9017\u53F7\u5206\u9694\uFF0C\u81F3\u5C11 2 \u6761\uFF0C\u6BCF\u6761 \u2264 90 \u5B57\u7B26",
|
|
9938
10011
|
(v) => v.split(",").map((s) => s.trim())
|
|
9939
|
-
).option("--final-url <url>", "\u5E7F\u544A\u843D\u5730\u9875\uFF08\u5173\u952E\u8BCD\u548C\u5E7F\u544A\u5171\u7528\uFF1B\u4E0D\u4F20\u5219\u7EE7\u627F --url\uFF09").option("--path1 <path>", "\u5C55\u793A URL \u8DEF\u5F84 1\uFF08\u2264 15 \u5B57\u7B26\uFF0C\u53EF\u9009\uFF09").option("--path2 <path>", "\u5C55\u793A URL \u8DEF\u5F84 2\uFF08\u2264 15 \u5B57\u7B26\uFF0C\u53EF\u9009\uFF09").option(
|
|
10012
|
+
).option("--final-url <url>", "\u5E7F\u544A\u843D\u5730\u9875\uFF08\u5173\u952E\u8BCD\u548C\u5E7F\u544A\u5171\u7528\uFF1B\u4E0D\u4F20\u5219\u7EE7\u627F --url\uFF09").option("--path1 <path>", "\u5C55\u793A URL \u8DEF\u5F84 1\uFF08\u2264 15 \u5B57\u7B26\uFF0C\u53EF\u9009\uFF09").option("--path2 <path>", "\u5C55\u793A URL \u8DEF\u5F84 2\uFF08\u2264 15 \u5B57\u7B26\uFF0C\u53EF\u9009\uFF09").option(
|
|
10013
|
+
"--product-words <words>",
|
|
10014
|
+
"\u63A8\u5E7F\u4EA7\u54C1\u8BCD\uFF0C\u9017\u53F7\u5206\u9694\uFF08\u5199\u5165\u5916\u5C42 productWords\uFF0C\u7528\u4E8E AI \u5173\u952E\u8BCD\u63A8\u8350\uFF09",
|
|
10015
|
+
(v) => v.split(",").map((s) => s.trim())
|
|
10016
|
+
).option(
|
|
10017
|
+
"--negative-keywords <kws>",
|
|
10018
|
+
"\u5426\u5B9A\u5173\u952E\u8BCD\uFF0C\u9017\u53F7\u5206\u9694\uFF08\u751F\u6210 NegativeKeywordsForBatchJob\uFF0C\u9ED8\u8BA4 BROAD \u5339\u914D\uFF09",
|
|
10019
|
+
(v) => v.split(",").map((s) => s.trim())
|
|
10020
|
+
).option(
|
|
10021
|
+
"--extensions-json <json>",
|
|
10022
|
+
"\u5E7F\u544A\u9644\u52A0\u529F\u80FD JSON \u6570\u7EC4\u5B57\u7B26\u4E32\uFF08ExtensionsForBatchJob\uFF1B\u5305\u542B CALL/SITELINK/STRUCTURED_SNIPPET \u7B49\uFF09"
|
|
10023
|
+
).option(
|
|
10024
|
+
"--extra-adgroups-json <json>",
|
|
10025
|
+
"\u989D\u5916\u5E7F\u544A\u7EC4 JSON \u6570\u7EC4\u5B57\u7B26\u4E32\uFF08\u8FFD\u52A0\u5230 AdGroupsForBatchJob\uFF1B\u6BCF\u6761\u987B\u5305\u542B Name/MaxCPCAmount/AdsForBatchJob/KeywordsForBatchJob \u7B49\u5B57\u6BB5\uFF09"
|
|
10026
|
+
).option(
|
|
10027
|
+
"--config-file <path>",
|
|
10028
|
+
"JSON \u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84\uFF08\u63A8\u8350 AI \u4F7F\u7528\uFF09\uFF1A\u5C06\u6240\u6709\u53C2\u6570\u5199\u5165 JSON \u6587\u4EF6\u518D\u4F20\u6B64\u8DEF\u5F84\uFF0C\u652F\u6301 headlines \u6570\u7EC4\uFF08\u5141\u8BB8\u5143\u7D20\u5185\u542B\u9017\u53F7\uFF09\u3001extensions/extraAdGroups \u76F4\u63A5\u4F5C\u4E3A JSON \u6570\u7EC4\uFF0CCLI \u663E\u5F0F\u4F20\u5165\u7684\u53C2\u6570\u4F1A\u8986\u76D6\u6587\u4EF6\u4E2D\u7684\u540C\u540D\u5B57\u6BB5"
|
|
10029
|
+
).option("--draft", "\u4EC5\u4FDD\u5B58\u4E3A\u8349\u7A3F\uFF08DraftStatus=Draft\uFF0C\u9700\u8981\u540E\u7EED ad batch publish \u53D1\u5E03\uFF09", false).option("-t, --token <token>", "Auth Token").option("--json", "JSON \u683C\u5F0F\u8F93\u51FA\u539F\u59CB\u54CD\u5E94", false).option("--verbose", "\u8BE6\u7EC6\u9519\u8BEF\u4FE1\u606F", false).action(async (opts) => {
|
|
10030
|
+
if (!opts.configFile) {
|
|
10031
|
+
const missing = [];
|
|
10032
|
+
if (!opts.account) missing.push("-a/--account");
|
|
10033
|
+
if (!opts.customerName) missing.push("--customer-name");
|
|
10034
|
+
if (!opts.name) missing.push("--name");
|
|
10035
|
+
if (opts.budget === void 0) missing.push("--budget");
|
|
10036
|
+
if (!opts.bidding) missing.push("--bidding");
|
|
10037
|
+
if (!opts.locationIds?.length) missing.push("--location-ids");
|
|
10038
|
+
if (!opts.adgroupName) missing.push("--adgroup-name");
|
|
10039
|
+
if (opts.maxCpc === void 0) missing.push("--max-cpc");
|
|
10040
|
+
if (missing.length > 0) {
|
|
10041
|
+
console.error(`
|
|
10042
|
+
\u274C \u7F3A\u5C11\u5FC5\u586B\u53C2\u6570\uFF1A${missing.join("\uFF0C")}
|
|
10043
|
+
\u63D0\u793A\uFF1A\u53EF\u4F7F\u7528 --config-file <path> \u4ECE JSON \u6587\u4EF6\u8BFB\u53D6\u6240\u6709\u53C2\u6570
|
|
10044
|
+
`);
|
|
10045
|
+
process.exit(1);
|
|
10046
|
+
}
|
|
10047
|
+
}
|
|
9940
10048
|
const strategies = ["TARGET_SPEND", "MANUAL_CPC", "TARGET_CPA", "TARGET_ROAS"];
|
|
9941
|
-
if (!strategies.includes(opts.bidding)) {
|
|
10049
|
+
if (opts.bidding && !strategies.includes(opts.bidding)) {
|
|
9942
10050
|
console.error(`
|
|
9943
10051
|
\u274C --bidding \u53EA\u63A5\u53D7 ${strategies.join(" | ")}
|
|
9944
10052
|
`);
|
|
@@ -9955,26 +10063,46 @@ adCmd.command("campaign-create").description(
|
|
|
9955
10063
|
`);
|
|
9956
10064
|
process.exit(1);
|
|
9957
10065
|
}
|
|
10066
|
+
let extensionsJson;
|
|
10067
|
+
if (opts.extensionsJson) {
|
|
10068
|
+
try {
|
|
10069
|
+
extensionsJson = JSON.parse(opts.extensionsJson);
|
|
10070
|
+
} catch {
|
|
10071
|
+
console.error("\n\u274C --extensions-json \u683C\u5F0F\u9519\u8BEF\uFF0C\u8BF7\u4F20\u5165\u5408\u6CD5\u7684 JSON \u6570\u7EC4\u5B57\u7B26\u4E32\n");
|
|
10072
|
+
process.exit(1);
|
|
10073
|
+
}
|
|
10074
|
+
}
|
|
10075
|
+
let extraAdGroupsJson;
|
|
10076
|
+
if (opts.extraAdgroupsJson) {
|
|
10077
|
+
try {
|
|
10078
|
+
extraAdGroupsJson = JSON.parse(opts.extraAdgroupsJson);
|
|
10079
|
+
} catch {
|
|
10080
|
+
console.error("\n\u274C --extra-adgroups-json \u683C\u5F0F\u9519\u8BEF\uFF0C\u8BF7\u4F20\u5165\u5408\u6CD5\u7684 JSON \u6570\u7EC4\u5B57\u7B26\u4E32\n");
|
|
10081
|
+
process.exit(1);
|
|
10082
|
+
}
|
|
10083
|
+
}
|
|
9958
10084
|
await runAdCampaignCreate({
|
|
10085
|
+
configFile: opts.configFile,
|
|
9959
10086
|
token: opts.token,
|
|
9960
|
-
|
|
9961
|
-
|
|
9962
|
-
|
|
9963
|
-
|
|
9964
|
-
|
|
10087
|
+
// 未使用 config-file 时,这些字段已通过上方校验确保非空;config-file 场景下函数内部会从文件补全
|
|
10088
|
+
account: opts.account ?? "",
|
|
10089
|
+
customerName: opts.customerName ?? "",
|
|
10090
|
+
name: opts.name ?? "",
|
|
10091
|
+
budget: opts.budget ?? 0,
|
|
10092
|
+
biddingStrategy: opts.bidding ?? "",
|
|
9965
10093
|
bidCeiling: opts.bidCeiling,
|
|
9966
10094
|
targetCpa: opts.targetCpa,
|
|
9967
10095
|
targetRoas: opts.targetRoas,
|
|
9968
10096
|
targetSearchNetwork: opts.searchNetwork,
|
|
9969
10097
|
targetContentNetwork: opts.contentNetwork,
|
|
9970
|
-
locationIds: opts.locationIds,
|
|
10098
|
+
locationIds: opts.locationIds ?? [],
|
|
9971
10099
|
languageIds: opts.langIds,
|
|
9972
10100
|
startDate: opts.start,
|
|
9973
10101
|
endDate: opts.end,
|
|
9974
10102
|
url: opts.url,
|
|
9975
10103
|
status: opts.status ?? "Enabled",
|
|
9976
|
-
adgroupName: opts.adgroupName,
|
|
9977
|
-
maxCpc: opts.maxCpc,
|
|
10104
|
+
adgroupName: opts.adgroupName ?? "",
|
|
10105
|
+
maxCpc: opts.maxCpc ?? 0,
|
|
9978
10106
|
keywords: opts.keywords,
|
|
9979
10107
|
matchType: opts.matchType,
|
|
9980
10108
|
headlines: opts.headlines,
|
|
@@ -9982,6 +10110,10 @@ adCmd.command("campaign-create").description(
|
|
|
9982
10110
|
finalUrl: opts.finalUrl,
|
|
9983
10111
|
path1: opts.path1,
|
|
9984
10112
|
path2: opts.path2,
|
|
10113
|
+
productWords: opts.productWords,
|
|
10114
|
+
negativeKeywords: opts.negativeKeywords,
|
|
10115
|
+
extensionsJson,
|
|
10116
|
+
extraAdGroupsJson,
|
|
9985
10117
|
draft: opts.draft,
|
|
9986
10118
|
json: opts.json,
|
|
9987
10119
|
verbose: opts.verbose
|
package/dist/skill/_meta.json
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
|
|
10
10
|
### 第一步:规则文档阅读(不得跳过)
|
|
11
11
|
|
|
12
|
-
操作开始前,**AI
|
|
12
|
+
操作开始前,**AI 必须先阅读完下面所有文档**,并在心智模型中内化规则后,才能进入任何「方案规划 / 广告创建 / 调整」步骤,严禁只读 `google-ads-launch-plan-template.md` 就直接出方案。
|
|
13
13
|
|
|
14
14
|
| 文档地址 | 文档内容 |
|
|
15
15
|
|----------|-----------|
|
|
@@ -591,7 +591,7 @@ siluzan-tso keyword -k "running shoes" --json
|
|
|
591
591
|
|
|
592
592
|
## ad campaign-create — 广告系列新增
|
|
593
593
|
|
|
594
|
-
|
|
594
|
+
新建搜索广告系列(异步批量任务)。支持两种创建模式:
|
|
595
595
|
|
|
596
596
|
- **一体化模式**(推荐):传入 `--keywords`、`--headlines`、`--descriptions`,一条命令完成系列 + 组 + 关键词 + 广告创意,与网页 AI 创建向导行为等价。
|
|
597
597
|
- **骨架模式**:仅创建系列和空广告组,后续用 `ad keyword-create`、`ad ad-create` 补充内容。
|
|
@@ -603,41 +603,202 @@ siluzan-tso keyword -k "running shoes" --json
|
|
|
603
603
|
> 广告组、关键词、广告创意的直接创建走另一套 Google 网关 API,均有对应的 `adgroup-create`、`keyword-create`、`ad-create` 等命令。
|
|
604
604
|
> 任务异步处理,任务 ID 可通过 `ad batch get --id <id>` 跟进进度。
|
|
605
605
|
|
|
606
|
-
|
|
606
|
+
---
|
|
607
|
+
|
|
608
|
+
### 🤖 AI 推荐用法:--config-file(JSON 配置文件)
|
|
609
|
+
|
|
610
|
+
**当参数复杂(多广告组、含附加功能、标题中有逗号)时,AI 应优先使用此方式**:将所有参数写入一个 JSON 文件,再用 `--config-file` 传入路径。
|
|
611
|
+
|
|
612
|
+
**优势:**
|
|
613
|
+
- `headlines` 是真正的字符串数组,元素内**允许含逗号**(如 `"Global Reach, Local Impact"`)
|
|
614
|
+
- `extensions` / `extraAdGroups` 直接写 JSON,不需要序列化为字符串
|
|
615
|
+
- 参数复杂时无 shell 转义问题,AI 一次生成即可成功
|
|
616
|
+
|
|
617
|
+
**AI 执行步骤:**
|
|
618
|
+
1. 用 Write 工具将配置写入 JSON 文件(如 `/tmp/campaign.json`)
|
|
619
|
+
2. 执行 `siluzan-tso ad campaign-create --config-file /tmp/campaign.json`
|
|
620
|
+
3. 用返回的任务 ID 查询进度
|
|
621
|
+
|
|
622
|
+
**JSON 配置文件完整 Schema:**
|
|
623
|
+
|
|
624
|
+
```json
|
|
625
|
+
{
|
|
626
|
+
"account": "6326027735",
|
|
627
|
+
"customerName": "账户显示名称(来自 list-accounts --json 的 mediaAccountName)",
|
|
628
|
+
"name": "搜索-品牌词-2026",
|
|
629
|
+
"budget": 100,
|
|
630
|
+
"bidding": "TARGET_SPEND",
|
|
631
|
+
"bidCeiling": 1.5,
|
|
632
|
+
"locationIds": ["2840", "2826", "2036"],
|
|
633
|
+
"languageIds": ["1000", "1017"],
|
|
634
|
+
"startDate": "2026-04-01",
|
|
635
|
+
"endDate": "2027-04-01",
|
|
636
|
+
"url": "https://www.example.com",
|
|
637
|
+
"status": "Enabled",
|
|
638
|
+
"adgroupName": "核心词_品牌词",
|
|
639
|
+
"maxCpc": 1.5,
|
|
640
|
+
"matchType": "BROAD",
|
|
641
|
+
"keywords": [
|
|
642
|
+
"brand keyword 1",
|
|
643
|
+
"brand keyword 2",
|
|
644
|
+
"brand keyword 3"
|
|
645
|
+
],
|
|
646
|
+
"headlines": [
|
|
647
|
+
"Brand Name: Quality Products",
|
|
648
|
+
"Trusted For Over 20 Years",
|
|
649
|
+
"Global Reach, Local Impact",
|
|
650
|
+
"Contact Us Today",
|
|
651
|
+
"Request A Free Quote Now"
|
|
652
|
+
],
|
|
653
|
+
"descriptions": [
|
|
654
|
+
"Top-quality products with certified standards. Contact us today!",
|
|
655
|
+
"Trusted by global partners. Request a free quote now!"
|
|
656
|
+
],
|
|
657
|
+
"finalUrl": "https://www.example.com/products",
|
|
658
|
+
"path1": "products",
|
|
659
|
+
"path2": "2026",
|
|
660
|
+
"productWords": ["brand", "product"],
|
|
661
|
+
"negativeKeywords": [
|
|
662
|
+
"free", "cheap", "wikipedia", "pdf", "ebay", "amazon"
|
|
663
|
+
],
|
|
664
|
+
"extensions": [
|
|
665
|
+
{
|
|
666
|
+
"level": "Campaign",
|
|
667
|
+
"typeV2": "CALL",
|
|
668
|
+
"AssetFieldType": "CALL",
|
|
669
|
+
"Properties": { "ContryCode": "CN", "PhoneNumber": "+86 400-XXX-XXXX" }
|
|
670
|
+
},
|
|
671
|
+
{
|
|
672
|
+
"level": "Campaign",
|
|
673
|
+
"typeV2": "SITELINK",
|
|
674
|
+
"AssetFieldType": "SITELINK",
|
|
675
|
+
"properties": {
|
|
676
|
+
"DestinationUrl": "https://www.example.com/about",
|
|
677
|
+
"Text": "About Us",
|
|
678
|
+
"Line2": "Learn about our story",
|
|
679
|
+
"Line3": "Explore our mission"
|
|
680
|
+
}
|
|
681
|
+
},
|
|
682
|
+
{
|
|
683
|
+
"level": "Campaign",
|
|
684
|
+
"typeV2": "STRUCTURED_SNIPPET",
|
|
685
|
+
"AssetFieldType": "STRUCTURED_SNIPPET",
|
|
686
|
+
"StructuredSnippetHeaderValue": {
|
|
687
|
+
"key": "Services",
|
|
688
|
+
"value": ["Quality Certified", "Global Shipping", "24/7 Support"]
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
],
|
|
692
|
+
"extraAdGroups": [
|
|
693
|
+
{
|
|
694
|
+
"MaxCPCAmount": 150,
|
|
695
|
+
"Name": "通用词_行业词",
|
|
696
|
+
"StatusV2": "Enabled",
|
|
697
|
+
"TypeV2": "SEARCH_STANDARD",
|
|
698
|
+
"RotationModeV2": "Unspecified",
|
|
699
|
+
"KeywordsForBatchJob": [
|
|
700
|
+
{
|
|
701
|
+
"KeywordText": ["industry keyword 1", "industry keyword 2"],
|
|
702
|
+
"MatchTypeV2": "BROAD",
|
|
703
|
+
"Finalurl": "https://www.example.com"
|
|
704
|
+
}
|
|
705
|
+
],
|
|
706
|
+
"AdsForBatchJob": [
|
|
707
|
+
{
|
|
708
|
+
"AdTitle": null,
|
|
709
|
+
"DestinationUrl": "https://www.example.com",
|
|
710
|
+
"Finalurl": "https://www.example.com",
|
|
711
|
+
"Path1": "", "Path2": "",
|
|
712
|
+
"TypeV2": "RESPONSIVE_SEARCH_AD",
|
|
713
|
+
"headlinePart1": "Industry Leading Products",
|
|
714
|
+
"headlinePart2": "Certified Quality Standards",
|
|
715
|
+
"headlinePart3": "Get A Free Quote Today",
|
|
716
|
+
"AddtionalHeadlines": [
|
|
717
|
+
"Trusted By Global Partners",
|
|
718
|
+
"20+ Years Of Experience",
|
|
719
|
+
"Contact Us Now"
|
|
720
|
+
],
|
|
721
|
+
"adDescription": "Top-quality industry products with global shipping. Contact us today!",
|
|
722
|
+
"adDescription2": "Certified standards and reliable delivery. Request a quote now!"
|
|
723
|
+
}
|
|
724
|
+
]
|
|
725
|
+
}
|
|
726
|
+
],
|
|
727
|
+
"draft": false
|
|
728
|
+
}
|
|
729
|
+
```
|
|
730
|
+
|
|
731
|
+
**字段说明:**
|
|
732
|
+
|
|
733
|
+
| 字段 | 必填 | 类型 | 说明 |
|
|
734
|
+
|------|------|------|------|
|
|
735
|
+
| `account` | ✅ | string | Google 账户 mediaCustomerId |
|
|
736
|
+
| `customerName` | ✅ | string | 账户名称(`list-accounts --json` 的 `mediaAccountName`) |
|
|
737
|
+
| `name` | ✅ | string | 广告系列名称 |
|
|
738
|
+
| `budget` | ✅ | number | 日预算,**主币种展示金额**(100 = 每天 100 USD/CNY,内部 ×100) |
|
|
739
|
+
| `bidding` | ✅ | string | 出价策略:`TARGET_SPEND` \| `MANUAL_CPC` \| `TARGET_CPA` \| `TARGET_ROAS` |
|
|
740
|
+
| `locationIds` | ✅ | string[] | 地理位置 ID 数组(`ad geo search` 获取) |
|
|
741
|
+
| `adgroupName` | ✅ | string | 第一个广告组名称 |
|
|
742
|
+
| `maxCpc` | ✅ | number | 第一个广告组最高 CPC,主币种展示金额(1.5 = 1.50 USD,内部 ×100) |
|
|
743
|
+
| `bidCeiling` | — | number | TARGET_SPEND 出价上限(主币种,内部 ×100) |
|
|
744
|
+
| `targetCpa` | — | number | TARGET_CPA 目标 CPA(主币种,内部 ×100) |
|
|
745
|
+
| `targetRoas` | — | number | TARGET_ROAS 目标 ROAS(如 2.5) |
|
|
746
|
+
| `languageIds` | — | string[] | 语言 ID 数组(默认 `["1000"]` = 英语,中文 = `"1017"`) |
|
|
747
|
+
| `startDate` / `endDate` | — | string | 日期 YYYY-MM-DD(默认:今天 / 2037-12-30) |
|
|
748
|
+
| `url` | — | string | 落地页 URL |
|
|
749
|
+
| `status` | — | string | `Enabled` \| `Paused`(默认 Enabled) |
|
|
750
|
+
| `matchType` | — | string | 默认匹配类型:`BROAD` \| `PHRASE` \| `EXACT` |
|
|
751
|
+
| `keywords` | — | string[] | 第一个广告组关键词 |
|
|
752
|
+
| `headlines` | — | string[] | 标题数组,至少 3 条,推荐 15 条,每条 ≤ 30 字符,**元素内允许含逗号** |
|
|
753
|
+
| `descriptions` | — | string[] | 描述数组,至少 2 条,推荐 4 条,每条 ≤ 90 字符 |
|
|
754
|
+
| `finalUrl` | — | string | 广告落地页 |
|
|
755
|
+
| `path1` / `path2` | — | string | 展示 URL 路径(各 ≤ 15 字符) |
|
|
756
|
+
| `productWords` | — | string[] | 推广产品词(用于 AI 关键词推荐) |
|
|
757
|
+
| `negativeKeywords` | — | string[] | 否定关键词数组(默认 BROAD 匹配) |
|
|
758
|
+
| `extensions` | — | array | 广告附加功能(CALL / SITELINK / STRUCTURED_SNIPPET),见 Schema |
|
|
759
|
+
| `extraAdGroups` | — | array | 额外广告组,追加到 AdGroupsForBatchJob,见 Schema |
|
|
760
|
+
| `draft` | — | boolean | `true` = 仅保存草稿(需后续 `ad batch publish` 发布) |
|
|
761
|
+
|
|
762
|
+
---
|
|
763
|
+
|
|
764
|
+
### 命令行直接传参(简单场景适用)
|
|
765
|
+
|
|
766
|
+
所有参数也可以直接通过 CLI 选项传入(复杂场景推荐用 `--config-file`):
|
|
607
767
|
|
|
608
768
|
| 选项 | 必填 | 说明 |
|
|
609
769
|
|------|------|------|
|
|
610
|
-
| `-a, --account <id>` |
|
|
611
|
-
| `--customer-name <name>` |
|
|
612
|
-
| `--name <name>` |
|
|
613
|
-
| `--budget <amount>` |
|
|
614
|
-
| `--bidding <strategy>` |
|
|
615
|
-
| `--location-ids <ids>` |
|
|
616
|
-
| `--adgroup-name <name>` |
|
|
617
|
-
| `--max-cpc <amount>` |
|
|
618
|
-
| `--keywords <kws>` | — |
|
|
619
|
-
| `--
|
|
620
|
-
| `--
|
|
621
|
-
| `--
|
|
622
|
-
| `--
|
|
623
|
-
| `--
|
|
624
|
-
| `--
|
|
625
|
-
| `--lang-ids <ids>` | — |
|
|
626
|
-
| `--bid-ceiling
|
|
627
|
-
| `--
|
|
628
|
-
| `--
|
|
629
|
-
| `--
|
|
630
|
-
| `--status` | — |
|
|
631
|
-
| `--draft` | — |
|
|
632
|
-
|
|
633
|
-
|
|
770
|
+
| `-a, --account <id>` | ✅* | Google 账户 mediaCustomerId(*使用 `--config-file` 时可从文件读取) |
|
|
771
|
+
| `--customer-name <name>` | ✅* | 账户名称 |
|
|
772
|
+
| `--name <name>` | ✅* | 广告系列名称 |
|
|
773
|
+
| `--budget <amount>` | ✅* | 日预算(主币种展示金额) |
|
|
774
|
+
| `--bidding <strategy>` | ✅* | 出价策略 |
|
|
775
|
+
| `--location-ids <ids>` | ✅* | 地理位置 ID,逗号分隔 |
|
|
776
|
+
| `--adgroup-name <name>` | ✅* | 第一个广告组名称 |
|
|
777
|
+
| `--max-cpc <amount>` | ✅* | 最高 CPC(主币种展示金额) |
|
|
778
|
+
| `--keywords <kws>` | — | 关键词,逗号分隔 |
|
|
779
|
+
| `--headlines <titles>` | — | 广告标题,逗号分隔(⚠️ 标题内不能含逗号,含逗号请用 `--config-file`) |
|
|
780
|
+
| `--descriptions <descs>` | — | 广告描述,逗号分隔 |
|
|
781
|
+
| `--product-words <words>` | — | 产品词,逗号分隔 |
|
|
782
|
+
| `--negative-keywords <kws>` | — | 否定关键词,逗号分隔 |
|
|
783
|
+
| `--extensions-json <json>` | — | 附加功能 JSON 数组字符串 |
|
|
784
|
+
| `--extra-adgroups-json <json>` | — | 额外广告组 JSON 数组字符串 |
|
|
785
|
+
| `--lang-ids <ids>` | — | 语言 ID,逗号分隔(默认 1000=英语) |
|
|
786
|
+
| `--bid-ceiling / --target-cpa / --target-roas` | — | 出价相关参数 |
|
|
787
|
+
| `--start / --end` | — | 日期 YYYY-MM-DD |
|
|
788
|
+
| `--url / --final-url` | — | 落地页 |
|
|
789
|
+
| `--path1 / --path2` | — | 展示路径 |
|
|
790
|
+
| `--status` | — | Enabled \| Paused |
|
|
791
|
+
| `--draft` | — | 仅保存草稿 |
|
|
792
|
+
| `--config-file <path>` | — | JSON 配置文件路径(AI 推荐) |
|
|
793
|
+
|
|
794
|
+
**典型用法(简单场景):**
|
|
634
795
|
|
|
635
796
|
```bash
|
|
636
797
|
# 前置:搜索投放地区 ID
|
|
637
798
|
siluzan-tso ad geo search -a 6326027735 -q "United States"
|
|
638
799
|
# 取出 id 字段,如 2840
|
|
639
800
|
|
|
640
|
-
#
|
|
801
|
+
# 一体化创建(CLI 直接传参,适合参数较少的场景)
|
|
641
802
|
siluzan-tso ad campaign-create \
|
|
642
803
|
-a 6326027735 \
|
|
643
804
|
--customer-name "测试账户" \
|
|
@@ -648,17 +809,28 @@ siluzan-tso ad campaign-create \
|
|
|
648
809
|
--adgroup-name "核心词_跑步鞋" \
|
|
649
810
|
--max-cpc 5 \
|
|
650
811
|
--url "https://www.brand-a.com/running-shoes" \
|
|
651
|
-
--keywords "running shoes,sport shoes,
|
|
652
|
-
--headlines "
|
|
653
|
-
--descriptions "
|
|
812
|
+
--keywords "running shoes,sport shoes,trail running" \
|
|
813
|
+
--headlines "Brand Running Shoes,Lightweight Design,Direct Factory Price" \
|
|
814
|
+
--descriptions "Trusted by millions worldwide.,Free shipping and 30-day returns." \
|
|
654
815
|
--final-url "https://www.brand-a.com/running-shoes" \
|
|
655
|
-
--path1 "
|
|
656
|
-
--path2 "
|
|
816
|
+
--path1 "running" \
|
|
817
|
+
--path2 "shoes"
|
|
657
818
|
|
|
658
|
-
#
|
|
819
|
+
# 查看创建进度
|
|
659
820
|
siluzan-tso ad batch get --id <taskId>
|
|
660
821
|
```
|
|
661
822
|
|
|
823
|
+
**典型用法(AI 推荐,config-file 方式):**
|
|
824
|
+
|
|
825
|
+
```bash
|
|
826
|
+
# 1. AI 先写好 JSON 配置文件(使用 Write 工具)
|
|
827
|
+
# 2. 执行创建
|
|
828
|
+
siluzan-tso ad campaign-create --config-file /tmp/campaign-config.json
|
|
829
|
+
|
|
830
|
+
# 3. 查看进度
|
|
831
|
+
siluzan-tso ad batch get --id <返回的taskId>
|
|
832
|
+
```
|
|
833
|
+
|
|
662
834
|
---
|
|
663
835
|
|
|
664
836
|
## ad campaign-edit — 广告系列编辑
|