socialdatax-skills 0.2.35 → 0.2.37

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/CATALOG.md CHANGED
@@ -6,7 +6,7 @@ Official website and API Key access: <https://socialdatax.com/ai?from=github>
6
6
 
7
7
  ## Quick Start
8
8
 
9
- Set your API Key in the environment:
9
+ For installed AI clients, persist `SOCIALDATAX_API_KEY` in the client Secret or user environment. The `export` below is only for a one-off shell run:
10
10
 
11
11
  ```bash
12
12
  export SOCIALDATAX_API_KEY="<your_api_key>"
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  This public package provides the unified skill installer and direct CLI helper for SocialDataX services.
4
4
 
5
- The current public tools support 小红书 / Xiaohongshu / XHS / RedNote, 抖音 / Douyin, 快手 / Kuaishou / Kwai, Bilibili / 哔哩哔哩 / B站, 微博 / Weibo, 视频号 / WeChat Channels, 知乎 / Zhihu, Instagram, X / Twitter, YouTube, and TikTok content research and analysis workflows, plus WeChat Official Account / 微信公众号 article details, XHS / Douyin / Kuaishou / Weibo local media download, WeChat Channels local media decrypt/save, Bilibili local video download, and 敏感词检测 / 违禁词检查 text checks. The public skill layer is intentionally named by capability so supported services can evolve without changing the installation model.
5
+ The current public tools support 小红书 / Xiaohongshu / XHS / RedNote, 抖音 / Douyin, 快手 / Kuaishou / Kwai, Bilibili / 哔哩哔哩 / B站, 微博 / Weibo, 视频号 / WeChat Channels, 知乎 / Zhihu, Instagram, X / Twitter, YouTube, and TikTok content research and analysis workflows, plus WeChat Official Account / 微信公众号 article details, XHS / Douyin / Kuaishou / Weibo / X / Twitter local media download, WeChat Channels local media decrypt/save, Bilibili local video download, and 敏感词检测 / 违禁词检查 text checks. The public skill layer is intentionally named by capability so supported services can evolve without changing the installation model.
6
6
 
7
7
  - direct `npx` JSON commands for agents that can run shell commands
8
8
  - AgentSkills-compatible installers split by capability for OpenClaw, Hermes Agent, Codex, Claude Code, and general agent skill directories
@@ -89,7 +89,7 @@ Common search phrases for this skill package:
89
89
  - Reserved future SocialDataX namespace names for existing platform listings without draft files yet: `com.socialdatax/kuaishou-insights`, `com.socialdatax/weibo-insights`, `com.socialdatax/wechat-channels-insights`, and `com.socialdatax/instagram-insights`.
90
90
  - Hosted endpoints without repo-tracked standalone listing materials yet: Bilibili, Zhihu, X / Twitter, YouTube, TikTok, and Sensitive Words Check.
91
91
  - Unified MCP registry name: none; this package installs skills and calls explicit hosted MCP entries.
92
- - Current public capability version: `0.2.35`
92
+ - Current public capability version: `0.2.37`
93
93
 
94
94
  ## Direct CLI
95
95
 
@@ -226,6 +226,7 @@ npx -y socialdatax-skills@latest x user-info --profile-url "<profile_url_or_shar
226
226
  npx -y socialdatax-skills@latest x user-posts --user-id "<user_id>" --pretty
227
227
  npx -y socialdatax-skills@latest x user-posts --username "<username>" --pretty
228
228
  npx -y socialdatax-skills@latest x user-posts --profile-url "<profile_url_or_share_text>" --pretty
229
+ npx -y socialdatax-skills@latest x download-media --url "<x_media_url>" --output-dir ./downloads --pretty
229
230
  npx -y socialdatax-skills@latest youtube search --keyword "camping" --pretty
230
231
  npx -y socialdatax-skills@latest youtube detail --url "<youtube_video_url>" --pretty
231
232
  npx -y socialdatax-skills@latest youtube comments --url "<youtube_video_url>" --pretty
@@ -244,7 +245,7 @@ npx -y socialdatax-skills@latest tiktok user-posts --profile-url "<profile_url_o
244
245
  npx -y socialdatax-skills@latest sensitive-check text --text "<content>" --platform xhs --pretty
245
246
  ```
246
247
 
247
- 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.
248
+ 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>`. `x download-media` accepts one X media URL returned by search or detail. These local commands write through a `.part` file, resume partial downloads when the server supports range requests, skip an already existing output file, and infer common image/video/audio extensions in `--output-dir` mode. X / Twitter media URLs are served from overseas CDN domains such as `pbs.twimg.com` and `video.twimg.com`; if the local download times out, make the CLI process use a proxy with `--proxy "http://127.0.0.1:7890"` or `HTTP_PROXY` / `HTTPS_PROXY` / `ALL_PROXY`, then retry.
248
249
 
249
250
  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.
250
251
 
@@ -332,7 +333,7 @@ npx -y socialdatax-skills@latest doctor
332
333
  npx -y socialdatax-skills@latest doctor --json
333
334
  ```
334
335
 
335
- The public package declares no npm lifecycle scripts such as `preinstall`, `install`, or `postinstall`. The installer copies AgentSkills files only, does not save API keys, and does not change MCP server configuration. Authenticated data calls require `SOCIALDATAX_API_KEY` at runtime, do not read local browser data, and do not perform account actions.
336
+ The public package declares no npm lifecycle scripts such as `preinstall`, `install`, or `postinstall`. The installer copies Skill files only, does not save API keys, and does not change MCP server configuration. Authenticated data calls require `SOCIALDATAX_API_KEY` at runtime, do not read local browser data, and do not perform account actions.
336
337
 
337
338
  ## Platform Names
338
339
 
@@ -403,6 +404,8 @@ npx -y socialdatax-skills@latest install media-search --target codex
403
404
  npx -y socialdatax-skills@latest install media-search --target codex --scope workspace
404
405
  npx -y socialdatax-skills@latest install media-search --target claude-code
405
406
  npx -y socialdatax-skills@latest install media-search --target claude-code --scope workspace
407
+ npx -y socialdatax-skills@latest install --path ~/.workbuddy/skills/
408
+ npx -y socialdatax-skills@latest install media-search --path ~/.workbuddy/skills/media-search
406
409
  npx -y socialdatax-skills@latest xhs search --keyword "露营" --pretty
407
410
  npx -y socialdatax-skills@latest xhs hot-search --pretty
408
411
  npx -y socialdatax-skills@latest xhs transcript --url "<note_url_or_share_text>" --pretty
