@bluevs/ttcli 0.0.4 → 0.0.6

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.4",
3
+ "version": "0.0.6",
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.4",
13
- "@bluevs/ttcli-darwin-x64": "0.0.4",
14
- "@bluevs/ttcli-linux-arm64": "0.0.4",
15
- "@bluevs/ttcli-linux-x64": "0.0.4",
16
- "@bluevs/ttcli-win32-arm64": "0.0.4",
17
- "@bluevs/ttcli-win32-x64": "0.0.4"
12
+ "@bluevs/ttcli-darwin-arm64": "0.0.6",
13
+ "@bluevs/ttcli-darwin-x64": "0.0.6",
14
+ "@bluevs/ttcli-linux-arm64": "0.0.6",
15
+ "@bluevs/ttcli-linux-x64": "0.0.6",
16
+ "@bluevs/ttcli-win32-arm64": "0.0.6",
17
+ "@bluevs/ttcli-win32-x64": "0.0.6"
18
18
  },
19
19
  "engines": {
20
20
  "node": ">=16"
@@ -1,12 +1,12 @@
1
1
  ---
2
2
  name: ttcli
3
- version: 0.0.4
3
+ version: 0.0.6
4
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。
5
5
  ---
6
6
 
7
7
  # TikTok CLI (`ttcli`)
8
8
 
9
- > **Skill version**: `0.0.4`
9
+ > **Skill version**: `0.0.6`
10
10
  >
11
11
  > 当用户问"ttcli skill 版本是多少 / skill 装好了吗 / 升级生效了没" 时,**直接念出上面的版本号**。
12
12
  > 用户期望更高版本但没看到 → 让他们:(1) 重跑 `npm install -g @bluevs/ttcli && bash $(npm root -g)/@bluevs/ttcli/skills/ttcli/scripts/install-skill.sh` (2) 然后**新开一个会话**(skill 在会话启动时加载,老会话不刷新)。
@@ -15,6 +15,24 @@ description: 直接调用 TikTok Marketing API(access token 鉴权,不经 VH
15
15
 
16
16
  This skill is intentionally short. Always treat `ttcli -h` and `ttcli <subcommand> -h` as the primary reference for flags.
17
17
 
18
+ ## 🚨 硬性约定:所有 TikTok 操作必须经 ttcli,不允许直接调 TikTok 接口
19
+
20
+ **严禁**绕过 ttcli 直接 `curl` / `wget` / `python requests` / 任何 HTTP 客户端调用 `business-api.tiktok.com` 的任何端点。**严禁**自己手搓接口、伪造 header、临时验证 token,或在 ttcli 没包某个端点时"先用 curl 临时跑一下"。
21
+
22
+ **理由**(每条都是真实踩过的坑):
23
+ - ttcli 已沉淀的字段名/枚举值校验(如 MINI_GAME 的 7 个隐性约束、`app_promotion_type` vs `promotion_type` 双层关系、`output_format` 决定 download 走 CSV 还是 JSON 等)一旦绕过就全丢
24
+ - 写操作的 `--dry-run` 安全网、`operation_status=DISABLE` 兜底、`request_id` 自动幂等都失效,容易真创出花钱的实体
25
+ - TikTok 的错误码不会自动经过 SKILL.md 的"错误信息要翻译"对照表,会原样甩到用户
26
+ - 日志/排查链路断掉,request_id 拿不到,工单提不了
27
+
28
+ **正确做法**:
29
+ - 想要的接口 ttcli 没包 → 告诉用户「这个接口 ttcli 还没封装,要我加吗?需要把官方文档贴给我」**而不是**自己 curl 临时跑
30
+ - 调试某个字段行为 → 用 `ttcli <cmd> --dry-run` 看 ttcli 会发什么 body,再用 `--human` 看响应;不要绕开
31
+ - 单纯查 ttcli 命令树 → `ttcli -h` / `ttcli <ns> -h` / `ttcli <ns> <cmd> -h`
32
+ - 只有当用户**明确说**"绕过 ttcli 直接 curl 这一次"时才允许直连——否则一律走 ttcli
33
+
34
+ 如果用户问「能不能直接调 X 接口」,先回「我们走 ttcli,让我看看包了没;没包的话我把这个接口加到 ttcli 里再用,会更稳」。
35
+
18
36
  ## 🚨 与用户对话的硬性约定:错误信息要翻译
19
37
 
20
38
  ttcli 和 TikTok 返回的报错都是技术语言(`missing required fields: [adgroup_id]`、`Invalid optimization goal`、`code=40002 ...`)。**严禁原样把这些贴给用户**——非技术用户看到字段名、code 数字会懵。
@@ -102,14 +120,25 @@ Before creating a campaign or adgroup, gather the IDs you'll reference:
102
120
 
103
121
  ```
104
122
  ttcli auth show # confirm credentials loaded + their source
123
+ ttcli bc list # → bc_id (Business Centers; needed for BC_AUTH_TT identities)
105
124
  ttcli app list # → app_id (Android/iOS apps)
106
125
  ttcli minis list # → minis_id (TikTok Minis: MINI_GAME / MINI_SERIES)
107
126
  ttcli identity list # → identity_id (Spark Ads publishing identity)
108
127
  ttcli smart-plus campaign list # → existing campaign_id (or you'll create one)
128
+ ttcli smart-plus adgroup list # → existing adgroup_id (filter by --campaign-ids / --promotion-type)
129
+ ttcli smart-plus ad list # → existing smart_plus_ad_id + creative_list (only explicitly-selected creatives)
109
130
  ttcli tool region \
110
131
  --placements=PLACEMENT_TIKTOK \
111
132
  --objective-type=APP_PROMOTION \
112
133
  --level-range=TO_COUNTRY # → location_id for targeting_spec.location_ids
134
+ ttcli tool targeting search \
135
+ --objective-type=APP_PROMOTION --promotion-type=APP_ANDROID \
136
+ --placements=PLACEMENT_TIKTOK --search-type=FUZZY_SEARCH \
137
+ --keywords=Kentucky # → geo_id by name (location_ids OR zipcode_ids)
138
+ ttcli tool targeting info \
139
+ --objective-type=APP_PROMOTION --promotion-type=APP_ANDROID \
140
+ --placements=PLACEMENT_TIKTOK \
141
+ --targeting-ids=6254925,6254928 # ← reverse: name + parent hierarchy from IDs (use --scene=ISP for isp_id lookup)
113
142
  ```
114
143
 
115
144
  `tool region` accepts `--language=zh-CN` (or any of 78 supported codes) for localized region names — useful when surfacing options to the user.
@@ -193,6 +222,15 @@ Minimal Android APP_INSTALL adgroup body (use as a template):
193
222
 
194
223
  Creating MINI_GAME adgroups has many undocumented or scattered constraints. The combination below was end-to-end verified. Use as the starting template; do not relax fields without a specific reason.
195
224
 
225
+ **🚨 Two-level terminology — don't conflate:**
226
+
227
+ | Field | Level | Valid values |
228
+ |---|---|---|
229
+ | `app_promotion_type` | **campaign** | `APP_INSTALL` / `APP_RETARGETING` / `MINIS` (only these 3) |
230
+ | `promotion_type` | **adgroup** (optimization placement) | `APP_ANDROID` / `APP_IOS` / `WEBSITE` / `MINI_APP` / `MINI_GAME` / `NATIVE_SERIES` / `LEAD_GENERATION` |
231
+
232
+ For TikTok Minis: **campaign** uses `app_promotion_type=MINIS`, then **adgroup** uses `promotion_type=MINI_GAME` (game) or `MINI_APP` (legacy minis) or `NATIVE_SERIES` (short drama). Sending `--app-promotion-type=MINI_GAME` will fail with `Invalid app_promotion_type` — that value lives one level down.
233
+
196
234
  **Campaign must be CBO=off + INFINITE**:
197
235
 
198
236
  ```
@@ -366,6 +404,65 @@ Common codes:
366
404
 
367
405
  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.
368
406
 
407
+ ## TikTok user OAuth (Login Kit / Content Posting API) — separate token type
408
+
409
+ ttcli's main job is Marketing API. But it also wraps OAuth-side endpoints under `tt-user` and `business`:
410
+
411
+ ```
412
+ ttcli tt-user token-info \ # inspect a user OAuth token
413
+ --app-id=aw1234abcd5678 \
414
+ --tt-access-token=act.xxxxx
415
+
416
+ ttcli business get \ # account profile + insights
417
+ --business-id=open_id_xxx \ # (= creator_id from tt-user token-info)
418
+ --tt-access-token=act.xxxxx \
419
+ --fields=username,display_name,followers_count,video_views,audience_countries
420
+ ```
421
+
422
+ **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.
423
+
424
+ | Endpoint | Auth model | Required scopes (some) |
425
+ |---|---|---|
426
+ | `/tt_user/token_info/get/` | access_token in JSON body, no header | n/a (introspection) |
427
+ | `/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` |
428
+
429
+ 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.
430
+
431
+ **`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**.
432
+
433
+ ## Reporting (async)
434
+
435
+ `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:
436
+
437
+ 1. **Create task** — `ttcli report task create ...` → `task_id`
438
+ 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.
439
+ 3. **Download** — `ttcli report task download --task-id=...`. Behavior depends on what `--output-format` you used at create:
440
+ - **CSV_STRING** (default at create) → returns raw CSV bytes directly to stdout. AI agents: parse CSV from stdout.
441
+ - **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.
442
+
443
+ **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.
444
+
445
+ **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.
446
+
447
+ **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).
448
+
449
+ **Lifetime metrics**: pass `--query-lifetime` to get all-time metrics; ignores `--start-date`/`--end-date`. No time/audience/country breakdowns allowed in lifetime mode.
450
+
451
+ **Recommended**: pass `--enable-title-translation=false` for stable header rows (TikTok periodically updates translated headers — raw field names don't change).
452
+
453
+ ```
454
+ ttcli report task create \
455
+ --report-type=BASIC --data-level=AUCTION_AD \
456
+ --dimensions=ad_id,stat_time_day \
457
+ --metrics=spend,impressions,clicks,cpc,ctr \
458
+ --start-date=2026-05-01 --end-date=2026-06-01 \
459
+ --filter-ad-ids=1866239337888050,1866239351340034 \
460
+ --output-format=CSV_DOWNLOAD \
461
+ --enable-title-translation=false
462
+ ```
463
+
464
+ Limits: 1 QPS per app; 500 async tasks/hour per advertiser; 30-day task validity.
465
+
369
466
  ## Credentials
370
467
 
371
468
  Priority: `--flag` > env var > config file (`~/.config/ttcli/credentials.json`, mode 0600).