@acedatacloud/skills 2026.801.2 → 2026.803.0

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": "@acedatacloud/skills",
3
- "version": "2026.801.2",
3
+ "version": "2026.803.0",
4
4
  "description": "Agent Skills for AceDataCloud AI services — music, image, video generation, LLM chat, web search. Compatible with Claude Code, GitHub Copilot, Gemini CLI, OpenAI Codex, and 30+ AI coding agents.",
5
5
  "keywords": [
6
6
  "agent-skills",
@@ -1,18 +1,18 @@
1
1
  ---
2
2
  name: tiktok
3
- description: Post videos to the user's TikTok — either published directly (with caption and privacy the user chooses) or uploaded to their drafts/inbox. Use when the user wants to publish or post a generated video to TikTok, send a video to their TikTok drafts, or check which TikTok account is connected.
3
+ description: Upload videos to the user's TikTok drafts/inbox, where they finish posting in the TikTok app. Use when the user wants to publish or post a generated video to TikTok, or send a video to their TikTok drafts.
4
4
  when_to_use: |
5
5
  Trigger when the user wants to push a video (e.g. one generated by
6
- the video skills) to TikTok. Direct Post publishes immediately with a
7
- caption and a privacy level the user picks; Upload drops it into
8
- drafts for them to finish in the app. Always confirm caption and
9
- privacy with the user before posting — never post unattended.
6
+ the video skills) to TikTok. Upload drops it into their drafts and
7
+ they finish posting in the app — this is the working path. Direct
8
+ Post (publishing immediately with caption + privacy) requires an
9
+ audit this app has not passed yet; do not use it.
10
10
  connections: [tiktok]
11
11
  allowed_tools: [Bash]
12
12
  license: Apache-2.0
13
13
  metadata:
14
14
  author: acedatacloud
15
- version: "2.1"
15
+ version: "2.2"
16
16
  ---
17
17
 
18
18
  Call the **TikTok API v2** with `curl + jq`. The user's OAuth bearer token is in
@@ -42,65 +42,42 @@ scopes (`user.info.stats`, `user.info.profile`, `video.list`), which this app
42
42
  has not been granted — don't call `/v2/video/list/` or request those fields,
43
43
  they will fail with `scope_not_authorized`.
44
44
 
45
- ## Direct Post — publish straight to the account
45
+ ## Upload the video to the user's drafts
46
46
 
47
- Three steps, in this order. **Step 1 is mandatory** and its result drives what
48
- you may offer the user.
47
+ This is the working path. The video lands in the creator's TikTok **inbox /
48
+ drafts**; they open the TikTok app to add caption, sound and privacy, then post.
49
+ Confirm with the user before uploading.
49
50
 
50
- ### 1. Query creator info (required before every post)
51
+ Two source modes. **`FILE_UPLOAD` is the reliable one** — `PULL_FROM_URL` only
52
+ works from a domain verified in the developer portal, and ours is not verified
53
+ yet (it returns `url_ownership_unverified`).
51
54
 
52
- ```bash
53
- curl -sS -X POST -H "$AUTH" -H "Content-Type: application/json" \
54
- "$T/post/publish/creator_info/query/" | jq '.data, .error.code'
55
- ```
56
-
57
- Returns `creator_nickname`, `privacy_level_options`, `comment_disabled`,
58
- `duet_disabled`, `stitch_disabled`, `max_video_post_duration_sec`.
59
-
60
- Use it to:
61
- - **Show the user `creator_nickname`** so they know which account will receive
62
- the post.
63
- - **Offer only the returned `privacy_level_options`** — a public account gets
64
- `PUBLIC_TO_EVERYONE` / `MUTUAL_FOLLOW_FRIENDS` / `SELF_ONLY`; a private one
65
- gets `FOLLOWER_OF_CREATOR` / `MUTUAL_FOLLOW_FRIENDS` / `SELF_ONLY`.
66
- - **Reject a too-long video** against `max_video_post_duration_sec`.
67
- - Not offer comment/duet/stitch toggles that come back disabled.
68
- - If it returns a spam/cap error, **stop** and tell the user to try later.
55
+ ### FILE_UPLOAD (default)
69
56
 
