siluzan-tso-cli 1.1.29-beta.15 → 1.1.29-beta.17
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 +131 -73
- package/dist/skill/_meta.json +2 -2
- package/dist/skill/assets/pmax-create-template.json +3 -1
- package/dist/skill/assets/pmax-create-template.md +14 -0
- package/dist/skill/assets/pmax-lead-form-template.json +4 -3
- package/dist/skill/assets/pmax-lead-form-template.md +9 -2
- package/dist/skill/references/core/workflows.md +11 -3
- package/dist/skill/references/google-ads/google-ads-campaign-plan.md +1 -1
- package/dist/skill/references/google-ads/pmax-api.md +1 -1
- package/dist/skill/references/google-ads/rules/google-ads-pmax-guide.md +2 -0
- package/dist/skill/scripts/install.ps1 +1 -1
- package/dist/skill/scripts/install.sh +1 -1
- package/package.json +1 -1
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.29-beta.
|
|
54
|
+
> **注意**:当前为测试版(1.1.29-beta.17),供内部测试使用。正式发布后安装命令将改为 `npm install -g siluzan-tso-cli`。
|
|
55
55
|
|
|
56
56
|
| 助手 | 建议 `--ai` |
|
|
57
57
|
| ----------------------- | ------------------------------------ |
|
package/dist/index.js
CHANGED
|
@@ -4278,6 +4278,65 @@ var init_balance = __esm({
|
|
|
4278
4278
|
}
|
|
4279
4279
|
});
|
|
4280
4280
|
|
|
4281
|
+
// src/utils/media-account-batch.ts
|
|
4282
|
+
function mediaAccountDataBatchSize(media) {
|
|
4283
|
+
switch (media) {
|
|
4284
|
+
case "BingV2":
|
|
4285
|
+
case "TikTok":
|
|
4286
|
+
case "Yandex":
|
|
4287
|
+
return 10;
|
|
4288
|
+
case "Kwai":
|
|
4289
|
+
return 20;
|
|
4290
|
+
default:
|
|
4291
|
+
return 100;
|
|
4292
|
+
}
|
|
4293
|
+
}
|
|
4294
|
+
async function fetchBalanceAndOverviewSequential(opts) {
|
|
4295
|
+
const { media, accountIds, config, startDate, endDate, verbose } = opts;
|
|
4296
|
+
const balanceMap = /* @__PURE__ */ new Map();
|
|
4297
|
+
const overviewMap = /* @__PURE__ */ new Map();
|
|
4298
|
+
if (accountIds.length === 0) {
|
|
4299
|
+
return { balanceMap, overviewMap, chunkSize: mediaAccountDataBatchSize(media), chunkCount: 0 };
|
|
4300
|
+
}
|
|
4301
|
+
const chunkSize = mediaAccountDataBatchSize(media);
|
|
4302
|
+
const chunks = [];
|
|
4303
|
+
for (let i = 0; i < accountIds.length; i += chunkSize) {
|
|
4304
|
+
chunks.push(accountIds.slice(i, i + chunkSize));
|
|
4305
|
+
}
|
|
4306
|
+
for (let chunkIdx = 0; chunkIdx < chunks.length; chunkIdx++) {
|
|
4307
|
+
const ids = chunks[chunkIdx];
|
|
4308
|
+
const oneBased = chunkIdx + 1;
|
|
4309
|
+
opts.onChunkStart?.(oneBased, chunks.length, ids);
|
|
4310
|
+
const overviewChunk = await fetchOverviewMap(
|
|
4311
|
+
media,
|
|
4312
|
+
ids,
|
|
4313
|
+
config,
|
|
4314
|
+
startDate,
|
|
4315
|
+
endDate,
|
|
4316
|
+
verbose
|
|
4317
|
+
);
|
|
4318
|
+
opts.onOverviewDone?.(oneBased, chunks.length, ids, overviewChunk.size);
|
|
4319
|
+
for (const [k, v] of overviewChunk) overviewMap.set(k, v);
|
|
4320
|
+
const balanceChunk = await fetchBalanceMap(
|
|
4321
|
+
media,
|
|
4322
|
+
ids,
|
|
4323
|
+
config,
|
|
4324
|
+
startDate,
|
|
4325
|
+
endDate,
|
|
4326
|
+
verbose
|
|
4327
|
+
);
|
|
4328
|
+
opts.onBalanceDone?.(oneBased, chunks.length, ids, balanceChunk.size);
|
|
4329
|
+
for (const [k, v] of balanceChunk) balanceMap.set(k, v);
|
|
4330
|
+
}
|
|
4331
|
+
return { balanceMap, overviewMap, chunkSize, chunkCount: chunks.length };
|
|
4332
|
+
}
|
|
4333
|
+
var init_media_account_batch = __esm({
|
|
4334
|
+
"src/utils/media-account-batch.ts"() {
|
|
4335
|
+
"use strict";
|
|
4336
|
+
init_balance();
|
|
4337
|
+
}
|
|
4338
|
+
});
|
|
4339
|
+
|
|
4281
4340
|
// src/commands/accounts-digest/list-fetch.ts
|
|
4282
4341
|
async function fetchAccountsByMedia(media, config, opts) {
|
|
4283
4342
|
const cfg = DIGEST_PLATFORM_CONFIG[media];
|
|
@@ -4426,30 +4485,47 @@ async function runAccountsDigest(opts) {
|
|
|
4426
4485
|
}
|
|
4427
4486
|
}
|
|
4428
4487
|
const validIds = accountsList.filter((it) => it.ma?.mediaCustomerId && !it.ma?.invalidOAuthToken).map((it) => String(it.ma.mediaCustomerId));
|
|
4429
|
-
|
|
4430
|
-
|
|
4431
|
-
|
|
4432
|
-
|
|
4433
|
-
const overviewMap = /* @__PURE__ */ new Map();
|
|
4434
|
-
if (chunks.length > 0) {
|
|
4488
|
+
let balanceMap = /* @__PURE__ */ new Map();
|
|
4489
|
+
let overviewMap = /* @__PURE__ */ new Map();
|
|
4490
|
+
let batchChunkSize = null;
|
|
4491
|
+
if (validIds.length > 0) {
|
|
4435
4492
|
process.stderr.write(
|
|
4436
|
-
`\u23F3 \
|
|
4493
|
+
`\u23F3 [accounts-digest] \u6709\u6548\u8D26\u6237 ${validIds.length} \u4E2A\uFF1B\u4E32\u884C\u62C9\u53D6\u6D88\u8017 \u2192 \u4F59\u989D\uFF08\u4E0E Web \u8D26\u6237\u5217\u8868\u5206\u6279\u53E3\u5F84\u4E00\u81F4\uFF09\u3002
|
|
4437
4494
|
`
|
|
4438
4495
|
);
|
|
4439
|
-
const
|
|
4440
|
-
|
|
4441
|
-
|
|
4442
|
-
|
|
4443
|
-
|
|
4444
|
-
|
|
4445
|
-
|
|
4446
|
-
|
|
4447
|
-
|
|
4448
|
-
|
|
4449
|
-
|
|
4450
|
-
|
|
4451
|
-
|
|
4452
|
-
|
|
4496
|
+
const fetched = await fetchBalanceAndOverviewSequential({
|
|
4497
|
+
media,
|
|
4498
|
+
accountIds: validIds,
|
|
4499
|
+
config,
|
|
4500
|
+
startDate: opts.startDate,
|
|
4501
|
+
endDate: opts.endDate,
|
|
4502
|
+
verbose: opts.verbose,
|
|
4503
|
+
onChunkStart: (chunkIdx, totalChunks, ids) => {
|
|
4504
|
+
process.stderr.write(
|
|
4505
|
+
` \u2192 [\u6D88\u8017] \u7B2C ${chunkIdx}/${totalChunks} \u6279\uFF08${ids.length} \u6237\uFF09\u2026
|
|
4506
|
+
`
|
|
4507
|
+
);
|
|
4508
|
+
},
|
|
4509
|
+
onOverviewDone: (chunkIdx, totalChunks, ids, size) => {
|
|
4510
|
+
process.stderr.write(
|
|
4511
|
+
` \u2713 [\u6D88\u8017] \u7B2C ${chunkIdx}/${totalChunks} \u6279\u5B8C\u6210\uFF08${ids.length} \u6237 \u2192 ${size} \u6761\uFF09
|
|
4512
|
+
`
|
|
4513
|
+
);
|
|
4514
|
+
process.stderr.write(
|
|
4515
|
+
` \u2192 [\u4F59\u989D] \u7B2C ${chunkIdx}/${totalChunks} \u6279\uFF08${ids.length} \u6237\uFF09\u2026
|
|
4516
|
+
`
|
|
4517
|
+
);
|
|
4518
|
+
},
|
|
4519
|
+
onBalanceDone: (chunkIdx, totalChunks, ids, size) => {
|
|
4520
|
+
process.stderr.write(
|
|
4521
|
+
` \u2713 [\u4F59\u989D] \u7B2C ${chunkIdx}/${totalChunks} \u6279\u5B8C\u6210\uFF08${ids.length} \u6237 \u2192 ${size} \u6761\uFF09
|
|
4522
|
+
`
|
|
4523
|
+
);
|
|
4524
|
+
}
|
|
4525
|
+
});
|
|
4526
|
+
balanceMap = fetched.balanceMap;
|
|
4527
|
+
overviewMap = fetched.overviewMap;
|
|
4528
|
+
batchChunkSize = fetched.chunkSize;
|
|
4453
4529
|
}
|
|
4454
4530
|
const rows = [];
|
|
4455
4531
|
for (const item of accountsList) {
|
|
@@ -4505,6 +4581,7 @@ async function runAccountsDigest(opts) {
|
|
|
4505
4581
|
/** 取数策略:list = 全量翻清单后过滤,subset = 跳过翻页直接对 -a 指定的 ID 拉数据 */
|
|
4506
4582
|
source: scannedFromList ? "list" : "subset",
|
|
4507
4583
|
returned: rows.length,
|
|
4584
|
+
batchChunkSize: validIds.length > 0 ? batchChunkSize : null,
|
|
4508
4585
|
totals: {
|
|
4509
4586
|
spend: +totals.spend.toFixed(2),
|
|
4510
4587
|
clicks: totals.clicks,
|
|
@@ -4581,6 +4658,7 @@ var init_run = __esm({
|
|
|
4581
4658
|
init_balance();
|
|
4582
4659
|
init_cli_table();
|
|
4583
4660
|
init_currency_display();
|
|
4661
|
+
init_media_account_batch();
|
|
4584
4662
|
init_list_fetch();
|
|
4585
4663
|
}
|
|
4586
4664
|
});
|
|
@@ -105251,6 +105329,7 @@ init_cli_json_snapshot();
|
|
|
105251
105329
|
init_balance();
|
|
105252
105330
|
init_cli_table();
|
|
105253
105331
|
init_currency_display();
|
|
105332
|
+
init_media_account_batch();
|
|
105254
105333
|
|
|
105255
105334
|
// src/commands/balance-scan/list-fetch.ts
|
|
105256
105335
|
init_auth();
|
|
@@ -105394,18 +105473,6 @@ async function fetchAllAccountPages(media, pageSize, maxPages, config, verbose)
|
|
|
105394
105473
|
}
|
|
105395
105474
|
|
|
105396
105475
|
// src/commands/balance-scan/run.ts
|
|
105397
|
-
function balanceScanChunkSize(media) {
|
|
105398
|
-
switch (media) {
|
|
105399
|
-
case "BingV2":
|
|
105400
|
-
case "TikTok":
|
|
105401
|
-
case "Yandex":
|
|
105402
|
-
return 10;
|
|
105403
|
-
case "Kwai":
|
|
105404
|
-
return 20;
|
|
105405
|
-
default:
|
|
105406
|
-
return 100;
|
|
105407
|
-
}
|
|
105408
|
-
}
|
|
105409
105476
|
async function runBalanceScan(opts) {
|
|
105410
105477
|
let config = loadConfig(opts.token);
|
|
105411
105478
|
if (opts.refreshDp) {
|
|
@@ -105463,53 +105530,44 @@ async function runBalanceScan(opts) {
|
|
|
105463
105530
|
}
|
|
105464
105531
|
let balanceMap = /* @__PURE__ */ new Map();
|
|
105465
105532
|
let overviewMap = /* @__PURE__ */ new Map();
|
|
105533
|
+
let batchChunkSize = null;
|
|
105466
105534
|
if (validIds.length > 0) {
|
|
105467
|
-
const chunkSize =
|
|
105468
|
-
const chunks = [];
|
|
105469
|
-
for (let i = 0; i < validIds.length; i += chunkSize) {
|
|
105470
|
-
chunks.push(validIds.slice(i, i + chunkSize));
|
|
105471
|
-
}
|
|
105535
|
+
const chunkSize = mediaAccountDataBatchSize(media);
|
|
105472
105536
|
process.stderr.write(
|
|
105473
|
-
`\u23F3 [balance-scan] \u6709\u6548\u8D26\u6237 ${validIds.length} \u4E2A\
|
|
105537
|
+
`\u23F3 [balance-scan] \u6709\u6548\u8D26\u6237 ${validIds.length} \u4E2A\uFF1B\u6309 Web \u8D26\u6237\u5217\u8868\u53E3\u5F84\u4E32\u884C\u62C9\u53D6\u6D88\u8017 \u2192 \u4F59\u989D\uFF08\u6BCF\u6279 \u2264${chunkSize} \u6237\uFF0C\u5355\u8BF7\u6C42\u6700\u957F\u7EA6 10 \u5206\u949F\uFF09\u3002
|
|
105474
105538
|
`
|
|
105475
105539
|
);
|
|
105476
|
-
|
|
105477
|
-
|
|
105478
|
-
|
|
105479
|
-
|
|
105540
|
+
const fetched = await fetchBalanceAndOverviewSequential({
|
|
105541
|
+
media,
|
|
105542
|
+
accountIds: validIds,
|
|
105543
|
+
config,
|
|
105544
|
+
verbose: opts.verbose,
|
|
105545
|
+
onChunkStart: (chunkIdx, totalChunks, ids) => {
|
|
105546
|
+
process.stderr.write(
|
|
105547
|
+
` \u2192 [\u8FD17\u65E5\u6D88\u8017] \u7B2C ${chunkIdx}/${totalChunks} \u6279\u8BF7\u6C42\u4E2D\uFF08${ids.length} \u6237\uFF09\u2026
|
|
105480
105548
|
`
|
|
105481
|
-
|
|
105482
|
-
|
|
105483
|
-
|
|
105484
|
-
|
|
105485
|
-
|
|
105486
|
-
void 0,
|
|
105487
|
-
void 0,
|
|
105488
|
-
opts.verbose
|
|
105489
|
-
);
|
|
105490
|
-
process.stderr.write(
|
|
105491
|
-
` \u2713 [\u8FD17\u65E5\u6D88\u8017] \u7B2C ${chunkIdx + 1}/${chunks.length} \u6279\u5B8C\u6210\uFF08${ids.length} \u6237 \u2192 ${overviewChunk.size} \u6761\uFF09
|
|
105549
|
+
);
|
|
105550
|
+
},
|
|
105551
|
+
onOverviewDone: (chunkIdx, totalChunks, ids, size) => {
|
|
105552
|
+
process.stderr.write(
|
|
105553
|
+
` \u2713 [\u8FD17\u65E5\u6D88\u8017] \u7B2C ${chunkIdx}/${totalChunks} \u6279\u5B8C\u6210\uFF08${ids.length} \u6237 \u2192 ${size} \u6761\uFF09
|
|
105492
105554
|
`
|
|
105493
|
-
|
|
105494
|
-
|
|
105495
|
-
|
|
105496
|
-
` \u2192 [\u4F59\u989D] \u7B2C ${chunkIdx + 1}/${chunks.length} \u6279\u8BF7\u6C42\u4E2D\uFF08${ids.length} \u6237\uFF09\u2026
|
|
105555
|
+
);
|
|
105556
|
+
process.stderr.write(
|
|
105557
|
+
` \u2192 [\u4F59\u989D] \u7B2C ${chunkIdx}/${totalChunks} \u6279\u8BF7\u6C42\u4E2D\uFF08${ids.length} \u6237\uFF09\u2026
|
|
105497
105558
|
`
|
|
105498
|
-
|
|
105499
|
-
|
|
105500
|
-
|
|
105501
|
-
|
|
105502
|
-
|
|
105503
|
-
void 0,
|
|
105504
|
-
void 0,
|
|
105505
|
-
opts.verbose
|
|
105506
|
-
);
|
|
105507
|
-
process.stderr.write(
|
|
105508
|
-
` \u2713 [\u4F59\u989D] \u7B2C ${chunkIdx + 1}/${chunks.length} \u6279\u5B8C\u6210\uFF08${ids.length} \u6237 \u2192 ${balanceChunk.size} \u6761\uFF09
|
|
105559
|
+
);
|
|
105560
|
+
},
|
|
105561
|
+
onBalanceDone: (chunkIdx, totalChunks, ids, size) => {
|
|
105562
|
+
process.stderr.write(
|
|
105563
|
+
` \u2713 [\u4F59\u989D] \u7B2C ${chunkIdx}/${totalChunks} \u6279\u5B8C\u6210\uFF08${ids.length} \u6237 \u2192 ${size} \u6761\uFF09
|
|
105509
105564
|
`
|
|
105510
|
-
|
|
105511
|
-
|
|
105512
|
-
}
|
|
105565
|
+
);
|
|
105566
|
+
}
|
|
105567
|
+
});
|
|
105568
|
+
balanceMap = fetched.balanceMap;
|
|
105569
|
+
overviewMap = fetched.overviewMap;
|
|
105570
|
+
batchChunkSize = fetched.chunkSize;
|
|
105513
105571
|
process.stderr.write(`\u23F3 [balance-scan] \u4F59\u989D\u4E0E\u6D88\u8017\u5DF2\u9F50\uFF0C\u6B63\u5728\u6309\u9608\u503C\u7B5B\u9009\u2026
|
|
105514
105572
|
`);
|
|
105515
105573
|
}
|
|
@@ -105568,7 +105626,7 @@ async function runBalanceScan(opts) {
|
|
|
105568
105626
|
},
|
|
105569
105627
|
pageSize,
|
|
105570
105628
|
maxPages,
|
|
105571
|
-
batchChunkSize: validIds.length > 0 ?
|
|
105629
|
+
batchChunkSize: validIds.length > 0 ? batchChunkSize : null,
|
|
105572
105630
|
totalReported: total ?? null,
|
|
105573
105631
|
generatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
105574
105632
|
};
|
package/dist/skill/_meta.json
CHANGED
|
@@ -12,7 +12,9 @@
|
|
|
12
12
|
"本地视频:填 videoPath(或别名 video);创建后 CLI 自动 PyAPI 上传并链接,与 --json-out 无关",
|
|
13
13
|
"提交前:ad pmax-validate → 用户确认 → ad pmax-create",
|
|
14
14
|
"campaignExtensions 可选:创建成功后 CLI 自动挂 callout/snippet/leadForm(非 pmax POST body)",
|
|
15
|
-
"
|
|
15
|
+
"Lead Gen/B2B 询盘方案:默认保留 campaignExtensions.leadForm(勿只写 callouts/snippets);用户明确不要表单才删 leadForm",
|
|
16
|
+
"方案 Markdown 须单列「潜在客户表单」节(标题/描述/字段/privacyPolicyUrl);与 JSON 一致",
|
|
17
|
+
"不需要任何附加资产时删除整个 campaignExtensions 块"
|
|
16
18
|
]
|
|
17
19
|
},
|
|
18
20
|
|
|
@@ -18,11 +18,25 @@
|
|
|
18
18
|
| 图片 | **只填 `imagePaths`** 指向本地 PNG/JPEG;`pmax-create` 会自动 multipart 上传并用 assetId 创建(勿把 Base64 提交进 Git) |
|
|
19
19
|
| 视频 | JSON 填 **`videoPath`**(别名 `video` 亦可);`pmax-create` 成功后 **必定**经 PyAPI 上传并链接(含 `--json-out`)。已有 YouTube 用 `youtubeUrlOrId` |
|
|
20
20
|
| 附加资产 | 可选填 **`campaignExtensions`** |
|
|
21
|
+
| Lead Gen 方案 | **默认**在 `campaignExtensions` 含 **`leadForm`**(B2B/询盘/留资);仅 callouts/snippets 不算完整方案;用户明确不要才省略 |
|
|
22
|
+
| 存量补表单 | 活动已创建时用 `ad extension lead-form`(见 `pmax-lead-form-template.md` 方式 B) |
|
|
21
23
|
| 改已上线 PMax | 先 `ad pmax-get` 看 `_brandGuidelinesActive`;改品牌见 `pmax-api.md` § Brand Guidelines |
|
|
22
24
|
| 列表复核 | `ad campaigns -a <id> --json-out ./snap`,`channelTypeV2` 应为 `PERFORMANCE_MAX` |
|
|
23
25
|
|
|
24
26
|
---
|
|
25
27
|
|
|
28
|
+
## 方案生成(Agent 出投放方案时)
|
|
29
|
+
|
|
30
|
+
出 PMax **投放方案**(Markdown + JSON)时,除标题/描述/图片外,**Lead Gen / B2B 场景默认包含潜在客户表单**:
|
|
31
|
+
|
|
32
|
+
1. JSON:`campaignExtensions.leadForm` 字段结构与 `pmax-lead-form-template.json` 的 `leadForm` 相同(`businessName`、`headline`、`description`、`privacyPolicyUrl`、`finalUrl`、`fields`)。
|
|
33
|
+
2. Markdown:单独一节 **「潜在客户表单」**,列出表单标题、描述、收集字段、隐私政策 URL;**不得**只在 JSON 里写而方案正文遗漏。
|
|
34
|
+
3. `privacyPolicyUrl`:从落地页站点找 `/privacy`、`/terms` 等;找不到时向用户确认,**禁止**编造 URL。
|
|
35
|
+
4. 用户说「不要表单 / 仅品牌曝光 / 纯电商 Shopping」→ 可省略 `leadForm` 并在方案中说明原因。
|
|
36
|
+
5. `pmax-validate` 会校验 `leadForm`;创建后 `--json-out` 的 `campaignExtensions.leadForm` 段含 `ok` / `error`。
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
26
40
|
## 推荐命令顺序
|
|
27
41
|
|
|
28
42
|
```bash
|
|
@@ -4,9 +4,10 @@
|
|
|
4
4
|
"doc": "pmax-lead-form-template.md",
|
|
5
5
|
"note": "以 _ 开头的键仅作说明,CLI 提交前会剥离",
|
|
6
6
|
"agentPitfalls": [
|
|
7
|
-
"
|
|
8
|
-
"
|
|
9
|
-
"
|
|
7
|
+
"新建 PMax(Lead Gen/B2B):优先 campaignExtensions.leadForm 随 pmax-create 一次挂载(见 pmax-create-template.json)",
|
|
8
|
+
"存量补挂:用本文件 + ad extension lead-form(须已有 campaignId)",
|
|
9
|
+
"LEAD_FORM 仅 Campaign 级;每活动通常 1 个;补挂前 ad extension list --type LEAD_FORM --campaign-id 检查",
|
|
10
|
+
"privacyPolicyUrl 须为 https 可访问页(可从官网 footer 找 privacy/terms)",
|
|
10
11
|
"账户须在 Google Ads UI 接受 Lead Form ToS",
|
|
11
12
|
"WhatsApp 见 pmax-whatsapp-template.json 或 campaignExtensions.businessMessage"
|
|
12
13
|
]
|
|
@@ -1,8 +1,15 @@
|
|
|
1
1
|
# `ad extension lead-form` JSON 配置说明
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
PMax **潜在客户表单**(`LEAD_FORM`)有两种挂载方式:
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
| 方式 | 何时用 | 入口 |
|
|
6
|
+
| ---- | ------ | ---- |
|
|
7
|
+
| **A(推荐·新建活动)** | `pmax-create` 一次性创建系列 + 扩展 | `pmax-create` JSON 的 `campaignExtensions.leadForm`(见 `pmax-create-template.json`) |
|
|
8
|
+
| **B(存量补挂)** | 活动已存在、需单独加/改表单 | 本文件 + `ad extension lead-form` |
|
|
9
|
+
|
|
10
|
+
**Lead Gen / B2B 询盘类 PMax 方案**:默认在 `campaignExtensions` 中包含 `leadForm`;用户明确不要表单时才省略。
|
|
11
|
+
|
|
12
|
+
模板 JSON:同目录 [`pmax-lead-form-template.json`](pmax-lead-form-template.json)(方式 B 用;方式 A 只取其中的 `leadForm` 对象嵌入 `pmax-create` JSON)。
|
|
6
13
|
|
|
7
14
|
---
|
|
8
15
|
|
|
@@ -59,9 +59,17 @@
|
|
|
59
59
|
- **必读**:方案与门禁 `google-ads/google-ads-campaign-plan.md` + **`assets/campaign-create-template.json`**(先 Read)+ `assets/campaign-create-template.md`;命令参数 `google-ads/google-ads.md`;PMax 加 **`assets/pmax-create-template.json`** + `assets/pmax-create-template.md` + `google-ads/pmax-api.md`。
|
|
60
60
|
- **创建路径选择**:
|
|
61
61
|
- 已有 AI 智投草稿 → 走 **W4**。
|
|
62
|
-
-
|
|
63
|
-
-
|
|
64
|
-
-
|
|
62
|
+
- **PMax 出方案/创建** → **`assets/pmax-create-template.json`**(先 Read)+ `pmax-create-template.md` + `pmax-api.md`:`pmax-validate` → 用户确认 → `pmax-create`(**勿**用 Search `campaign-create`)。
|
|
63
|
+
- 搜索系列出方案 → `google-ads-campaign-plan.md`:JSON → `campaign-validate` → 用户确认 → `campaign-create`。
|
|
64
|
+
- 已有完整结构化 JSON → 对应 validate → create。
|
|
65
|
+
- **步骤(PMax 方案 → 创建)**:
|
|
66
|
+
1. 账户:`list-accounts -m Google -k <id>`;落地页与品牌从官网/RAG 归纳。
|
|
67
|
+
2. 地域/语言:`ad geo search` 取 location id;语言 id 写入 JSON。
|
|
68
|
+
3. 复制 `pmax-create-template.json` 填文案/预算/图片;**Lead Gen/B2B 默认** `campaignExtensions.leadForm`(方案 Markdown 须单列表单节)。
|
|
69
|
+
4. 门禁:`ad pmax-validate --config-file ./pmax.json --json-out ./snap-pmax`。
|
|
70
|
+
5. 输出 JSON + Markdown 方案 → 用户确认 → `ad pmax-create --commit "…"`。
|
|
71
|
+
6. 复核:`ad campaigns` / `ad pmax-get`;缺表单时 `ad extension lead-form` 补挂。
|
|
72
|
+
- **步骤(Search 一体化创建)**:
|
|
65
73
|
1. 地域 ID:`ad geo search -a <id> -q "United States"` 写入 `campaign.targetedLocations[].id`。
|
|
66
74
|
2. 门禁:`ad campaign-validate --config-file ./campaign.json`(必跑)。
|
|
67
75
|
3. 用户确认后创建:`ad campaign-create --config-file ./campaign.json`,记录返回 taskId。
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
- 可执行真相只有 **JSON**(`assets/campaign-create-template.json` 同构);Markdown 只读投影。
|
|
15
15
|
- **Agent Read 顺序(建系列前必做)**:① `assets/campaign-create-template.json`(复制/改写的结构真相源)→ ② `assets/campaign-create-template.md`(字段说明与踩坑)。**禁止**只读 `.md` 凭印象拼 JSON。
|
|
16
16
|
- 改需求 **只改 JSON**,再 `campaign-validate`,再刷新 Markdown。
|
|
17
|
-
- **PMax 系列创建**走独立流水线(勿用本文件 JSON
|
|
17
|
+
- **PMax 系列创建**走独立流水线(勿用本文件 JSON 模板):**先 Read `assets/pmax-create-template.json`** + `assets/pmax-create-template.md` + `ad pmax-validate` / `ad pmax-create`;**Lead Gen/B2B 方案默认含 `campaignExtensions.leadForm`**(方案 Markdown 须单列表单);运营诊断见 `google-ads/rules/google-ads-pmax-guide.md`。
|
|
18
18
|
- 搜索网络:仅 Google 搜索(`TargetSearchNetwork`/`TargetContentNetwork`/`TargetPartnerSearchNetwork` 均为 false)。
|
|
19
19
|
|
|
20
20
|
---
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
|
|
42
42
|
网关 `POST .../campaign/pmax` **不**接受扩展字段;CLI 通过 **`campaignExtensions`** 在 `pmax-create` 成功后**自动编排** `extensionmanagement`(与手动 `ad extension *` 等价)。
|
|
43
43
|
|
|
44
|
-
**方式 A(推荐,一条命令)**:在 `pmax-create` JSON 中填 `campaignExtensions
|
|
44
|
+
**方式 A(推荐,一条命令)**:在 `pmax-create` JSON 中填 `campaignExtensions`。**Lead Gen / B2B 询盘方案须含 `leadForm`**(勿只写 callouts/snippets;用户明确不要表单才省略):
|
|
45
45
|
|
|
46
46
|
```json
|
|
47
47
|
{
|
|
@@ -122,6 +122,8 @@ Asset Group 是 PMax 中素材与受众信号的组合单元。**推荐分组维
|
|
|
122
122
|
素材组 C: 通用品牌认知 → 首页 + 广泛受众信号
|
|
123
123
|
```
|
|
124
124
|
|
|
125
|
+
**新建活动(CLI)**:方案 JSON 默认含 `campaignExtensions.leadForm`(潜在客户表单),与 callouts/snippets 一并由 `pmax-create` 挂载;字段见 `assets/pmax-create-template.md` § 方案生成。存量活动补挂见 `assets/pmax-lead-form-template.md`。
|
|
126
|
+
|
|
125
127
|
### 3.5 每个素材组的完整性要求
|
|
126
128
|
|
|
127
129
|
每个素材组必须包含**完整素材套件**:长标题 × 5、短标题 × 5、描述 × 5;横版图 (1.91:1) × 4+、方形图 (1:1) × 4+、竖版图 (4:5) × 2+;视频 × 1+(建议 3 个);商家名称、Logo、最终网址、行动号召。
|
|
@@ -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.29-beta.
|
|
12
|
+
$PKG_VERSION = '1.1.29-beta.17'
|
|
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.29-beta.
|
|
12
|
+
readonly PKG_VERSION="1.1.29-beta.17"
|
|
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"
|