@@ -428,6 +431,7 @@ npx -y socialdatax-skills@latest wechat transcript --encrypted-object-id "<encry
428
431
  npx -y socialdatax-skills@latest zhihu search --keyword "露营" --pretty
429
432
  npx -y socialdatax-skills@latest instagram search --keyword "camping" --pretty
430
433
  npx -y socialdatax-skills@latest x search --keyword "camping" --pretty
434
+ npx -y socialdatax-skills@latest x download-media --url "<x_media_url>" --output-dir ./downloads --pretty
431
435
  npx -y socialdatax-skills@latest youtube search --keyword "camping" --pretty
432
436
  npx -y socialdatax-skills@latest tiktok search --keyword "camping" --pretty
433
437
  npx -y socialdatax-skills@latest sensitive-check text --text "<content>" --platform xhs --pretty
@@ -455,6 +459,7 @@ Default install locations:
455
459
  - Codex workspace scope: `./.codex/skills/<skill-name>`
456
460
  - Claude Code: `~/.claude/skills/<skill-name>`
457
461
  - Claude Code workspace scope: `./.claude/skills/<skill-name>`
462
+ - WorkBuddy: use `--path` with the Skills directory supported by your current WorkBuddy client, for example `npx -y socialdatax-skills@latest install --path ~/.workbuddy/skills/`
458
463
 
459
464
  If no skill name is provided, all skills are installed. If a destination already exists, re-run with `--force` to replace an existing directory for the same skill. Use `--path <directory>` to install one skill to a custom directory, or multiple skills under a custom parent directory. The `shared` scope is only meaningful for `--target hermes`; use `--target agents` for the shared AgentSkills directory directly.
460
465
 
@@ -570,6 +575,7 @@ Current X / Twitter workflows include:
570
575
  - Fetch paginated replies under a first-level comment by post_id and comment_id.
571
576
  - Read creator profile data by user_id, username, or profile URL/share text.
572
577
  - Fetch paginated public posts by creator user_id, username, or profile URL/share text.
578
+ - Save returned `media_items[].cover_image_url` or `media_items[].video_url` media links locally with `x download-media`. Search results may already include these URLs; use detail as a fallback when search media fields are absent. If X CDN downloads time out, make the CLI process use a proxy with `--proxy "http://127.0.0.1:7890"` or `HTTP_PROXY` / `HTTPS_PROXY` / `ALL_PROXY`, then retry.
573
579
 
574
580
  Current YouTube workflows include:
575
581
 
@@ -801,6 +807,8 @@ Current Sensitive Words Check workflows include:
801
807
 
802
808
  For agents that can execute shell commands, use the direct CLI. This is the recommended default for installed skills:
803
809
 
