ppxc-leads-mcp 0.1.5 → 0.1.8

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.
Files changed (34) hide show
  1. package/LICENSE.md +11 -0
  2. package/README.md +39 -111
  3. package/dist/backend/config.js +0 -1
  4. package/dist/backend/ppxc-client.js +0 -1
  5. package/dist/backend/ppxc-login-window.js +0 -1
  6. package/dist/backend/token-store.js +0 -1
  7. package/dist/browser/comments.js +0 -1
  8. package/dist/browser/douyin-runner.js +0 -1
  9. package/dist/browser/kernel/electron-profile.js +0 -1
  10. package/dist/browser/kernel/logger.js +0 -1
  11. package/dist/browser/kernel/page-scripts/index.js +0 -1
  12. package/dist/browser/kernel/runner-page-manager.js +0 -1
  13. package/dist/browser/kernel/runner-page-session.js +75 -2
  14. package/dist/browser/kernel/runner-page-session.search-parser.js +0 -1
  15. package/dist/browser/kernel/runner-page-session.user-agent.js +0 -1
  16. package/dist/browser/platform-runner.js +0 -1
  17. package/dist/browser/platforms/detect-platform.js +0 -1
  18. package/dist/browser/platforms/douyin/adapter.js +0 -1
  19. package/dist/browser/platforms/douyin/comments.js +0 -1
  20. package/dist/browser/platforms/kuaishou/adapter.js +0 -1
  21. package/dist/browser/platforms/kuaishou/comments.js +0 -1
  22. package/dist/browser/platforms/registry.js +0 -1
  23. package/dist/browser/platforms/shared/cdp-json-waiter.js +0 -1
  24. package/dist/browser/platforms/types.js +0 -1
  25. package/dist/browser/platforms/xiaohongshu/adapter.js +0 -1
  26. package/dist/browser/platforms/xiaohongshu/comments.js +33 -5
  27. package/dist/browser/usage-throttle.js +0 -1
  28. package/dist/main.js +0 -1
  29. package/dist/mcp/battle-report.js +0 -1
  30. package/dist/mcp/content-insights.js +0 -1
  31. package/dist/mcp/diagnostics.js +0 -1
  32. package/dist/mcp/server.js +0 -1
  33. package/dist/version.js +0 -1
  34. package/package.json +2 -9
package/LICENSE.md ADDED
@@ -0,0 +1,11 @@
1
+ # PPXC Leads MCP Proprietary License
2
+
3
+ Copyright (c) PPXC. All rights reserved.
4
+
5
+ This package is provided only for use with the PPXC service.
6
+
7
+ You may install and run this package to connect an MCP-capable assistant to your own PPXC account.
8
+
9
+ You may not copy, modify, decompile, reverse engineer, republish, redistribute, sublicense, sell, rent, host, or use this package or any substantial part of it to build a competing or derivative service without prior written permission from PPXC.
10
+
11
+ This package is provided "as is" without warranties of any kind. PPXC may update, limit, suspend, or discontinue access to service-backed functionality according to PPXC account, billing, safety, and abuse-control rules.
package/README.md CHANGED
@@ -1,20 +1,13 @@
1
1
  # PPXC Leads MCP
2
2
 
3
- PPXC「从评论区发现高意向客户」的能力,做成智能体可直接调用的 MCP 工具包。
3
+ PPXC Leads MCP lets an MCP-capable assistant help a PPXC user find potential customers from public comments on supported social platforms.
4
4
 
5
- 发布包:<https://www.npmjs.com/package/ppxc-leads-mcp>
6
- 官网接入页:<https://opc1.me/download/mcp>
7
- 公开仓库:<https://github.com/yuanjian068yuan/ppxc-leads-mcp>
5
+ Package: <https://www.npmjs.com/package/ppxc-leads-mcp>
6
+ Setup guide: <https://opc1.me/download/mcp>
8
7
 
9
- > **先读这两份,再动手:**
10
- > 1. [`AGENTS.md`](./AGENTS.md) — 本仓库的硬性边界规则(AI 协作员必读第一文件)
11
- > 2. [`docs/开发手册.md`](./docs/开发手册.md) — 架构、工具契约、平台适配层、验收标准
12
- >
13
- > 产品化(从本地半成品到正式可分发小组件)的路线:[`docs/产品化规划-2026-06-11.md`](./docs/产品化规划-2026-06-11.md)
8
+ ## Install MCP
14
9
 