70
- ### 2. Ask the user to confirm
71
-
72
- Before initializing, the user must explicitly confirm — this is a TikTok
73
- requirement, not just good manners:
74
-
75
- - the **caption** (max 2200 UTF-16 chars; `#tag` and `@mention` work),
76
- - the **privacy level**, **chosen by them from the options above — never
77
- default to one, never assume `PUBLIC_TO_EVERYONE`**,
78
- - and tell them: *"By posting, you agree to TikTok's Music Usage Confirmation."*
79
-
80
- ### 3. Initialize the post
57
+ Download the video locally first, then init with its exact byte size. Files
58
+ under 64 MB go up as a single chunk.
81
59
 
82
60
  ```bash
83
- VIDEO_URL="https://cdn.acedata.cloud/….mp4" # verified-domain public URL
84
- curl -sS -X POST -H "$AUTH" -H "Content-Type: application/json" \
85
- -d "$(jq -n --arg t "$CAPTION" --arg p "$PRIVACY" --arg u "$VIDEO_URL" '{
86
- post_info: {title:$t, privacy_level:$p, is_aigc:true},
87
- source_info: {source:"PULL_FROM_URL", video_url:$u}}')" \
88
- "$T/post/publish/video/init/" | jq '{publish_id:.data.publish_id, error}'
61
+ SIZE=$(stat -f%z video.mp4 2>/dev/null || stat -c%s video.mp4)
62
+
63
+ INIT=$(curl -sS -X POST -H "$AUTH" -H "Content-Type: application/json" \
64
+ -d "$(jq -n --argjson s "$SIZE" \
65
+ '{source_info:{source:"FILE_UPLOAD", video_size:$s, chunk_size:$s, total_chunk_count:1}}')" \
66
+ "$T/post/publish/inbox/video/init/")
67
+ echo "$INIT" | jq '{publish_id:.data.publish_id, error:.error.code}'
68
+
69
+ UPLOAD_URL=$(echo "$INIT" | jq -r '.data.upload_url')
70
+ curl -sS -X PUT "$UPLOAD_URL" \
71
+ -H "Content-Type: video/mp4" \
72
+ -H "Content-Length: $SIZE" \
73
+ -H "Content-Range: bytes 0-$((SIZE-1))/$SIZE" \
74
+ --data-binary @video.mp4 -o /dev/null -w '%{http_code}\n'
89
75
  ```
90
76
 
91
- **Set `is_aigc: true` whenever the video was AI-generated** (anything from our
92
- video skills). TikTok labels it "Creator labeled as AI-generated" — required
93
- disclosure, and omitting it risks the post being taken down.
94
-
95
- Optional `post_info` fields: `disable_comment`, `disable_duet`,
96
- `disable_stitch`, `video_cover_timestamp_ms`. If the user says the video is a
97
- paid partnership, set `brand_content_toggle: true` (which cannot be combined
98
- with `SELF_ONLY`); for promoting their own business use `brand_organic_toggle`.
77
+ The `PUT` returns **201** when the whole file landed (206 for intermediate
78
+ chunks). `upload_url` expires in **1 hour**.
99
79
 
100
- ## Upload to drafts instead
101
-
102
- When the user would rather finish in the app, use the inbox endpoint — no
103
- caption or privacy, and **no `creator_info` call needed**:
80
+ ### PULL_FROM_URL (only from a verified domain)
104
81
 
105
82
  ```bash
106
83
  curl -sS -X POST -H "$AUTH" -H "Content-Type: application/json" \
@@ -108,35 +85,49 @@ curl -sS -X POST -H "$AUTH" -H "Content-Type: application/json" \
108
85
  "$T/post/publish/inbox/video/init/" | jq '{publish_id:.data.publish_id, error}'
109
86
  ```
110
87
 
111
- ## Poll status (both flows)
88
+ Needs HTTPS and **no redirects** (any 3xx fails). Domain verification covers
89
+ subdomains downward only.
90
+
91
+ ## Poll status
112
92
 
113
93
  ```bash
114
94
  curl -sS -X POST -H "$AUTH" -H "Content-Type: application/json" \
115
95
  -d "$(jq -n --arg id "$PUBLISH_ID" '{publish_id:$id}')" \
116
- "$T/post/publish/status/fetch/" | jq '.data | {status, fail_reason, publicaly_available_post_id}'
96
+ "$T/post/publish/status/fetch/" | jq '.data | {status, fail_reason}'
117
97
  ```
118
98
 