810
+ For installed AI clients, persist `SOCIALDATAX_API_KEY` in the client Secret or user environment. The `export` below is only for a one-off shell run.
811
+
804
812
  ```bash
805
813
  export SOCIALDATAX_API_KEY="<SOCIALDATAX_API_KEY>"
806
814
  npx -y socialdatax-skills@latest xhs search --keyword "露营" --pretty
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.35";
25
+ const PACKAGE_VERSION = "0.2.37";
26
26
  const PACKAGE_SPEC = `${PACKAGE_NAME}@latest`;
27
27
  const LOG_PREFIX = `[${PACKAGE_NAME}]`;
28
28
  const MIN_NODE_VERSION = "20.18.1";
@@ -172,7 +172,7 @@ const XHS_DIRECT_ACTION_OPTIONS = {
172
172
  "pretty",
173
173
  ],
174
174
  transcript: ["url", "noteId", "jobId", "maxWaitSeconds", "pretty"],
175
- "download-media": ["url", "output", "outputDir", "pretty"],
175
+ "download-media": ["url", "output", "outputDir", "proxy", "pretty"],
176
176
  };
177
177
  const XHS_DIRECT_ACTION_NAMES = Object.keys(XHS_DIRECT_ACTION_OPTIONS).join(", ");
178
178
  const XHS_OPTION_DISPLAY_NAMES = {
@@ -195,6 +195,7 @@ const XHS_OPTION_DISPLAY_NAMES = {
195
195
  maxWaitSeconds: "--max-wait-seconds",
196
196
  output: "--output",
197
197
  outputDir: "--output-dir",
198
+ proxy: "--proxy",
198
199
  };
199
200
  const XHS_SEARCH_SORT_TYPES = [
200
201
  "general",
@@ -262,7 +263,7 @@ const DOUYIN_DIRECT_ACTION_OPTIONS = {
262
263
  ],
263
264
  "user-series": ["secUserId", "profileUrl", "pageToken", "pages", "all", "maxItems", "pretty"],
264
265
  transcript: ["url", "awemeId", "jobId", "maxWaitSeconds", "pretty"],
265
- "download-media": ["url", "output", "outputDir", "pretty"],
266
+ "download-media": ["url", "output", "outputDir", "proxy", "pretty"],
266
267
  };
267
268
  const DOUYIN_DIRECT_ACTION_NAMES = Object.keys(DOUYIN_DIRECT_ACTION_OPTIONS).join(", ");
268
269
  const DOUYIN_SEARCH_SORT_TYPES = ["general", "time_descending", "like_count_descending"];
@@ -295,6 +296,7 @@ const DOUYIN_OPTION_DISPLAY_NAMES = {
295
296
  maxWaitSeconds: "--max-wait-seconds",
296
297
  output: "--output",
297
298
  outputDir: "--output-dir",
299
+ proxy: "--proxy",
298
300
  };
299
301
  const KUAISHOU_DIRECT_ACTION_OPTIONS = {
300
302
  "hot-search": ["pretty"],
@@ -333,7 +335,7 @@ const KUAISHOU_DIRECT_ACTION_OPTIONS = {
333
335
  "pretty",
334
336
  ],
335
337
  transcript: ["url", "photoId", "jobId", "maxWaitSeconds", "pretty"],
336
- "download-media": ["url", "output", "outputDir", "pretty"],
338
+ "download-media": ["url", "output", "outputDir", "proxy", "pretty"],
337
339
  };
338
340
  const KUAISHOU_DIRECT_ACTION_NAMES = Object.keys(KUAISHOU_DIRECT_ACTION_OPTIONS).join(", ");
339
341
  const KUAISHOU_OPTION_DISPLAY_NAMES = {
@@ -353,6 +355,7 @@ const KUAISHOU_OPTION_DISPLAY_NAMES = {
353
355
  maxWaitSeconds: "--max-wait-seconds",
354
356
  output: "--output",
355
357
  outputDir: "--output-dir",
358
+ proxy: "--proxy",
356
359
  };
357
360
  const BILIBILI_DIRECT_ACTION_OPTIONS = {
358
361
  "search-videos": [
@@ -578,6 +581,7 @@ const X_DIRECT_ACTION_OPTIONS = {
578
581
  "maxItems",
579
582
  "pretty",
580
583
  ],
584
+ "download-media": ["url", "output", "outputDir", "proxy", "pretty"],
581
585
  };
582
586
  const X_DIRECT_ACTION_NAMES = Object.keys(X_DIRECT_ACTION_OPTIONS).join(", ");
583
587
  const X_OPTION_DISPLAY_NAMES = {
@@ -593,6 +597,10 @@ const X_OPTION_DISPLAY_NAMES = {
593
597
  userId: "--user-id",
594
598
  username: "--username",
595
599
  profileUrl: "--profile-url",
600
+ url: "--url",
601
+ output: "--output",
602
+ outputDir: "--output-dir",
603
+ proxy: "--proxy",
596
604
  };
597
605
  const X_SEARCH_SORT_TYPES = ["hot", "time_descending"];
598
606
  const YOUTUBE_DIRECT_ACTION_OPTIONS = {
@@ -720,7 +728,7 @@ const WEIBO_DIRECT_ACTION_OPTIONS = {
720
728
  "pretty",
721
729
  ],
722
730
  transcript: ["postUrl", "postId", "jobId", "maxWaitSeconds", "pretty"],
723
- "download-media": ["url", "output", "outputDir", "pretty"],
731
+ "download-media": ["url", "output", "outputDir", "proxy", "pretty"],
724
732
  };
725
733
  const WEIBO_DIRECT_ACTION_NAMES = Object.keys(WEIBO_DIRECT_ACTION_OPTIONS).join(", ");
726
734
  const WEIBO_OPTION_DISPLAY_NAMES = {
@@ -741,6 +749,7 @@ const WEIBO_OPTION_DISPLAY_NAMES = {
741
749
  url: "--url",
742
750
  output: "--output",
743
751
  outputDir: "--output-dir",
752
+ proxy: "--proxy",
744
753
  };
745
754
  const WECHAT_DIRECT_ACTION_OPTIONS = {
746
755
  "hot-search": ["pretty"],
@@ -1737,11 +1746,66 @@ async function main() {
1737
1746
  process.exitCode = 1;
1738
1747
  }
1739
1748
  } catch (error) {
1740
- console.error(`${LOG_PREFIX} ${error.message}`);
1749
+ console.error(`${LOG_PREFIX} ${formatCliErrorMessage(error)}`);
1741
1750
  process.exit(1);
1742
1751
  }
1743
1752
  }
1744
1753
 
1754
+ export function formatCliErrorMessage(error) {
1755
+ const message = error?.message || String(error);
1756
+ if (error?.structuredContent) {
1757
+ return message;
1758
+ }
1759
+ if (isLocalDependencyInstallError(message)) {
1760
+ return `${message}
1761
+ Local dependency/runtime/network/authorization issue, not a SocialDataX API key or business data error: install or restore the required CLI dependencies or Node.js runtime when missing, allow node/npm/npx and npm registry access, allow required local file or directory permissions, then retry the same command. 本地依赖/运行环境/网络/授权问题,不是 SocialDataX API Key 或业务数据返回错误:缺少依赖或 node/npm/npx 时请安装或启用当前 CLI 所需依赖/运行时;已有环境时允许当前 Agent 运行 node/npm/npx、访问 npm registry 和目标文件或目录权限,然后重试原命令;不要改用公开网页搜索替代 SocialDataX 数据。`;
1762
+ }
1763
+ return message;
1764
+ }
1765
+
1766
+ function isLocalDependencyInstallError(message) {
1767
+ if (isWrappedSocialDataxDirectCallError(message)) {
1768
+ return false;
1769
+ }
1770
+
1771
+ return (
1772
+ isMissingNodeDependencyError(message) ||
1773
+ /Node\.js \d+\.\d+\.\d+ or newer is required/i.test(message) ||
1774
+ isLocalFilesystemPermissionError(message) ||
1775
+ /npm ERR!.*\bENOTFOUND\b/i.test(message) ||
1776
+ (/\bENOSPC\b/i.test(message) &&
1777
+ /(?:npm|npx|socialdatax-skills|node_modules)/i.test(message)) ||
1778
+ (/\b(?:ENOTFOUND|EAI_AGAIN|ECONNRESET|ECONNREFUSED|ETIMEDOUT)\b/.test(message) &&
1779
+ /(?:npm|registry\.npmjs\.org|npm registry)/i.test(message)) ||
1780
+ (/(?:npm|registry\.npmjs\.org|npm registry)/i.test(message) &&
1781
+ /\b(?:E401|E403|401|403|SELF_SIGNED_CERT_IN_CHAIN|UNABLE_TO_VERIFY_LEAF_SIGNATURE|CERT_HAS_EXPIRED|certificate)\b/i.test(message)) ||
1782
+ /\bnpx\b.*(?:EACCES|EPERM|ENOENT|permission|denied|not found|被拒绝)/i.test(message) ||
1783
+ /(?:EACCES|EPERM|ENOENT|permission|denied|not found|被拒绝).*\bnpx\b/i.test(message) ||
1784
+ /\bnpm\b.*(?:EACCES|EPERM|permission|denied|被拒绝)/i.test(message)
1785
+ );
1786
+ }
1787
+
1788
+ function isLocalFilesystemPermissionError(message) {
1789
+ return (
1790
+ /\b(?:EACCES|EPERM)\b/i.test(message) &&
1791
+ /(?:permission|denied|operation not permitted|mkdir|open|copyfile|cp|scandir|rename|unlink|rmdir)/i.test(message) &&
1792
+ !/https?:\/\//i.test(message)
1793
+ );
1794
+ }
1795
+
1796
+ function isWrappedSocialDataxDirectCallError(message) {
1797
+ return /Direct CLI call failed[\s\S]* at https?:\/\/\S+/i.test(message);
1798
+ }
1799
+
1800
+ function isMissingNodeDependencyError(message) {
1801
+ return (
1802
+ /Cannot find (?:package|module)/i.test(message) &&
1803
+ (/(?:imported from|ERR_MODULE_NOT_FOUND)[\s\S]*(?:cli\.mjs|socialdatax-skills|node_modules)/i.test(message) ||
1804
+ /Cannot find module ['"][^'"]*node_modules[\\/]/i.test(message) ||
1805
+ /Require stack:[\s\S]*(?:cli\.mjs|socialdatax-skills)/i.test(message))
1806
+ );
1807
+ }
1808
+
1745
1809
  function parseCommandArgs(args) {
1746
1810
  const options = {};
1747
1811
  const positional = [];
@@ -2072,6 +2136,21 @@ function validateInstagramDirectActionOptions(action, options) {
2072
2136
  }
2073
2137
 
2074
2138
  function validateXDirectActionOptions(action, options) {
2139
+ const allowedOptions = X_DIRECT_ACTION_OPTIONS[action];
2140
+ if (!allowedOptions) {
2141
+ return;
2142
+ }
2143
+
2144
+ if (action === "download-media") {
2145
+ validateDownloadMediaDirectActionOptions(
2146
+ "x",
2147
+ options,
2148
+ allowedOptions,
2149
+ X_OPTION_DISPLAY_NAMES
2150
+ );
2151
+ return;
2152
+ }
2153
+
2075
2154
  validateMcpDirectActionOptions(
2076
2155
  "x",
2077
2156
  action,
@@ -2329,6 +2408,15 @@ function expandHome(path) {
2329
2408
  if (path.startsWith("~/")) {
2330
2409
  return join(homedir(), path.slice(2));
2331
2410
  }
2411
+ if (path.startsWith("~\\")) {
2412
+ return join(
2413
+ homedir(),
2414
+ ...path
2415
+ .slice(2)
2416
+ .split(/[\\/]+/)
2417
+ .filter(Boolean)
2418
+ );
2419
+ }
2332
2420
  return path;
2333
2421
  }
2334
2422
 
@@ -2474,11 +2562,13 @@ async function installSkills(args) {
2474
2562
  console.log("No MCP server setup is required for the bundled skills.");
2475
2563
  console.log("No API key was stored by this installer.");
2476
2564
  console.log("No MCP server configuration was changed.");
2477
- console.log("Installed files are AgentSkills files only.");
2565
+ console.log("Installed files are Skill files only.");
2478
2566
  console.log(`Authenticated data calls require ${PRIMARY_API_KEY_ENV} at runtime.`);
2479
2567
  console.log("Data calls do not perform login, posting, editing, liking, commenting, or account actions.");
2480
2568
  console.log("Configure your API Key before making authenticated calls:");
2481
- console.log(` export ${PRIMARY_API_KEY_ENV}="<${PRIMARY_API_KEY_ENV}>"`);
2569
+ console.log(
2570
+ ` Persist ${PRIMARY_API_KEY_ENV} in the target AI client Secret or user environment; do not rely on a temporary shell export.`
2571
+ );
2482
2572
  printInstalledSkillAttributionNote();
2483
2573
  }
2484
2574
 
@@ -2497,7 +2587,7 @@ function resolveInstallDestination({
2497
2587
  path,
2498
2588
  usePathAsParent,
2499
2589
  }) {
2500
- const customPath = path && usePathAsParent ? join(path, skillName) : path;
2590
+ const customPath = path && usePathAsParent ? join(expandHome(path), skillName) : path;
2501
2591
  return resolveInstallDir({
2502
2592
  target,
2503
2593
  scope,
@@ -2633,7 +2723,7 @@ function buildDoctorReport() {
2633
2723
  recommendedNode: "22 LTS or newer",
2634
2724
  },
2635
2725
  install: {
2636
- writes: "AgentSkills directories only",
2726
+ writes: "Skill files only",
2637
2727
  apiKeyStored: false,
2638
2728
  mcpConfigChanged: false,
2639
2729
  supportsDryRun: true,
@@ -2677,7 +2767,7 @@ function printDoctor(args) {
2677
2767
  console.log("");
2678
2768
  console.log("Install safety:");
2679
2769
  console.log("- npm lifecycle scripts: none declared by this package.");
2680
- console.log("- install writes AgentSkills files only.");
2770
+ console.log("- install writes Skill files only.");
2681
2771
  console.log("- install does not store API keys.");
2682
2772
  console.log("- install does not change MCP server configuration.");
2683
2773
  console.log("- install --dry-run previews destinations without writing files.");
@@ -3023,6 +3113,9 @@ function printHelp() {
3023
3113
  console.log(` npx -y ${PACKAGE_SPEC} x user-posts --username "<username>" --pretty`);
3024
3114
  console.log(" Call the X creator posts tool by username.");
3025
3115
  console.log("");
3116
+ console.log(` npx -y ${PACKAGE_SPEC} x download-media --url "<x_media_url>" --output-dir ./downloads --pretty`);
3117
+ console.log(" Save one X image or video media URL returned by search or detail to a local file.");
3118
+ console.log("");
3026
3119
  console.log(` npx -y ${PACKAGE_SPEC} youtube search --keyword "camping" --pretty`);
3027
3120
  console.log(" Call the YouTube public video search tool directly and print JSON.");
3028
3121
  console.log("");
@@ -3122,6 +3215,14 @@ function printHelp() {
3122
3215
  );
3123
3216
  console.log(" Install one skill to ./.claude/skills.");
3124
3217
  console.log("");
3218
+ console.log(` npx -y ${PACKAGE_SPEC} install --path ~/.workbuddy/skills/`);
3219
+ console.log(" Install all skills under a custom WorkBuddy Skills parent directory.");
3220
+ console.log("");
3221
+ console.log(
3222
+ ` npx -y ${PACKAGE_SPEC} install media-search --path ~/.workbuddy/skills/media-search`
3223
+ );
3224
+ console.log(" Install one skill to a direct custom Skills directory.");
3225
+ console.log("");
3125
3226
  console.log("Available skills:");
3126
3227
  console.log(` ${AVAILABLE_SKILL_NAMES.join(", ")}`);
3127
3228
  console.log("");
@@ -3130,6 +3231,7 @@ function printHelp() {
3130
3231
  console.log(" --url <url-or-share-text>");
3131
3232
  console.log(" Content link, short link, or share text for URL-based detail/comment/article commands.");
3132
3233
  console.log(" For xhs/douyin/kuaishou/weibo download-media, pass one media URL returned by detail.");
3234
+ console.log(" For x download-media, pass one media URL returned by search or detail.");
3133
3235
  console.log(" For Douyin detail/comments, pass a content page link, not video.play_url.");
3134
3236
  console.log(" --media-url <video.video_url>");
3135
3237
  console.log(" WeChat detail result media URL for local decrypt-media download.");
@@ -3137,6 +3239,8 @@ function printHelp() {
3137
3239
  console.log(" Output file path for local decrypt-media, download-media, or Bilibili download.");
3138
3240
  console.log(" --output-dir <directory>");
3139
3241
  console.log(" Directory for download-media or Bilibili download output when --output is omitted.");
3242
+ console.log(" --proxy <http-or-https-proxy-url>");
3243
+ console.log(" Proxy for local download-media requests; otherwise HTTPS_PROXY, HTTP_PROXY, or ALL_PROXY is used when set.");
3140
3244
  console.log(" --ffmpeg-path <path>");
3141
3245
  console.log(" ffmpeg executable path for Bilibili download; defaults to ffmpeg.");
3142
3246
  console.log(" --keep-tracks");
@@ -3250,7 +3354,8 @@ function printHelp() {
3250
3354
  console.log(" --target <openclaw|hermes|agents|codex|claude-code|claude>");
3251
3355
  console.log(" For install.");
3252
3356
  console.log(" --scope <user|workspace|shared> shared is only for --target hermes.");
3253
- console.log(" --path <directory> Override install destination.");
3357
+ console.log(" --path <directory>");
3358
+ console.log(" For install. Multiple/all skills: parent directory; one skill: skill destination directory.");
3254
3359
  console.log(" --dry-run Preview install without writing files.");
3255
3360
  console.log(" --force Replace an existing directory for the same skill.");
3256
3361
  }
@@ -3323,7 +3428,9 @@ async function runXhsDirectCommand(args) {
3323
3428
  validateXhsDirectActionOptions(action, options);
3324
3429
 
3325
3430
  if (action === "download-media") {
3326
- const data = await downloadPlatformMediaFromUrl("xhs", options.url, options);
3431
+ const data = await downloadPlatformMediaFromUrl("xhs", options.url, options, {
3432
+ env: process.env,
3433
+ });
3327
3434
  process.stdout.write(JSON.stringify(data, null, options.pretty ? 2 : 0));
3328
3435
  process.stdout.write("\n");
3329
3436
  return;
@@ -3359,7 +3466,9 @@ async function runDouyinDirectCommand(args) {
3359
3466
  validateDouyinDirectActionOptions(action, options);
3360
3467
 
3361
3468
  if (action === "download-media") {
3362
- const data = await downloadPlatformMediaFromUrl("douyin", options.url, options);
3469
+ const data = await downloadPlatformMediaFromUrl("douyin", options.url, options, {
3470
+ env: process.env,
3471
+ });
3363
3472
  process.stdout.write(JSON.stringify(data, null, options.pretty ? 2 : 0));
3364
3473
  process.stdout.write("\n");
3365
3474
  return;
@@ -3395,7 +3504,9 @@ async function runKuaishouDirectCommand(args) {
3395
3504
  validateKuaishouDirectActionOptions(action, options);
3396
3505
 
3397
3506
  if (action === "download-media") {
3398
- const data = await downloadPlatformMediaFromUrl("kuaishou", options.url, options);
3507
+ const data = await downloadPlatformMediaFromUrl("kuaishou", options.url, options, {
3508
+ env: process.env,
3509
+ });
3399
3510
  process.stdout.write(JSON.stringify(data, null, options.pretty ? 2 : 0));
3400
3511
  process.stdout.write("\n");
3401
3512
  return;
@@ -3541,7 +3652,9 @@ async function runWeiboDirectCommand(args) {
3541
3652
  validateWeiboDirectActionOptions(action, options);
3542
3653
 
3543
3654
  if (action === "download-media") {
3544
- const data = await downloadPlatformMediaFromUrl("weibo", options.url, options);
3655
+ const data = await downloadPlatformMediaFromUrl("weibo", options.url, options, {
3656
+ env: process.env,
3657
+ });
3545
3658
  process.stdout.write(JSON.stringify(data, null, options.pretty ? 2 : 0));
3546
3659
  process.stdout.write("\n");
3547
3660
  return;
@@ -3644,12 +3757,39 @@ async function runInstagramDirectCommand(args) {
3644
3757
  }
3645
3758
 
3646
3759
  async function runXDirectCommand(args) {
3647
- await runMcpDirectCommand(args, {
3648
- displayName: "X",
3649
- actionNames: X_DIRECT_ACTION_NAMES,
3650
- validateActionOptions: validateXDirectActionOptions,
3651
- buildOperation: buildXOperation,
3652
- });
3760
+ const { options, positional } = parseCommandArgs(args);
3761
+ if (shouldPrintDirectHelp(options, positional)) {
3762
+ printHelp();
3763
+ return;
3764
+ }
3765
+ const action = positional[0];
3766
+ if (!action) {
3767
+ throw new Error(`Missing X command. Use ${X_DIRECT_ACTION_NAMES}.`);
3768
+ }
3769
+ if (positional.length > 1) {
3770
+ throw new Error(`Unexpected argument: ${positional[1]}`);
3771
+ }
3772
+ validateXDirectActionOptions(action, options);
3773
+
3774
+ if (action === "download-media") {
3775
+ const data = await downloadPlatformMediaFromUrl("x", options.url, options, {
3776
+ env: process.env,
3777
+ });
3778
+ process.stdout.write(JSON.stringify(data, null, options.pretty ? 2 : 0));
3779
+ process.stdout.write("\n");
3780
+ return;
3781
+ }
3782
+
3783
+ const operation = attachDirectMetadata(buildXOperation(action, options), options);
3784
+ const data = await callDirectOperationWithOptions(operation, options);
3785
+ const envelope = {
3786
+ platform: operation.platform.id,
3787
+ tool: operation.tool,
3788
+ arguments: operation.arguments,
3789
+ data,
3790
+ };
3791
+ process.stdout.write(JSON.stringify(envelope, null, options.pretty ? 2 : 0));
3792
+ process.stdout.write("\n");
3653
3793
  }
3654
3794
 
3655
3795
  async function runYoutubeDirectCommand(args) {
@@ -10,6 +10,8 @@ import {
10
10
  import { homedir } from "node:os";
11
11
  import { basename, dirname, extname, join, resolve } from "node:path";
12
12
 
13
+ import { ProxyAgent } from "undici";
14
+
13
15
  import {
14
16
  MEDIA_DOWNLOAD_MAX_BYTES,
15
17
  MEDIA_DOWNLOAD_TIMEOUT_MS,
@@ -43,6 +45,11 @@ const PLATFORM_MEDIA_DOWNLOAD_CONFIGS = {
43
45
  displayName: "Weibo",
44
46
  referer: "https://weibo.com/",
45
47
  },
48
+ x: {
49
+ platform: "x",
50
+ displayName: "X / Twitter",
51
+ referer: "https://x.com/",
52
+ },
46
53
  };
47
54
  const MEDIA_DOWNLOAD_MAX_ATTEMPTS = 10;
48
55
  const MEDIA_DOWNLOAD_RETRY_DELAY_MS = 250;
@@ -77,6 +84,10 @@ const MEDIA_URL_FORMAT_EXTENSIONS = new Map([
77
84
  ["mp4", "mp4"],
78
85
  ["m4a", "m4a"],
79
86
  ]);
87
+ const MEDIA_DOWNLOAD_PROXY_ENV_BY_PROTOCOL = {
88
+ "http:": ["HTTP_PROXY", "http_proxy", "ALL_PROXY", "all_proxy"],
89
+ "https:": ["HTTPS_PROXY", "https_proxy", "HTTP_PROXY", "http_proxy", "ALL_PROXY", "all_proxy"],
90
+ };
80
91
  export async function downloadPlatformMediaFromUrl(
81
92
  platform,
82
93
  url,
@@ -89,6 +100,7 @@ export async function downloadPlatformMediaFromUrl(
89
100
  now = () => new Date(),
90
101
  linkFile = linkPath,
91
102
  copyFile = copyFilePath,
103
+ env = {},
92
104
  } = {}
93
105
  ) {
94
106
  const config = resolvePlatformMediaDownloadConfig(platform);
@@ -106,16 +118,27 @@ export async function downloadPlatformMediaFromUrl(
106
118
  existingOutput.size
107
119
  );
108
120
  }
109
-
110
- return downloadWithPartResume(parsedUrl, target, {
121
+ const proxyDispatcher = createMediaDownloadProxyDispatcher(parsedUrl, options, {
111
122
  config,
112
- fetchMedia,
113
- downloadTimeoutMs,
114
- maxDownloadBytes,
115
- retryDelayMs,
116
- linkFile,
117
- copyFile,
123
+ env,
118
124
  });
125
+
126
+ try {
127
+ return await downloadWithPartResume(parsedUrl, target, {
128
+ config,
129
+ fetchMedia,
130
+ downloadTimeoutMs,
131
+ maxDownloadBytes,
132
+ retryDelayMs,
133
+ proxyDispatcher,
134
+ linkFile,
135
+ copyFile,
136
+ });
137
+ } finally {
138
+ if (proxyDispatcher) {
139
+ await proxyDispatcher.close().catch(ignoreError);
140
+ }
141
+ }
119
142
  }
120
143
 
121
144
  async function resolvePlatformMediaTarget(parsedUrl, options = {}, now, config) {
@@ -172,6 +195,7 @@ async function downloadWithPartResume(
172
195
  downloadTimeoutMs,
173
196
  maxDownloadBytes,
174
197
  retryDelayMs,
198
+ proxyDispatcher,
175
199
  linkFile,
176
200
  copyFile,
177
201
  }
@@ -184,6 +208,7 @@ async function downloadWithPartResume(
184
208
  fetchMedia,
185
209
  downloadTimeoutMs,
186
210
  maxDownloadBytes,
211
+ proxyDispatcher,
187
212
  linkFile,
188
213
  copyFile,
189
214
  });
@@ -221,7 +246,10 @@ async function downloadWithPartResume(
221
246
  }
222
247
 
223
248
  if (isAbortLikeError(lastError)) {
224
- throw new Error(`${config.displayName} media download timed out.`);
249
+ throw new Error(formatPlatformMediaTimeoutMessage(config));
250
+ }
251
+ if (isNetworkLikeError(lastError)) {
252
+ throw new Error(formatPlatformMediaNetworkFailureMessage(config));
225
253
  }
226
254
  throw new Error(`${config.displayName} media download failed before completion.`);
227
255
  }
@@ -234,6 +262,7 @@ async function downloadOnce(
234
262
  fetchMedia,
235
263
  downloadTimeoutMs,
236
264
  maxDownloadBytes,
265
+ proxyDispatcher,
237
266
  linkFile,
238
267
  copyFile,
239
268
  }
@@ -252,6 +281,7 @@ async function downloadOnce(
252
281
  const response = await fetchMedia(parsedUrl.href, {
253
282
  headers,
254
283
  ...(signal ? { signal } : {}),
284
+ ...(proxyDispatcher ? { dispatcher: proxyDispatcher } : {}),
255
285
  });
256
286
 
257
287
  if (MEDIA_EXPIRED_HTTP_STATUSES.has(response.status)) {
@@ -536,6 +566,53 @@ function parseHttpUrl(value, config) {
536
566
  return parsed;
537
567
  }
538
568
 
569
+ function createMediaDownloadProxyDispatcher(parsedUrl, options, { config, env }) {
570
+ const proxyUrl = resolveMediaDownloadProxyUrl(parsedUrl, options, {
571
+ config,
572
+ env,
573
+ });
574
+ return proxyUrl ? new ProxyAgent(proxyUrl) : undefined;
575
+ }
576
+
577
+ function resolveMediaDownloadProxyUrl(parsedUrl, options = {}, { config, env }) {
578
+ if (options.proxy !== undefined) {
579
+ return normalizeMediaDownloadProxyUrl(options.proxy, "--proxy", config);
580
+ }
581
+ const envName = MEDIA_DOWNLOAD_PROXY_ENV_BY_PROTOCOL[parsedUrl.protocol]?.find(
582
+ (name) => normalizeOptionalString(env?.[name])
583
+ );
584
+ if (!envName) {
585
+ return "";
586
+ }
587
+ return normalizeMediaDownloadProxyUrl(env[envName], envName, config);
588
+ }
589
+
590
+ function normalizeMediaDownloadProxyUrl(value, displayName, config) {
591
+ const normalized = normalizeOptionalString(value);
592
+ if (!normalized) {
593
+ return "";
594
+ }
595
+ let parsed;
596
+ try {
597
+ parsed = new URL(normalized);
598
+ } catch {
599
+ throw new Error(
600
+ `${displayName} must be an HTTP or HTTPS proxy URL for ${config.platform} download-media.`
601
+ );
602
+ }
603
+ if (!["http:", "https:"].includes(parsed.protocol) || !parsed.hostname) {
604
+ throw new Error(
605
+ `${displayName} must be an HTTP or HTTPS proxy URL for ${config.platform} download-media.`
606
+ );
607
+ }
608
+ return parsed.href;
609
+ }
610
+
611
+ function normalizeOptionalString(value) {
612
+ const normalized = String(value ?? "").trim();
613
+ return normalized === "" ? "" : normalized;
614
+ }
615
+
539
616
  function normalizePlatformDownloadHeaders(config) {
540
617
  return {
541
618
  "User-Agent": MEDIA_DOWNLOAD_USER_AGENT,
@@ -658,6 +735,28 @@ function isNetworkLikeError(error) {
658
735
  );
659
736
  }
660
737
 
738
+ function formatPlatformMediaTimeoutMessage(config) {
739
+ if (config.platform === "x") {
740
+ return [
741
+ "X / Twitter media download timed out.",
742
+ "X media is served from overseas CDN domains such as pbs.twimg.com and video.twimg.com;",
743
+ "make the CLI process use a proxy with --proxy \"http://127.0.0.1:7890\" or HTTP_PROXY / HTTPS_PROXY / ALL_PROXY, then retry.",
744
+ ].join(" ");
745
+ }
746
+ return `${config.displayName} media download timed out.`;
747
+ }
748
+
749
+ function formatPlatformMediaNetworkFailureMessage(config) {
750
+ if (config.platform === "x") {
751
+ return [
752
+ "X / Twitter media download could not reach the media CDN.",
753
+ "X media is served from overseas CDN domains such as pbs.twimg.com and video.twimg.com;",
754
+ "make the CLI process use a proxy with --proxy \"http://127.0.0.1:7890\" or HTTP_PROXY / HTTPS_PROXY / ALL_PROXY, then retry.",
755
+ ].join(" ");
756
+ }
757
+ return `${config.displayName} media download failed before completion.`;
758
+ }
759
+
661
760
  function isDownloadTooLargeError(error) {
662
761
  return error?.message === "Media download is too large for local media processing.";
663
762
  }
@@ -704,10 +803,14 @@ function inferUrlFormatExtension(parsedUrl) {
704
803
  const matched = /(?:^|[/?&])format\/([a-z0-9]+)(?:[/?&#]|$)/i.exec(
705
804
  `${parsedUrl.pathname}${parsedUrl.search}`
706
805
  );
707
- if (!matched) {
708
- return "";
806
+ if (matched) {
807
+ return MEDIA_URL_FORMAT_EXTENSIONS.get(sanitizeFileExtension(matched[1])) || "";
709
808
  }
710
- return MEDIA_URL_FORMAT_EXTENSIONS.get(sanitizeFileExtension(matched[1])) || "";
809
+ return (
810
+ MEDIA_URL_FORMAT_EXTENSIONS.get(
811
+ sanitizeFileExtension(parsedUrl.searchParams.get("format"))
812
+ ) || ""
813
+ );
711
814
  }
712
815
 
713
816
  function isExistingOutputPathError(error) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "socialdatax-skills",
3
- "version": "0.2.35",
3
+ "version": "0.2.37",
4
4
  "description": "SocialDataX / 社媒数据助手 skill installer and direct CLI helper for hosted MCP services.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -95,7 +95,8 @@
95
95
  "node": ">=20.18.1"
96
96
  },
97
97
  "dependencies": {
98
- "@modelcontextprotocol/sdk": "^1.29.0"
98
+ "@modelcontextprotocol/sdk": "^1.29.0",
99
+ "undici": "^7.29.0"
99
100
  },
100
101
  "overrides": {
101
102
  "hono": "4.12.27",
@@ -320,6 +320,7 @@ For Weibo and WeChat Channels / 视频号 comments, preserve returned content ID
320
320
 
321
321
  ## Troubleshooting
322
322
 
323
+ - If an SDK/dependency, npm network, Node.js/npm/npx availability, permission, or missing runtime error appears, treat it as a local runtime, dependency installation, network, or agent authorization issue, not a SocialDataX API key or business data error. If the current environment has permission, install or restore automatically. When network or execution authorization is needed, ask the user to approve or finish authorization, then continue the same command; do not use public web search as a substitute for SocialDataX data.
323
324
  - 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.
324
325
  - 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.
325
326
  - 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.
@@ -163,7 +163,7 @@ The command prints JSON with `platform`, `tool`, `arguments`, and `data`.
163
163
 
164
164
  ## Safety Boundary
165
165
 
166
- 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, Douyin, Kuaishou, and Weibo local save commands write only to the requested local `--output` path or `--output-dir` directory and do 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.
166
+ 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, Douyin, Kuaishou, Weibo, and X / Twitter local save commands write only to the requested local `--output` path or `--output-dir` directory and do 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.
167
167
 
168
168
  ## MCP Tools
169
169
 
@@ -224,6 +224,8 @@ When the user wants to save Douyin media after detail, pass each returned `image
224
224
  When the user wants to save Kuaishou media after detail, pass each returned `images[].url`, `video.play_url`, or `cover_image_url` to `npx -y socialdatax-skills@latest kuaishou download-media --url "<media_url>" --output-dir <directory> --pretty`; this local save command does not require `SOCIALDATAX_API_KEY`.
