socialdatax-skills 0.2.28 → 0.2.29

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 CHANGED
@@ -82,7 +82,7 @@ Common search phrases for this skill package:
82
82
  - Sensitive Words Check MCP server name: `sensitive-check`
83
83
  - Sensitive Words Check future MCP registry name: `com.socialdatax/sensitive-check`
84
84
  - Unified MCP registry name: none; this package installs skills and calls explicit platform services.
85
- - Current public capability version: `0.2.28`
85
+ - Current public capability version: `0.2.29`
86
86
 
87
87
  ## Direct CLI
88
88
 
@@ -176,6 +176,8 @@ npx -y socialdatax-skills@latest sensitive-check text --text "<content>" --platf
176
176
 
177
177
  Most direct CLI commands print a JSON envelope with `platform`, `tool`, `arguments`, and `data`. `sensitive-check` prints `platform`, `tool`, and `data` only; it does not echo the original text in CLI output. `wechat decrypt-media` is a local save command: pass the `video.video_url` returned by WeChat detail and an `--output` file path. It saves the media locally, decrypts when needed, and does not require `SOCIALDATAX_API_KEY`. `xhs download-media` is also local: pass one image or video media URL returned by XHS detail and either `--output <file>` or `--output-dir <directory>`. It writes through a `.part` file, resumes partial downloads when the server supports range requests, skips an already existing output file, and infers common image/video extensions in `--output-dir` mode.
178
178
 
179
+ For transcript commands, the direct CLI tries to deliver the final result in one run: submit may wait up to 210 seconds, and if `data.is_terminal` is not `true`, the CLI automatically continues matching get-job requests for up to 1200 seconds by default, with each get-job request waiting up to 240 seconds. Use positive `--max-wait-seconds <seconds>` to tune that follow-up window. Do not submit a duplicate transcript job just to poll status.
180
+
179
181
  `bilibili download` calls SocialDataX once to fetch short-lived download links, then downloads video and audio tracks on the local machine and merges them with local `ffmpeg`. Set `SOCIALDATAX_API_KEY`, install `ffmpeg`, and pass either `--output <file>` or `--output-dir <directory>`. The download-links request consumes 10 credits; the local track download and merge do not consume additional SocialDataX credits. Use `--ffmpeg-path <path>` when `ffmpeg` is not on `PATH`, and `--keep-tracks` to retain the separate track files after merge.
180
182
 
181
183
  For XHS outputs with a returned `note_url` field, when `note_url` is non-null, preserve it exactly as the full URL, including `xsec_token` query parameters, such as in final answers or display. Do not modify, truncate, redact, normalize, rebuild, or replace it with a link assembled from `note_id`. If `note_url` is null, do not synthesize a public link from `note_id`.
@@ -192,7 +194,7 @@ For commands that accept `--page-token`, continue only with the complete returne
192
194
 
193
195
  Search pagination uses `--page-token` when continuing with a returned `next_page_token`. Omit `--page-token` on the first search request and pass the complete returned `next_page_token` only when continuing the same search chain.
194
196
 
195
- Transcript commands submit a bounded video speech-to-text job or check an existing job. Pass exactly one entrypoint for each call: URL/share text, platform content ID, or `--job-id`. Completed get-job responses return transcript plus content context, not summary.
197
+ Transcript commands submit a bounded video speech-to-text job or check an existing job. Pass exactly one entrypoint for each call: URL/share text, platform content ID, or `--job-id`. Direct CLI transcript commands wait and poll the same job by default; completed get-job responses return transcript plus content context, not summary.
196
198
 
197
199
  Generated Agent Skill examples include `--source-client socialdatax-skills --source-platform <marketplace> --source-skill <skill-slug>` so SocialDataX can attribute authenticated direct data CLI usage to the current marketplace Skill. Keep those values unchanged when running commands from a Skill; omit them for ad hoc CLI use. Authenticated direct data CLI commands also accept `SOCIALDATAX_SOURCE_CLIENT`, `SOCIALDATAX_SOURCE_PLATFORM`, and `SOCIALDATAX_SOURCE_SKILL` as environment-variable fallbacks.
198
200
 
@@ -357,7 +359,7 @@ Current XHS workflows include:
357
359
  - Fetch paginated replies under a first-level comment.
358
360
  - Read creator profile data from a profile link, short link, share text, or user ID.
359
361
  - Fetch paginated creator note lists from a user ID, profile link, short link, or share text for content style and account research.
360
- - Submit and check video note 口播转文字 / speech-to-text transcript jobs; submit tools 提交后最多等待 15 秒.
362
+ - Submit and check video note 口播转文字 / speech-to-text transcript jobs; submit tools 提交后最多等待 210 秒,未完成时继续查询同一个 job_id 直到终态.
361
363
 
362
364
  Current Kuaishou workflows include:
363
365
 
@@ -370,7 +372,7 @@ Current Kuaishou workflows include:
370
372
  - Continue Kuaishou list pagination only when `next_page_token` is non-empty; an empty string means there are no more results to request.
371
373
  - Read creator profile data from a non-empty user_id, profile link, short link, or share text. Live/fw-user profile shares are supported for profile data; successful results return a reusable non-empty user_id.
372
374
  - Fetch paginated creator work lists from a non-empty user_id, or from a profile link, short link, or share text that resolves directly to a non-empty user_id. For live/fw-user profile shares, call creator profile first and use the returned non-empty user_id.
373
- - Submit and check video work 口播转文字 / speech-to-text transcript jobs; submit tools 提交后最多等待 15 秒.
375
+ - Submit and check video work 口播转文字 / speech-to-text transcript jobs; submit tools 提交后最多等待 210 秒,未完成时继续查询同一个 job_id 直到终态.
374
376
 
375
377
  Current Bilibili workflows include:
376
378
 
@@ -389,7 +391,7 @@ Current Douyin workflows include:
389
391
  - Read creator profile data from a profile link, short link, share text, or sec_user_id.
390
392
  - Fetch paginated creator work lists from a sec_user_id, profile link, short link, or share text.
391
393
  - Fetch paginated creator short-drama series lists from a sec_user_id, profile link, short link, or share text.
