socialdatax-skills 0.2.32 → 0.2.33

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
@@ -86,7 +86,7 @@ Common search phrases for this skill package:
86
86
  - Sensitive Words Check MCP server name: `sensitive-check`
87
87
  - Sensitive Words Check future MCP registry name: `com.socialdatax/sensitive-check`
88
88
  - Unified MCP registry name: none; this package installs skills and calls explicit platform services.
89
- - Current public capability version: `0.2.32`
89
+ - Current public capability version: `0.2.33`
90
90
 
91
91
  ## Direct CLI
92
92
 
@@ -183,7 +183,7 @@ npx -y socialdatax-skills@latest sensitive-check text --text "<content>" --platf
183
183
 
184
184
  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/douyin/kuaishou/weibo download-media` is also local: pass one media URL returned by 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/audio extensions in `--output-dir` mode.
185
185
 
186
- 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.
186
+ For transcript commands, the direct CLI tries to deliver the final result in one run: submit may wait up to 240 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.
187
187
 
188
188
  `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.
189
189
 
@@ -369,7 +369,7 @@ Current XHS workflows include:
369
369
  - Fetch paginated replies under a first-level comment.
370
370
  - Read creator profile data from a profile link, short link, share text, or user ID.
371
371
  - Fetch paginated creator note lists from a user ID, profile link, short link, or share text for content style and account research.
372
- - Submit and check video note 口播转文字 / speech-to-text transcript jobs; submit tools 提交后最多等待 210 秒,未完成时继续查询同一个 job_id 直到终态.
372
+ - Submit and check video note 口播转文字 / speech-to-text transcript jobs; submit tools 提交后最多等待 240 秒,未完成时继续查询同一个 job_id 直到终态.
373
373
  - Save returned `image_items[].image_url`, `image_items[].live_photo.video_url`, or `video.video_url` media links locally with `xhs download-media`.
374
374
 
375
375
  Current Kuaishou workflows include:
@@ -383,7 +383,7 @@ Current Kuaishou workflows include:
383
383
  - Continue Kuaishou list pagination only when `next_page_token` is non-empty; an empty string means there are no more results to request.
384
384
  - 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.
385
385
  - 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.
386
- - Submit and check video work 口播转文字 / speech-to-text transcript jobs; submit tools 提交后最多等待 210 秒,未完成时继续查询同一个 job_id 直到终态.
386
+ - Submit and check video work 口播转文字 / speech-to-text transcript jobs; submit tools 提交后最多等待 240 秒,未完成时继续查询同一个 job_id 直到终态.
387
387
  - Save returned `images[].url`, `video.play_url`, or `cover_image_url` media links locally with `kuaishou download-media`.
388
388
 
389
389
  Current Bilibili workflows include:
@@ -403,7 +403,7 @@ Current Douyin workflows include:
403
403
  - Read creator profile data from a profile link, short link, share text, or sec_user_id.
404
404
  - Fetch paginated creator work lists from a sec_user_id, profile link, short link, or share text.
405
405
  - Fetch paginated creator short-drama series lists from a sec_user_id, profile link, short link, or share text.
406
- - Submit and check video work 口播转文字 / speech-to-text transcript jobs; submit tools 提交后最多等待 210 秒,未完成时继续查询同一个 job_id 直到终态.
406
+ - Submit and check video work 口播转文字 / speech-to-text transcript jobs; submit tools 提交后最多等待 240 秒,未完成时继续查询同一个 job_id 直到终态.
407
407
  - Save returned `images[].url`, `images[].live_photo.play_url`, `video.play_url`, `music.play_url`, or `cover_image_url` media links locally with `douyin download-media`.
408
408
 
409
409
  Current Weibo workflows include:
@@ -416,7 +416,7 @@ Current Weibo workflows include:
416
416
  - Continue Weibo list pagination only when `next_page_token` is non-empty; an empty string means there are no more results to request.
417
417
  - Read creator profile data from a profile link, short link, share text, or user_id.
418
418
  - Fetch paginated creator post lists from a user_id, profile link, short link, or share text.
419
- - Submit and check Weibo video 口播转文字 / speech-to-text transcript jobs; submit tools 提交后最多等待 210 秒,未完成时继续查询同一个 job_id 直到终态.
419
+ - Submit and check Weibo video 口播转文字 / speech-to-text transcript jobs; submit tools 提交后最多等待 240 秒,未完成时继续查询同一个 job_id 直到终态.
420
420
  - Save returned `image_urls[]` or `video.video_url` media links locally with `weibo download-media`.