225
225
  For Bilibili detail, preserve returned content IDs and URLs because comments, replies, and article/dynamic reactions may need them.
226
226
  For Zhihu, Instagram, X / Twitter, YouTube, and TikTok detail results, keep the returned URL and public content ID values exactly as returned for follow-up comments or creator lookups.
227
+ When the user wants to save X / Twitter media after search or detail, pass each returned `media_items[].cover_image_url` or `media_items[].video_url` to `npx -y socialdatax-skills@latest x download-media --url "<media_url>" --output-dir <directory> --pretty`; this local save command does not require `SOCIALDATAX_API_KEY`. Search results may already include these media URLs; use detail as a fallback when search media fields are absent.
228
+ If an X / Twitter local media download times out, explain that X media is served from overseas CDN domains such as `pbs.twimg.com` and `video.twimg.com`; ask the user to make the download process use their proxy, for example by adding `--proxy "http://127.0.0.1:7890"` or setting `HTTP_PROXY` / `HTTPS_PROXY` / `ALL_PROXY`, then retry the same `x download-media` command.
227
229
  Detail access is read-only and does not provide account actions.
228
230
  For Weibo detail, include `post_id`, content, author, media, interaction counts, publish time, and post URL when available.
229
231
  When the user wants to save Weibo media after detail, pass each returned `image_urls[]` or `video.video_url` to `npx -y socialdatax-skills@latest weibo download-media --url "<media_url>" --output-dir <directory> --pretty`; this local save command does not require `SOCIALDATAX_API_KEY`.