392
- - Submit and check video work 口播转文字 / speech-to-text transcript jobs; submit tools 提交后最多等待 15 秒.
394
+ - Submit and check video work 口播转文字 / speech-to-text transcript jobs; submit tools 提交后最多等待 210 秒,未完成时继续查询同一个 job_id 直到终态.
393
395
 
394
396
  Current Weibo workflows include:
395
397
 
@@ -401,7 +403,7 @@ Current Weibo workflows include:
401
403
  - Continue Weibo list pagination only when `next_page_token` is non-empty; an empty string means there are no more results to request.
402
404
  - Read creator profile data from a profile link, short link, share text, or user_id.
403
405
  - Fetch paginated creator post lists from a user_id, profile link, short link, or share text.
404
- - Submit and check Weibo video 口播转文字 / speech-to-text transcript jobs; submit tools 提交后最多等待 15 秒.
406
+ - Submit and check Weibo video 口播转文字 / speech-to-text transcript jobs; submit tools 提交后最多等待 210 秒,未完成时继续查询同一个 job_id 直到终态.
405
407
 
406
408
  Current WeChat / 微信 workflows include:
407
409
 
@@ -415,7 +417,7 @@ Current WeChat / 微信 workflows include:
415
417
  - Continue WeChat Channels list pagination only when `next_page_token` is non-empty; an empty string means there are no more results to request.
416
418
  - Read creator profile data from a finder user_id.
417
419
  - Fetch paginated creator video lists from a finder user_id or a video link/share text.
418
- - Submit and check WeChat Channels / 视频号 video 口播转文字 / speech-to-text transcript jobs; submit tools 提交后最多等待 15 秒.
420
+ - Submit and check WeChat Channels / 视频号 video 口播转文字 / speech-to-text transcript jobs; submit tools 提交后最多等待 210 秒,未完成时继续查询同一个 job_id 直到终态.
419
421
 
420
422
  Current Sensitive Words Check workflows include:
421
423
 
@@ -439,9 +441,9 @@ Current Sensitive Words Check workflows include:
439
441
  | `xhs_get_user_info_by_profile_url` | Resolve a profile link, short link, or share text into creator profile data. |
440
442
  | `xhs_get_user_posted_notes_by_user_id` | Fetch a paginated list of notes published by a creator when the caller already has a user ID. |
441
443
  | `xhs_get_user_posted_notes_by_profile_url` | Fetch a paginated list of notes published by a creator from a profile link, short link, or share text. |
442
- | `xhs_submit_video_speech_text_by_note_url` | Submit an XHS video note speech-to-text transcript job from a note link, short link, or share text; submit waits up to 15 seconds. |
443
- | `xhs_submit_video_speech_text_by_note_id` | Submit an XHS video note speech-to-text transcript job when the caller already has a note ID; submit waits up to 15 seconds. |
444
- | `xhs_get_video_speech_text_job` | Check an XHS speech-to-text transcript job by job_id without creating a new task. Returns transcript plus content context, not summary. |
444
+ | `xhs_submit_video_speech_text_by_note_url` | Submit an XHS video note speech-to-text transcript job from a note link, short link, or share text; submit waits up to 210 seconds. If unfinished, continue checking the same job_id with get-job wait_seconds up to 240 until is_terminal is true. |
445
+ | `xhs_submit_video_speech_text_by_note_id` | Submit an XHS video note speech-to-text transcript job when the caller already has a note ID; submit waits up to 210 seconds. If unfinished, continue checking the same job_id with get-job wait_seconds up to 240 until is_terminal is true. |
446
+ | `xhs_get_video_speech_text_job` | Check an XHS speech-to-text transcript job by job_id without creating a new task; optional wait_seconds 0-240 can long-poll the same job in one request. Continue querying the same job_id until is_terminal is true. Returns transcript plus content context, not summary. |
445
447
 
446
448
  ## Douyin Tools
447
449
 
@@ -460,9 +462,9 @@ Current Sensitive Words Check workflows include:
460
462
  | `douyin_get_user_posted_videos_by_profile_url` | Fetch a paginated list of works published by a creator from a profile link, short link, or share text. |
461
463
  | `douyin_get_user_series_by_sec_user_id` | Fetch a paginated list of short-drama series by a creator when the caller already has a sec_user_id. |
462
464
  | `douyin_get_user_series_by_profile_url` | Fetch a paginated list of short-drama series by a creator from a profile link, short link, or share text. |
463
- | `douyin_submit_video_speech_text_by_video_url` | Submit a Douyin video speech-to-text transcript job from a content page link, short link, or share text; submit waits up to 15 seconds. |
464
- | `douyin_submit_video_speech_text_by_aweme_id` | Submit a Douyin video speech-to-text transcript job when the caller already has an aweme_id; submit waits up to 15 seconds. |
465
- | `douyin_get_video_speech_text_job` | Check a Douyin speech-to-text transcript job by job_id without creating a new task. Returns transcript plus content context, not summary. |
465
+ | `douyin_submit_video_speech_text_by_video_url` | Submit a Douyin video speech-to-text transcript job from a content page link, short link, or share text; submit waits up to 210 seconds. If unfinished, continue checking the same job_id with get-job wait_seconds up to 240 until is_terminal is true. |
466
+ | `douyin_submit_video_speech_text_by_aweme_id` | Submit a Douyin video speech-to-text transcript job when the caller already has an aweme_id; submit waits up to 210 seconds. If unfinished, continue checking the same job_id with get-job wait_seconds up to 240 until is_terminal is true. |
467
+ | `douyin_get_video_speech_text_job` | Check a Douyin speech-to-text transcript job by job_id without creating a new task; optional wait_seconds 0-240 can long-poll the same job in one request. Continue querying the same job_id until is_terminal is true. Returns transcript plus content context, not summary. |
466
468
 
467
469
  ## Kuaishou Tools
468
470
 
@@ -480,9 +482,9 @@ Current Sensitive Words Check workflows include:
480
482
  | `kuaishou_get_user_info_by_profile_url` | Resolve a Kuaishou profile link, including live/fw-user profile shares, short link, or share text into creator profile data; successful results return a reusable non-empty user_id. |
481
483
  | `kuaishou_get_user_posted_videos_by_user_id` | Fetch a paginated list of works published by a creator when the caller already has a non-empty user_id. |