421
421
 
422
422
  Current WeChat / 微信 workflows include:
@@ -431,7 +431,7 @@ Current WeChat / 微信 workflows include:
431
431
  - Continue WeChat Channels list pagination only when `next_page_token` is non-empty; an empty string means there are no more results to request.
432
432
  - Read creator profile data from a `v2_...@finder` user_id.
433
433
  - Fetch paginated creator video lists from a `v2_...@finder` user_id or a video link/share text.
434
- - Submit and check WeChat Channels / 视频号 video 口播转文字 / speech-to-text transcript jobs; submit tools 提交后最多等待 210 秒,未完成时继续查询同一个 job_id 直到终态.
434
+ - Submit and check WeChat Channels / 视频号 video 口播转文字 / speech-to-text transcript jobs; submit tools 提交后最多等待 240 秒,未完成时继续查询同一个 job_id 直到终态.
435
435
 
436
436
  Current Sensitive Words Check workflows include:
437
437
 
@@ -455,9 +455,9 @@ Current Sensitive Words Check workflows include:
455
455
  | `xhs_get_user_info_by_profile_url` | Resolve a profile link, short link, or share text into creator profile data. |
456
456
  | `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. |
457
457
  | `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. |
458
- | `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. |
459
- | `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. |
460
- | `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. |
458
+ | `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 240 seconds. If unfinished, continue checking the same job_id until is_terminal is true. |
459
+ | `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 240 seconds. If unfinished, continue checking the same job_id until is_terminal is true. |
460
+ | `xhs_get_video_speech_text_job` | Check an XHS speech-to-text transcript job by job_id without creating a new task. Each call waits up to 240 seconds for the same job. If unfinished, continue querying the same job_id until is_terminal is true. Returns transcript plus content context, not summary. |
461
461
 
462
462
  ## Douyin Tools
463
463
 
@@ -476,9 +476,9 @@ Current Sensitive Words Check workflows include:
476
476
  | `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. |
477
477
  | `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. |
478
478
  | `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. |
479
- | `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. |
480
- | `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. |
481
- | `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. |
479
+ | `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 240 seconds. If unfinished, continue checking the same job_id until is_terminal is true. |
480
+ | `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 240 seconds. If unfinished, continue checking the same job_id until is_terminal is true. |
481
+ | `douyin_get_video_speech_text_job` | Check a Douyin speech-to-text transcript job by job_id without creating a new task. Each call waits up to 240 seconds for the same job. If unfinished, continue querying the same job_id until is_terminal is true. Returns transcript plus content context, not summary. |
482
482
 
483
483
  ## Kuaishou Tools
484
484
 
@@ -496,9 +496,9 @@ Current Sensitive Words Check workflows include:
496
496
  | `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. |
497
497
  | `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. |
498
498
  | `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. |
499
- | `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. |
500
- | `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. |
501
- | `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. |
499
+ | `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 240 seconds. If unfinished, continue checking the same job_id until is_terminal is true. |
500
+ | `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 240 seconds. If unfinished, continue checking the same job_id until is_terminal is true. |
501
+ | `kuaishou_get_video_speech_text_job` | Check a Kuaishou speech-to-text transcript job by job_id without creating a new task. Each call waits up to 240 seconds for the same job. If unfinished, continue querying the same job_id until is_terminal is true. Returns transcript plus content context, not summary. |
502
502
 
503
503
  ## Bilibili Tools
504
504
 
@@ -521,9 +521,9 @@ Current Sensitive Words Check workflows include:
521
521
  | `weibo_get_user_info_by_profile_url` | Resolve a Weibo profile link, short link, or share text into creator profile data. |
522
522
  | `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. |
523
523
  | `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. |
524
- | `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. |
525
- | `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. |
526
- | `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. |
524
+ | `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 240 seconds. If unfinished, continue checking the same job_id until is_terminal is true. |
525
+ | `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 240 seconds. If unfinished, continue checking the same job_id until is_terminal is true. |
526
+ | `weibo_get_video_speech_text_job` | Check a Weibo speech-to-text transcript job by job_id without creating a new task. Each call waits up to 240 seconds for the same job. If unfinished, continue querying the same job_id until is_terminal is true. Returns transcript plus content context, not summary. |
527
527
 