@@ -233,6 +235,7 @@ For WeChat Official Account / 微信公众号 article detail, include article ti
233
235
 
234
236
  ## Troubleshooting
235
237
 
238
+ - If an SDK/dependency, npm network, Node.js/npm/npx availability, permission, or missing runtime error appears, treat it as a local runtime, dependency installation, network, or agent authorization issue, not a SocialDataX API key or business data error. If the current environment has permission, install or restore automatically. When network or execution authorization is needed, ask the user to approve or finish authorization, then continue the same command; do not use public web search as a substitute for SocialDataX data.
236
239
  - 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.
237
240
  - 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.
238
241
  - 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.
@@ -272,7 +272,7 @@ Weibo and WeChat Channels search pagination:
272
272
 
273
273
  ## Safety Boundary
274
274
 
275
- This skill is read-only. It uses `SOCIALDATAX_API_KEY` from the user's environment at runtime. Generated Skill files do not contain API keys. It does not read local browser data or perform login, posting, liking, commenting, or account changes. Prefer the direct CLI; hosted MCP tools are optional when the current agent already supports authenticated streamable HTTP MCP.
275
+ Search access is read-only. It uses `SOCIALDATAX_API_KEY` from the user's environment at runtime for SocialDataX search 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 X / Twitter local save command writes only to the requested local `--output` path or `--output-dir` directory 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.
276
276
 