482
484
  | `kuaishou_get_user_posted_videos_by_profile_url` | Fetch a paginated list of works published by a creator from a profile link, short link, or share text that resolves directly to a non-empty user_id; for live/fw-user profile shares, call creator profile first and use the returned non-empty user_id. |
483
- | `kuaishou_submit_video_speech_text_by_video_url` | Submit a Kuaishou video speech-to-text transcript job from a work page link, short link, or share text; submit waits up to 15 seconds. |
484
- | `kuaishou_submit_video_speech_text_by_photo_id` | Submit a Kuaishou video speech-to-text transcript job when the caller already has a photo_id; submit waits up to 15 seconds. |
485
- | `kuaishou_get_video_speech_text_job` | Check a Kuaishou speech-to-text transcript job by job_id without creating a new task. Returns transcript plus content context, not summary. |
485
+ | `kuaishou_submit_video_speech_text_by_video_url` | Submit a Kuaishou video speech-to-text transcript job from a work page link, short link, or share text; submit waits up to 210 seconds. If unfinished, continue checking the same job_id with get-job wait_seconds up to 240 until is_terminal is true. |
486
+ | `kuaishou_submit_video_speech_text_by_photo_id` | Submit a Kuaishou video speech-to-text transcript job when the caller already has a photo_id; submit waits up to 210 seconds. If unfinished, continue checking the same job_id with get-job wait_seconds up to 240 until is_terminal is true. |
487
+ | `kuaishou_get_video_speech_text_job` | Check a Kuaishou speech-to-text transcript job by job_id without creating a new task; optional wait_seconds 0-240 can long-poll the same job in one request. Continue querying the same job_id until is_terminal is true. Returns transcript plus content context, not summary. |
486
488
 
487
489
  ## Bilibili Tools
488
490
 
@@ -505,9 +507,9 @@ Current Sensitive Words Check workflows include:
505
507
  | `weibo_get_user_info_by_profile_url` | Resolve a Weibo profile link, short link, or share text into creator profile data. |
506
508
  | `weibo_get_user_posts_by_user_id` | Fetch a paginated list of posts published by a creator when the caller already has a user_id. |
507
509
  | `weibo_get_user_posts_by_profile_url` | Fetch a paginated list of posts published by a creator from a profile link, short link, or share text. |
508
- | `weibo_submit_video_speech_text_by_post_url` | Submit a Weibo video speech-to-text transcript job from a post URL, short link, or share text; submit waits up to 15 seconds. |
509
- | `weibo_submit_video_speech_text_by_post_id` | Submit a Weibo video speech-to-text transcript job when the caller already has a post_id; submit waits up to 15 seconds. |
510
- | `weibo_get_video_speech_text_job` | Check a Weibo speech-to-text transcript job by job_id without creating a new task. Returns transcript plus content context, not summary. |
510
+ | `weibo_submit_video_speech_text_by_post_url` | Submit a Weibo video speech-to-text transcript job from a post URL, short link, or share text; submit waits up to 210 seconds. If unfinished, continue checking the same job_id with get-job wait_seconds up to 240 until is_terminal is true. |
511
+ | `weibo_submit_video_speech_text_by_post_id` | Submit a Weibo video speech-to-text transcript job when the caller already has a post_id; submit waits up to 210 seconds. If unfinished, continue checking the same job_id with get-job wait_seconds up to 240 until is_terminal is true. |
512
+ | `weibo_get_video_speech_text_job` | Check a Weibo speech-to-text transcript job by job_id without creating a new task; optional wait_seconds 0-240 can long-poll the same job in one request. Continue querying the same job_id until is_terminal is true. Returns transcript plus content context, not summary. |
511
513
 
512
514
  ## WeChat Tools
513
515
 
@@ -524,9 +526,9 @@ Current Sensitive Words Check workflows include:
524
526
  | `wechat_get_user_info_by_user_id` | Fetch creator profile data when the finder user_id is already known. |
525
527
  | `wechat_get_user_posted_videos_by_user_id` | Fetch a paginated list of videos published by a creator when the finder user_id is already known. |
526
528
  | `wechat_get_user_posted_videos_by_url` | Fetch a paginated list of videos published by a creator from a video link or share text. |
527
- | `wechat_submit_video_speech_text_by_video_url` | Submit a WeChat Channels / 视频号 video speech-to-text transcript job from a video link or share text; submit waits up to 15 seconds. |
528
- | `wechat_submit_video_speech_text_by_encrypted_object_id` | Submit a WeChat Channels / 视频号 video speech-to-text transcript job when encrypted_object_id is already known; submit waits up to 15 seconds. |
529
- | `wechat_get_video_speech_text_job` | Check a WeChat Channels / 视频号 speech-to-text transcript job by job_id without creating a new task. Returns transcript plus content context, not summary. |
529
+ | `wechat_submit_video_speech_text_by_video_url` | Submit a WeChat Channels / 视频号 video speech-to-text transcript job from a video link or share text; submit waits up to 210 seconds. If unfinished, continue checking the same job_id with get-job wait_seconds up to 240 until is_terminal is true. |
530
+ | `wechat_submit_video_speech_text_by_encrypted_object_id` | Submit a WeChat Channels / 视频号 video speech-to-text transcript job when encrypted_object_id is already known; submit waits up to 210 seconds. If unfinished, continue checking the same job_id with get-job wait_seconds up to 240 until is_terminal is true. |
531
+ | `wechat_get_video_speech_text_job` | Check a WeChat Channels / 视频号 speech-to-text transcript job by job_id without creating a new task; optional wait_seconds 0-240 can long-poll the same job in one request. Continue querying the same job_id until is_terminal is true. Returns transcript plus content context, not summary. |
530
532
 
531
533
  ## Sensitive Words Check Tools
532
534
 
package/cli.mjs CHANGED
@@ -22,7 +22,7 @@ import { downloadXhsMediaFromUrl } from "./lib/media/xhs-download.mjs";
22
22
  export { decryptWechatMediaCommand };
23
23
 
24
24
  const PACKAGE_NAME = "socialdatax-skills";
25
- const PACKAGE_VERSION = "0.2.28";
25
+ const PACKAGE_VERSION = "0.2.29";
26
26
  const PACKAGE_SPEC = `${PACKAGE_NAME}@latest`;
