crawlpro-win 1.0.0
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/LICENSE +19 -0
- package/README.md +129 -0
- package/README_CN.md +123 -0
- package/SKILL.md +279 -0
- package/bin/crawlpro-win.cjs +376 -0
- package/main.exe +0 -0
- package/package.json +49 -0
- package/start_chrome_debug.bat +63 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
Proprietary License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Xi'an Wenyao Network Information Technology Co., Ltd.
|
|
4
|
+
All rights reserved.
|
|
5
|
+
|
|
6
|
+
CrawlPro and its accompanying software, binaries, documentation, and other
|
|
7
|
+
materials are proprietary and confidential. Possession of a copy does not
|
|
8
|
+
grant ownership or any intellectual-property rights.
|
|
9
|
+
|
|
10
|
+
Use of CrawlPro is permitted only under a valid license or written agreement
|
|
11
|
+
issued by Xi'an Wenyao Network Information Technology Co., Ltd. Except where
|
|
12
|
+
that license or agreement expressly permits it, copying, modification,
|
|
13
|
+
distribution, sublicensing, sale, reverse engineering, decompilation, or
|
|
14
|
+
creation of derivative works is prohibited.
|
|
15
|
+
|
|
16
|
+
Third-party components remain subject to their respective license terms.
|
|
17
|
+
|
|
18
|
+
For licensing enquiries, contact Xi'an Wenyao Network Information Technology
|
|
19
|
+
Co., Ltd. through https://www.wenyaotech.com/.
|
package/README.md
ADDED
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
# CrawlPro
|
|
2
|
+
|
|
3
|
+
English | Simplified Chinese is included as `README_CN.md` in the npm package.
|
|
4
|
+
|
|
5
|
+
CrawlPro is a proprietary Agent Skill for finding and organizing publicly
|
|
6
|
+
visible Douyin customer signals. It supports keyword-based collection, customer
|
|
7
|
+
workbooks, assisted follow-up, user-authorized replies, and Excel/HTML analysis.
|
|
8
|
+
|
|
9
|
+
Developed by **Xi'an Wenyao Network Information Technology Co., Ltd.**
|
|
10
|
+
|
|
11
|
+
[Company Website](https://www.wenyaotech.com/)
|
|
12
|
+
|
|
13
|
+
## Main Capabilities
|
|
14
|
+
|
|
15
|
+
- Collect public videos, comments, and profiles by keyword.
|
|
16
|
+
- Filter comments into customer-intent levels and export customer workbooks.
|
|
17
|
+
- Draft replies or send them only after explicit user authorization.
|
|
18
|
+
- Support human-confirmed assisted replies and follow-up status updates.
|
|
19
|
+
- Generate cleaned Excel workbooks and HTML customer reports.
|
|
20
|
+
- Keep keyword catalogs and runtime settings local to the user's computer.
|
|
21
|
+
|
|
22
|
+
## Supported Platforms
|
|
23
|
+
|
|
24
|
+
- macOS on Apple Silicon (`arm64`)
|
|
25
|
+
- Windows 10 or 11 (`x64`)
|
|
26
|
+
|
|
27
|
+
Intel Mac, Windows ARM64, and Linux are not currently supported.
|
|
28
|
+
|
|
29
|
+
## Requirements
|
|
30
|
+
|
|
31
|
+
- Node.js 18 or later and npm 9 or later for installation
|
|
32
|
+
- Hermes, OpenClaw, Claude Code, or Codex
|
|
33
|
+
- Microsoft Edge or Google Chrome; Safari is not supported
|
|
34
|
+
- A local MongoDB or Redis instance for keyword catalogs
|
|
35
|
+
- A valid CrawlPro License Key
|
|
36
|
+
|
|
37
|
+
Python is not required when using the packaged executable.
|
|
38
|
+
|
|
39
|
+
## Install
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
npx --yes crawlpro
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
The unified installer detects the operating system and architecture, installs
|
|
46
|
+
only the matching platform package, detects installed agents, and asks where to
|
|
47
|
+
install CrawlPro.
|
|
48
|
+
|
|
49
|
+
Install into one, several, or all detected agents:
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
npx --yes crawlpro --agent hermes
|
|
53
|
+
npx --yes crawlpro --agent claude,codex
|
|
54
|
+
npx --yes crawlpro --all --yes
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Inspect detection or preview changes without writing files:
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
npx --yes crawlpro --list
|
|
61
|
+
npx --yes crawlpro --all --dry-run
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
The platform-specific commands remain available:
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
npx --yes crawlpro-mac
|
|
68
|
+
npx --yes crawlpro-win
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Updates replace only packaged release files. Existing `license.json`,
|
|
72
|
+
`app/settings.json`, output files, browser profiles, and local databases are
|
|
73
|
+
preserved.
|
|
74
|
+
|
|
75
|
+
## First Run
|
|
76
|
+
|
|
77
|
+
Start a new Agent session after installation, then ask the Agent to use
|
|
78
|
+
CrawlPro. Before browser workflows, CrawlPro checks whether Edge or Chrome is
|
|
79
|
+
available and whether Douyin is logged in.
|
|
80
|
+
|
|
81
|
+
The packaged commands are:
|
|
82
|
+
|
|
83
|
+
```text
|
|
84
|
+
macOS: ./main check
|
|
85
|
+
Windows: .\main.exe check
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
If the browser cannot be started, use `start_chrome_debug.sh` on macOS or
|
|
89
|
+
`start_chrome_debug.bat` on Windows, complete login, and run the check again.
|
|
90
|
+
|
|
91
|
+
## License Activation
|
|
92
|
+
|
|
93
|
+
CrawlPro requires a valid License Key. To purchase or request one, contact the
|
|
94
|
+
CrawlPro team through the [company website](https://www.wenyaotech.com/).
|
|
95
|
+
|
|
96
|
+
The License Key is not included in the npm package. Enter it only in the local
|
|
97
|
+
CrawlPro activation prompt. The resulting device-bound `license.json` must not
|
|
98
|
+
be shared or committed to source control.
|
|
99
|
+
|
|
100
|
+
## Responsible Use and Security
|
|
101
|
+
|
|
102
|
+
- Process only publicly visible information and follow applicable laws and
|
|
103
|
+
platform terms.
|
|
104
|
+
- Sending replies requires explicit user authorization and user-defined limits.
|
|
105
|
+
- Never place License Keys, API keys, customer data, or local configuration in
|
|
106
|
+
Agent conversations, logs, or source control.
|
|
107
|
+
- Do not modify or bypass CrawlPro license validation or platform safeguards.
|
|
108
|
+
- The npm packages contain no user credentials or runtime customer data.
|
|
109
|
+
|
|
110
|
+
## Maintainer Commands
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
npm test
|
|
114
|
+
npm run pack:check
|
|
115
|
+
|
|
116
|
+
npm publish --workspace packages/macos --registry=https://registry.npmjs.org/
|
|
117
|
+
npm publish --workspace packages/windows --registry=https://registry.npmjs.org/
|
|
118
|
+
npm publish --workspace packages/cli --registry=https://registry.npmjs.org/
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
Publish both platform packages before publishing the unified `crawlpro` entry
|
|
122
|
+
package.
|
|
123
|
+
|
|
124
|
+
## License
|
|
125
|
+
|
|
126
|
+
CrawlPro is distributed under a Proprietary License. The complete license is
|
|
127
|
+
included in the npm package as the `LICENSE` file.
|
|
128
|
+
|
|
129
|
+
Copyright © 2026 Xi'an Wenyao Network Information Technology Co., Ltd.
|
package/README_CN.md
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
# CrawlPro
|
|
2
|
+
|
|
3
|
+
[English](https://www.npmjs.com/package/crawlpro) | 中文
|
|
4
|
+
|
|
5
|
+
CrawlPro 是一套专有 Agent Skill,用于从抖音公开可见内容中查找和整理客户
|
|
6
|
+
信号,支持关键词采集、客户表格、辅助跟进、用户授权回复以及 Excel/HTML
|
|
7
|
+
客户分析。
|
|
8
|
+
|
|
9
|
+
本项目由 **西安文鳐网络信息科技有限责任公司** 开发。
|
|
10
|
+
|
|
11
|
+
[公司官网](https://www.wenyaotech.com/)
|
|
12
|
+
|
|
13
|
+
## 核心能力
|
|
14
|
+
|
|
15
|
+
- 按关键词采集公开可见的视频、评论和用户资料。
|
|
16
|
+
- 对评论进行客户意向分级,并导出客户工作簿。
|
|
17
|
+
- 草拟回复;只有获得用户明确授权后才执行发送。
|
|
18
|
+
- 支持人工确认的辅助回复和客户跟进状态更新。
|
|
19
|
+
- 生成清洗后的 Excel 工作簿和 HTML 客户报告。
|
|
20
|
+
- 关键词库和运行配置保存在用户本机。
|
|
21
|
+
|
|
22
|
+
## 支持平台
|
|
23
|
+
|
|
24
|
+
- Apple Silicon(`arm64`)macOS
|
|
25
|
+
- Windows 10 或 Windows 11(`x64`)
|
|
26
|
+
|
|
27
|
+
暂不支持 Intel Mac、Windows ARM64 和 Linux。
|
|
28
|
+
|
|
29
|
+
## 使用要求
|
|
30
|
+
|
|
31
|
+
- 安装时需要 Node.js 18 或更高版本、npm 9 或更高版本
|
|
32
|
+
- Hermes、OpenClaw、Claude Code 或 Codex
|
|
33
|
+
- Microsoft Edge 或 Google Chrome;不支持 Safari
|
|
34
|
+
- 本地 MongoDB 或 Redis,用于保存关键词库
|
|
35
|
+
- 有效的 CrawlPro License Key
|
|
36
|
+
|
|
37
|
+
使用预编译程序时不需要安装 Python。
|
|
38
|
+
|
|
39
|
+
## 安装
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
npx --yes crawlpro
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
统一安装器会判断操作系统和 CPU 架构,只安装匹配的平台包,然后检测已经
|
|
46
|
+
安装的 Agent,并询问需要把 CrawlPro 安装到哪里。
|
|
47
|
+
|
|
48
|
+
安装到一个、多个或全部 Agent:
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
npx --yes crawlpro --agent hermes
|
|
52
|
+
npx --yes crawlpro --agent claude,codex
|
|
53
|
+
npx --yes crawlpro --all --yes
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
查看检测结果或预演安装,不写入文件:
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
npx --yes crawlpro --list
|
|
60
|
+
npx --yes crawlpro --all --dry-run
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
仍然可以直接使用平台包:
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
npx --yes crawlpro-mac
|
|
67
|
+
npx --yes crawlpro-win
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
更新只会替换发布文件,已有的 `license.json`、`app/settings.json`、输出文件、
|
|
71
|
+
浏览器资料和本地数据库不会被删除。
|
|
72
|
+
|
|
73
|
+
## 首次使用
|
|
74
|
+
|
|
75
|
+
安装后请在对应 Agent 中新建会话,并要求 Agent 使用 CrawlPro。开始浏览器
|
|
76
|
+
工作流前,CrawlPro 会检查 Edge 或 Chrome 是否可用,以及抖音是否已登录。
|
|
77
|
+
|
|
78
|
+
程序命令为:
|
|
79
|
+
|
|
80
|
+
```text
|
|
81
|
+
macOS: ./main check
|
|
82
|
+
Windows: .\main.exe check
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
浏览器无法启动时,macOS 运行 `start_chrome_debug.sh`,Windows 运行
|
|
86
|
+
`start_chrome_debug.bat`;完成登录后再次执行检查。
|
|
87
|
+
|
|
88
|
+
## License 激活
|
|
89
|
+
|
|
90
|
+
使用 CrawlPro 需要有效的 License Key。如需购买或申请,请通过
|
|
91
|
+
[公司官网](https://www.wenyaotech.com/)联系 CrawlPro 团队。
|
|
92
|
+
|
|
93
|
+
License Key 不包含在 npm 安装包中。请仅在本机 CrawlPro 激活提示中输入。
|
|
94
|
+
激活后生成的 `license.json` 与设备绑定,不要转发或提交到源代码仓库。
|
|
95
|
+
|
|
96
|
+
## 合规和安全说明
|
|
97
|
+
|
|
98
|
+
- 只处理公开可见的信息,并遵守适用法律和平台规则。
|
|
99
|
+
- 发送回复必须获得用户明确授权,并使用用户设定的数量限制。
|
|
100
|
+
- 不要在 Agent 对话、日志或源码中提供 License Key、API Key、客户数据或
|
|
101
|
+
本地配置。
|
|
102
|
+
- 不要修改或绕过 CrawlPro License 校验及平台安全限制。
|
|
103
|
+
- npm 安装包不包含用户凭证或运行时客户数据。
|
|
104
|
+
|
|
105
|
+
## 维护者命令
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
npm test
|
|
109
|
+
npm run pack:check
|
|
110
|
+
|
|
111
|
+
npm publish --workspace packages/macos --registry=https://registry.npmjs.org/
|
|
112
|
+
npm publish --workspace packages/windows --registry=https://registry.npmjs.org/
|
|
113
|
+
npm publish --workspace packages/cli --registry=https://registry.npmjs.org/
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
必须先发布两个平台包,再发布统一入口包 `crawlpro`。
|
|
117
|
+
|
|
118
|
+
## License
|
|
119
|
+
|
|
120
|
+
CrawlPro 使用 Proprietary License,完整许可证条款包含在 npm 安装包的
|
|
121
|
+
`LICENSE` 文件中。
|
|
122
|
+
|
|
123
|
+
Copyright © 2026 西安文鳐网络信息科技有限责任公司
|
package/SKILL.md
ADDED
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: crawlpro
|
|
3
|
+
description: Operate the packaged CrawlPro application for Douyin login check, customer collection, keyword catalogs, follow-up status, and Excel/HTML customer analysis, plus drafting comment replies within user-set limits. Use when the user asks to run CrawlPro workflows such as check, collect, reply, semi-reply, follow-up, analyze, or config. Verifies browser login before browser workflows, confirms required limits before execution, processes only publicly visible data, and never delivers private settings or internal state files.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# CrawlPro agent workflow
|
|
7
|
+
|
|
8
|
+
Use only the packaged application in the current release directory:
|
|
9
|
+
|
|
10
|
+
```text
|
|
11
|
+
Windows: ./main.exe
|
|
12
|
+
macOS: ./main
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
For source development, the equivalent command is `uv run python main.py`.
|
|
16
|
+
|
|
17
|
+
The included browser launcher is also platform-specific:
|
|
18
|
+
|
|
19
|
+
```text
|
|
20
|
+
Windows: start_chrome_debug.bat
|
|
21
|
+
macOS: ./start_chrome_debug.sh
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Core rules
|
|
25
|
+
|
|
26
|
+
1. Determine the requested workflow before running commands.
|
|
27
|
+
2. Ask for missing keywords, limits and send mode.
|
|
28
|
+
3. Run `APP check` before login, collection or reply workflows.
|
|
29
|
+
4. Do not continue browser work unless `APP check` exits `0`.
|
|
30
|
+
5. Never claim a reply was sent unless the application confirms it.
|
|
31
|
+
6. Never claim a file exists without checking it.
|
|
32
|
+
7. Never attach `app/settings.json`, `license.json`, `active_collection.json`, `reply_tracking.json`, browser profiles or API keys.
|
|
33
|
+
8. Deliver only generated `.xlsx`, `.html`, `.log` when explicitly requested, or the application-generated ZIP.
|
|
34
|
+
|
|
35
|
+
`APP` below means the platform command shown above.
|
|
36
|
+
|
|
37
|
+
## Command router
|
|
38
|
+
|
|
39
|
+
| Request | Command |
|
|
40
|
+
|---|---|
|
|
41
|
+
| Browser/login check | `APP check` |
|
|
42
|
+
| Guided login | `APP login` |
|
|
43
|
+
| Collect target customers | `APP collect` |
|
|
44
|
+
| Draft/send replies within limits | `APP reply` |
|
|
45
|
+
| Assisted reply (manual confirm) | `APP semi-reply` |
|
|
46
|
+
| Follow-up status | `APP follow-up` |
|
|
47
|
+
| Customer analysis | `APP analyze` |
|
|
48
|
+
| Settings and keywords | `APP config` |
|
|
49
|
+
| View model-generated keyword content | `APP keywords` |
|
|
50
|
+
| Initialize or verify database | `APP init-db` |
|
|
51
|
+
|
|
52
|
+
Interactive commands require a real terminal. If no PTY is available, ask the user to open the application and select the matching menu item.
|
|
53
|
+
|
|
54
|
+
## Browser pre-flight
|
|
55
|
+
|
|
56
|
+
Run before every browser workflow:
|
|
57
|
+
|
|
58
|
+
```text
|
|
59
|
+
APP check
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
| Exit | Meaning | Action |
|
|
63
|
+
|---|---|---|
|
|
64
|
+
| `0` | Browser connected and logged in | Continue |
|
|
65
|
+
| `1` | Browser connected but logged out | Ask the user to scan QR, then rerun |
|
|
66
|
+
| `2` | Browser unavailable | Ask the user to run the included browser launcher, then rerun |
|
|
67
|
+
|
|
68
|
+
CrawlPro uses Edge first and Chrome as fallback on CDP port `19553`.
|
|
69
|
+
Safari cannot be used because this workflow requires the Chromium CDP interface.
|
|
70
|
+
On macOS, ask the user to install Edge or Chrome when neither is available.
|
|
71
|
+
|
|
72
|
+
## Customer collection
|
|
73
|
+
|
|
74
|
+
Ask for:
|
|
75
|
+
|
|
76
|
+
1. Search keyword or comma-separated keywords;
|
|
77
|
+
2. latest-first search: `yes` or `no` (`no` means hot/most-liked);
|
|
78
|
+
3. videos per keyword;
|
|
79
|
+
4. comments to retain per video (default: all; a positive number sets a cap);
|
|
80
|
+
5. maximum target customers;
|
|
81
|
+
6. maximum profiles;
|
|
82
|
+
7. whether to use an existing keyword bundle or generate one with the model.
|
|
83
|
+
|
|
84
|
+
If existing defaults are requested:
|
|
85
|
+
|
|
86
|
+
- read numeric limits from `app/settings.json` when it exists;
|
|
87
|
+
- obtain active keywords and generated content through `APP keywords`;
|
|
88
|
+
- do not expect keywords inside `settings.json`.
|
|
89
|
+
|
|
90
|
+
Run:
|
|
91
|
+
|
|
92
|
+
```text
|
|
93
|
+
APP collect "keywordA,keywordB" --videos 10 --leads 50 --profiles 50
|
|
94
|
+
APP collect "keywordA" --comments 20 # optional per-video cap
|
|
95
|
+
APP collect "keywordA" --sort hot
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
Every keyword is searched independently. Duplicate video IDs are removed.
|
|
99
|
+
Only publicly visible comments and profile data are processed. Intent grading
|
|
100
|
+
prioritizes explicit request actions and rejects praise-only comments. Count
|
|
101
|
+
repeated values by distinct users, not raw comment rows. Any number used by at
|
|
102
|
+
least three users under one video is treated as that video's spoken code; two
|
|
103
|
+
users require surrounding request evidence. Normalize repeated text for
|
|
104
|
+
punctuation and platform emoji tags. Repeated request phrases are high intent,
|
|
105
|
+
ambiguous repeated phrases are medium-intent candidates, and repeated
|
|
106
|
+
praise/reactions remain low. All signals are scoped to one video.
|
|
107
|
+
|
|
108
|
+
Treat store/address, on-site service, delivery coverage, and supported-city
|
|
109
|
+
questions as high intent; bare creator-location questions as medium; and
|
|
110
|
+
filming-location or scenery questions as low.
|
|
111
|
+
|
|
112
|
+
Expected primary output:
|
|
113
|
+
|
|
114
|
+
```text
|
|
115
|
+
output/Test/collections/<keyword-group>/target_customs.xlsx
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
The collection ZIP contains only final customer workbooks, cleaned analysis workbooks, HTML reports, and `.log` files. Runtime JSON, search tables, per-video comment tables, and per-user detail tables stay out of the bundle.
|
|
119
|
+
|
|
120
|
+
## Reply
|
|
121
|
+
|
|
122
|
+
Ask for:
|
|
123
|
+
|
|
124
|
+
1. keyword set;
|
|
125
|
+
2. live search or `target_customs.xlsx` source;
|
|
126
|
+
3. latest-first search (`yes`/`no`), videos per keyword, and comments per video when live search is selected;
|
|
127
|
+
4. whether to draft replies only or send them (sending requires explicit user authorization and confirmation);
|
|
128
|
+
5. model use: `yes` or `no`;
|
|
129
|
+
6. daily reply limit;
|
|
130
|
+
7. allowed comment age;
|
|
131
|
+
8. whether to collect profiles.
|
|
132
|
+
|
|
133
|
+
Commands:
|
|
134
|
+
|
|
135
|
+
```text
|
|
136
|
+
APP reply "keywordA"
|
|
137
|
+
APP reply "keywordA" --no-reply
|
|
138
|
+
APP reply "keywordA" --profile
|
|
139
|
+
APP reply "keywordA" --from-leads
|
|
140
|
+
APP reply "keywordA" --sort hot
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
Before sending, state the selected keyword, source and reply limit, and confirm the user has authorized sending. Sending happens only within the user-set daily limit and must not bypass platform interaction limits. Live search is the default and has no Excel input. `--from-leads` reads only `target_customs.xlsx`. `activate_customs.xlsx` is an optional profile/reply-status output, and `all_comment_users.xlsx` is never a sending source. Stop if selectors are missing or sending cannot be confirmed.
|
|
144
|
+
|
|
145
|
+
## Assisted reply
|
|
146
|
+
|
|
147
|
+
Run:
|
|
148
|
+
|
|
149
|
+
```text
|
|
150
|
+
APP semi-reply
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
This workflow requires a person at the computer. It reads only the active keyword group's `target_customs.xlsx`, filters customers, opens the related comment and either sends after confirmation or fills the text for manual sending. It does not read `activate_customs.xlsx` or `all_comment_users.xlsx`.
|
|
154
|
+
|
|
155
|
+
## Follow-up
|
|
156
|
+
|
|
157
|
+
Run:
|
|
158
|
+
|
|
159
|
+
```text
|
|
160
|
+
APP follow-up
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
The user marks rows as 未跟进 (not followed up), 已私信 (DM sent), 已加微信 (WeChat added) or 已成交 (deal closed). The workflow writes back to Excel. If Excel/WPS locks the file, ask the user to close it and retry.
|
|
164
|
+
|
|
165
|
+
## Analysis
|
|
166
|
+
|
|
167
|
+
Ask for:
|
|
168
|
+
|
|
169
|
+
1. `target_customs.xlsx`, `activate_customs.xlsx`, `all_comment_users.xlsx`, or an explicit Excel path;
|
|
170
|
+
2. brief or full terminal statistics;
|
|
171
|
+
3. whether the generated Excel/HTML should be delivered.
|
|
172
|
+
|
|
173
|
+
Commands:
|
|
174
|
+
|
|
175
|
+
```text
|
|
176
|
+
APP analyze
|
|
177
|
+
APP analyze --source target
|
|
178
|
+
APP analyze --source profiles
|
|
179
|
+
APP analyze --source comments
|
|
180
|
+
APP analyze --brief
|
|
181
|
+
APP analyze --file "path/to/customer_table.xlsx" --full
|
|
182
|
+
APP evaluate-intent "path/to/labeled_comments.xlsx"
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
Without a source flag, an interactive terminal offers all three customer tables. `--daily` remains a compatibility alias for `--source profiles`; `--csv` remains a compatibility alias for `--file`, but the input is an Excel workbook.
|
|
186
|
+
|
|
187
|
+
Each schema is normalized from its real fields. The formal `activate_customs.xlsx` includes intent and reply status; compatible older internal profile data may omit intent, which stays unlabelled. Comment-user workbooks map highest intent, aggregated comments, latest comment time and source videos.
|
|
188
|
+
|
|
189
|
+
Generated files are placed beside the source workbook under `analysis/`:
|
|
190
|
+
|
|
191
|
+
```text
|
|
192
|
+
analysis/<source>_clean.xlsx
|
|
193
|
+
analysis/<source>_report.html
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
The cleaned workbook and HTML contain only customer delivery fields: user identity, contact information, intent, source video, intent comment, comment time and IP region. The HTML is a simple customer table (user, Douyin ID, user-homepage link, contact method, contact detail, intent, video link, video title, intent comment, comment time, IP region), sorted by intent level (high → mid) then newest comment first, with a region filter dropdown — not a dashboard.
|
|
197
|
+
|
|
198
|
+
The analysis ZIP contains the cleaned workbook, the HTML report, and the same main tables/logs found in the keyword directory.
|
|
199
|
+
|
|
200
|
+
## Intent evaluation
|
|
201
|
+
|
|
202
|
+
Use `APP evaluate-intent <labeled_comments.xlsx>` only when the user supplies a
|
|
203
|
+
manually labeled workbook with `评论内容` and `预期意向` columns. The command
|
|
204
|
+
uses the production classifier, including video-scoped repeated codes when
|
|
205
|
+
`视频ID` is present. It makes no model call, spends no tokens, and writes no
|
|
206
|
+
copy of the labeled data.
|
|
207
|
+
|
|
208
|
+
## Settings and keyword catalog
|
|
209
|
+
|
|
210
|
+
`app/settings.json` contains only English application settings:
|
|
211
|
+
|
|
212
|
+
- `llm`;
|
|
213
|
+
- `database`;
|
|
214
|
+
- `parameters`.
|
|
215
|
+
|
|
216
|
+
Keywords, intent words, reply-intent words and reply templates are stored with English field names in the password-free MongoDB/Redis `keywords` collection. `APP init-db` also migrates compatible legacy Chinese field names without regenerating content.
|
|
217
|
+
|
|
218
|
+
Commands:
|
|
219
|
+
|
|
220
|
+
```text
|
|
221
|
+
APP init-db
|
|
222
|
+
APP config
|
|
223
|
+
APP config "keywordA,keywordB"
|
|
224
|
+
APP config --reset
|
|
225
|
+
APP keywords
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
Ask for confirmation before `APP config --reset` because it overwrites application settings. API keys must be entered privately in the application, never in chat.
|
|
229
|
+
|
|
230
|
+
## Output and delivery
|
|
231
|
+
|
|
232
|
+
Main customer outputs:
|
|
233
|
+
|
|
234
|
+
```text
|
|
235
|
+
target_customs.xlsx
|
|
236
|
+
activate_customs.xlsx
|
|
237
|
+
all_comment_users.xlsx
|
|
238
|
+
analysis/<source>_clean.xlsx
|
|
239
|
+
analysis/<source>_report.html
|
|
240
|
+
deliverables/*.zip
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
Internal files such as `active_collection.json` and `reply_tracking.json` are necessary runtime state, not delivery files.
|
|
244
|
+
|
|
245
|
+
After success, report:
|
|
246
|
+
|
|
247
|
+
1. command and keyword set;
|
|
248
|
+
2. approved limits;
|
|
249
|
+
3. processed/success/failure counts;
|
|
250
|
+
4. whether real replies were sent;
|
|
251
|
+
5. exact existing output paths;
|
|
252
|
+
6. remaining user action.
|
|
253
|
+
|
|
254
|
+
Use the printed `DELIVERABLE_PATH=...` value for ZIP delivery. Do not search for or construct a guessed ZIP path.
|
|
255
|
+
|
|
256
|
+
## Recovery
|
|
257
|
+
|
|
258
|
+
| Problem | Action |
|
|
259
|
+
|---|---|
|
|
260
|
+
| Browser exit `1` | Ask for QR login and rerun check |
|
|
261
|
+
| Browser exit `2` | Run platform browser launcher and retry |
|
|
262
|
+
| Search returns no data | Verify login and keywords |
|
|
263
|
+
| Page disconnect | Keep browser open, rerun check, retry once |
|
|
264
|
+
| Model failure | Ask whether to retry or continue without generated content |
|
|
265
|
+
| Reply selector missing | Stop sending and report probable layout change |
|
|
266
|
+
| `send_unconfirmed` | Do not count as sent |
|
|
267
|
+
| Excel locked | Ask user to close Excel/WPS and retry |
|
|
268
|
+
| Analysis source missing/empty | Stop and report the exact expected path |
|
|
269
|
+
| Database unavailable | Verify the local service and URL, run `APP init-db`, and report its exact error |
|
|
270
|
+
| macOS exit `137` / `Killed: 9` | Stop retrying; compare `uname -m` with `file ./main`, then report the output of `codesign --verify --deep --strict --verbose=4 ./main` |
|
|
271
|
+
|
|
272
|
+
## Safety
|
|
273
|
+
|
|
274
|
+
- Follow Douyin terms of service and applicable privacy and data-protection laws.
|
|
275
|
+
- Process only publicly visible comment and profile data, and only for user-authorized private-domain communication.
|
|
276
|
+
- Keep collection and reply volume conservative; never bypass platform interaction limits or anti-automation measures.
|
|
277
|
+
- Do not use customer data for harassment, fraud, spam, or unlawful resale.
|
|
278
|
+
- Password-free database URLs are restricted to loopback hosts or a local Redis Unix socket; do not attempt to bypass this validation.
|
|
279
|
+
- Never expose private settings, API keys, licensing data, or browser profiles.
|
|
@@ -0,0 +1,376 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
'use strict';
|
|
4
|
+
|
|
5
|
+
const childProcess = require('node:child_process');
|
|
6
|
+
const fs = require('node:fs');
|
|
7
|
+
const os = require('node:os');
|
|
8
|
+
const path = require('node:path');
|
|
9
|
+
const readline = require('node:readline');
|
|
10
|
+
|
|
11
|
+
const SKILL_NAME = 'crawlpro';
|
|
12
|
+
const PACKAGE_ROOT = path.resolve(__dirname, '..');
|
|
13
|
+
const PLATFORM = process.env.CRAWLPRO_INSTALL_TEST_PLATFORM || process.platform;
|
|
14
|
+
const ARCH = process.env.CRAWLPRO_INSTALL_TEST_ARCH || process.arch;
|
|
15
|
+
const INSTALL_HOME = process.env.CRAWLPRO_INSTALL_HOME || os.homedir();
|
|
16
|
+
const LOCAL_APP_DATA = process.env.CRAWLPRO_INSTALL_LOCALAPPDATA ||
|
|
17
|
+
process.env.LOCALAPPDATA || path.join(INSTALL_HOME, 'AppData', 'Local');
|
|
18
|
+
|
|
19
|
+
const SOURCE_FILES = [
|
|
20
|
+
['SKILL.md', 'SKILL.md'],
|
|
21
|
+
['README.md', 'README.md'],
|
|
22
|
+
['README_CN.md', 'README_CN.md'],
|
|
23
|
+
['main.exe', 'main.exe'],
|
|
24
|
+
['start_chrome_debug.bat', 'start_chrome_debug.bat'],
|
|
25
|
+
];
|
|
26
|
+
|
|
27
|
+
const CANONICAL_FILES = [
|
|
28
|
+
'SKILL.md',
|
|
29
|
+
'README.md',
|
|
30
|
+
'README_CN.md',
|
|
31
|
+
'main.exe',
|
|
32
|
+
'start_chrome_debug.bat',
|
|
33
|
+
];
|
|
34
|
+
|
|
35
|
+
function hermesHome() {
|
|
36
|
+
return process.env.HERMES_HOME || path.join(INSTALL_HOME, '.hermes');
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const AGENTS = {
|
|
40
|
+
hermes: {
|
|
41
|
+
label: 'Hermes Agent',
|
|
42
|
+
command: 'hermes',
|
|
43
|
+
detectPaths: () => [
|
|
44
|
+
hermesHome(),
|
|
45
|
+
path.join(LOCAL_APP_DATA, 'Programs', 'Hermes'),
|
|
46
|
+
],
|
|
47
|
+
destination: () => path.join(hermesHome(), 'skills', SKILL_NAME),
|
|
48
|
+
},
|
|
49
|
+
openclaw: {
|
|
50
|
+
label: 'OpenClaw',
|
|
51
|
+
command: 'openclaw',
|
|
52
|
+
detectPaths: () => [path.join(INSTALL_HOME, '.openclaw')],
|
|
53
|
+
destination: () => path.join(INSTALL_HOME, '.openclaw', 'skills', SKILL_NAME),
|
|
54
|
+
},
|
|
55
|
+
claude: {
|
|
56
|
+
label: 'Claude Code',
|
|
57
|
+
command: 'claude',
|
|
58
|
+
detectPaths: () => [path.join(INSTALL_HOME, '.claude')],
|
|
59
|
+
destination: () => path.join(INSTALL_HOME, '.claude', 'skills', SKILL_NAME),
|
|
60
|
+
},
|
|
61
|
+
codex: {
|
|
62
|
+
label: 'Codex',
|
|
63
|
+
command: 'codex',
|
|
64
|
+
detectPaths: () => [
|
|
65
|
+
path.join(INSTALL_HOME, '.codex'),
|
|
66
|
+
path.join(LOCAL_APP_DATA, 'Programs', 'Codex'),
|
|
67
|
+
],
|
|
68
|
+
destination: () => path.join(INSTALL_HOME, '.agents', 'skills', SKILL_NAME),
|
|
69
|
+
},
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
const AGENT_ORDER = ['hermes', 'openclaw', 'claude', 'codex'];
|
|
73
|
+
|
|
74
|
+
function usage() {
|
|
75
|
+
console.log(`CrawlPro Windows Skill 安装器
|
|
76
|
+
|
|
77
|
+
用法:
|
|
78
|
+
npx crawlpro
|
|
79
|
+
npx crawlpro --agent hermes,codex
|
|
80
|
+
npx crawlpro --all --yes
|
|
81
|
+
|
|
82
|
+
平台包直接调用:
|
|
83
|
+
npx crawlpro-win
|
|
84
|
+
|
|
85
|
+
选项:
|
|
86
|
+
--agent <名称> 安装到指定 Agent;可用逗号分隔多个名称
|
|
87
|
+
支持 hermes、openclaw、claude、codex
|
|
88
|
+
--all 安装到检测到的全部 Agent
|
|
89
|
+
--yes, -y 更新已有安装时不再询问
|
|
90
|
+
--dry-run 只显示操作,不写入文件
|
|
91
|
+
--list 只显示检测结果
|
|
92
|
+
--help, -h 显示帮助`);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function parseArgs(argv) {
|
|
96
|
+
const options = {
|
|
97
|
+
requestedAgents: '',
|
|
98
|
+
all: false,
|
|
99
|
+
yes: false,
|
|
100
|
+
dryRun: false,
|
|
101
|
+
list: false,
|
|
102
|
+
help: false,
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
106
|
+
const argument = argv[index];
|
|
107
|
+
if (argument === '--agent') {
|
|
108
|
+
const value = argv[index + 1];
|
|
109
|
+
if (!value || value.startsWith('--')) {
|
|
110
|
+
throw new Error('--agent 后需要 Agent 名称。');
|
|
111
|
+
}
|
|
112
|
+
options.requestedAgents = value;
|
|
113
|
+
index += 1;
|
|
114
|
+
} else if (argument.startsWith('--agent=')) {
|
|
115
|
+
options.requestedAgents = argument.slice('--agent='.length);
|
|
116
|
+
} else if (argument === '--all') {
|
|
117
|
+
options.all = true;
|
|
118
|
+
} else if (argument === '--yes' || argument === '-y') {
|
|
119
|
+
options.yes = true;
|
|
120
|
+
} else if (argument === '--dry-run') {
|
|
121
|
+
options.dryRun = true;
|
|
122
|
+
} else if (argument === '--list') {
|
|
123
|
+
options.list = true;
|
|
124
|
+
} else if (argument === '--help' || argument === '-h') {
|
|
125
|
+
options.help = true;
|
|
126
|
+
} else {
|
|
127
|
+
throw new Error(`未知参数:${argument}`);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
if (options.requestedAgents && options.all) {
|
|
132
|
+
throw new Error('--agent 和 --all 不能同时使用。');
|
|
133
|
+
}
|
|
134
|
+
return options;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function commandExists(command) {
|
|
138
|
+
try {
|
|
139
|
+
const result = childProcess.spawnSync('where.exe', [command], {
|
|
140
|
+
stdio: 'ignore',
|
|
141
|
+
windowsHide: true,
|
|
142
|
+
});
|
|
143
|
+
return result.status === 0;
|
|
144
|
+
} catch {
|
|
145
|
+
return false;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function agentDetected(key) {
|
|
150
|
+
const agent = AGENTS[key];
|
|
151
|
+
return commandExists(agent.command) || agent.detectPaths().some((entry) => fs.existsSync(entry));
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function printDetection() {
|
|
155
|
+
console.log('CrawlPro 支持的 Agent:');
|
|
156
|
+
for (const key of AGENT_ORDER) {
|
|
157
|
+
const status = agentDetected(key) ? '已检测' : '未检测';
|
|
158
|
+
const suffix = agentDetected(key) ? ` ${AGENTS[key].destination()}` : '';
|
|
159
|
+
console.log(` [${status}] ${AGENTS[key].label}${suffix}`);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function normalizeAgent(value) {
|
|
164
|
+
const normalized = value.trim().toLowerCase();
|
|
165
|
+
const aliases = {
|
|
166
|
+
hermes: 'hermes',
|
|
167
|
+
'hermes-agent': 'hermes',
|
|
168
|
+
openclaw: 'openclaw',
|
|
169
|
+
'open-claw': 'openclaw',
|
|
170
|
+
claude: 'claude',
|
|
171
|
+
'claude-code': 'claude',
|
|
172
|
+
claudecode: 'claude',
|
|
173
|
+
codex: 'codex',
|
|
174
|
+
'openai-codex': 'codex',
|
|
175
|
+
};
|
|
176
|
+
return aliases[normalized] || null;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
function parseRequestedAgents(raw) {
|
|
180
|
+
const selected = [];
|
|
181
|
+
for (const item of raw.replaceAll(',', ',').split(',')) {
|
|
182
|
+
if (!item.trim()) continue;
|
|
183
|
+
const key = normalizeAgent(item);
|
|
184
|
+
if (!key) throw new Error(`不支持的 Agent:${item.trim()}`);
|
|
185
|
+
if (!selected.includes(key)) selected.push(key);
|
|
186
|
+
}
|
|
187
|
+
return selected;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function ask(question) {
|
|
191
|
+
const terminal = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
192
|
+
return new Promise((resolve) => {
|
|
193
|
+
terminal.question(question, (answer) => {
|
|
194
|
+
terminal.close();
|
|
195
|
+
resolve(answer.trim());
|
|
196
|
+
});
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
async function selectInteractively(detected) {
|
|
201
|
+
if (!process.stdin.isTTY || !process.stdout.isTTY) {
|
|
202
|
+
throw new Error('当前不是交互式终端,请使用 --agent 或 --all。');
|
|
203
|
+
}
|
|
204
|
+
console.log('\n请选择安装目标,可输入一个或多个编号(例如 1,3):');
|
|
205
|
+
detected.forEach((key, index) => console.log(` ${index + 1}) ${AGENTS[key].label}`));
|
|
206
|
+
console.log(' a) 全部');
|
|
207
|
+
console.log(' q) 退出');
|
|
208
|
+
const answer = (await ask('选择:')).replaceAll(',', ',').replaceAll(/\s/g, '');
|
|
209
|
+
if (/^q$/i.test(answer)) return [];
|
|
210
|
+
if (/^a$/i.test(answer)) return [...detected];
|
|
211
|
+
|
|
212
|
+
const selected = [];
|
|
213
|
+
for (const value of answer.split(',')) {
|
|
214
|
+
if (!/^\d+$/.test(value)) throw new Error(`无效选项:${value}`);
|
|
215
|
+
const index = Number(value) - 1;
|
|
216
|
+
if (index < 0 || index >= detected.length) throw new Error(`无效选项:${value}`);
|
|
217
|
+
if (!selected.includes(detected[index])) selected.push(detected[index]);
|
|
218
|
+
}
|
|
219
|
+
return selected;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
function verifyPackage() {
|
|
223
|
+
for (const [sourceRelative] of SOURCE_FILES) {
|
|
224
|
+
const source = path.join(PACKAGE_ROOT, sourceRelative);
|
|
225
|
+
if (!fs.statSync(source, { throwIfNoEntry: false })?.isFile()) {
|
|
226
|
+
throw new Error(`npm 包不完整,缺少 ${sourceRelative}。`);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
function installationExists(destination) {
|
|
232
|
+
return CANONICAL_FILES.some((relative) => fs.existsSync(path.join(destination, relative)));
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
function timestamp() {
|
|
236
|
+
const now = new Date();
|
|
237
|
+
const pad = (value) => String(value).padStart(2, '0');
|
|
238
|
+
return `${now.getFullYear()}${pad(now.getMonth() + 1)}${pad(now.getDate())}-` +
|
|
239
|
+
`${pad(now.getHours())}${pad(now.getMinutes())}${pad(now.getSeconds())}-${process.pid}`;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
function backupExistingFiles(key, destination) {
|
|
243
|
+
const backupRoot = path.join(
|
|
244
|
+
LOCAL_APP_DATA,
|
|
245
|
+
'CrawlProInstaller',
|
|
246
|
+
'backups',
|
|
247
|
+
key,
|
|
248
|
+
timestamp(),
|
|
249
|
+
);
|
|
250
|
+
let copied = 0;
|
|
251
|
+
for (const relative of CANONICAL_FILES) {
|
|
252
|
+
const source = path.join(destination, relative);
|
|
253
|
+
if (!fs.existsSync(source)) continue;
|
|
254
|
+
const target = path.join(backupRoot, relative);
|
|
255
|
+
fs.mkdirSync(path.dirname(target), { recursive: true });
|
|
256
|
+
fs.copyFileSync(source, target);
|
|
257
|
+
copied += 1;
|
|
258
|
+
}
|
|
259
|
+
if (copied > 0) console.log(` 旧版本已备份:${backupRoot}`);
|
|
260
|
+
return backupRoot;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
function atomicCopy(source, destination) {
|
|
264
|
+
fs.mkdirSync(path.dirname(destination), { recursive: true });
|
|
265
|
+
const temporary = `${destination}.crawlpro-new-${process.pid}`;
|
|
266
|
+
const previous = `${destination}.crawlpro-old-${process.pid}`;
|
|
267
|
+
fs.copyFileSync(source, temporary);
|
|
268
|
+
|
|
269
|
+
let movedPrevious = false;
|
|
270
|
+
try {
|
|
271
|
+
if (fs.existsSync(destination)) {
|
|
272
|
+
fs.renameSync(destination, previous);
|
|
273
|
+
movedPrevious = true;
|
|
274
|
+
}
|
|
275
|
+
fs.renameSync(temporary, destination);
|
|
276
|
+
if (movedPrevious) fs.rmSync(previous, { force: true });
|
|
277
|
+
} catch (error) {
|
|
278
|
+
fs.rmSync(temporary, { force: true });
|
|
279
|
+
if (movedPrevious && !fs.existsSync(destination) && fs.existsSync(previous)) {
|
|
280
|
+
fs.renameSync(previous, destination);
|
|
281
|
+
}
|
|
282
|
+
if (error && ['EBUSY', 'EPERM', 'EACCES'].includes(error.code)) {
|
|
283
|
+
throw new Error(`无法更新 ${destination};请关闭 CrawlPro 和对应 Agent 后重试。`);
|
|
284
|
+
}
|
|
285
|
+
throw error;
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
async function installAgent(key, options) {
|
|
290
|
+
const agent = AGENTS[key];
|
|
291
|
+
const destination = agent.destination();
|
|
292
|
+
console.log(`\n安装到 ${agent.label}:${destination}`);
|
|
293
|
+
|
|
294
|
+
if (installationExists(destination) && !options.yes && !options.dryRun) {
|
|
295
|
+
if (!process.stdin.isTTY || !process.stdout.isTTY) {
|
|
296
|
+
throw new Error('目标已存在;非交互模式更新请加 --yes。');
|
|
297
|
+
}
|
|
298
|
+
const answer = await ask(' 已存在 CrawlPro,备份并更新?[y/N] ');
|
|
299
|
+
if (!/^y$/i.test(answer)) {
|
|
300
|
+
console.log(' 已跳过。');
|
|
301
|
+
return;
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
if (options.dryRun) {
|
|
306
|
+
console.log(' [dry-run] 将保留现有授权、配置和输出数据,并更新发布文件。');
|
|
307
|
+
return;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
if (installationExists(destination)) backupExistingFiles(key, destination);
|
|
311
|
+
for (const [sourceRelative, destinationRelative] of SOURCE_FILES) {
|
|
312
|
+
atomicCopy(
|
|
313
|
+
path.join(PACKAGE_ROOT, sourceRelative),
|
|
314
|
+
path.join(destination, destinationRelative),
|
|
315
|
+
);
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
for (const required of ['main.exe', 'start_chrome_debug.bat']) {
|
|
319
|
+
if (!fs.statSync(path.join(destination, required), { throwIfNoEntry: false })?.isFile()) {
|
|
320
|
+
throw new Error(`发布文件校验失败:${required}`);
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
console.log(' 安装完成。现有配置和生成数据未被删除。');
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
async function main() {
|
|
327
|
+
const options = parseArgs(process.argv.slice(2));
|
|
328
|
+
if (options.help) {
|
|
329
|
+
usage();
|
|
330
|
+
return;
|
|
331
|
+
}
|
|
332
|
+
verifyPackage();
|
|
333
|
+
if (PLATFORM !== 'win32' || ARCH !== 'x64') {
|
|
334
|
+
throw new Error(`crawlpro-win 仅支持 Windows x64;当前系统为 ${PLATFORM} ${ARCH}。`);
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
const detected = AGENT_ORDER.filter(agentDetected);
|
|
338
|
+
if (options.list) {
|
|
339
|
+
printDetection();
|
|
340
|
+
return;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
let selected;
|
|
344
|
+
if (options.requestedAgents) {
|
|
345
|
+
selected = parseRequestedAgents(options.requestedAgents);
|
|
346
|
+
for (const key of selected) {
|
|
347
|
+
if (!agentDetected(key)) {
|
|
348
|
+
console.error(`警告:未检测到 ${AGENTS[key].label},仍按显式参数继续安装。`);
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
} else if (options.all) {
|
|
352
|
+
selected = [...detected];
|
|
353
|
+
} else {
|
|
354
|
+
printDetection();
|
|
355
|
+
if (detected.length === 0) {
|
|
356
|
+
throw new Error('没有检测到受支持的 Agent。安装 Agent 后重试,或使用 --agent 显式指定目标。');
|
|
357
|
+
}
|
|
358
|
+
selected = await selectInteractively(detected);
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
if (selected.length === 0) {
|
|
362
|
+
if (!options.requestedAgents && !options.all) console.log('已取消。');
|
|
363
|
+
else throw new Error('没有可安装的目标。');
|
|
364
|
+
return;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
for (const key of selected) await installAgent(key, options);
|
|
368
|
+
console.log(options.dryRun
|
|
369
|
+
? '\ndry-run 完成,没有写入文件。'
|
|
370
|
+
: '\nCrawlPro 安装完成。请在对应 Agent 中新建会话,让 Skill 清单重新加载。');
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
main().catch((error) => {
|
|
374
|
+
console.error(`错误:${error.message}`);
|
|
375
|
+
process.exitCode = 1;
|
|
376
|
+
});
|
package/main.exe
ADDED
|
Binary file
|
package/package.json
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "crawlpro-win",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Install the prebuilt CrawlPro Skill for Windows x64 into Hermes, OpenClaw, Claude Code, or Codex.",
|
|
5
|
+
"license": "SEE LICENSE IN LICENSE",
|
|
6
|
+
"author": "Xi'an Wenyao Network Information Technology Co., Ltd.",
|
|
7
|
+
"homepage": "https://www.wenyaotech.com/",
|
|
8
|
+
"bin": {
|
|
9
|
+
"crawlpro-win": "bin/crawlpro-win.cjs"
|
|
10
|
+
},
|
|
11
|
+
"files": [
|
|
12
|
+
"bin/crawlpro-win.cjs",
|
|
13
|
+
"main.exe",
|
|
14
|
+
"start_chrome_debug.bat",
|
|
15
|
+
"SKILL.md",
|
|
16
|
+
"README.md",
|
|
17
|
+
"README_CN.md",
|
|
18
|
+
"LICENSE"
|
|
19
|
+
],
|
|
20
|
+
"os": [
|
|
21
|
+
"win32"
|
|
22
|
+
],
|
|
23
|
+
"cpu": [
|
|
24
|
+
"x64"
|
|
25
|
+
],
|
|
26
|
+
"engines": {
|
|
27
|
+
"node": ">=18",
|
|
28
|
+
"npm": ">=9"
|
|
29
|
+
},
|
|
30
|
+
"scripts": {
|
|
31
|
+
"prepack": "node ../../tooling/sync-shared.cjs windows",
|
|
32
|
+
"prepublishOnly": "npm test",
|
|
33
|
+
"test": "node ../../tooling/sync-shared.cjs windows && node --check bin/crawlpro-win.cjs && node test/npm-installer-smoke.cjs"
|
|
34
|
+
},
|
|
35
|
+
"publishConfig": {
|
|
36
|
+
"access": "public",
|
|
37
|
+
"registry": "https://registry.npmjs.org/"
|
|
38
|
+
},
|
|
39
|
+
"keywords": [
|
|
40
|
+
"agent-skill",
|
|
41
|
+
"claude-code",
|
|
42
|
+
"codex",
|
|
43
|
+
"crawlpro",
|
|
44
|
+
"douyin",
|
|
45
|
+
"hermes-agent",
|
|
46
|
+
"openclaw",
|
|
47
|
+
"windows"
|
|
48
|
+
]
|
|
49
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
@echo off
|
|
2
|
+
chcp 65001 >nul
|
|
3
|
+
echo ============================================
|
|
4
|
+
echo Start Chromium debug mode on port 19553
|
|
5
|
+
echo ============================================
|
|
6
|
+
echo.
|
|
7
|
+
|
|
8
|
+
rem Prefer Microsoft Edge, then fall back to Google Chrome.
|
|
9
|
+
set "BROWSER="
|
|
10
|
+
set "BROWSER_NAME="
|
|
11
|
+
set "PROFILE_NAME="
|
|
12
|
+
if exist "C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe" (
|
|
13
|
+
set "BROWSER=C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe"
|
|
14
|
+
set "BROWSER_NAME=Microsoft Edge"
|
|
15
|
+
set "PROFILE_NAME=.douyin_edge_profile"
|
|
16
|
+
)
|
|
17
|
+
if not defined BROWSER if exist "C:\Program Files\Microsoft\Edge\Application\msedge.exe" (
|
|
18
|
+
set "BROWSER=C:\Program Files\Microsoft\Edge\Application\msedge.exe"
|
|
19
|
+
set "BROWSER_NAME=Microsoft Edge"
|
|
20
|
+
set "PROFILE_NAME=.douyin_edge_profile"
|
|
21
|
+
)
|
|
22
|
+
if not defined BROWSER if exist "%LOCALAPPDATA%\Microsoft\Edge\Application\msedge.exe" (
|
|
23
|
+
set "BROWSER=%LOCALAPPDATA%\Microsoft\Edge\Application\msedge.exe"
|
|
24
|
+
set "BROWSER_NAME=Microsoft Edge"
|
|
25
|
+
set "PROFILE_NAME=.douyin_edge_profile"
|
|
26
|
+
)
|
|
27
|
+
if not defined BROWSER if exist "C:\Program Files\Google\Chrome\Application\chrome.exe" (
|
|
28
|
+
set "BROWSER=C:\Program Files\Google\Chrome\Application\chrome.exe"
|
|
29
|
+
set "BROWSER_NAME=Google Chrome"
|
|
30
|
+
set "PROFILE_NAME=.douyin_chrome_profile"
|
|
31
|
+
)
|
|
32
|
+
if not defined BROWSER if exist "C:\Program Files (x86)\Google\Chrome\Application\chrome.exe" (
|
|
33
|
+
set "BROWSER=C:\Program Files (x86)\Google\Chrome\Application\chrome.exe"
|
|
34
|
+
set "BROWSER_NAME=Google Chrome"
|
|
35
|
+
set "PROFILE_NAME=.douyin_chrome_profile"
|
|
36
|
+
)
|
|
37
|
+
if not defined BROWSER if exist "%LOCALAPPDATA%\Google\Chrome\Application\chrome.exe" (
|
|
38
|
+
set "BROWSER=%LOCALAPPDATA%\Google\Chrome\Application\chrome.exe"
|
|
39
|
+
set "BROWSER_NAME=Google Chrome"
|
|
40
|
+
set "PROFILE_NAME=.douyin_chrome_profile"
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
if not defined BROWSER (
|
|
44
|
+
echo Microsoft Edge or Google Chrome was not found.
|
|
45
|
+
echo Install a Chromium browser that supports --remote-debugging-port=19553.
|
|
46
|
+
pause
|
|
47
|
+
exit /b 1
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
echo Browser: %BROWSER_NAME%
|
|
51
|
+
echo Executable: %BROWSER%
|
|
52
|
+
|
|
53
|
+
rem Keep separate persistent profiles for Edge and Chrome.
|
|
54
|
+
set "DEBUG_PROFILE=%~dp0%PROFILE_NAME%"
|
|
55
|
+
if not exist "%DEBUG_PROFILE%" mkdir "%DEBUG_PROFILE%"
|
|
56
|
+
|
|
57
|
+
start "" "%BROWSER%" --remote-debugging-port=19553 --user-data-dir="%DEBUG_PROFILE%" --new-window "https://www.douyin.com/"
|
|
58
|
+
|
|
59
|
+
echo.
|
|
60
|
+
echo %BROWSER_NAME% started. Scan the Douyin QR code on the first run.
|
|
61
|
+
echo Run main.exe check again after login.
|
|
62
|
+
echo.
|
|
63
|
+
pause
|