277
277
  ## MCP Tools
278
278
 
@@ -351,11 +351,13 @@ For Kuaishou search results, include `photo_id`, `share_url`, author facts, and
351
351
  For Bilibili search results, include content IDs, URLs, titles or descriptions, creator facts, and visible interaction counts when the user needs traceability.
352
352
  For Zhihu search results, include content URLs, titles or summaries, creator facts, and visible interaction counts when the user needs traceability.
353
353
  For Instagram, X / Twitter, YouTube, and TikTok search results, include returned post or video URLs, public IDs, author facts, and visible interaction counts when the user needs traceability.
354
+ For X / Twitter search results, preserve returned `media_items[].cover_image_url` and `media_items[].video_url` values when present. When the user wants to save X media, pass one returned media URL at a time to `npx -y socialdatax-skills@latest x download-media --url "<media_url>" --output-dir <directory> --pretty`; this local save command does not require `SOCIALDATAX_API_KEY`. If the CLI process does not inherit the user's local proxy, add `--proxy "http://127.0.0.1:7890"` or set `HTTP_PROXY` / `HTTPS_PROXY` / `ALL_PROXY`. If search media fields are absent, use X detail as the fallback before downloading.
354
355
  For Weibo search results, include `post_id`, `post_url`, author facts, interaction counts, and publish time when the user needs traceability.