528
528
  ## WeChat Tools
529
529
 
@@ -540,9 +540,9 @@ Current Sensitive Words Check workflows include:
540
540
  | `wechat_get_user_info_by_user_id` | Fetch creator profile data when the `v2_...@finder` user_id is already known. |
541
541
  | `wechat_get_user_posted_videos_by_user_id` | Fetch a paginated list of videos published by a creator when the `v2_...@finder` user_id is already known. |
542
542
  | `wechat_get_user_posted_videos_by_url` | Fetch a paginated list of videos published by a creator from a video link or share text. |
543
- | `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. |
544
- | `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. |
545
- | `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. |
543
+ | `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 240 seconds. If unfinished, continue checking the same job_id until is_terminal is true. |
544
+ | `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 240 seconds. If unfinished, continue checking the same job_id until is_terminal is true. |
545
+ | `wechat_get_video_speech_text_job` | Check a WeChat Channels / 视频号 speech-to-text transcript job by job_id without creating a new task. Each call waits up to 240 seconds for the same job. If unfinished, continue querying the same job_id until is_terminal is true. Returns transcript plus content context, not summary. |
546
546
 
547
547
  ## Sensitive Words Check Tools
548
548
 
package/cli.mjs CHANGED
@@ -22,7 +22,7 @@ import { decryptWechatMediaCommand } from "./lib/media/wechat-decrypt.mjs";
22
22
  export { decryptWechatMediaCommand };
23
23
 
24
24
  const PACKAGE_NAME = "socialdatax-skills";
25
- const PACKAGE_VERSION = "0.2.32";
25
+ const PACKAGE_VERSION = "0.2.33";
26
26
  const PACKAGE_SPEC = `${PACKAGE_NAME}@latest`;
27
27
  const LOG_PREFIX = `[${PACKAGE_NAME}]`;
28
28
  const MIN_NODE_VERSION = "20.18.1";
@@ -43,15 +43,16 @@ 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
45
  const TRANSCRIPT_SUBMIT_WAIT_DESCRIPTION =
46
- "提交后最多等待 210 秒;未完成时继续用对应 get-job 查询同一个 job_id 直到终态,不要重复提交.";
46
+ "提交后最多等待 240 秒;未完成时继续用对应 get-job 查询同一个 job_id 直到终态,不要重复提交.";
47
47
  const TRANSCRIPT_GET_JOB_WAIT_DESCRIPTION =
48
- "Optional wait_seconds 0-240 can long-poll the same job in one request.";
48
+ "Each call waits up to 240 seconds for the same job. If unfinished, continue querying the same job_id until is_terminal is true.";
49
49
  const TRANSCRIPT_DEFAULT_MAX_WAIT_SECONDS = 1200;
50
- const TRANSCRIPT_GET_JOB_WAIT_SECONDS = 240;
51
50
  const TRANSCRIPT_FALLBACK_POLL_SECONDS = 2;
51
+ const TRANSCRIPT_MCP_CALL_TIMEOUT_MS = 330_000;
52
+ const MCP_REQUEST_TIMEOUT_ERROR_CODE = -32001;
52
53
 
53
54
  export function buildTranscriptJobDescription(subject) {
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}`;
55
+ return `Check ${subject} by job_id without starting a new task. ${TRANSCRIPT_GET_JOB_WAIT_DESCRIPTION} ${TRANSCRIPT_JOB_DESCRIPTION_SUFFIX}`;
55
56
  }
56
57
 
57
58
  const AVAILABLE_SKILLS = [
@@ -3764,10 +3765,10 @@ function buildXhsSubCommentsCall(options) {
3764
3765
  };
3765
3766
  }
3766
3767
 
