@bluevs/ttcli 0.0.3 → 0.0.5
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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bluevs/ttcli",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.5",
|
|
4
4
|
"description": "TikTok CLI - designed for AI Agent and developers",
|
|
5
5
|
"bin": {
|
|
6
6
|
"ttcli": "scripts/run.js"
|
|
@@ -9,12 +9,12 @@
|
|
|
9
9
|
"postinstall": "node -e \"console.log('\\n📦 ttcli installed. To install the openclaw skill, run:\\n bash $(npm root -g)/@bluevs/ttcli/skills/ttcli/scripts/install-skill.sh\\n')\""
|
|
10
10
|
},
|
|
11
11
|
"optionalDependencies": {
|
|
12
|
-
"@bluevs/ttcli-darwin-arm64": "0.0.
|
|
13
|
-
"@bluevs/ttcli-darwin-x64": "0.0.
|
|
14
|
-
"@bluevs/ttcli-linux-arm64": "0.0.
|
|
15
|
-
"@bluevs/ttcli-linux-x64": "0.0.
|
|
16
|
-
"@bluevs/ttcli-win32-arm64": "0.0.
|
|
17
|
-
"@bluevs/ttcli-win32-x64": "0.0.
|
|
12
|
+
"@bluevs/ttcli-darwin-arm64": "0.0.5",
|
|
13
|
+
"@bluevs/ttcli-darwin-x64": "0.0.5",
|
|
14
|
+
"@bluevs/ttcli-linux-arm64": "0.0.5",
|
|
15
|
+
"@bluevs/ttcli-linux-x64": "0.0.5",
|
|
16
|
+
"@bluevs/ttcli-win32-arm64": "0.0.5",
|
|
17
|
+
"@bluevs/ttcli-win32-x64": "0.0.5"
|
|
18
18
|
},
|
|
19
19
|
"engines": {
|
|
20
20
|
"node": ">=16"
|
package/skills/ttcli/SKILL.md
CHANGED
|
@@ -1,14 +1,48 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: ttcli
|
|
3
|
+
version: 0.0.5
|
|
3
4
|
description: 直接调用 TikTok Marketing API(access token 鉴权,不经 VH 平台/项目层)。当用户提供原生 TikTok access_token + advertiser_id、要直连 TikTok 创建或查询 Smart+ campaign/adgroup/ad、或查 identity/app/region 等元数据时使用。注意:用户走 VH 项目流程(po_xxxx + account_id)的 TikTok 创编应改走 vhcli tiktok。
|
|
4
5
|
---
|
|
5
6
|
|
|
6
7
|
# TikTok CLI (`ttcli`)
|
|
7
8
|
|
|
9
|
+
> **Skill version**: `0.0.5`
|
|
10
|
+
>
|
|
11
|
+
> 当用户问"ttcli skill 版本是多少 / skill 装好了吗 / 升级生效了没" 时,**直接念出上面的版本号**。
|
|
12
|
+
> 用户期望更高版本但没看到 → 让他们:(1) 重跑 `npm install -g @bluevs/ttcli && bash $(npm root -g)/@bluevs/ttcli/skills/ttcli/scripts/install-skill.sh` (2) 然后**新开一个会话**(skill 在会话启动时加载,老会话不刷新)。
|
|
13
|
+
|
|
8
14
|
`ttcli` wraps TikTok Marketing API (`business-api.tiktok.com`) as a CLI optimized for AI agents. JSON output by default; the `--human` flag exists for project-owner debugging only — agents should never pass it.
|
|
9
15
|
|
|
10
16
|
This skill is intentionally short. Always treat `ttcli -h` and `ttcli <subcommand> -h` as the primary reference for flags.
|
|
11
17
|
|
|
18
|
+
## 🚨 与用户对话的硬性约定:错误信息要翻译
|
|
19
|
+
|
|
20
|
+
ttcli 和 TikTok 返回的报错都是技术语言(`missing required fields: [adgroup_id]`、`Invalid optimization goal`、`code=40002 ...`)。**严禁原样把这些贴给用户**——非技术用户看到字段名、code 数字会懵。
|
|
21
|
+
|
|
22
|
+
**做法**:把每个技术错误翻译成业务语言后回给用户,**保留 request_id 仅供你自己排查**。
|
|
23
|
+
|
|
24
|
+
参考翻译表(不全,按这个方式扩展):
|
|
25
|
+
|
|
26
|
+
| 技术错误(你看到的) | 翻给用户(你要说的) |
|
|
27
|
+
|---|---|
|
|
28
|
+
| `missing required fields: [adgroup_id]` | "我还需要知道把广告挂在哪个广告组下,能告诉我广告组 ID 或名字吗?" |
|
|
29
|
+
| `missing required fields: [campaign_name]` | "你这条 campaign 想叫什么名字?" |
|
|
30
|
+
| `code=40105 Access token is incorrect or has been revoked` | "你的授权过期或被撤销了,请去 TikTok Marketing 后台重新生成 access token 给我" |
|
|
31
|
+
| `code=40001 No permission to operate advertiser` | "当前 token 没权限管这个广告主,你的 token 对应的广告主 ID 可能不一样" |
|
|
32
|
+
| `code=40002 placement_type only supports PLACEMENT_TYPE_NORMAL` | "小游戏只能选'手动版位',我帮你设好" |
|
|
33
|
+
| `code=40002 Invalid optimization goal. Please change your goal or promotion type` | "这个推广类型不支持你说的优化目标,小游戏一般用 ROAS(投产比)优化,要换吗?" |
|
|
34
|
+
| `code=40002 This ad does not support infinite budget mode` | "这种广告必须有日预算,不能无限。我帮你设个最低日预算" |
|
|
35
|
+
| `code=40002 Allowlist not enabled` | "这个高级功能你账户没开通白名单,要联系 TikTok 客户经理开通;我们换一种简单方案吧" |
|
|
36
|
+
| `code=40002 Something went wrong. Contact support` | "TikTok 后台报了个不明错误。我先把请求 ID 记下来:`<request_id>`,必要时你拿这个找 TikTok 技术支持。我们试另一种配置看能不能绕过" |
|
|
37
|
+
| `body is not a JSON object` / 客户端 validation | 不要暴露 JSON / body 概念。直接说"我组装的请求格式有问题,让我重做"——然后自己重组 |
|
|
38
|
+
| 网络层错误(EOF、context deadline)| "网络抖了一下,我重试一次" |
|
|
39
|
+
|
|
40
|
+
**原则**:
|
|
41
|
+
- 用户看到的应该是"接下来你/我做什么",不是"什么字段错了"
|
|
42
|
+
- 翻译要保留**关键判断点**(让用户能给出对的输入),不能含糊到"出错了"
|
|
43
|
+
- 多个字段同时缺 → 合并成一个自然问句,不要逐个列字段名
|
|
44
|
+
- request_id 留你脑子里 / 日志里,**不主动甩给用户**,除非用户明确要"找 TikTok 提工单"
|
|
45
|
+
|
|
12
46
|
## ttcli vs vhcli tiktok — 选哪一个
|
|
13
47
|
|
|
14
48
|
openclaw 同时装了两个 TikTok 广告工具,触发条件不同:
|
|
@@ -68,14 +102,21 @@ Before creating a campaign or adgroup, gather the IDs you'll reference:
|
|
|
68
102
|
|
|
69
103
|
```
|
|
70
104
|
ttcli auth show # confirm credentials loaded + their source
|
|
105
|
+
ttcli bc list # → bc_id (Business Centers; needed for BC_AUTH_TT identities)
|
|
71
106
|
ttcli app list # → app_id (Android/iOS apps)
|
|
72
107
|
ttcli minis list # → minis_id (TikTok Minis: MINI_GAME / MINI_SERIES)
|
|
73
108
|
ttcli identity list # → identity_id (Spark Ads publishing identity)
|
|
74
109
|
ttcli smart-plus campaign list # → existing campaign_id (or you'll create one)
|
|
110
|
+
ttcli smart-plus adgroup list # → existing adgroup_id (filter by --campaign-ids / --promotion-type)
|
|
111
|
+
ttcli smart-plus ad list # → existing smart_plus_ad_id + creative_list (only explicitly-selected creatives)
|
|
75
112
|
ttcli tool region \
|
|
76
113
|
--placements=PLACEMENT_TIKTOK \
|
|
77
114
|
--objective-type=APP_PROMOTION \
|
|
78
115
|
--level-range=TO_COUNTRY # → location_id for targeting_spec.location_ids
|
|
116
|
+
ttcli tool targeting search \
|
|
117
|
+
--objective-type=APP_PROMOTION --promotion-type=APP_ANDROID \
|
|
118
|
+
--placements=PLACEMENT_TIKTOK --search-type=FUZZY_SEARCH \
|
|
119
|
+
--keywords=Kentucky # → geo_id by name (location_ids OR zipcode_ids)
|
|
79
120
|
```
|
|
80
121
|
|
|
81
122
|
`tool region` accepts `--language=zh-CN` (or any of 78 supported codes) for localized region names — useful when surfacing options to the user.
|
|
@@ -272,7 +313,9 @@ Per the doc: "对于现有广告账号,当广告组的投放广告位为自动
|
|
|
272
313
|
| `tiktok_item_id` (Spark Ads) | `/tt_video/info/` | `ttcli tt-video info --auth-code=...` |
|
|
273
314
|
| List posts under an identity | `/identity/video/get/` | `ttcli identity video list --identity-id=... --identity-type=...` |
|
|
274
315
|
| Verify `identity_id` | `/identity/info/` | `ttcli identity info --identity-id=... --identity-type=...` |
|
|
275
|
-
|
|
|
316
|
+
| Search ad-library videos (`video_id`) | `/file/video/ad/search/` | `ttcli file video search` |
|
|
317
|
+
| Search ad-library images (`image_id`) | `/file/image/ad/search/` | `ttcli file image search` |
|
|
318
|
+
| Upload video → `video_id` | `/file/video/ad/upload/` | ⏳ not yet wrapped |
|
|
276
319
|
| Upload image → `web_uri` | `/file/image/ad/upload/` | ⏳ not yet wrapped |
|
|
277
320
|
| Music → `music_id` | (separate music API) | ⏳ not yet wrapped |
|
|
278
321
|
|
|
@@ -330,6 +373,65 @@ Common codes:
|
|
|
330
373
|
|
|
331
374
|
The CLI does NOT validate enum values, type correctness, or conditional-required fields. It only checks that unconditionally required fields are present. TikTok is the source of truth for everything else — surface its error messages directly.
|
|
332
375
|
|
|
376
|
+
## TikTok user OAuth (Login Kit / Content Posting API) — separate token type
|
|
377
|
+
|
|
378
|
+
ttcli's main job is Marketing API. But it also wraps OAuth-side endpoints under `tt-user` and `business`:
|
|
379
|
+
|
|
380
|
+
```
|
|
381
|
+
ttcli tt-user token-info \ # inspect a user OAuth token
|
|
382
|
+
--app-id=aw1234abcd5678 \
|
|
383
|
+
--tt-access-token=act.xxxxx
|
|
384
|
+
|
|
385
|
+
ttcli business get \ # account profile + insights
|
|
386
|
+
--business-id=open_id_xxx \ # (= creator_id from tt-user token-info)
|
|
387
|
+
--tt-access-token=act.xxxxx \
|
|
388
|
+
--fields=username,display_name,followers_count,video_views,audience_countries
|
|
389
|
+
```
|
|
390
|
+
|
|
391
|
+
**These are different token types** from the rest of ttcli. The Marketing API token in `auth show` is for ad management (`/smart_plus/...`, `/report/...`). The token here is a **TikTok user OAuth token** from Login Kit / Content Posting API consumer flow — used for posting videos, reading user profile, etc. They are not interchangeable.
|
|
392
|
+
|
|
393
|
+
| Endpoint | Auth model | Required scopes (some) |
|
|
394
|
+
|---|---|---|
|
|
395
|
+
| `/tt_user/token_info/get/` | access_token in JSON body, no header | n/a (introspection) |
|
|
396
|
+
| `/business/get/` | Access-Token header (with user OAuth token) | `user.info.basic`, `user.info.username`, `user.info.profile`, `user.info.stats`, `user.account.type`, `user.insights` |
|
|
397
|
+
|
|
398
|
+
If the user only has a Marketing API token, both will return `code=40105 access_token invalid` — that's the signal to ask for a Login-Kit-flow token instead.
|
|
399
|
+
|
|
400
|
+
**`business get` defaults**: TikTok defaults `fields=["display_name","profile_image"]` — pass `--fields=...` to get more. Daily metrics have **24-48h delay**; same-day data is unavailable. Audience demographic fields require **≥100 followers**.
|
|
401
|
+
|
|
402
|
+
## Reporting (async)
|
|
403
|
+
|
|
404
|
+
`ttcli report task create` posts to `/report/task/create/` and returns a `task_id`. The actual report data is NOT in the response — async reports are three steps:
|
|
405
|
+
|
|
406
|
+
1. **Create task** — `ttcli report task create ...` → `task_id`
|
|
407
|
+
2. **Poll status** — `ttcli report task check --task-id=...` → `status` (QUEUING / PROCESSING / SUCCESS / FAILED / CANCELED). Poll every ~30s; tasks typically take 30s–a few minutes.
|
|
408
|
+
3. **Download** — `ttcli report task download --task-id=...`. Behavior depends on what `--output-format` you used at create:
|
|
409
|
+
- **CSV_STRING** (default at create) → returns raw CSV bytes directly to stdout. AI agents: parse CSV from stdout.
|
|
410
|
+
- **CSV_DOWNLOAD / XLSX_DOWNLOAD** → returns JSON with a 1-hour signed `download_url`. Pass `--out=FILE --fetch` to follow the URL and write the file in one shot.
|
|
411
|
+
|
|
412
|
+
**Bad task_id gotcha**: TikTok returns `code=51010 internal system error` for unknown task_ids on `task check` (not a clean 4xx). Treat 51010 as "task not found / not yours" when polling.
|
|
413
|
+
|
|
414
|
+
**Stdout vs JSON**: `task download` is the one ttcli command where `--human`/JSON modes are ignored on the CSV_STRING branch — the body IS the data, so it's written verbatim to stdout. Other commands honor JSON-by-default.
|
|
415
|
+
|
|
416
|
+
**Async filtering is restricted**: only `campaign_ids` / `adgroup_ids` / `ad_ids` / `country_code` (max 20000 IDs total). For other filters, use sync `/report/integrated/get/` (also not yet wrapped).
|
|
417
|
+
|
|
418
|
+
**Lifetime metrics**: pass `--query-lifetime` to get all-time metrics; ignores `--start-date`/`--end-date`. No time/audience/country breakdowns allowed in lifetime mode.
|
|
419
|
+
|
|
420
|
+
**Recommended**: pass `--enable-title-translation=false` for stable header rows (TikTok periodically updates translated headers — raw field names don't change).
|
|
421
|
+
|
|
422
|
+
```
|
|
423
|
+
ttcli report task create \
|
|
424
|
+
--report-type=BASIC --data-level=AUCTION_AD \
|
|
425
|
+
--dimensions=ad_id,stat_time_day \
|
|
426
|
+
--metrics=spend,impressions,clicks,cpc,ctr \
|
|
427
|
+
--start-date=2026-05-01 --end-date=2026-06-01 \
|
|
428
|
+
--filter-ad-ids=1866239337888050,1866239351340034 \
|
|
429
|
+
--output-format=CSV_DOWNLOAD \
|
|
430
|
+
--enable-title-translation=false
|
|
431
|
+
```
|
|
432
|
+
|
|
433
|
+
Limits: 1 QPS per app; 500 async tasks/hour per advertiser; 30-day task validity.
|
|
434
|
+
|
|
333
435
|
## Credentials
|
|
334
436
|
|
|
335
437
|
Priority: `--flag` > env var > config file (`~/.config/ttcli/credentials.json`, mode 0600).
|
|
@@ -33,14 +33,32 @@ WORKSPACE_SKILL_DIR="$WORKSPACE/skills/ttcli"
|
|
|
33
33
|
NPM_ROOT="$(npm root -g 2>/dev/null || echo '')"
|
|
34
34
|
PKG_DIR="${NPM_ROOT:+$NPM_ROOT/@bluevs/ttcli}"
|
|
35
35
|
|
|
36
|
-
#
|
|
37
|
-
#
|
|
36
|
+
# 版本来源(按优先级):
|
|
37
|
+
# 1. SKILL.md frontmatter 的 version: 字段 —— 单一权威源
|
|
38
|
+
# 2. SKILL.md mtime fallback —— frontmatter 缺 version: 字段时兜底
|
|
39
|
+
#
|
|
40
|
+
# 开发期(SOURCE_DIR 不在 npm 包目录内)会在 frontmatter 版本号后加 -dev-{mtime}
|
|
41
|
+
# 后缀,确保改 SKILL.md 不动 frontmatter 时也能触发同步。
|
|
42
|
+
# 生产期(SOURCE_DIR 在 npm 包目录内,post-install 场景)直接用 frontmatter
|
|
43
|
+
# 版本号,跟 npm package.json 对齐,便于用户报"我装的是哪个版本"。
|
|
38
44
|
get_version() {
|
|
39
|
-
|
|
40
|
-
|
|
45
|
+
local v
|
|
46
|
+
v=$(awk -F': *' '/^version: *[^ ]/ { print $2; exit }' "$SKILL_FILE" | tr -d '\r ')
|
|
47
|
+
if [ -z "$v" ]; then
|
|
48
|
+
date -r "$SKILL_FILE" "+local-%Y%m%d-%H%M%S" 2>/dev/null || echo "local-unknown"
|
|
49
|
+
return
|
|
50
|
+
fi
|
|
51
|
+
|
|
52
|
+
# 生产模式:SOURCE_DIR 在 npm 包内 → 直接用 frontmatter 版本
|
|
53
|
+
if [ -n "$PKG_DIR" ] && [ -d "$PKG_DIR" ] && [[ "$SOURCE_DIR" == "$PKG_DIR"* ]]; then
|
|
54
|
+
echo "$v"
|
|
55
|
+
return
|
|
41
56
|
fi
|
|
42
|
-
|
|
43
|
-
|
|
57
|
+
|
|
58
|
+
# 开发模式:加 -dev-{mtime} 后缀
|
|
59
|
+
local mtime
|
|
60
|
+
mtime=$(date -r "$SKILL_FILE" "+%Y%m%d-%H%M%S" 2>/dev/null || echo "unknown")
|
|
61
|
+
echo "${v}-dev-${mtime}"
|
|
44
62
|
}
|
|
45
63
|
|
|
46
64
|
# ============================================================
|