355
356
  For WeChat Channels / 视频号 search results, include `encrypted_object_id`, author facts, interaction counts, publish time, and duration when the user needs traceability.
356
357
 
357
358
  ## Troubleshooting
358
359
 
360
+ - If an SDK/dependency, npm network, Node.js/npm/npx availability, permission, or missing runtime error appears, treat it as a local runtime, dependency installation, network, or agent authorization issue, not a SocialDataX API key or business data error. If the current environment has permission, install or restore automatically. When network or execution authorization is needed, ask the user to approve or finish authorization, then continue the same command; do not use public web search as a substitute for SocialDataX data.
359
361
  - 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.
360
362
  - 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.
361
363
  - 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.
@@ -166,6 +166,7 @@ If the result says no processable video resource is available or the job failed,
166
166
 
167
167
  ## Troubleshooting
168
168
 
169
+ - If an SDK/dependency, npm network, Node.js/npm/npx availability, permission, or missing runtime error appears, treat it as a local runtime, dependency installation, network, or agent authorization issue, not a SocialDataX API key or business data error. If the current environment has permission, install or restore automatically. When network or execution authorization is needed, ask the user to approve or finish authorization, then continue the same command; do not use public web search as a substitute for SocialDataX data.
169
170
  - If the response returns `insufficient_balance` or says the balance/credits are insufficient, do not submit another job or keep polling. Show the recharge URL from the error exactly as returned, then continue the same command after the user recharges.
