opencli-plugin-socialdatax 0.1.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 SocialDataX
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,62 @@
1
+ # SocialDataX OpenCLI Plugin | 社媒数据助手 OpenCLI 插件
2
+
3
+ SocialDataX / 社媒数据助手 provides hosted, read-only social data tools for 小红书 / Xiaohongshu / XHS / RedNote and 抖音 / Douyin workflows.
4
+
5
+ Use this OpenCLI plugin when you want API Key based data access without relying on a logged-in browser session. It is a complement to OpenCLI's built-in browser-backed `xiaohongshu` adapter, not a replacement for that adapter.
6
+
7
+ - Product: `SocialDataX` / `社媒数据助手`
8
+ - Website: <https://socialdatax.com>
9
+ - Platforms: 小红书 / Xiaohongshu / XHS / RedNote, 抖音 / Douyin
10
+ - Runtime package: `socialdatax-skills@latest`
11
+ - API key environment variable: `SOCIALDATAX_API_KEY`
12
+
13
+ ## Local Install
14
+
15
+ ```bash
16
+ opencli plugin install /Users/devin/work/cx/code/social-service/public-listings/socialdatax-opencli
17
+ ```
18
+
19
+ Set your API Key before data calls:
20
+
21
+ ```bash
22
+ export SOCIALDATAX_API_KEY="<SOCIALDATAX_API_KEY>"
23
+ ```
24
+
25
+ ## Commands
26
+
27
+ ```bash
28
+ opencli socialdatax xhs-search --keyword "露营桌" -f json
29
+ opencli socialdatax xhs-detail --note-id "<note_id>" -f json
30
+ opencli socialdatax xhs-comments --note-id "<note_id>" -f table
31
+ opencli socialdatax xhs-sub-comments --note-id "<note_id>" --comment-id "<comment_id>" -f table
32
+ opencli socialdatax xhs-user-info --user-id "<user_id>" -f json
33
+ opencli socialdatax xhs-user-posts --user-id "<user_id>" -f table
34
+
35
+ opencli socialdatax douyin-search --keyword "露营桌" -f json
36
+ opencli socialdatax douyin-detail --aweme-id "<aweme_id>" -f json
37
+ opencli socialdatax douyin-comments --aweme-id "<aweme_id>" -f table
38
+ opencli socialdatax douyin-user-info --sec-user-id "<sec_user_id>" -f json
39
+ opencli socialdatax douyin-user-posts --sec-user-id "<sec_user_id>" -f table
40
+ ```
41
+
42
+ Each command forwards to `npx -y socialdatax-skills@latest ...` and flattens returned JSON so OpenCLI can render table, JSON, YAML, or CSV output.
43
+
44
+ ## Read-Only Boundary
45
+
46
+ This plugin only reads public content data through hosted SocialDataX services. It does not log in to user accounts, read browser data, publish posts, like, comment, follow, edit, delete, or perform account actions.
47
+
48
+ ## Search Aliases
49
+
50
+ Users and agents may search for this plugin with:
51
+
52
+ - `SocialDataX`
53
+ - `社媒数据助手`
54
+ - `小红书`
55
+ - `Xiaohongshu`
56
+ - `XHS`
57
+ - `RedNote`
58
+ - `抖音`
59
+ - `Douyin`
60
+ - `social media data`
61
+ - `creator analytics`
62
+ - `comment analysis`
@@ -0,0 +1,303 @@
1
+ import { spawn } from "node:child_process";
2
+
3
+ export const PRODUCT_NAME = "SocialDataX / 社媒数据助手";
4
+ export const PRIMARY_API_KEY_ENV = "SOCIALDATAX_API_KEY";
5
+ export const DEFAULT_PACKAGE_SPEC = "socialdatax-skills@latest";
6
+
7
+ const TOOL_COMMANDS = {
8
+ "xhs-search": {
9
+ platform: "xhs",
10
+ action: "search",
11
+ listPath: ["data", "items"],
12
+ pageMeta: ["next_page", "total_count"],
13
+ options: {
14
+ keyword: "--keyword",
15
+ page: "--page",
16
+ sortType: "--sort-type",
17
+ noteType: "--note-type",
18
+ publishTimeRange: "--publish-time-range",
19
+ },
20
+ },
21
+ "xhs-detail": {
22
+ platform: "xhs",
23
+ action: "detail",
24
+ options: {
25
+ noteId: "--note-id",
26
+ url: "--url",
27
+ },
28
+ },
29
+ "xhs-comments": {
30
+ platform: "xhs",
31
+ action: "comments",
32
+ listPath: ["data", "items"],
33
+ pageMeta: ["next_page_token", "comment_count", "top_level_comment_count"],
34
+ options: {
35
+ noteId: "--note-id",
36
+ url: "--url",
37
+ pageToken: "--page-token",
38
+ },
39
+ },
40
+ "xhs-sub-comments": {
41
+ platform: "xhs",
42
+ action: "sub-comments",
43
+ listPath: ["data", "items"],
44
+ pageMeta: ["next_page_token"],
45
+ options: {
46
+ noteId: "--note-id",
47
+ commentId: "--comment-id",
48
+ pageToken: "--page-token",
49
+ },
50
+ },
51
+ "xhs-user-info": {
52
+ platform: "xhs",
53
+ action: "user-info",
54
+ options: {
55
+ userId: "--user-id",
56
+ profileUrl: "--profile-url",
57
+ },
58
+ },
59
+ "xhs-user-posts": {
60
+ platform: "xhs",
61
+ action: "user-posts",
62
+ listPath: ["data", "items"],
63
+ pageMeta: ["next_page_token"],
64
+ options: {
65
+ userId: "--user-id",
66
+ profileUrl: "--profile-url",
67
+ pageToken: "--page-token",
68
+ },
69
+ },
70
+ "douyin-search": {
71
+ platform: "douyin",
72
+ action: "search",
73
+ listPath: ["data", "items"],
74
+ pageMeta: ["next_page_token"],
75
+ options: {
76
+ keyword: "--keyword",
77
+ pageToken: "--page-token",
78
+ sortType: "--sort-type",
79
+ publishTimeRange: "--publish-time-range",
80
+ durationRange: "--duration-range",
81
+ contentType: "--content-type",
82
+ },
83
+ },
84
+ "douyin-detail": {
85
+ platform: "douyin",
86
+ action: "detail",
87
+ options: {
88
+ awemeId: "--aweme-id",
89
+ url: "--url",
90
+ },
91
+ },
92
+ "douyin-comments": {
93
+ platform: "douyin",
94
+ action: "comments",
95
+ listPath: ["data", "items"],
96
+ pageMeta: ["next_page_token", "comment_count"],
97
+ options: {
98
+ awemeId: "--aweme-id",
99
+ url: "--url",
100
+ pageToken: "--page-token",
101
+ },
102
+ },
103
+ "douyin-user-info": {
104
+ platform: "douyin",
105
+ action: "user-info",
106
+ options: {
107
+ secUserId: "--sec-user-id",
108
+ profileUrl: "--profile-url",
109
+ },
110
+ },
111
+ "douyin-user-posts": {
112
+ platform: "douyin",
113
+ action: "user-posts",
114
+ listPath: ["data", "items"],
115
+ pageMeta: ["next_page_token"],
116
+ options: {
117
+ secUserId: "--sec-user-id",
118
+ profileUrl: "--profile-url",
119
+ pageToken: "--page-token",
120
+ },
121
+ },
122
+ };
123
+
124
+ const OPTION_ALIASES = {
125
+ "aweme-id": "awemeId",
126
+ "comment-id": "commentId",
127
+ "content-type": "contentType",
128
+ "duration-range": "durationRange",
129
+ keyword: "keyword",
130
+ "note-id": "noteId",
131
+ "note-type": "noteType",
132
+ page: "page",
133
+ "page-token": "pageToken",
134
+ "profile-url": "profileUrl",
135
+ "publish-time-range": "publishTimeRange",
136
+ "sec-user-id": "secUserId",
137
+ "sort-type": "sortType",
138
+ url: "url",
139
+ "user-id": "userId",
140
+ };
141
+
142
+ export function listSocialDataXCommands() {
143
+ return Object.keys(TOOL_COMMANDS);
144
+ }
145
+
146
+ export function normalizeCommandName(commandName) {
147
+ return commandName.replace(/^socialdatax[-:]/, "");
148
+ }
149
+
150
+ export function normalizeOptions(input = {}) {
151
+ const normalized = {};
152
+ for (const [key, value] of Object.entries(input)) {
153
+ const canonical = OPTION_ALIASES[key] || key;
154
+ normalized[canonical] = value;
155
+ }
156
+ return normalized;
157
+ }
158
+
159
+ export function buildSocialDataXArgs(commandName, inputOptions = {}) {
160
+ const normalizedCommand = normalizeCommandName(commandName);
161
+ const command = TOOL_COMMANDS[normalizedCommand];
162
+ if (!command) {
163
+ throw new Error(
164
+ `Unsupported SocialDataX command "${commandName}". Use one of: ${listSocialDataXCommands().join(", ")}.`
165
+ );
166
+ }
167
+
168
+ const options = normalizeOptions(inputOptions);
169
+ const args = [command.platform, command.action];
170
+ for (const [optionKey, cliFlag] of Object.entries(command.options)) {
171
+ const value = options[optionKey];
172
+ if (value === undefined || value === null || value === false || value === "") {
173
+ continue;
174
+ }
175
+ args.push(cliFlag, String(value));
176
+ }
177
+ return args;
178
+ }
179
+
180
+ export function buildSocialDataXProcess(commandName, inputOptions = {}, env = process.env) {
181
+ const apiKey = env[PRIMARY_API_KEY_ENV];
182
+ if (!apiKey) {
183
+ throw new Error(`Missing API Key. Set ${PRIMARY_API_KEY_ENV} before running SocialDataX OpenCLI commands.`);
184
+ }
185
+
186
+ const mockBin = env.SOCIALDATAX_OPENCLI_SKILLS_BIN;
187
+ if (mockBin) {
188
+ return {
189
+ file: mockBin,
190
+ args: buildSocialDataXArgs(commandName, inputOptions),
191
+ };
192
+ }
193
+
194
+ return {
195
+ file: "npx",
196
+ args: ["-y", env.SOCIALDATAX_OPENCLI_PACKAGE_SPEC || DEFAULT_PACKAGE_SPEC, ...buildSocialDataXArgs(commandName, inputOptions)],
197
+ };
198
+ }
199
+
200
+ export async function runSocialDataXCommand(commandName, inputOptions = {}, runtime = {}) {
201
+ const env = runtime.env || process.env;
202
+ const spawn = runtime.spawn || spawnJsonCommand;
203
+ const processSpec = buildSocialDataXProcess(commandName, inputOptions, env);
204
+ const envelope = await spawn(processSpec.file, processSpec.args, {
205
+ env: { ...env },
206
+ });
207
+ return flattenSocialDataXEnvelope(commandName, envelope);
208
+ }
209
+
210
+ export async function spawnJsonCommand(file, args, options = {}) {
211
+ return new Promise((resolve, reject) => {
212
+ const child = spawn(file, args, {
213
+ env: options.env,
214
+ stdio: ["ignore", "pipe", "pipe"],
215
+ });
216
+ let stdout = "";
217
+ let stderr = "";
218
+ child.stdout.setEncoding("utf8");
219
+ child.stderr.setEncoding("utf8");
220
+ child.stdout.on("data", (chunk) => {
221
+ stdout += chunk;
222
+ });
223
+ child.stderr.on("data", (chunk) => {
224
+ stderr += chunk;
225
+ });
226
+ child.on("error", reject);
227
+ child.on("close", (status) => {
228
+ if (status !== 0) {
229
+ reject(new Error(trimError(stderr) || `socialdatax-skills exited with status ${status}.`));
230
+ return;
231
+ }
232
+ try {
233
+ resolve(JSON.parse(stdout));
234
+ } catch (error) {
235
+ reject(new Error(`Failed to parse socialdatax-skills JSON output: ${error.message}`));
236
+ }
237
+ });
238
+ });
239
+ }
240
+
241
+ export function flattenSocialDataXEnvelope(commandName, envelope) {
242
+ const normalizedCommand = normalizeCommandName(commandName);
243
+ const command = TOOL_COMMANDS[normalizedCommand];
244
+ if (!command) {
245
+ throw new Error(`Unsupported SocialDataX command "${commandName}".`);
246
+ }
247
+ if (!command.listPath) {
248
+ return [flattenObject(envelope?.data ?? envelope)];
249
+ }
250
+
251
+ const items = getPath(envelope, command.listPath);
252
+ if (!Array.isArray(items)) {
253
+ return [];
254
+ }
255
+
256
+ const meta = {};
257
+ for (const key of command.pageMeta || []) {
258
+ if (envelope?.data && Object.prototype.hasOwnProperty.call(envelope.data, key)) {
259
+ meta[key] = envelope.data[key];
260
+ }
261
+ }
262
+ return items.map((item) => flattenObject({ ...meta, ...item }));
263
+ }
264
+
265
+ export function flattenObject(value, prefix = "", output = {}) {
266
+ if (value === null || value === undefined) {
267
+ if (prefix) {
268
+ output[prefix] = value ?? "";
269
+ }
270
+ return output;
271
+ }
272
+ if (Array.isArray(value)) {
273
+ if (prefix) {
274
+ output[prefix] = JSON.stringify(value);
275
+ }
276
+ return output;
277
+ }
278
+ if (typeof value !== "object") {
279
+ if (prefix) {
280
+ output[prefix] = value;
281
+ }
282
+ return output;
283
+ }
284
+ for (const [key, child] of Object.entries(value)) {
285
+ const childKey = prefix ? `${prefix}_${key}` : key;
286
+ if (child && typeof child === "object" && !Array.isArray(child)) {
287
+ flattenObject(child, childKey, output);
288
+ } else if (Array.isArray(child)) {
289
+ output[childKey] = JSON.stringify(child);
290
+ } else {
291
+ output[childKey] = child ?? "";
292
+ }
293
+ }
294
+ return output;
295
+ }
296
+
297
+ function getPath(value, path) {
298
+ return path.reduce((current, key) => current?.[key], value);
299
+ }
300
+
301
+ function trimError(text) {
302
+ return String(text || "").trim();
303
+ }
@@ -0,0 +1,25 @@
1
+ {
2
+ "name": "socialdatax",
3
+ "displayName": "SocialDataX / 社媒数据助手",
4
+ "description": "SocialDataX / 社媒数据助手 provides hosted read-only social data tools for 小红书 / Xiaohongshu / XHS / RedNote and 抖音 / Douyin: search content, read details, analyze comments, creator profiles, and creator posts.",
5
+ "version": "0.1.0",
6
+ "opencli": ">=1.0.0",
7
+ "homepage": "https://socialdatax.com",
8
+ "keywords": [
9
+ "SocialDataX",
10
+ "社媒数据助手",
11
+ "小红书",
12
+ "Xiaohongshu",
13
+ "XHS",
14
+ "RedNote",
15
+ "抖音",
16
+ "Douyin",
17
+ "social media data",
18
+ "content insights",
19
+ "creator analytics",
20
+ "comments"
21
+ ],
22
+ "commands": [
23
+ "socialdatax"
24
+ ]
25
+ }
package/package.json ADDED
@@ -0,0 +1,44 @@
1
+ {
2
+ "name": "opencli-plugin-socialdatax",
3
+ "version": "0.1.0",
4
+ "description": "SocialDataX / 社媒数据助手 OpenCLI plugin for 小红书, Xiaohongshu, XHS, RedNote, 抖音, and Douyin read-only social data workflows.",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "files": [
8
+ "README.md",
9
+ "LICENSE",
10
+ "opencli-plugin.json",
11
+ "socialdatax.ts",
12
+ "lib/**",
13
+ "skills/**"
14
+ ],
15
+ "scripts": {
16
+ "test": "node --test test/*.test.mjs"
17
+ },
18
+ "keywords": [
19
+ "socialdatax",
20
+ "社媒数据助手",
21
+ "opencli",
22
+ "opencli-plugin",
23
+ "xiaohongshu",
24
+ "小红书",
25
+ "xhs",
26
+ "rednote",
27
+ "douyin",
28
+ "抖音",
29
+ "social-media",
30
+ "social-data",
31
+ "content-analysis",
32
+ "creator-analytics"
33
+ ],
34
+ "homepage": "https://socialdatax.com",
35
+ "bugs": {
36
+ "url": "https://socialdatax.com"
37
+ },
38
+ "engines": {
39
+ "node": ">=21.0.0"
40
+ },
41
+ "peerDependencies": {
42
+ "@jackwener/opencli": ">=1.0.0"
43
+ }
44
+ }
@@ -0,0 +1,46 @@
1
+ ---
2
+ name: socialdatax-opencli
3
+ description: Use SocialDataX / 社媒数据助手 through OpenCLI for read-only 小红书 / Xiaohongshu / XHS / RedNote and 抖音 / Douyin social data workflows including search, details, comments, creator profiles, and creator posts.
4
+ metadata:
5
+ opencli:
6
+ requires:
7
+ env:
8
+ - SOCIALDATAX_API_KEY
9
+ bins:
10
+ - opencli
11
+ - node
12
+ - npm
13
+ primaryEnv: SOCIALDATAX_API_KEY
14
+ homepage: https://socialdatax.com
15
+ ---
16
+
17
+ # SocialDataX OpenCLI
18
+
19
+ Use this skill when the user asks to use OpenCLI for SocialDataX / 社媒数据助手, 小红书 / Xiaohongshu / XHS / RedNote, or 抖音 / Douyin read-only data workflows.
20
+
21
+ Prefer the OpenCLI plugin commands when installed:
22
+
23
+ ```bash
24
+ opencli socialdatax xhs-search --keyword "<keyword>" -f json
25
+ opencli socialdatax xhs-detail --note-id "<note_id>" -f json
26
+ opencli socialdatax xhs-comments --note-id "<note_id>" -f table
27
+ opencli socialdatax xhs-sub-comments --note-id "<note_id>" --comment-id "<comment_id>" -f table
28
+ opencli socialdatax xhs-user-info --user-id "<user_id>" -f json
29
+ opencli socialdatax xhs-user-posts --user-id "<user_id>" -f table
30
+ opencli socialdatax douyin-search --keyword "<keyword>" -f json
31
+ opencli socialdatax douyin-detail --aweme-id "<aweme_id>" -f json
32
+ opencli socialdatax douyin-comments --aweme-id "<aweme_id>" -f table
33
+ opencli socialdatax douyin-user-info --sec-user-id "<sec_user_id>" -f json
34
+ opencli socialdatax douyin-user-posts --sec-user-id "<sec_user_id>" -f table
35
+ ```
36
+
37
+ If the OpenCLI plugin is not installed, fall back to the direct CLI:
38
+
39
+ ```bash
40
+ npx -y socialdatax-skills@latest xhs search --keyword "<keyword>" --pretty
41
+ npx -y socialdatax-skills@latest douyin search --keyword "<keyword>" --pretty
42
+ ```
43
+
44
+ Set `SOCIALDATAX_API_KEY` before data calls. This skill is read-only: do not use it for login, posting, liking, commenting, following, editing, deleting, or other account actions.
45
+
46
+ OpenCLI's built-in `xiaohongshu` adapter is browser-session based. `opencli socialdatax ...` is API Key based hosted data access and covers both 小红书 / Xiaohongshu / XHS / RedNote and 抖音 / Douyin.
package/socialdatax.ts ADDED
@@ -0,0 +1,145 @@
1
+ import { cli, Strategy } from "@jackwener/opencli/registry";
2
+ import { runSocialDataXCommand } from "./lib/socialdatax-core.mjs";
3
+
4
+ const COMMON_COLUMNS = [
5
+ "note_id",
6
+ "note_url",
7
+ "aweme_id",
8
+ "sec_user_id",
9
+ "comment_id",
10
+ "title",
11
+ "desc",
12
+ "content",
13
+ "nickname",
14
+ "author_nickname",
15
+ "profile_nickname",
16
+ "like_count",
17
+ "comment_count",
18
+ "top_level_comment_count",
19
+ "next_page",
20
+ "next_page_token",
21
+ ];
22
+
23
+ const pageTokenOption = {
24
+ name: "page-token",
25
+ type: "string",
26
+ help: "Opaque pagination token returned by the previous page.",
27
+ };
28
+
29
+ function localCommand(name, description, options = {}) {
30
+ return cli({
31
+ site: "socialdatax",
32
+ name,
33
+ description,
34
+ access: "read",
35
+ example: `opencli socialdatax ${name} -f json`,
36
+ strategy: Strategy.LOCAL,
37
+ browser: false,
38
+ args: options,
39
+ columns: COMMON_COLUMNS,
40
+ async func(argv) {
41
+ return runSocialDataXCommand(name, argv);
42
+ },
43
+ });
44
+ }
45
+
46
+ localCommand(
47
+ "xhs-search",
48
+ "Search 小红书 / Xiaohongshu / XHS / RedNote notes with SocialDataX / 社媒数据助手.",
49
+ [
50
+ { name: "keyword", type: "string", required: true, help: "Search keyword." },
51
+ { name: "page", type: "int", help: "1-based page number." },
52
+ { name: "sort-type", type: "string", help: "general, time_descending, like_count_descending, comment_count_descending, or collect_count_descending." },
53
+ { name: "note-type", type: "string", help: "all, image, or video." },
54
+ { name: "publish-time-range", type: "string", help: "all, day, week, or half_year." },
55
+ ]
56
+ );
57
+ localCommand(
58
+ "xhs-detail",
59
+ "Read one 小红书 / Xiaohongshu / XHS / RedNote note detail.",
60
+ [
61
+ { name: "note-id", type: "string", help: "Known XHS note ID." },
62
+ { name: "url", type: "string", help: "XHS note URL, short link, share text, or note ID." },
63
+ ]
64
+ );
65
+ localCommand(
66
+ "xhs-comments",
67
+ "Fetch 小红书 / Xiaohongshu / XHS / RedNote first-level comments.",
68
+ [
69
+ { name: "note-id", type: "string", help: "Known XHS note ID." },
70
+ { name: "url", type: "string", help: "XHS note URL, short link, or share text." },
71
+ pageTokenOption,
72
+ ]
73
+ );
74
+ localCommand(
75
+ "xhs-sub-comments",
76
+ "Fetch 小红书 / Xiaohongshu / XHS / RedNote replies under one comment.",
77
+ [
78
+ { name: "note-id", type: "string", required: true, help: "Known XHS note ID." },
79
+ { name: "comment-id", type: "string", required: true, help: "First-level comment ID." },
80
+ pageTokenOption,
81
+ ]
82
+ );
83
+ localCommand(
84
+ "xhs-user-info",
85
+ "Read 小红书 / Xiaohongshu / XHS / RedNote creator profile data.",
86
+ [
87
+ { name: "user-id", type: "string", help: "Known XHS user ID." },
88
+ { name: "profile-url", type: "string", help: "XHS profile URL, short link, or share text." },
89
+ ]
90
+ );
91
+ localCommand(
92
+ "xhs-user-posts",
93
+ "Fetch 小红书 / Xiaohongshu / XHS / RedNote creator notes.",
94
+ [
95
+ { name: "user-id", type: "string", help: "Known XHS user ID." },
96
+ { name: "profile-url", type: "string", help: "XHS profile URL, short link, or share text." },
97
+ pageTokenOption,
98
+ ]
99
+ );
100
+ localCommand(
101
+ "douyin-search",
102
+ "Search 抖音 / Douyin works with SocialDataX / 社媒数据助手.",
103
+ [
104
+ { name: "keyword", type: "string", required: true, help: "Search keyword." },
105
+ pageTokenOption,
106
+ { name: "sort-type", type: "string", help: "general, time_descending, or like_count_descending." },
107
+ { name: "publish-time-range", type: "string", help: "all, day, week, or half_year." },
108
+ { name: "duration-range", type: "string", help: "all, under_1_minute, one_to_five_minutes, or over_5_minutes." },
109
+ { name: "content-type", type: "string", help: "all, video, or image." },
110
+ ]
111
+ );
112
+ localCommand(
113
+ "douyin-detail",
114
+ "Read one 抖音 / Douyin work detail.",
115
+ [
116
+ { name: "aweme-id", type: "string", help: "Known Douyin aweme_id." },
117
+ { name: "url", type: "string", help: "Douyin content URL, short link, or share text." },
118
+ ]
119
+ );
120
+ localCommand(
121
+ "douyin-comments",
122
+ "Fetch 抖音 / Douyin first-level comments.",
123
+ [
124
+ { name: "aweme-id", type: "string", help: "Known Douyin aweme_id." },
125
+ { name: "url", type: "string", help: "Douyin content URL, short link, or share text." },
126
+ pageTokenOption,
127
+ ]
128
+ );
129
+ localCommand(
130
+ "douyin-user-info",
131
+ "Read 抖音 / Douyin creator profile data.",
132
+ [
133
+ { name: "sec-user-id", type: "string", help: "Known Douyin sec_user_id." },
134
+ { name: "profile-url", type: "string", help: "Douyin profile URL, short link, or share text." },
135
+ ]
136
+ );
137
+ localCommand(
138
+ "douyin-user-posts",
139
+ "Fetch 抖音 / Douyin creator works.",
140
+ [
141
+ { name: "sec-user-id", type: "string", help: "Known Douyin sec_user_id." },
142
+ { name: "profile-url", type: "string", help: "Douyin profile URL, short link, or share text." },
143
+ pageTokenOption,
144
+ ]
145
+ );