27
27
  const LOG_PREFIX = `[${PACKAGE_NAME}]`;
28
28
  const MIN_NODE_VERSION = "20.18.1";
@@ -42,9 +42,16 @@ const SOURCE_SKILL_HEADER = "X-SocialDataX-Source-Skill";
42
42
  const SOURCE_ATTRIBUTION_PATTERN = /^[a-z0-9][a-z0-9-]{0,99}$/;
43
43
  export const TRANSCRIPT_JOB_DESCRIPTION_SUFFIX =
44
44
  "Returns transcript plus content context, not summary.";
45
+ const TRANSCRIPT_SUBMIT_WAIT_DESCRIPTION =
46
+ "提交后最多等待 210 秒;未完成时继续用对应 get-job 查询同一个 job_id 直到终态,不要重复提交.";
47
+ const TRANSCRIPT_GET_JOB_WAIT_DESCRIPTION =
48
+ "Optional wait_seconds 0-240 can long-poll the same job in one request.";
49
+ const TRANSCRIPT_DEFAULT_MAX_WAIT_SECONDS = 1200;
50
+ const TRANSCRIPT_GET_JOB_WAIT_SECONDS = 240;
51
+ const TRANSCRIPT_FALLBACK_POLL_SECONDS = 2;
45
52
 
46
53
  export function buildTranscriptJobDescription(subject) {
47
- return `Check ${subject} by job_id without starting a new task. ${TRANSCRIPT_JOB_DESCRIPTION_SUFFIX}`;
54
+ return `Check ${subject} by job_id without starting a new task. ${TRANSCRIPT_GET_JOB_WAIT_DESCRIPTION} Continue querying the same job_id until is_terminal is true. ${TRANSCRIPT_JOB_DESCRIPTION_SUFFIX}`;
48
55
  }
49
56
 
50
57
  const AVAILABLE_SKILLS = [
@@ -163,7 +170,7 @@ const XHS_DIRECT_ACTION_OPTIONS = {
163
170
  "sinceDays",
164
171
  "pretty",
165
172
  ],
166
- transcript: ["url", "noteId", "jobId", "pretty"],
173
+ transcript: ["url", "noteId", "jobId", "maxWaitSeconds", "pretty"],
167
174
  "download-media": ["url", "output", "outputDir", "pretty"],
168
175
  };
169
176
  const XHS_DIRECT_ACTION_NAMES = Object.keys(XHS_DIRECT_ACTION_OPTIONS).join(", ");
@@ -184,6 +191,7 @@ const XHS_OPTION_DISPLAY_NAMES = {
184
191
  profileUrl: "--profile-url",
185
192
  userId: "--user-id",
186
193
  jobId: "--job-id",
194
+ maxWaitSeconds: "--max-wait-seconds",
187
195
  output: "--output",
188
196
  outputDir: "--output-dir",
189
197
  };
@@ -252,7 +260,7 @@ const DOUYIN_DIRECT_ACTION_OPTIONS = {
252
260
  "pretty",
253
261
  ],
254
262
  "user-series": ["secUserId", "profileUrl", "pageToken", "pages", "all", "maxItems", "pretty"],
255
- transcript: ["url", "awemeId", "jobId", "pretty"],
263
+ transcript: ["url", "awemeId", "jobId", "maxWaitSeconds", "pretty"],
256
264
  };
257
265
  const DOUYIN_DIRECT_ACTION_NAMES = Object.keys(DOUYIN_DIRECT_ACTION_OPTIONS).join(", ");
258
266
  const DOUYIN_SEARCH_SORT_TYPES = ["general", "time_descending", "like_count_descending"];
@@ -282,6 +290,7 @@ const DOUYIN_OPTION_DISPLAY_NAMES = {
282
290
  commentId: "--comment-id",
283
291
  secUserId: "--sec-user-id",
284
292
  jobId: "--job-id",
293
+ maxWaitSeconds: "--max-wait-seconds",
285
294
  };
286
295
  const KUAISHOU_DIRECT_ACTION_OPTIONS = {
287
296
  "hot-search": ["pretty"],
@@ -319,7 +328,7 @@ const KUAISHOU_DIRECT_ACTION_OPTIONS = {
319
328
  "sinceDays",
320
329
  "pretty",
321
330
  ],
322
- transcript: ["url", "photoId", "jobId", "pretty"],
331
+ transcript: ["url", "photoId", "jobId", "maxWaitSeconds", "pretty"],
323
332
  };
324
333
  const KUAISHOU_DIRECT_ACTION_NAMES = Object.keys(KUAISHOU_DIRECT_ACTION_OPTIONS).join(", ");
