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 +1 -1
- package/README.md +12 -4
- package/cli.mjs +162 -22
- package/lib/media/platform-download.mjs +115 -12
- package/package.json +3 -2
- package/skills/media-comments/SKILL.md +1 -0
- package/skills/media-detail/SKILL.md +4 -1
- package/skills/media-search/SKILL.md +3 -1
- package/skills/media-transcript/SKILL.md +1 -0
- package/skills/media-user-info/SKILL.md +1 -0
- package/skills/media-user-posts/SKILL.md +1 -0
- package/skills/sensitive-check/SKILL.md +1 -0
- package/skills/socialdatax-content-research-assistant/SKILL.md +1 -0
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
|
-
|
|
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.
|
|
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>`.
|
|
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
|
|
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.
|
|
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
|
|
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
|
|
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(
|
|
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: "
|
|
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
|
|
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>
|
|
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
|
-
|
|
3648
|
-
|
|
3649
|
-
|
|
3650
|
-
|
|
3651
|
-
|
|
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
|
-
|
|
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(
|
|
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 (
|
|
708
|
-
return "";
|
|
806
|
+
if (matched) {
|
|
807
|
+
return MEDIA_URL_FORMAT_EXTENSIONS.get(sanitizeFileExtension(matched[1])) || "";
|
|
709
808
|
}
|
|
710
|
-
return
|
|
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.
|
|
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
|
|
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
|
-
|
|
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.
|