3767
- async function callDirectOperation(operation) {
3768
+ async function callDirectOperation(operation, requestOptions) {
3768
3769
  switch (operation.backend) {
3769
3770
  case "mcp":
3770
- return callMcpBackend(operation);
3771
+ return callMcpBackend(operation, requestOptions);
3771
3772
  default:
3772
3773
  throw new Error(`Unsupported direct CLI backend: ${operation.backend}.`);
3773
3774
  }
@@ -3785,7 +3786,7 @@ async function callDirectOperationWithOptions(operation, options) {
3785
3786
 
3786
3787
  async function callTranscriptDirectOperation(operation, options) {
3787
3788
  const maxWaitSeconds = parseTranscriptMaxWaitSeconds(options);
3788
- let currentOperation = transcriptInitialOperation(operation, maxWaitSeconds);
3789
+ let currentOperation = transcriptInitialOperation(operation);
3789
3790
  let data = await callDirectOperation(currentOperation);
3790
3791
  if (maxWaitSeconds <= 0 || isTerminalTranscriptData(data)) {
3791
3792
  return data;
@@ -3822,12 +3823,18 @@ async function callTranscriptDirectOperation(operation, options) {
3822
3823
  break;
3823
3824
  }
3824
3825
 
3825
- currentOperation = transcriptGetJobOperation(
3826
- operation,
3827
- jobId,
3828
- transcriptWaitSecondsForCall(remainingSeconds)
3829
- );
3830
- data = await callDirectOperation(currentOperation);
3826
+ currentOperation = transcriptGetJobOperation(operation, jobId);
3827
+ try {
3828
+ data = await callDirectOperation(currentOperation, {
3829
+ requestDeadlineMs: Date.now() + remainingSeconds * 1000,
3830
+ preserveRequestTimeout: true,
3831
+ });
3832
+ } catch (error) {
3833
+ if (isMcpRequestTimeoutError(error)) {
3834
+ break;
3835
+ }
3836
+ throw error;
3837
+ }
3831
3838
  jobId = readTranscriptJobId(data, operation) || jobId;
3832
3839
  }
3833
3840
 
@@ -3844,38 +3851,21 @@ function parseTranscriptMaxWaitSeconds(options) {
3844
3851
  return TRANSCRIPT_DEFAULT_MAX_WAIT_SECONDS;
3845
3852
  }
3846
3853
 
3847
- function transcriptInitialOperation(operation, maxWaitSeconds) {
3848
- const initialOperation = cloneDirectOperation(operation);
3849
- const isJobLookup =
3850
- operation.transcriptIsJobLookup ||
3851
- operation.tool === operation.transcriptJobTool ||
3852
- Object.hasOwn(operation.arguments, "job_id");
3853
- if (isJobLookup && maxWaitSeconds > 0) {
3854
- initialOperation.arguments.wait_seconds =
3855
- transcriptWaitSecondsForCall(maxWaitSeconds);
3856
- }
3857
- return initialOperation;
3854
+ function transcriptInitialOperation(operation) {
3855
+ return cloneDirectOperation(operation);
3858
3856
  }
3859
3857
 
3860
- function transcriptGetJobOperation(operation, jobId, waitSeconds) {
3858
+ function transcriptGetJobOperation(operation, jobId) {
3861
3859
  const jobArgument = operation.transcriptJobArgument || "job_id";
3862
3860
  return {
3863
3861
  ...operation,
3864
3862
  tool: operation.transcriptJobTool,
3865
3863
  arguments: {
3866
3864
  [jobArgument]: jobId,
3867
- wait_seconds: waitSeconds,
3868
3865
  },
3869
3866
  };
3870
3867
  }
3871
3868
 
3872
- function transcriptWaitSecondsForCall(remainingSeconds) {
3873
- return Math.min(
3874
- TRANSCRIPT_GET_JOB_WAIT_SECONDS,
3875
- Math.max(1, Math.ceil(remainingSeconds))
3876
- );
3877
- }
3878
-
3879
3869
  function transcriptRemainingWaitSeconds(startedAtMs, maxWaitSeconds) {
3880
3870
  return maxWaitSeconds - (Date.now() - startedAtMs) / 1000;
3881
3871
  }
@@ -4490,7 +4480,10 @@ function buildRepliesOperationForComment(platform, comment, parentArguments, par
4490
4480
  }
4491
4481
  }
4492
4482
 
4493
- async function callMcpBackend(operation) {
4483
+ async function callMcpBackend(
4484
+ operation,
4485
+ { requestDeadlineMs, requestTimeoutMs, preserveRequestTimeout = false } = {}
4486
+ ) {
4494
4487
  ensureSupportedNodeVersion();
4495
4488
  const { platform, tool } = operation;
4496
4489
  const apiKey = readDirectApiKey(platform);
@@ -4520,11 +4513,22 @@ async function callMcpBackend(operation) {
4520
4513
  });
4521
4514
 
4522
4515
  try {
4523
- await client.connect(transport);
4524
- const result = await client.callTool({
4525
- name: tool,
4526
- arguments: operation.arguments,
4516
+ const transcriptConnectOptions = transcriptMcpRequestOptions(operation, {
4517
+ requestDeadlineMs,
4518
+ requestTimeoutMs,
4527
4519
  });
4520
+ await connectMcpClient(client, transport, transcriptConnectOptions);
4521
+ const result = await client.callTool(
4522
+ {
4523
+ name: tool,
4524
+ arguments: operation.arguments,
4525
+ },
4526
+ undefined,
4527
+ transcriptMcpRequestOptions(operation, {
4528
+ requestDeadlineMs,
4529
+ requestTimeoutMs,
4530
+ })
4531
+ );
4528
4532
  if (result.isError) {
4529
4533
  const errorMessage =
4530
4534
  result.structuredContent?.message ||
@@ -4536,12 +4540,70 @@ async function callMcpBackend(operation) {
4536
4540
  }
4537
4541
  return result.structuredContent ?? result;
4538
4542
  } catch (error) {
4543
+ if (preserveRequestTimeout && isMcpRequestTimeoutError(error)) {
4544
+ throw error;
4545
+ }
4539
4546
  throw formatDirectCallError({ error, operation, upstreamUrl });
4540
4547
  } finally {
4541
4548
  await client.close().catch(() => {});
4542
4549
  }
4543
4550
  }
4544
4551
 
4552
+ function isMcpRequestTimeoutError(error) {
4553
+ return error?.code === MCP_REQUEST_TIMEOUT_ERROR_CODE;
4554
+ }
4555
+
4556
+ async function connectMcpClient(client, transport, requestOptions) {
4557
+ if (requestOptions?.timeout === undefined) {
4558
+ await client.connect(transport, requestOptions);
4559
+ return;
4560
+ }
4561
+ await withMcpRequestTimeout(
4562
+ client.connect(transport, requestOptions),
4563
+ requestOptions.timeout,
4564
+ () => client.close().catch(() => {})
4565
+ );
4566
+ }
4567
+
4568
+ function transcriptMcpRequestOptions(
4569
+ operation,
4570
+ { requestDeadlineMs, requestTimeoutMs }
4571
+ ) {
4572
+ if (operation.operation !== "transcript") {
4573
+ return undefined;
4574
+ }
4575
+ if (requestDeadlineMs !== undefined) {
4576
+ return {
4577
+ timeout: Math.max(Math.ceil(requestDeadlineMs - Date.now()), 1),
4578
+ };
4579
+ }
4580
+ return {
4581
+ timeout: requestTimeoutMs ?? TRANSCRIPT_MCP_CALL_TIMEOUT_MS,
4582
+ };
4583
+ }
4584
+
4585
+ async function withMcpRequestTimeout(promise, timeoutMs, onTimeout) {
4586
+ let timeoutId;
4587
+ const timeoutPromise = new Promise((_, reject) => {
4588
+ timeoutId = setTimeout(() => {
4589
+ reject(mcpRequestTimeoutError(timeoutMs));
4590
+ Promise.resolve(onTimeout?.()).catch(() => {});
4591
+ }, timeoutMs);
4592
+ });
4593
+ try {
4594
+ return await Promise.race([promise, timeoutPromise]);
4595
+ } finally {
4596
+ clearTimeout(timeoutId);
4597
+ }
4598
+ }
4599
+
4600
+ function mcpRequestTimeoutError(timeoutMs) {
4601
+ const error = new Error("Request timed out");
4602
+ error.code = MCP_REQUEST_TIMEOUT_ERROR_CODE;
4603
+ error.data = { timeout: timeoutMs };
4604
+ return error;
4605
+ }
4606
+
4545
4607
  function readDirectApiKey(platform) {
4546
4608
  const apiKey = readFirstEnv(platform.apiKeyEnv);
4547
4609
  if (!apiKey) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "socialdatax-skills",
3
- "version": "0.2.32",
3
+ "version": "0.2.33",
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",
@@ -235,8 +235,6 @@ Do not pass `page` to `wechat_search_videos`; omit `page_token` on the first req
235
235
  Continue WeChat Channels pagination only when `next_page_token` is not empty. Pass the complete returned `next_page_token` back unchanged as `page_token` for the same keyword and filter chain.
236
236
  `--since-days` uses CLI-side filtering only and is not an MCP tool argument; for MCP-only calls, request newest-first/native publish-time filters where available and filter returned `publish_time` values in your analysis.
237
237
 
238
- XHS search parameter naming reminder: direct CLI uses `--sort-type`, `--publish-time-range`, and `--note-type`; the `xhs_search_notes` MCP tool uses `sort_type`, `publish_time_range`, and `note_type`. Do not pass `sortType`, `publishTimeRange`, or `noteType`.
239
-
240
238
  ## Output Guidance
241
239
 
242
240
  Summarize visible evidence separately from interpretation. Include useful content IDs, URLs, titles or descriptions, authors, counts, and publish time when the user needs traceability.
@@ -122,7 +122,7 @@ Optional arguments:
122
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.
123
123
 
124
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.
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.
125
+ Direct CLI transcript commands try to deliver the final result in one run: submit waits server-side up to 240 seconds, then the CLI keeps querying the same `job_id`; each get-job call also waits up to 240 seconds for that job. Do not start a second submit job just to poll status.
126
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.
127
127
 
128
128
  ## Safety Boundary
@@ -155,7 +155,7 @@ If MCP tools are already available in the current agent, choose the platform-spe
155
155
  - Kuaishou: `kuaishou_submit_video_speech_text_by_video_url`, `kuaishou_submit_video_speech_text_by_photo_id`, `kuaishou_get_video_speech_text_job`.
156
156
  - Weibo: `weibo_submit_video_speech_text_by_post_url`, `weibo_submit_video_speech_text_by_post_id`, `weibo_get_video_speech_text_job`.
157
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`.
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`.
158
+ Do not start a duplicate transcript job only to poll status. If an MCP response is non-terminal, you must call `data.next_action.tool_name` with `data.next_action.arguments` exactly as returned; those arguments include the same `job_id`. The next response may still be non-terminal. Continue until `is_terminal` is `true`.
159
159
 
160
160
  ## Output Guidance
161
161
 
@@ -473,8 +473,6 @@ MCP tools matching the direct CLI commands above:
473
473
  Use the automatically listed MCP tools above as the source of truth for tool names. Pick the narrowest tool for the user's platform and task.
474
474
  For search pagination, omit `page_token` on the first request and pass only the complete returned `next_page_token` when continuing the same chain. Use `page_token` for all new search continuation calls.
475
475
 
476
- XHS search parameter naming reminder: direct CLI uses `--sort-type`, `--publish-time-range`, and `--note-type`; the `xhs_search_notes` MCP tool uses `sort_type`, `publish_time_range`, and `note_type`. Do not pass `sortType`, `publishTimeRange`, or `noteType`.
477
-
478
476
  ## Output Guidance
479
477
 
480
478
  For broad research, summarize visible evidence separately from interpretation and organize findings by platform, content angles, audience needs, trend signals, comment themes, creator positioning, and practical next steps.
@@ -486,7 +484,6 @@ For hot-search, report ranking signals separately from keyword search results.
486
484
 
487
485
  ## Troubleshooting
488
486
 
489
- - For non-balance, non-rate-limit network or API errors, preserve the error message, check `SOCIALDATAX_API_KEY`, parameters, and link or ID format, then retry once when appropriate.
487
+ - For non-balance network or API errors, preserve the error message, check `SOCIALDATAX_API_KEY`, parameters, and link or ID format, then retry once when appropriate.
490
488
  - If the response returns `insufficient_balance` or says the balance/credits are insufficient, do not retry repeatedly. Show the recharge URL from the error exactly as returned, then continue the same command after the user recharges.
491
489
  - If the user has recharged but still sees insufficient balance, confirm `SOCIALDATAX_API_KEY` belongs to the same account that was recharged; if needed, copy a fresh API Key from the official dashboard.
492
- - Batch processing: process items one by one as a queue by default; when the user explicitly needs batch efficiency, use at most 3 concurrent requests. As each item finishes, continue with the next one. Do not launch a large burst of requests. If `rate_limited` or a too-frequent-request error appears, do not abandon the task; stop starting new requests, wait for the returned wait time, or wait 2 seconds if none is provided, then continue the same queue from the unfinished position. Preserve completed results. If `insufficient_balance` or insufficient credits appears, stop remaining requests immediately, output the results already obtained, and ask the user to recharge or switch to an API Key with balance before continuing.