325
334
  const KUAISHOU_OPTION_DISPLAY_NAMES = {
@@ -336,6 +345,7 @@ const KUAISHOU_OPTION_DISPLAY_NAMES = {
336
345
  commentId: "--comment-id",
337
346
  userId: "--user-id",
338
347
  jobId: "--job-id",
348
+ maxWaitSeconds: "--max-wait-seconds",
339
349
  };
340
350
  const BILIBILI_DIRECT_ACTION_OPTIONS = {
341
351
  download: ["url", "output", "outputDir", "ffmpegPath", "keepTracks", "pretty"],
@@ -384,7 +394,7 @@ const WEIBO_DIRECT_ACTION_OPTIONS = {
384
394
  "sinceDays",
385
395
  "pretty",
386
396
  ],
387
- transcript: ["postUrl", "postId", "jobId", "pretty"],
397
+ transcript: ["postUrl", "postId", "jobId", "maxWaitSeconds", "pretty"],
388
398
  };
389
399
  const WEIBO_DIRECT_ACTION_NAMES = Object.keys(WEIBO_DIRECT_ACTION_OPTIONS).join(", ");
390
400
  const WEIBO_OPTION_DISPLAY_NAMES = {
@@ -401,6 +411,7 @@ const WEIBO_OPTION_DISPLAY_NAMES = {
401
411
  profileUrl: "--profile-url",
402
412
  userId: "--user-id",
403
413
  jobId: "--job-id",
414
+ maxWaitSeconds: "--max-wait-seconds",
404
415
  };
405
416
  const WECHAT_DIRECT_ACTION_OPTIONS = {
406
417
  "hot-search": ["pretty"],
@@ -450,7 +461,7 @@ const WECHAT_DIRECT_ACTION_OPTIONS = {
450
461
  "sinceDays",
451
462
  "pretty",
452
463
  ],
453
- transcript: ["url", "encryptedObjectId", "jobId", "pretty"],
464
+ transcript: ["url", "encryptedObjectId", "jobId", "maxWaitSeconds", "pretty"],
454
465
  "decrypt-media": ["mediaUrl", "output", "pretty"],
455
466
  };
456
467
  const WECHAT_LOCAL_DIRECT_ACTIONS = new Set(["decrypt-media"]);
@@ -485,6 +496,7 @@ const WECHAT_OPTION_DISPLAY_NAMES = {
485
496
  jobId: "--job-id",
486
497
  mediaUrl: "--media-url",
487
498
  output: "--output",
499
+ maxWaitSeconds: "--max-wait-seconds",
488
500
  };
489
501
  const SENSITIVE_CHECK_DIRECT_ACTION_OPTIONS = {
490
502
  text: ["text", "platform", "pretty"],
@@ -567,12 +579,12 @@ const PLATFORMS = {
567
579
  {
568
580
  name: "xhs_submit_video_speech_text_by_note_url",
569
581
  description:
570
- "Submit a video note speech-to-text transcript task from a note link, short link, or share text; 提交后最多等待 15 秒.",
582
+ `Submit a video note speech-to-text transcript task from a note link, short link, or share text; ${TRANSCRIPT_SUBMIT_WAIT_DESCRIPTION}`,
571
583
  },
572
584
  {
573
585
  name: "xhs_submit_video_speech_text_by_note_id",
574
586
  description:
575
- "Submit a video note speech-to-text transcript task from a note_id; 提交后最多等待 15 秒.",
587
+ `Submit a video note speech-to-text transcript task from a note_id; ${TRANSCRIPT_SUBMIT_WAIT_DESCRIPTION}`,
576
588
  },
577
589
  {
578
590
  name: "xhs_get_video_speech_text_job",
@@ -651,12 +663,12 @@ const PLATFORMS = {
651
663
  {
652
664
  name: "douyin_submit_video_speech_text_by_video_url",
653
665
  description:
654
- "Submit a Douyin work video speech-to-text transcript task from a work page link, short link, or share text; 提交后最多等待 15 秒.",
666
+ `Submit a Douyin work video speech-to-text transcript task from a work page link, short link, or share text; ${TRANSCRIPT_SUBMIT_WAIT_DESCRIPTION}`,
655
667
  },
656
668
  {
657
669
  name: "douyin_submit_video_speech_text_by_aweme_id",
658
670
  description:
659
- "Submit a Douyin work video speech-to-text transcript task from an aweme_id; 提交后最多等待 15 秒.",
671
+ `Submit a Douyin work video speech-to-text transcript task from an aweme_id; ${TRANSCRIPT_SUBMIT_WAIT_DESCRIPTION}`,
660
672
  },
661
673
  {
662
674
  name: "douyin_get_video_speech_text_job",
@@ -735,12 +747,12 @@ const PLATFORMS = {
735
747
  {
736
748
  name: "kuaishou_submit_video_speech_text_by_video_url",
737
749
  description:
738
- "Submit a Kuaishou work video speech-to-text transcript task from a work page link, short link, or share text; 提交后最多等待 15 秒.",
750
+ `Submit a Kuaishou work video speech-to-text transcript task from a work page link, short link, or share text; ${TRANSCRIPT_SUBMIT_WAIT_DESCRIPTION}`,
739
751
  },
740
752
  {
741
753
  name: "kuaishou_submit_video_speech_text_by_photo_id",
742
754
  description:
743
- "Submit a Kuaishou work video speech-to-text transcript task from a photo_id; 提交后最多等待 15 秒.",
755
+ `Submit a Kuaishou work video speech-to-text transcript task from a photo_id; ${TRANSCRIPT_SUBMIT_WAIT_DESCRIPTION}`,
744
756
  },
745
757
  {
746
758
  name: "kuaishou_get_video_speech_text_job",
@@ -840,12 +852,12 @@ const PLATFORMS = {
840
852
  {
841
853
  name: "weibo_submit_video_speech_text_by_post_url",
842
854
  description:
843
- "Submit a Weibo video speech-to-text transcript task from a post URL, short link, or share text; 提交后最多等待 15 秒.",
855
+ `Submit a Weibo video speech-to-text transcript task from a post URL, short link, or share text; ${TRANSCRIPT_SUBMIT_WAIT_DESCRIPTION}`,
844
856
  },
845
857
  {
846
858
  name: "weibo_submit_video_speech_text_by_post_id",
847
859
  description:
848
- "Submit a Weibo video speech-to-text transcript task from a post_id; 提交后最多等待 15 秒.",
860
+ `Submit a Weibo video speech-to-text transcript task from a post_id; ${TRANSCRIPT_SUBMIT_WAIT_DESCRIPTION}`,
849
861
  },
850
862
  {
851
863
  name: "weibo_get_video_speech_text_job",
@@ -917,12 +929,12 @@ const PLATFORMS = {
917
929
  {
918
930
  name: "wechat_submit_video_speech_text_by_video_url",
919
931
  description:
920
- "Submit a WeChat Channels / 视频号 video speech-to-text transcript task from a video link or share text; 提交后最多等待 15 秒.",
932
+ `Submit a WeChat Channels / 视频号 video speech-to-text transcript task from a video link or share text; ${TRANSCRIPT_SUBMIT_WAIT_DESCRIPTION}`,
921
933
  },
922
934
  {
923
935
  name: "wechat_submit_video_speech_text_by_encrypted_object_id",
924
936
  description:
925
- "Submit a WeChat Channels / 视频号 video speech-to-text transcript task from an encrypted_object_id; 提交后最多等待 15 秒.",
937
+ `Submit a WeChat Channels / 视频号 video speech-to-text transcript task from an encrypted_object_id; ${TRANSCRIPT_SUBMIT_WAIT_DESCRIPTION}`,
926
938
  },
927
939
  {
928
940
  name: "wechat_get_video_speech_text_job",
@@ -1124,6 +1136,7 @@ function validateXhsDirectActionOptions(action, options) {
1124
1136
  validateKnownOptions(options, allowedDirectOptions(allowedOptions));
1125
1137
  validateDirectMetaOptions(options);
1126
1138
  validateDirectPaginationOptions("xhs", action, options);
1139
+ validateDirectTranscriptOptions(action, options);
1127
1140
  for (const key of allowedOptions) {
1128
1141
  if (!DIRECT_BOOLEAN_OPTIONS.has(key)) {
1129
1142
  requireOptionValue(options, key, XHS_OPTION_DISPLAY_NAMES[key]);
@@ -1140,6 +1153,7 @@ function validateDouyinDirectActionOptions(action, options) {
1140
1153
  validateKnownOptions(options, allowedDirectOptions(allowedOptions));
1141
1154
  validateDirectMetaOptions(options);
1142
1155
  validateDirectPaginationOptions("douyin", action, options);
1156
+ validateDirectTranscriptOptions(action, options);
1143
1157
  for (const key of allowedOptions) {
1144
1158
  if (!DIRECT_BOOLEAN_OPTIONS.has(key)) {
1145
1159
  requireOptionValue(options, key, DOUYIN_OPTION_DISPLAY_NAMES[key]);
@@ -1156,6 +1170,7 @@ function validateKuaishouDirectActionOptions(action, options) {
1156
1170
  validateKnownOptions(options, allowedDirectOptions(allowedOptions));
1157
1171
  validateDirectMetaOptions(options);
1158
1172
  validateDirectPaginationOptions("kuaishou", action, options);
1173
+ validateDirectTranscriptOptions(action, options);
1159
1174
  for (const key of allowedOptions) {
1160
1175
  if (!DIRECT_BOOLEAN_OPTIONS.has(key)) {
1161
1176
  requireOptionValue(options, key, KUAISHOU_OPTION_DISPLAY_NAMES[key]);
@@ -1200,6 +1215,7 @@ function validateWeiboDirectActionOptions(action, options) {
1200
1215
  validateKnownOptions(options, allowedDirectOptions(allowedOptions));
1201
1216
  validateDirectMetaOptions(options);
1202
1217
  validateDirectPaginationOptions("weibo", action, options);
1218
+ validateDirectTranscriptOptions(action, options);
1203
1219
  for (const key of allowedOptions) {
1204
1220
  if (!DIRECT_BOOLEAN_OPTIONS.has(key)) {
1205
1221
  requireOptionValue(options, key, WEIBO_OPTION_DISPLAY_NAMES[key]);
@@ -1220,6 +1236,7 @@ function validateWechatDirectActionOptions(action, options) {
1220
1236
  validateKnownOptions(options, allowedDirectOptions(allowedOptions));
1221
1237
  validateDirectMetaOptions(options);
1222
1238
  validateDirectPaginationOptions("wechat", action, options);
1239
+ validateDirectTranscriptOptions(action, options);
1223
1240
  }
1224
1241
  for (const key of allowedOptions) {
1225
1242
  if (!DIRECT_BOOLEAN_OPTIONS.has(key)) {
@@ -1283,6 +1300,18 @@ function validateDirectPaginationOptions(platformId, action, options) {
1283
1300
  }
1284
1301
  }
1285
1302
 
1303
+ function validateDirectTranscriptOptions(action, options) {
1304
+ if (action !== "transcript") {
1305
+ return;
1306
+ }
1307
+ if (options.maxWaitSeconds !== undefined) {
1308
+ parsePositiveIntegerOption(
1309
+ options.maxWaitSeconds,
1310
+ "--max-wait-seconds"
1311
+ );
1312
+ }
1313
+ }
1314
+
1286
1315
  function parsePositiveIntegerOption(value, displayName) {
1287
1316
  const text = String(value);
1288
1317
  if (!/^\d+$/.test(text)) {
@@ -2254,9 +2283,7 @@ async function runXhsDirectCommand(args) {
2254
2283
  }
2255
2284
 
2256
2285
  const operation = attachDirectMetadata(buildXhsOperation(action, options), options);
2257
- const data = shouldUsePaginatedDirectOutput(options)
2258
- ? await callPaginatedDirectOperation(operation, options)
2259
- : await callDirectOperation(operation);
2286
+ const data = await callDirectOperationWithOptions(operation, options);
2260
2287
  const envelope = {
2261
2288
  platform: operation.platform.id,
2262
2289
  tool: operation.tool,
@@ -2285,9 +2312,7 @@ async function runDouyinDirectCommand(args) {
2285
2312
  validateDouyinDirectActionOptions(action, options);
2286
2313
 
2287
2314
  const operation = attachDirectMetadata(buildDouyinOperation(action, options), options);
2288
- const data = shouldUsePaginatedDirectOutput(options)
2289
- ? await callPaginatedDirectOperation(operation, options)
2290
- : await callDirectOperation(operation);
2315
+ const data = await callDirectOperationWithOptions(operation, options);
2291
2316
  const envelope = {
2292
2317
  platform: operation.platform.id,
2293
2318
  tool: operation.tool,
@@ -2316,9 +2341,7 @@ async function runKuaishouDirectCommand(args) {
2316
2341
  validateKuaishouDirectActionOptions(action, options);
2317
2342
 
2318
2343
  const operation = attachDirectMetadata(buildKuaishouOperation(action, options), options);
2319
- const data = shouldUsePaginatedDirectOutput(options)
2320
- ? await callPaginatedDirectOperation(operation, options)
2321
- : await callDirectOperation(operation);
2344
+ const data = await callDirectOperationWithOptions(operation, options);
2322
2345
  const envelope = {
2323
2346
  platform: operation.platform.id,
2324
2347
  tool: operation.tool,
@@ -2447,9 +2470,7 @@ async function runWeiboDirectCommand(args) {
2447
2470
  validateWeiboDirectActionOptions(action, options);
2448
2471
 
2449
2472
  const operation = attachDirectMetadata(buildWeiboOperation(action, options), options);
2450
- const data = shouldUsePaginatedDirectOutput(options)
2451
- ? await callPaginatedDirectOperation(operation, options)
2452
- : await callDirectOperation(operation);
2473
+ const data = await callDirectOperationWithOptions(operation, options);
2453
2474
  const envelope = {
2454
2475
  platform: operation.platform.id,
2455
2476
  tool: operation.tool,
@@ -2485,9 +2506,7 @@ async function runWechatDirectCommand(args) {
2485
2506
  }
2486
2507
 
2487
2508
  const operation = attachDirectMetadata(buildWechatOperation(action, options), options);
2488
- const data = shouldUsePaginatedDirectOutput(options)
2489
- ? await callPaginatedDirectOperation(operation, options)
2490
- : await callDirectOperation(operation);
2509
+ const data = await callDirectOperationWithOptions(operation, options);
2491
2510
  const envelope = {
2492
2511
  platform: operation.platform.id,
2493
2512
  tool: operation.tool,
@@ -3136,13 +3155,15 @@ function buildSensitiveCheckOperation(action, options) {
3136
3155
  }
3137
3156
  }
3138
3157
 
3139
- function buildDirectOperation(operation, { tool, toolArguments }, platform = PLATFORMS.xhs) {
3158
+ function buildDirectOperation(operation, call, platform = PLATFORMS.xhs) {
3159
+ const { tool, toolArguments, ...metadata } = call;
3140
3160
  return {
3141
3161
  platform,
3142
3162
  operation,
3143
3163
  backend: "mcp",
3144
3164
  tool,
3145
3165
  arguments: toolArguments,
3166
+ ...metadata,
3146
3167
  };
3147
3168
  }
3148
3169
 
@@ -3299,6 +3320,9 @@ function buildTranscriptCall(
3299
3320
  toolArguments: {
3300
3321
  [choice.argument]: choice.value,
3301
3322
  },
3323
+ transcriptJobTool: jobTool,
3324
+ transcriptJobArgument: jobArgument,
3325
+ transcriptIsJobLookup: choice.tool === jobTool,
3302
3326
  };
3303
3327
  }
3304
3328
 
@@ -3663,6 +3687,144 @@ async function callDirectOperation(operation) {
3663
3687
  }
3664
3688
  }
3665
3689
 
3690
+ async function callDirectOperationWithOptions(operation, options) {
3691
+ if (shouldUsePaginatedDirectOutput(options)) {
3692
+ return callPaginatedDirectOperation(operation, options);
3693
+ }
3694
+ if (operation.operation === "transcript") {
3695
+ return callTranscriptDirectOperation(operation, options);
3696
+ }
3697
+ return callDirectOperation(operation);
3698
+ }
3699
+
3700
+ async function callTranscriptDirectOperation(operation, options) {
3701
+ const maxWaitSeconds = parseTranscriptMaxWaitSeconds(options);
3702
+ let currentOperation = transcriptInitialOperation(operation, maxWaitSeconds);
3703
+ let data = await callDirectOperation(currentOperation);
3704
+ if (maxWaitSeconds <= 0 || isTerminalTranscriptData(data)) {
3705
+ return data;
3706
+ }
3707
+
3708
+ let jobId = readTranscriptJobId(data, operation);
3709
+ if (!jobId || !operation.transcriptJobTool) {
3710
+ return data;
3711
+ }
3712
+
3713
+ const startedAtMs = Date.now();
3714
+ while (!isTerminalTranscriptData(data)) {
3715
+ const remainingBeforeSleep = transcriptRemainingWaitSeconds(
3716
+ startedAtMs,
3717
+ maxWaitSeconds
3718
+ );
3719
+ if (remainingBeforeSleep <= 0) {
3720
+ break;
3721
+ }
3722
+
3723
+ const sleepSeconds = Math.min(
3724
+ transcriptNextPollAfterSeconds(data),
3725
+ remainingBeforeSleep
3726
+ );
3727
+ if (sleepSeconds > 0) {
3728
+ await sleepForSeconds(sleepSeconds);
3729
+ }
3730
+
3731
+ const remainingSeconds = transcriptRemainingWaitSeconds(
3732
+ startedAtMs,
3733
+ maxWaitSeconds
3734
+ );
3735
+ if (remainingSeconds <= 0) {
3736
+ break;
3737
+ }
3738
+
3739
+ currentOperation = transcriptGetJobOperation(
3740
+ operation,
3741
+ jobId,
3742
+ transcriptWaitSecondsForCall(remainingSeconds)
3743
+ );
3744
+ data = await callDirectOperation(currentOperation);
3745
+ jobId = readTranscriptJobId(data, operation) || jobId;
3746
+ }
3747
+
3748
+ return data;
3749
+ }
3750
+
3751
+ function parseTranscriptMaxWaitSeconds(options) {
3752
+ if (options.maxWaitSeconds !== undefined) {
3753
+ return parsePositiveIntegerOption(
3754
+ options.maxWaitSeconds,
3755
+ "--max-wait-seconds"
3756
+ );
3757
+ }
3758
+ return TRANSCRIPT_DEFAULT_MAX_WAIT_SECONDS;
3759
+ }
3760
+
3761
+ function transcriptInitialOperation(operation, maxWaitSeconds) {
3762
+ const initialOperation = cloneDirectOperation(operation);
3763
+ const isJobLookup =
3764
+ operation.transcriptIsJobLookup ||
3765
+ operation.tool === operation.transcriptJobTool ||
3766
+ Object.hasOwn(operation.arguments, "job_id");
3767
+ if (isJobLookup && maxWaitSeconds > 0) {
3768
+ initialOperation.arguments.wait_seconds =
3769
+ transcriptWaitSecondsForCall(maxWaitSeconds);
3770
+ }
3771
+ return initialOperation;
3772
+ }
3773
+
3774
+ function transcriptGetJobOperation(operation, jobId, waitSeconds) {
3775
+ const jobArgument = operation.transcriptJobArgument || "job_id";
3776
+ return {
3777
+ ...operation,
3778
+ tool: operation.transcriptJobTool,
3779
+ arguments: {
3780
+ [jobArgument]: jobId,
3781
+ wait_seconds: waitSeconds,
3782
+ },
3783
+ };
3784
+ }
3785
+
3786
+ function transcriptWaitSecondsForCall(remainingSeconds) {
3787
+ return Math.min(
3788
+ TRANSCRIPT_GET_JOB_WAIT_SECONDS,
3789
+ Math.max(1, Math.ceil(remainingSeconds))
3790
+ );
3791
+ }
3792
+
3793
+ function transcriptRemainingWaitSeconds(startedAtMs, maxWaitSeconds) {
3794
+ return maxWaitSeconds - (Date.now() - startedAtMs) / 1000;
3795
+ }
3796
+
3797
+ function transcriptNextPollAfterSeconds(data) {
3798
+ const value = Number(data?.next_poll_after_seconds);
3799
+ if (Number.isFinite(value) && value >= 0) {
3800
+ return value;
3801
+ }
3802
+ return TRANSCRIPT_FALLBACK_POLL_SECONDS;
3803
+ }
3804
+
3805
+ function readTranscriptJobId(data, operation) {
3806
+ const candidate =
3807
+ data?.job_id ||
3808
+ data?.next_action?.job_id ||
3809
+ data?.next_action?.arguments?.job_id ||
3810
+ operation.arguments?.[operation.transcriptJobArgument || "job_id"];
3811
+ const normalized = String(candidate || "").trim();
3812
+ return normalized || undefined;
3813
+ }
3814
+
3815
+ function isTerminalTranscriptData(data) {
3816
+ if (data?.is_terminal === true) {
3817
+ return true;
3818
+ }
3819
+ return ["succeeded", "failed", "expired"].includes(data?.status);
3820
+ }
3821
+
3822
+ function sleepForSeconds(seconds) {
3823
+ return new Promise((resolve) => {
3824
+ setTimeout(resolve, Math.max(seconds, 0) * 1000);
3825
+ });
3826
+ }
3827
+
3666
3828
  function shouldUsePaginatedDirectOutput(options) {
3667
3829
  return Boolean(
3668
3830
  options.all ||
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "socialdatax-skills",
3
- "version": "0.2.28",
3
+ "version": "0.2.29",
4
4
  "description": "SocialDataX / 社媒数据助手 skill installer and direct CLI helper for hosted platform social media MCP services.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -112,7 +112,7 @@ The command prints JSON with `platform`, `tool`, `arguments`, and `data`.
112
112
 
113
113
  ## Safety Boundary
114
114
 
115
- Platform detail access is read-only. It uses `SOCIALDATAX_API_KEY` from the user's environment at runtime for SocialDataX detail requests. Generated Skill files do not contain API keys. It does not read local browser data or perform login, posting, liking, commenting, or account changes. The optional WeChat Channels / 视频号 local save command writes only to the user-provided `--output` file, decrypts when needed, and does not require `SOCIALDATAX_API_KEY`. Prefer the direct CLI; hosted MCP tools are optional when the current agent already supports authenticated streamable HTTP MCP.
115
+ Platform detail access is read-only. It uses `SOCIALDATAX_API_KEY` from the user's environment at runtime for SocialDataX detail requests. Generated Skill files do not contain API keys. It does not read local browser data or perform login, posting, liking, commenting, or account changes. The optional XHS local save command writes only to the requested local `--output` path or `--output-dir` directory and does not require `SOCIALDATAX_API_KEY`. The optional WeChat Channels / 视频号 local save command writes only to the user-provided `--output` file, decrypts when needed, and does not require `SOCIALDATAX_API_KEY`. Prefer the direct CLI; hosted MCP tools are optional when the current agent already supports authenticated streamable HTTP MCP.
116
116
 
117
117
  ## MCP Tools
118
118
 
@@ -118,10 +118,12 @@ Required arguments:
118
118
 
119
119
  Optional arguments:
120
120
 
121
+ - Direct CLI transcript commands automatically wait and poll the same job by default; use positive `--max-wait-seconds <seconds>` to tune the CLI-side follow-up window after the first response.
121
122
  - `--source-client socialdatax-skills --source-platform npm --source-skill media-transcript`: usage attribution for this Agent Skill; keep these values unchanged when running examples from this Skill.
122
123
 
123
124
  Use the direct CLI first when the agent can run shell commands. These video speech-to-text transcript / 口播转文字 workflows submit a bounded analysis job or check an existing job.
124
- Submit commands may wait briefly after creating the job. If the transcript is not ready, return the `job_id` and next action instead of starting a second job.
125
+ Direct CLI transcript commands try to deliver the final result in one run: submit waits server-side up to 210 seconds, then the CLI keeps querying the same `job_id` with get-job long polling of up to 240 seconds per request for a bounded follow-up window. Do not start a second submit job just to poll status.
126
+ If the direct CLI returns a non-terminal job because the command was interrupted or reached `--max-wait-seconds`, keep the returned `job_id` and continue with the matching `transcript --job-id <job_id>` command.
125
127
 
126
128
  ## Safety Boundary
127
129
 
@@ -153,11 +155,11 @@ If MCP tools are already available in the current agent, choose the platform-spe
153
155
  - Kuaishou: `kuaishou_submit_video_speech_text_by_video_url`, `kuaishou_submit_video_speech_text_by_photo_id`, `kuaishou_get_video_speech_text_job`.
154
156
  - Weibo: `weibo_submit_video_speech_text_by_post_url`, `weibo_submit_video_speech_text_by_post_id`, `weibo_get_video_speech_text_job`.
155
157
  - WeChat Channels / 视频号: `wechat_submit_video_speech_text_by_video_url`, `wechat_submit_video_speech_text_by_encrypted_object_id`, `wechat_get_video_speech_text_job`.
156
- Do not start a duplicate transcript job only to poll status. Use the platform get-job tool with the returned `job_id`.
158
+ Do not start a duplicate transcript job only to poll status. If an MCP response is non-terminal, call `data.next_action.tool_name` with `data.next_action.arguments` exactly as returned; those arguments include the same `job_id` and a bounded `wait_seconds` value. Continue until `is_terminal` is `true`.
157
159
 
158
160
  ## Output Guidance
159
161
 
160
162
  Return the transcript text and content context when available; include content IDs, titles or descriptions, author facts, and duration when the response provides them.
161
163
  This v1 surface does not return summary.
162
- When a job is still running, include the `job_id`, status, and next action so the user or agent can check later.
164
+ When a job is still running, continue querying the same `job_id` until a terminal result is available; only surface `job_id`, status, and next action if the tool cannot continue, the command/session is interrupted, the bounded wait window is reached, or the user asks to stop.
163
165
  If the result says no processable video resource is available or the job failed, report that as the observed job result instead of retrying with another platform.