15
- ## 安装(一行配置)
16
-
17
- 在支持 MCP 的智能体(Claude 桌面版 / Cursor / 其他)配置里加一段:
10
+ Add this server to an MCP-capable host:
18
11
 
19
12
  ```json
20
13
  {
@@ -27,11 +20,7 @@
27
20
  }
28
21
  ```
29
22
 
30
- - 首次使用会自动下载运行环境(约 100MB)。如果下载失败,换网络重试;国内网络可在 `env` 里额外加 `ELECTRON_MIRROR=https://npmmirror.com/mirrors/electron/`。
31
- - 默认连 PPXC 生产站;开发联调时在 `env` 里加 `PPXC_API_BASE` 指向本地后端。
32
- - 装好后对智能体说「检查登录状态」,按提示完成 PPXC 登录和平台扫码即可使用。
33
-
34
- Windows 宿主如果不能直接执行 `npx`,用:
23
+ If a Windows host cannot execute `npx` directly, use:
35
24
 
36
25
  ```json
37
26
  {
@@ -44,116 +33,55 @@ Windows 宿主如果不能直接执行 `npx`,用:
44
33
  }
45
34
  ```
46
35
 
47
- ## 一句话定位
48
-
49
- 用户在智能体里一行配置装好本工具包;首次登录 PPXC 账号 + 按需扫各平台二维码;之后对智能体说「分析这条内容 / 用这个词在小红书找客户」,工具包用自带的隐藏浏览器内核抓取数据、送 PPXC 后端分析,把「总体判断 + 高意向客户 + 跟进话术 + 内容选题」直接返回给智能体。
50
-
51
- ## 支持的平台
52
-
53
- | 平台 | 链接分析评论 | 关键词搜索 | 数据获取方式 |
54
- |---|---|---|---|
55
- | 抖音 | ✅ | ✅ | 页内 fetch + 搜索 sniffer(已真机验证) |
56
- | 小红书 | ✅ 真机验证通过(2026-06-11) | ✅ 真机验证通过(2026-06-11) | CDP 监听 + 滚动;笔记页需带 xsec_token 签名链接 |
57
- | 快手 | ✅ 真机验证通过(2026-06-11) | ✅ 真机验证通过(2026-06-11) | CDP 监听 graphql(rootCommentsV2);搜索走 /rest/v/search/feed |
58
-
59
- ## 它不是什么
60
-
61
- - **不是**桌面客户端:没有产品界面,唯一允许出现的窗口是登录窗和验证码窗
62
- - **不是**浏览器插件:不依赖用户手动安装插件
63
- - **不是**爬虫平台:借用户本人登录态、保守频率、撞验证就请真人处理,不做任何过验证 / 共享账号的灰色手段
36
+ First launch downloads the local browser runtime. Node.js 18 or newer is required.
64
37
 
65
- ## 隐私与安全
38
+ ## Install Skill
66
39
 
67
- - 平台登录态和 PPXC 登录凭证只保存在用户本机。
68
- - 不上传平台 token / Cookie,不把它们写进日志或诊断包。
69
- - 评论文本会通过 HTTPS 发送到 PPXC 后端做 AI 意向判断和话术生成。
70
- - 分析出的客户结果会写入用户自己的 PPXC 客户池。
71
- - 遇到验证码或平台安全验证时,组件只会弹出窗口让用户本人处理,不做自动绕过。
40
+ The official workflow skill is distributed in:
72
41
 