119
- Status: `PROCESSING_DOWNLOAD` / `PROCESSING_UPLOAD` → `SEND_TO_USER_INBOX`
120
- (drafts flow) or `PUBLISH_COMPLETE` (direct post), or `FAILED` with
121
- `fail_reason`. Tell the user processing takes a few minutes.
99
+ `PROCESSING_UPLOAD` / `PROCESSING_DOWNLOAD` → **`SEND_TO_USER_INBOX`** = done,
100
+ the video is waiting in the creator's TikTok inbox. Tell them to open the
101
+ TikTok app to finish posting, and that processing takes a few minutes.
122
102
 
123
103
  Don't retry on `auth_removed`, `spam_risk_text`, `spam_risk`, or
124
104
  `spam_risk_user_banned_from_posting` — those are terminal. `internal` is
125
105
  retryable.
126
106
 
107
+ ## Direct Post — NOT available yet
108
+
109
+ Direct Post (`/v2/post/publish/video/init/`) publishes straight to the profile
110
+ with a caption and privacy level. **Do not call it.** It needs a Content
111
+ Posting API audit this app has not passed, so every attempt fails:
112
+
113
+ ```
114
+ 403 unaudited_client_can_only_post_to_private_accounts
115
+ ```
116
+
117
+ The `video.publish` scope is granted and `creator_info/query` works, which
118
+ makes it look available — it is not. Until the audit clears, use the drafts
119
+ path above.
120
+
127
121
  ## Gotchas
128
122
 
129
- - **Never hardcode `privacy_level`.** TikTok rejects values outside
130
- `privacy_level_options` (`privacy_level_option_mismatch`) and treats a
131
- defaulted privacy setting as a guideline violation for the whole app.
132
- - `publicaly_available_post_id` (TikTok's own spelling — don't "fix" it) is a
133
- **list**, and is **empty for non-public posts and for public posts still in
134
- moderation**. Empty ≠ failure.
135
- - `PULL_FROM_URL` needs the URL's domain verified in the developer app, HTTPS,
136
- and **no redirects** (any 3xx fails). Verification covers subdomains
137
- downward only. If the video isn't on a verified domain, use chunked
138
- `FILE_UPLOAD` (init with `video_size`/`chunk_size`/`total_chunk_count`, then
139
- `PUT` byte ranges to `upload_url`, which expires in 1 hour).
123
+ - **Never hardcode `privacy_level`** if Direct Post is ever enabled. TikTok
124
+ rejects values outside `privacy_level_options` and treats a defaulted privacy
125
+ setting as a guideline violation for the whole app.
140
126
  - Rate limits per user token: `creator_info/query` 20/min, `video/init/`
141
127
  **6/min**, `status/fetch/` 30/min. TikTok also caps posts at roughly 15/day
142
- per creator, shared across all apps.
128
+ per creator, **shared across all apps** — another app can exhaust it.
129
+ - `SEND_TO_USER_INBOX` is success for this flow, not an intermediate state.
130
+ Don't wait for `PUBLISH_COMPLETE` — that only happens once the user posts
131
+ from the app.
132
+
133
+
@@ -39,6 +39,9 @@ execution:
39
39
  origins:
40
40
  - https://www.xiaohongshu.com
41
41
  - https://creator.xiaohongshu.com
42
+ # Without this the session lands on origins[0] — the consumer homepage,
43
+ # which carries no creator surface at all.
44
+ initial_url: https://creator.xiaohongshu.com/publish/publish
42
45
  capabilities:
43
46
  - tabs.read
44
47
  - tabs.manage
@@ -139,6 +139,14 @@ def test_browser_execution_frontmatter_contract() -> None:
139
139
  assert re.search(r"^ skill_revision: 4\.3\.0$", frontmatter, re.MULTILINE)
140
140
  assert re.search(r"^ provider: xiaohongshu/xiaohongshu$", frontmatter, re.MULTILINE)
141
141
  assert _nested_list(frontmatter, "origins") == EXPECTED_ORIGINS
142
+ # Publishing lives on creator.; without an explicit initial_url the session
143
+ # silently lands on origins[0], the consumer homepage, and every run has to
144
+ # navigate off it before it can do anything.
145
+ assert re.search(
146
+ r"^ initial_url: https://creator\.xiaohongshu\.com/publish/publish$",
147
+ frontmatter,
148
+ re.MULTILINE,
149
+ )
142
150
  assert _nested_list(frontmatter, "capabilities") == EXPECTED_CAPABILITIES
143
151
  assert _top_level_list(frontmatter, "allowed_tools") == EXPECTED_FACADES
144
152
  assert " browser_contract: contracts/browser-manifest.compact.json" in frontmatter