170
171
  - 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.
171
172
  - If a transcript `job_id` already exists, only check that same job; do not submit the video again.
@@ -227,6 +227,7 @@ For YouTube, report channel profile facts separately from video-list evidence.
227
227
 
228
228
  ## Troubleshooting
229
229
 
230
+ - If an SDK/dependency, npm network, Node.js/npm/npx availability, permission, or missing runtime error appears, treat it as a local runtime, dependency installation, network, or agent authorization issue, not a SocialDataX API key or business data error. If the current environment has permission, install or restore automatically. When network or execution authorization is needed, ask the user to approve or finish authorization, then continue the same command; do not use public web search as a substitute for SocialDataX data.
230
231
  - 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.
231
232
  - 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.
232
233
  - 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.
@@ -315,6 +315,7 @@ For WeChat Channels / 视频号 creator videos, report object IDs, descriptions,
315
315
 
316
316
  ## Troubleshooting
317
317
 
318
+ - If an SDK/dependency, npm network, Node.js/npm/npx availability, permission, or missing runtime error appears, treat it as a local runtime, dependency installation, network, or agent authorization issue, not a SocialDataX API key or business data error. If the current environment has permission, install or restore automatically. When network or execution authorization is needed, ask the user to approve or finish authorization, then continue the same command; do not use public web search as a substitute for SocialDataX data.
318
319
  - 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.
319
320
  - 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.
320
321
  - 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.
@@ -79,6 +79,7 @@ Do not echo the full original text back unless the user explicitly asks; prefer
79
79
 
80
80
  ## Troubleshooting
81
81
 
82
+ - If an SDK/dependency, npm network, Node.js/npm/npx availability, permission, or missing runtime error appears, treat it as a local runtime, dependency installation, network, or agent authorization issue, not a SocialDataX API key or business data error. If the current environment has permission, install or restore automatically. When network or execution authorization is needed, ask the user to approve or finish authorization, then continue the same command; do not use public web search as a substitute for SocialDataX data.
82
83
  - 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.
83
84
  - 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.
84
85
  - 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.
@@ -345,6 +345,7 @@ For hot-search, report ranking signals separately from keyword search results.
345
345
 
346
346
  ## Troubleshooting
347
347
 
348
+ - If an SDK/dependency, npm network, Node.js/npm/npx availability, permission, or missing runtime error appears, treat it as a local runtime, dependency installation, network, or agent authorization issue, not a SocialDataX API key or business data error. If the current environment has permission, install or restore automatically. When network or execution authorization is needed, ask the user to approve or finish authorization, then continue the same command; do not use public web search as a substitute for SocialDataX data.
348
349
  - 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.
349
350
  - 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.
350
351
  - 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.