73
- ## 形态(方案 A:自带浏览器内核的 CDP MCP)
74
-
75
- ```
76
- 智能体(Claude / Cursor / ...)
77
- │ MCP 协议(stdio)
78
-
79
- 本工具包(无界面后台进程)
80
- ├── MCP 协议层 ← 7 个 tool,platform 参数 / 链接自动识别
81
- ├── 浏览器执行层 ← 平台插座 + 三平台方言件 + 隐藏窗口内核
82
- └── 后端对接层 ← 调 PPXC 生产后端的同步分析接口
42
+ ```text
43
+ skills/ppxc-find-customers/SKILL.md
83
44
  ```
84
45
 
85
- ## 工具
86
-
87
- | 工具 | 用户场景 | 状态 |
88
- |---|---|---|
89
- | `check_status_and_login` | 检查 PPXC + 抖音/小红书/快手登录;`login_*` 弹对应扫码窗 | 已接入三平台 |
90
- | `list_products` | 列出账号下产品,拿 productId | 第二期 |
91
- | `analyze_video_comments` | 给内容链接 + 产品 → 读评论 → AI 分析 → 战报 + 入池;platform 可省略 | 抖音已验;小红书/快手待真机 |
92
- | `search_keyword_for_leads` | 给关键词 + **platform** → 搜内容 → 读评论 → 分析 → 汇总战报 | 抖音已验;小红书/快手待真机 |
93
- | `suggest_search_keywords` | 开搜前先要词:读后端想词委员会为产品生成的精选搜索词(带词型+理由) | 0.2 新增 |
94
- | `query_leads` | 问「之前挖到的客户 / 高意向有哪些」→ 只读查客户池,支持按词/天数/状态筛 | 0.2 新增 |
95
- | `export_diagnostics` | 用户说「不好用 / 要反馈」→ 把运行日志打包成桌面上的诊断文件 | 已实测 |
96
-
97
- ## FAQ
98
-
99
- ### 需要什么环境?
100
-
101
- macOS 或 Windows,Node.js 18+,以及一个 PPXC 账号。目标平台账号由用户本人扫码登录。
46
+ Install the whole `skills/ppxc-find-customers/` folder into your assistant's skill directory, or import the folder through the host's skill UI. For Codex Desktop / Codex CLI, the usual destination is:
102
47
 
103
- ### 为什么不能云托管?
104
-
105
- 本工具依赖用户本机的平台登录态和扫码/验证码窗口,必须在用户自己的电脑上运行。市场上架时请选择 Local / stdio 模式。
106
-
107
- ### 第一次启动慢怎么办?
108
-
109
- 首次会下载 Electron 浏览器内核,约 100MB。下载失败通常是网络问题,换网络重试即可;国内网络可按官网 FAQ 加镜像环境变量。
110
-
111
- ### 能不能自动私信客户?
112
-
113
- 不做。工具只帮助识别潜在客户、生成跟进话术和主页/来源链接,具体触达由用户本人判断和执行。
114
-
115
- ### 怎么反馈问题?
116
-
117
- 对智能体说「导出诊断信息」,它会在桌面生成脱敏诊断文件,发给 PPXC 支持人员即可。
118
-
119
- ## 桌面战报(0.2)
120
-
121
- 两件分析工具挖到客户后,会在用户桌面自动生成一份**客户战报**(自包含网页文件,离线可开):总览数字、行动清单(先跟谁 + 为什么)、客户卡片(评论原话 / 判断理由 / 一键复制话术 / 主页直达)、每个词的成绩单。可转发给同事直接照着跟进。
122
-
123
- ## 官方技能说明书(教智能体怎么用)
124
-
125
- [`skills/ppxc-find-customers/SKILL.md`](./skills/ppxc-find-customers/SKILL.md) 是给智能体的标准工作流:先查登录 → 选产品 → 要词 → 开搜 → 固定格式汇报(含战报文件位置)→ 隔天复盘换词,并写死了额度 / 验证码 / 电力的应对话术。装进 Claude / Cursor 等宿主的技能目录即可,npm 包里也随包分发。
48
+ ```text
49
+ ~/.codex/skills/ppxc-find-customers/
50
+ ```
126
51
 
127
- ## 风控(按平台分日额度 + 全局 30 秒间隔)
52
+ MCP installation exposes the tools. Skill installation teaches the assistant the safe workflow: check login, choose product, suggest keywords, search for leads, query the customer pool, and export diagnostics when needed.
128
53
 
129
- 详见 [`docs/开发手册.md` §8](./docs/开发手册.md)。小红书/快手初版比抖音更保守。
54
+ ## Tools
130
55
 
131
- ## 仓库关系(重要)
56
+ The package exposes seven MCP tools:
132
57
 
133
- 本仓库**独立**,不与 `/Users/jianshi/ppxc`(网页 + 后端)、`/Users/jianshi/ppxc-desktop`(桌面客户端稳定线)共享代码、分支、git 历史。
58
+ - `check_status_and_login`
59
+ - `list_products`
60
+ - `suggest_search_keywords`
61
+ - `search_keyword_for_leads`
62
+ - `analyze_video_comments`
63
+ - `query_leads`
64
+ - `export_diagnostics`
134
65
 
135
- - 后端:本仓库作为 PPXC 后端的**客户端**调用;分析接口 `POST /api/v2/comments/analyze` 在 `ppxc` 仓库,支持 `userProfileUrl` 按平台传入主页链接。
136
- - 桌面客户端:从 `ppxc-desktop` 稳定线**单向搬运**执行内核,搬完解耦。
66
+ ## Privacy And Safety
137
67
 
138
- 详细边界见 [`AGENTS.md`](./AGENTS.md)。
68
+ - Platform login sessions and PPXC login credentials stay on the user's machine.
69
+ - Platform tokens and cookies are not uploaded to PPXC and are not written to diagnostics.
70
+ - Public comment text is sent to PPXC over HTTPS for customer-intent analysis, follow-up suggestions, and customer-pool storage.
71
+ - Results are saved only to the user's own PPXC customer pool.
72
+ - Captchas, QR-code login, and security checks are handled by the user in a local window. This package does not bypass verification.
73
+ - The tool does not send private messages or automate outreach.
139
74
 
140
- ## 开发状态
75
+ ## Troubleshooting
141
76
 
142
- **抖音三期主链路均已真机验证**(链接分析 + 关键词搜索 + 入池)。
77
+ If the assistant says the MCP tools are unavailable, restart or refresh the MCP host after adding the config.
143
78
 
144
- 🟡 **多平台适配层(2026-06-11)**:
79
+ If startup is slow, wait for the browser runtime download to finish. In mainland China networks, the setup guide includes a mirror configuration for Electron downloads.
145
80
 
146
- - 阶段 A:`platform-runner` + `platforms/*` 插座架构;抖音逻辑平移;`typecheck` + `build` + `regression:static` 通过。
147
- - 阶段 B:**小红书真机验证通过**——扫码登录、关键词搜索(17 笔记/20 评论)、短链笔记拉评论(10 评论 + 主页链接)三链路实测 OK。探路结论:① 访客也有 web_session cookie,登录态必须读页面状态;② 笔记页必须带 xsec_token 签名链接,裸链接会被「暂时无法浏览」拦截;③ 短链跳转在慢网络下需轮询等待。MCP 工具层端到端(含后端分析+入池)待联调。
148
- - 阶段 C:**快手真机验证通过**——扫码登录、关键词搜索(20 视频/18 评论)、单视频链接拉评论(13 评论 + 主页链接)三链路实测 OK。探路结论:① 扫码先发 passToken(id.kuaishou.com),主站会话票叫 `kuaishou.server.webday7_st`(带灰度后缀且 httpOnly),userId/did 都不能当登录凭证;② 搜索接口是 `/rest/v/search/feed`(作者在 feed 级、评论数字段不可靠);③ 视频页评论走 graphql `visionCommentList`,真数据在 `rootCommentsV2`(老字段是空壳)。MCP 工具层端到端(含后端分析+入池)待联调。
149
- - 阶段 D:后端 `userProfileUrl` 优先使用 MCP 传入的主页链接;开发手册/README 已更新。
81
+ If a platform asks for QR-code login or verification, complete it manually in the local window, then retry the assistant request.
150
82
 
151
- 验证命令:
83
+ If something fails, ask the assistant to run `export_diagnostics` and send the generated diagnostic file to PPXC support.
152
84
 
153
- ```bash
154
- npm run regression:static # 静态回归(无需登录)
155
- npm run smoke:comments # 抖音 smoke(需登录 + PPXC_MCP_VIDEO_URL)
156
- PPXC_MCP_PLATFORM=xiaohongshu PPXC_MCP_LOGIN=1 npm run spike:probe # 小红书探路
157
- ```
85
+ ## License
158
86
 
159
- 运行与验证步骤:见 [`docs/第一期-运行与验证.md`](./docs/第一期-运行与验证.md)。
87
+ This package is proprietary. It may be installed and used as part of the PPXC service, but copying, modifying, reverse engineering, republishing, or redistributing it is not allowed except with written permission from PPXC.
@@ -10,4 +10,3 @@ function getApiBase() {
10
10
  function isHttpUrl(value) {
11
11
  return /^https?:\/\//i.test(value);
12
12
  }
13
- //# sourceMappingURL=config.js.map
@@ -154,4 +154,3 @@ async function queryLeads(opts) {
154
154
  const paywallLocked = res.headers.get("x-ppxc-paywall") === "locked";
155
155
  return { rows: Array.isArray(data) ? data : [], paywallLocked };
156
156
  }
157
- //# sourceMappingURL=ppxc-client.js.map
@@ -165,4 +165,3 @@ async function logoutPpxc() {
165
165
  log.warn("clear login partition failed", err instanceof Error ? err.message : String(err));
166
166
  }
167
167
  }
168
- //# sourceMappingURL=ppxc-login-window.js.map
@@ -62,4 +62,3 @@ function clearToken() {
62
62
  catch {
63
63
  }
64
64
  }
65
- //# sourceMappingURL=token-store.js.map
@@ -6,4 +6,3 @@ Object.defineProperty(exports, "buildCommentUrl", { enumerable: true, get: funct
6
6
  Object.defineProperty(exports, "commentsFromJson", { enumerable: true, get: function () { return comments_1.commentsFromJson; } });
7
7
  Object.defineProperty(exports, "extractAwemeId", { enumerable: true, get: function () { return comments_1.extractAwemeId; } });
8
8
  Object.defineProperty(exports, "isDouyinShortLink", { enumerable: true, get: function () { return comments_1.isDouyinShortLink; } });
9
- //# sourceMappingURL=comments.js.map
@@ -12,4 +12,3 @@ Object.defineProperty(exports, "startDouyinLogin", { enumerable: true, get: func
12
12
  Object.defineProperty(exports, "startPlatformLogin", { enumerable: true, get: function () { return platform_runner_1.startPlatformLogin; } });
13
13
  Object.defineProperty(exports, "shutdownRunner", { enumerable: true, get: function () { return platform_runner_1.shutdownRunner; } });
14
14
  Object.defineProperty(exports, "mapSearchResultForDouyin", { enumerable: true, get: function () { return platform_runner_1.mapSearchResultForDouyin; } });
15
- //# sourceMappingURL=douyin-runner.js.map
@@ -29,4 +29,3 @@ function configureDesktopElectronProfile(app, options) {
29
29
  }
30
30
  return { profileName, userDataDir };
31
31
  }
32
- //# sourceMappingURL=electron-profile.js.map
@@ -54,4 +54,3 @@ exports.logger = {
54
54
  scope: createScopedLogger,
55
55
  };
56
56
  exports.LOG_FILE_PATH = LOG_FILE;
57
- //# sourceMappingURL=logger.js.map
@@ -1419,4 +1419,3 @@ exports.DOUYIN_PRESCROLL_SCRIPT = `(async () => {
1419
1419
  return { ok: false, message: (err && err.message) || String(err) };
1420
1420
  }
1421
1421
  })();`;
1422
- //# sourceMappingURL=index.js.map
@@ -142,4 +142,3 @@ class RunnerPageManager {
142
142
  }
143
143
  }
144
144
  exports.RunnerPageManager = RunnerPageManager;
145
- //# sourceMappingURL=runner-page-manager.js.map
@@ -136,6 +136,77 @@ const DOUYIN_SEARCH_HOOK_BINDING = "__OPC1_DOUYIN_SEARCH_CAPTURE__";
136
136
  const MAX_PAGE_HOOK_PAYLOAD_CHARS = 5 * 1024 * 1024;
137
137
  const ENABLE_SEARCH_SNIFFER = process.env.OPC1_DISABLE_DOUYIN_SEARCH_SNIFFER !== "1";
138
138
  const ENABLE_PAGE_SEARCH_HOOK = process.env.OPC1_DISABLE_DOUYIN_PAGE_SEARCH_HOOK !== "1";
139
+ function normalizePlatformProxyMode(rawValue) {
140
+ const raw = String(rawValue ?? "").trim().toLowerCase();
141
+ if (!raw)
142
+ return null;
143
+ if (raw === "direct" || raw === "system" || raw === "auto_detect")
144
+ return raw;
145
+ if (raw === "auto-detect" || raw === "auto")
146
+ return "auto_detect";
147
+ if (raw === "off" || raw === "0" || raw === "false")
148
+ return "system";
149
+ return null;
150
+ }
151
+ function platformProxyEnvKey(partition) {
152
+ if (partition.includes("kuaishou"))
153
+ return "PPXC_MCP_PLATFORM_PROXY_KUAISHOU";
154
+ if (partition.includes("xhs"))
155
+ return "PPXC_MCP_PLATFORM_PROXY_XIAOHONGSHU";
156
+ if (partition.includes("douyin"))
157
+ return "PPXC_MCP_PLATFORM_PROXY_DOUYIN";
158
+ return null;
159
+ }
160
+ function defaultPlatformProxyMode(partition) {
161
+ if (partition.includes("kuaishou"))
162
+ return "system";
163
+ return "direct";
164
+ }
165
+ function resolvePlatformProxyMode(partition) {
166
+ const platformKey = platformProxyEnvKey(partition);
167
+ const platformOverride = platformKey
168
+ ? normalizePlatformProxyMode(process.env[platformKey])
169
+ : null;
170
+ if (platformOverride)
171
+ return platformOverride;
172
+ const globalOverride = normalizePlatformProxyMode(process.env.PPXC_MCP_PLATFORM_PROXY);
173
+ if (globalOverride)
174
+ return globalOverride;
175
+ const raw = String(process.env.PPXC_MCP_PLATFORM_PROXY ?? "")
176
+ .trim()
177
+ .toLowerCase();
178
+ if (raw) {
179
+ log.warn("unknown PPXC_MCP_PLATFORM_PROXY value, use platform default", {
180
+ value: raw,
181
+ partition,
182
+ });
183
+ }
184
+ if (platformKey) {
185
+ const platformRaw = String(process.env[platformKey] ?? "").trim().toLowerCase();
186
+ if (platformRaw) {
187
+ log.warn("unknown platform proxy override, use platform default", {
188
+ key: platformKey,
189
+ value: platformRaw,
190
+ partition,
191
+ });
192
+ }
193
+ }
194
+ return defaultPlatformProxyMode(partition);
195
+ }
196
+ async function applyPlatformProxyPolicy(ses, slotId, partition) {
197
+ const mode = resolvePlatformProxyMode(partition);
198
+ try {
199
+ await ses.setProxy({ mode });
200
+ log.info(`slot ${slotId} platform proxy applied`, { mode, partition });
201
+ }
202
+ catch (err) {
203
+ log.warn(`slot ${slotId} platform proxy apply failed`, {
204
+ mode,
205
+ partition,
206
+ msg: err instanceof Error ? err.message : String(err),
207
+ });
208
+ }
209
+ }
139
210
  function readBoundedIntEnv(name, fallback, min, max) {
140
211
  const parsed = Math.floor(Number(process.env[name]));
141
212
  if (!Number.isFinite(parsed))
@@ -273,6 +344,7 @@ class RunnerPageSession {
273
344
  backgroundThrottling: false,
274
345
  },
275
346
  });
347
+ this._networkPolicyReady = applyPlatformProxyPolicy(this._win.webContents.session, this._slotId, this._partition);
276
348
  this._win.webContents.setAudioMuted(true);
277
349
  this._win.webContents.on("did-attach-webview", (_e, attached) => {
278
350
  try {
@@ -523,6 +595,7 @@ class RunnerPageSession {
523
595
  async loadUrl(url) {
524
596
  return await withDouyinNavigationPermit(this._slotId, async () => {
525
597
  this._assertAlive();
598
+ await this._networkPolicyReady;
526
599
  const wc = this._win.webContents;
527
600
  const timeoutMs = this._navigationTimeoutMs;
528
601
  let timer = null;
@@ -586,7 +659,8 @@ class RunnerPageSession {
586
659
  }
587
660
  navigateNoWait(url) {
588
661
  this._assertAlive();
589
- this._win.webContents.loadURL(url).catch((err) => {
662
+ const wc = this._win.webContents;
663
+ void this._networkPolicyReady.then(() => wc.loadURL(url)).catch((err) => {
590
664
  const raw = err instanceof Error ? err : new Error(String(err));
591
665
  if (isNavigationAbortedError(raw)) {
592
666
  log.info(`slot ${this._slotId} navigateNoWait aborted url=${url} msg=${raw.message}`);
@@ -1462,4 +1536,3 @@ async function safeJson(wc, script, label, timeoutMs) {
1462
1536
  };
1463
1537
  }
1464
1538
  }
1465
- //# sourceMappingURL=runner-page-session.js.map
@@ -184,4 +184,3 @@ function asNumber(value) {
184
184
  }
185
185
  return undefined;
186
186
  }
187
- //# sourceMappingURL=runner-page-session.search-parser.js.map
@@ -29,4 +29,3 @@ function resolveDouyinUserAgent(platform = process.platform, electronVersions =
29
29
  return buildUserAgent("Macintosh; Intel Mac OS X 10_15_7", chromeVersion);
30
30
  }
31
31
  }
32
- //# sourceMappingURL=runner-page-session.user-agent.js.map
@@ -309,4 +309,3 @@ function mapSearchResultForDouyin(result) {
309
309
  totalComments: result.totalComments,
310
310
  };
311
311
  }
312
- //# sourceMappingURL=platform-runner.js.map
@@ -30,4 +30,3 @@ function normalizePlatformId(explicit, contentUrl) {
30
30
  }
31
31
  return "douyin";
32
32
  }
33
- //# sourceMappingURL=detect-platform.js.map
@@ -159,4 +159,3 @@ function detectPlatformFromUrl(input) {
159
159
  return "douyin";
160
160
  return null;
161
161
  }
162
- //# sourceMappingURL=adapter.js.map
@@ -127,4 +127,3 @@ function extractAwemeId(input) {
127
127
  function isDouyinShortLink(input) {
128
128
  return /v\.douyin\.com|\/share\//i.test(String(input ?? ""));
129
129
  }
130
- //# sourceMappingURL=comments.js.map
@@ -175,4 +175,3 @@ exports.kuaishouAdapter = {
175
175
  return null;
176
176
  },
177
177
  };
178
- //# sourceMappingURL=adapter.js.map
@@ -167,4 +167,3 @@ exports.KS_SCROLL_SCRIPT = `(async () => {
167
167
  }
168
168
  return { ok: true };
169
169
  })();`;
170
- //# sourceMappingURL=comments.js.map
@@ -20,4 +20,3 @@ function getPlatformAdapter(platform) {
20
20
  function listPlatformAdapters() {
21
21
  return Object.values(ADAPTERS);
22
22
  }
23
- //# sourceMappingURL=registry.js.map
@@ -72,4 +72,3 @@ async function waitForJsonResponses(wc, urlMatcher, timeoutMs, maxResponses = 3)
72
72
  timer = setTimeout(finish, Math.max(500, timeoutMs));
73
73
  });
74
74
  }
75
- //# sourceMappingURL=cdp-json-waiter.js.map
@@ -1,3 +1,2 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- //# sourceMappingURL=types.js.map
@@ -230,4 +230,3 @@ exports.xiaohongshuAdapter = {
230
230
  return null;
231
231
  },
232
232
  };
233
- //# sourceMappingURL=adapter.js.map
@@ -148,12 +148,41 @@ exports.XHS_PROBE_SCRIPT = `(async () => {
148
148
  exports.XHS_OPEN_LOGIN_SCRIPT = `(async () => {
149
149
  try {
150
150
  const sleep = (ms) => new Promise(r => setTimeout(r, ms));
151
- const clickables = Array.from(document.querySelectorAll('button, a, [role="button"], div'));
151
+ const visible = (el) => {
152
+ const rect = el && el.getBoundingClientRect ? el.getBoundingClientRect() : null;
153
+ const style = el ? window.getComputedStyle(el) : null;
154
+ return !!rect && rect.width > 0 && rect.height > 0 && style && style.visibility !== 'hidden' && style.display !== 'none';
155
+ };
156
+ const clickables = Array.from(document.querySelectorAll('button, [role="button"], a'));
152
157
  for (const el of clickables) {
153
- const t = String(el.innerText || el.textContent || '').trim();
154
- if (/^登录$|扫码登录|手机号登录/.test(t)) { el.click(); await sleep(800); break; }
158
+ if (!visible(el)) continue;
159
+ const t = String(el.innerText || el.textContent || el.getAttribute('aria-label') || '').trim();
160
+ const href = String(el.getAttribute('href') || '');
161
+ if (/发布|创作|publish|creator/i.test(t + ' ' + href)) continue;
162
+ if (/^(登录|扫码登录|手机号登录)$/.test(t)) {
163
+ el.click();
164
+ await sleep(800);
165
+ return { ok: true, clicked: true, text: t };
166
+ }
155
167
  }
156
- return { ok: true };
168
+ const exactTextNodes = [];
169
+ const walker = document.createTreeWalker(document.body, NodeFilter.SHOW_TEXT);
170
+ while (exactTextNodes.length < 20) {
171
+ const node = walker.nextNode();
172
+ if (!node) break;
173
+ if (String(node.nodeValue || '').trim() === '登录') exactTextNodes.push(node);
174
+ }
175
+ for (const node of exactTextNodes) {
176
+ const el = node.parentElement && node.parentElement.closest('button, [role="button"], a, div');
177
+ if (!el || !visible(el)) continue;
178
+ const href = String(el.getAttribute('href') || '');
179
+ const text = String(el.innerText || el.textContent || '').trim();
180
+ if (/发布|创作|publish|creator/i.test(text + ' ' + href)) continue;
181
+ el.click();
182
+ await sleep(800);
183
+ return { ok: true, clicked: true, text };
184
+ }
185
+ return { ok: true, clicked: false };
157
186
  } catch (e) { return { ok: false }; }
158
187
  })();`;
159
188
  exports.XHS_SCROLL_COMMENTS_SCRIPT = `(async () => {
@@ -181,4 +210,3 @@ exports.XHS_LOGIN_STATE_SCRIPT = `(() => {
181
210
  return { ok: false, loggedIn: false };
182
211
  }
183
212
  })();`;
184
- //# sourceMappingURL=comments.js.map
@@ -69,4 +69,3 @@ function consumeCrawlQuota(platform, units, dailyLimit) {
69
69
  writeState(state);
70
70
  return { ok: true, usedToday: state.usedByPlatform[platform] ?? 0, limit: dailyLimit };
71
71
  }
72
- //# sourceMappingURL=usage-throttle.js.map
package/dist/main.js CHANGED
@@ -63,4 +63,3 @@ else {
63
63
  electron_1.app.exit(1);
64
64
  });
65
65
  }
66
- //# sourceMappingURL=main.js.map
@@ -322,4 +322,3 @@ function exportBattleReport(input) {
322
322
  return { ok: false, error: msg };
323
323
  }
324
324
  }
325
- //# sourceMappingURL=battle-report.js.map
@@ -63,4 +63,3 @@ function deriveContentAngles(leads, topN = 3) {
63
63
  angles.sort((a, b) => b.count - a.count);
64
64
  return angles.slice(0, topN);
65
65
  }
66
- //# sourceMappingURL=content-insights.js.map
@@ -79,4 +79,3 @@ function exportDiagnostics() {
79
79
  return { ok: false, error: msg };
80
80
  }
81
81
  }
82
- //# sourceMappingURL=diagnostics.js.map
@@ -841,4 +841,3 @@ async function startMcpServer(onInputClosed) {
841
841
  const transport = new stdio_js_1.StdioServerTransport(mcpInputStream(onInputClosed), process.stdout);
842
842
  await server.connect(transport);
843
843
  }
844
- //# sourceMappingURL=server.js.map
package/dist/version.js CHANGED
@@ -16,4 +16,3 @@ function readOwnVersion() {
16
16
  }
17
17
  }
18
18
  exports.OWN_VERSION = readOwnVersion();
19
- //# sourceMappingURL=version.js.map
package/package.json CHANGED
@@ -1,17 +1,10 @@
1
1
  {
2
2
  "name": "ppxc-leads-mcp",
3
3
  "productName": "PPXC Leads MCP",
4
- "version": "0.1.5",
5
- "description": "PPXC 找客户能力的 MCP 工具包:智能体可调用的抖音/小红书/快手评论客户发现工具",
4
+ "version": "0.1.8",
5
+ "description": "PPXC 找客户能力的 MCP 本地连接器",
6
6
  "license": "UNLICENSED",
7
7
  "homepage": "https://opc1.me/download/mcp",
8
- "repository": {
9
- "type": "git",
10
- "url": "git+https://github.com/yuanjian068yuan/ppxc-leads-mcp.git"
11
- },
12
- "bugs": {
13
- "url": "https://github.com/yuanjian068yuan/ppxc-leads-mcp/issues"
14
- },
15
8
  "main": "dist/main.js",
16
9
  "type": "commonjs",
17
10
  "bin": {