wechat-devtools-mcp 0.1.0__tar.gz
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.
- wechat_devtools_mcp-0.1.0/.gitignore +19 -0
- wechat_devtools_mcp-0.1.0/LICENSE +21 -0
- wechat_devtools_mcp-0.1.0/PKG-INFO +142 -0
- wechat_devtools_mcp-0.1.0/README.md +127 -0
- wechat_devtools_mcp-0.1.0/pyproject.toml +36 -0
- wechat_devtools_mcp-0.1.0/src/wechat_devtools_mcp/__init__.py +1 -0
- wechat_devtools_mcp-0.1.0/src/wechat_devtools_mcp/scripts/automation.js +191 -0
- wechat_devtools_mcp-0.1.0/src/wechat_devtools_mcp/scripts/cdp_listener.js +103 -0
- wechat_devtools_mcp-0.1.0/src/wechat_devtools_mcp/scripts/console_listener.js +189 -0
- wechat_devtools_mcp-0.1.0/src/wechat_devtools_mcp/scripts/live_logger.js +65 -0
- wechat_devtools_mcp-0.1.0/src/wechat_devtools_mcp/scripts/navigate_capture.js +141 -0
- wechat_devtools_mcp-0.1.0/src/wechat_devtools_mcp/scripts/package-lock.json +1782 -0
- wechat_devtools_mcp-0.1.0/src/wechat_devtools_mcp/scripts/package.json +16 -0
- wechat_devtools_mcp-0.1.0/src/wechat_devtools_mcp/scripts/run_test_script.js +168 -0
- wechat_devtools_mcp-0.1.0/src/wechat_devtools_mcp/scripts/ui_debug.js +157 -0
- wechat_devtools_mcp-0.1.0/src/wechat_devtools_mcp/server.py +2706 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 WaterTian
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: wechat-devtools-mcp
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: MCP Server for WeChat DevTools CLI - 微信开发者工具 MCP 服务
|
|
5
|
+
Project-URL: Homepage, https://github.com/WaterTian/wechat-devtools
|
|
6
|
+
Project-URL: Repository, https://github.com/WaterTian/wechat-devtools.git
|
|
7
|
+
Project-URL: Issues, https://github.com/WaterTian/wechat-devtools/issues
|
|
8
|
+
Author-email: WaterTian <changewater@qq.com>
|
|
9
|
+
License: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Requires-Python: >=3.10
|
|
12
|
+
Requires-Dist: mcp[cli]
|
|
13
|
+
Requires-Dist: pydantic>=2.0
|
|
14
|
+
Description-Content-Type: text/markdown
|
|
15
|
+
|
|
16
|
+
# 微信开发者工具 MCP Server
|
|
17
|
+
|
|
18
|
+
[](https://pypi.org/project/wechat-devtools-mcp/)
|
|
19
|
+
[](https://opensource.org/licenses/MIT)
|
|
20
|
+
|
|
21
|
+
> 将微信开发者工具 CLI 封装为 [MCP](https://modelcontextprotocol.io/) (Model Context Protocol) 服务,使编辑器中的 AI 能够直接调用微信 CLI 命令,实现小程序**开发、测试、调试、自动化**全流程闭环。
|
|
22
|
+
|
|
23
|
+
<!-- mcp-name: io.github.WaterTian/wechat-devtools -->
|
|
24
|
+
|
|
25
|
+
参考文档:
|
|
26
|
+
|
|
27
|
+
- [微信开发者工具 CLI](https://developers.weixin.qq.com/miniprogram/dev/devtools/cli.html)
|
|
28
|
+
- [小程序自动化 SDK](https://developers.weixin.qq.com/miniprogram/dev/devtools/auto/quick-start.html)
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## 能力概览
|
|
33
|
+
|
|
34
|
+
### v4.0 — 自动化交互
|
|
35
|
+
|
|
36
|
+
| 能力 | 说明 |
|
|
37
|
+
|------|------|
|
|
38
|
+
| **点击 UI 元素** | 通过 CSS 选择器模拟 `tap` 点击任意元素 |
|
|
39
|
+
| **表单输入** | 向 `input` / `textarea` 输入文本内容 |
|
|
40
|
+
| **修改页面 Data** | 动态 `setData` 热更新 UI,免编译 |
|
|
41
|
+
| **调用页面方法** | 远程触发 `onPullDownRefresh` 等页面方法 |
|
|
42
|
+
| **获取元素详情** | 读取元素 text / wxml / style / size / offset |
|
|
43
|
+
| **Mock wx API** | 覆盖 `showModal`、`chooseLocation` 等系统调用返回值 |
|
|
44
|
+
| **调用 wx API** | 直接执行 `wx.setNavigationBarTitle` 等接口 |
|
|
45
|
+
| **页面导航栈** | 查看完整的页面栈信息 |
|
|
46
|
+
|
|
47
|
+
### v3.x — 实时调试
|
|
48
|
+
>
|
|
49
|
+
> 💡 **核心建议**:在排查问题和采集日志时,**强烈建议直接使用 `wechat_get_cdp_logs`获取 CDP 高清日志**。
|
|
50
|
+
|
|
51
|
+
| 能力 | 说明 |
|
|
52
|
+
|------|------|
|
|
53
|
+
| **CDP 高清日志** | 🆕 **[推荐]** 捕获底层 WXML 警告、网络报错、系统事件和完整堆栈 |
|
|
54
|
+
| **Console 日志** | 实时捕获页面层级 `console.log/warn/error` 输出 |
|
|
55
|
+
| **异常监控** | 监听 JS 异常(含完整调用栈) |
|
|
56
|
+
| **持久连接** | 自动化端口一次开启常驻,多工具顺序调用无需重连 |
|
|
57
|
+
|
|
58
|
+
---
|
|
59
|
+
|
|
60
|
+
## 安装与配置
|
|
61
|
+
|
|
62
|
+
### 1. 全局安装 (推荐)
|
|
63
|
+
|
|
64
|
+
使用 `pip` 即可直接安装:
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
pip install wechat-devtools-mcp
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
或者使用 `uvx` 直接运行(无需安装):
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
uvx wechat-devtools-mcp
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### 2. Node.js 依赖 (自动化功能必需)
|
|
77
|
+
|
|
78
|
+
部分高级调试功能涉及 `miniprogram-automator`,安装包后需在对应目录下初始化 Node.js 环境:
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
# 进入包安装目录下的 scripts 文件夹执行
|
|
82
|
+
npm install
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### 3. 编辑器配置
|
|
86
|
+
|
|
87
|
+
#### VS Code / Cursor / Claude Desktop
|
|
88
|
+
|
|
89
|
+
在 MCP 配置文件中添加:
|
|
90
|
+
|
|
91
|
+
```json
|
|
92
|
+
{
|
|
93
|
+
"mcpServers": {
|
|
94
|
+
"wechat-devtools": {
|
|
95
|
+
"command": "wechat-devtools-mcp",
|
|
96
|
+
"env": {
|
|
97
|
+
"WECHAT_DEVTOOLS_CLI": "C:\\Program Files (x86)\\Tencent\\微信web开发者工具\\cli.bat",
|
|
98
|
+
"WECHAT_PROJECT_PATH": "D:\\path\\to\\your\\miniprogram"
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
---
|
|
106
|
+
|
|
107
|
+
## 环境变量说明
|
|
108
|
+
|
|
109
|
+
| 变量名 | 说明 | 默认值 | 必填 |
|
|
110
|
+
|--------|------|--------|------|
|
|
111
|
+
| `WECHAT_DEVTOOLS_CLI` | 微信开发者工具 CLI 路径 | 无 | **是** |
|
|
112
|
+
| `WECHAT_PROJECT_PATH` | 默认小程序项目绝对路径 | 无 | **是** |
|
|
113
|
+
| `WECHAT_CLI_TIMEOUT` | CLI 命令超时时间(秒) | `60` | 否 |
|
|
114
|
+
| `NODE_PATH` | Node.js 可执行文件路径 | `node` | 否 |
|
|
115
|
+
|
|
116
|
+
---
|
|
117
|
+
|
|
118
|
+
## AI 开发工作流指南
|
|
119
|
+
|
|
120
|
+
1. **理解上下文**: `wechat_project_info` + `wechat_read_page`
|
|
121
|
+
2. **执行修改**: (编写代码) -> `wechat_compile_check` (检查编译)
|
|
122
|
+
3. **效果预览**: `wechat_preview_page`
|
|
123
|
+
4. **自动化巡检**: `wechat_auto` -> `wechat_capture_screenshot` -> `wechat_get_cdp_logs`
|
|
124
|
+
|
|
125
|
+
---
|
|
126
|
+
|
|
127
|
+
## 目录结构 (源码)
|
|
128
|
+
|
|
129
|
+
```
|
|
130
|
+
wechat-devtools-mcp/
|
|
131
|
+
├── pyproject.toml # Python 项目定义
|
|
132
|
+
├── server.json # MCP Registry 元数据
|
|
133
|
+
├── LICENSE # MIT 许可证
|
|
134
|
+
├── src/
|
|
135
|
+
│ └── wechat_devtools_mcp/
|
|
136
|
+
│ ├── server.py # MCP Server 主入口
|
|
137
|
+
│ └── scripts/ # Node.js 辅助脚本
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
## 许可证
|
|
141
|
+
|
|
142
|
+
MIT
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
# 微信开发者工具 MCP Server
|
|
2
|
+
|
|
3
|
+
[](https://pypi.org/project/wechat-devtools-mcp/)
|
|
4
|
+
[](https://opensource.org/licenses/MIT)
|
|
5
|
+
|
|
6
|
+
> 将微信开发者工具 CLI 封装为 [MCP](https://modelcontextprotocol.io/) (Model Context Protocol) 服务,使编辑器中的 AI 能够直接调用微信 CLI 命令,实现小程序**开发、测试、调试、自动化**全流程闭环。
|
|
7
|
+
|
|
8
|
+
<!-- mcp-name: io.github.WaterTian/wechat-devtools -->
|
|
9
|
+
|
|
10
|
+
参考文档:
|
|
11
|
+
|
|
12
|
+
- [微信开发者工具 CLI](https://developers.weixin.qq.com/miniprogram/dev/devtools/cli.html)
|
|
13
|
+
- [小程序自动化 SDK](https://developers.weixin.qq.com/miniprogram/dev/devtools/auto/quick-start.html)
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## 能力概览
|
|
18
|
+
|
|
19
|
+
### v4.0 — 自动化交互
|
|
20
|
+
|
|
21
|
+
| 能力 | 说明 |
|
|
22
|
+
|------|------|
|
|
23
|
+
| **点击 UI 元素** | 通过 CSS 选择器模拟 `tap` 点击任意元素 |
|
|
24
|
+
| **表单输入** | 向 `input` / `textarea` 输入文本内容 |
|
|
25
|
+
| **修改页面 Data** | 动态 `setData` 热更新 UI,免编译 |
|
|
26
|
+
| **调用页面方法** | 远程触发 `onPullDownRefresh` 等页面方法 |
|
|
27
|
+
| **获取元素详情** | 读取元素 text / wxml / style / size / offset |
|
|
28
|
+
| **Mock wx API** | 覆盖 `showModal`、`chooseLocation` 等系统调用返回值 |
|
|
29
|
+
| **调用 wx API** | 直接执行 `wx.setNavigationBarTitle` 等接口 |
|
|
30
|
+
| **页面导航栈** | 查看完整的页面栈信息 |
|
|
31
|
+
|
|
32
|
+
### v3.x — 实时调试
|
|
33
|
+
>
|
|
34
|
+
> 💡 **核心建议**:在排查问题和采集日志时,**强烈建议直接使用 `wechat_get_cdp_logs`获取 CDP 高清日志**。
|
|
35
|
+
|
|
36
|
+
| 能力 | 说明 |
|
|
37
|
+
|------|------|
|
|
38
|
+
| **CDP 高清日志** | 🆕 **[推荐]** 捕获底层 WXML 警告、网络报错、系统事件和完整堆栈 |
|
|
39
|
+
| **Console 日志** | 实时捕获页面层级 `console.log/warn/error` 输出 |
|
|
40
|
+
| **异常监控** | 监听 JS 异常(含完整调用栈) |
|
|
41
|
+
| **持久连接** | 自动化端口一次开启常驻,多工具顺序调用无需重连 |
|
|
42
|
+
|
|
43
|
+
---
|
|
44
|
+
|
|
45
|
+
## 安装与配置
|
|
46
|
+
|
|
47
|
+
### 1. 全局安装 (推荐)
|
|
48
|
+
|
|
49
|
+
使用 `pip` 即可直接安装:
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
pip install wechat-devtools-mcp
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
或者使用 `uvx` 直接运行(无需安装):
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
uvx wechat-devtools-mcp
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### 2. Node.js 依赖 (自动化功能必需)
|
|
62
|
+
|
|
63
|
+
部分高级调试功能涉及 `miniprogram-automator`,安装包后需在对应目录下初始化 Node.js 环境:
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
# 进入包安装目录下的 scripts 文件夹执行
|
|
67
|
+
npm install
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### 3. 编辑器配置
|
|
71
|
+
|
|
72
|
+
#### VS Code / Cursor / Claude Desktop
|
|
73
|
+
|
|
74
|
+
在 MCP 配置文件中添加:
|
|
75
|
+
|
|
76
|
+
```json
|
|
77
|
+
{
|
|
78
|
+
"mcpServers": {
|
|
79
|
+
"wechat-devtools": {
|
|
80
|
+
"command": "wechat-devtools-mcp",
|
|
81
|
+
"env": {
|
|
82
|
+
"WECHAT_DEVTOOLS_CLI": "C:\\Program Files (x86)\\Tencent\\微信web开发者工具\\cli.bat",
|
|
83
|
+
"WECHAT_PROJECT_PATH": "D:\\path\\to\\your\\miniprogram"
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
---
|
|
91
|
+
|
|
92
|
+
## 环境变量说明
|
|
93
|
+
|
|
94
|
+
| 变量名 | 说明 | 默认值 | 必填 |
|
|
95
|
+
|--------|------|--------|------|
|
|
96
|
+
| `WECHAT_DEVTOOLS_CLI` | 微信开发者工具 CLI 路径 | 无 | **是** |
|
|
97
|
+
| `WECHAT_PROJECT_PATH` | 默认小程序项目绝对路径 | 无 | **是** |
|
|
98
|
+
| `WECHAT_CLI_TIMEOUT` | CLI 命令超时时间(秒) | `60` | 否 |
|
|
99
|
+
| `NODE_PATH` | Node.js 可执行文件路径 | `node` | 否 |
|
|
100
|
+
|
|
101
|
+
---
|
|
102
|
+
|
|
103
|
+
## AI 开发工作流指南
|
|
104
|
+
|
|
105
|
+
1. **理解上下文**: `wechat_project_info` + `wechat_read_page`
|
|
106
|
+
2. **执行修改**: (编写代码) -> `wechat_compile_check` (检查编译)
|
|
107
|
+
3. **效果预览**: `wechat_preview_page`
|
|
108
|
+
4. **自动化巡检**: `wechat_auto` -> `wechat_capture_screenshot` -> `wechat_get_cdp_logs`
|
|
109
|
+
|
|
110
|
+
---
|
|
111
|
+
|
|
112
|
+
## 目录结构 (源码)
|
|
113
|
+
|
|
114
|
+
```
|
|
115
|
+
wechat-devtools-mcp/
|
|
116
|
+
├── pyproject.toml # Python 项目定义
|
|
117
|
+
├── server.json # MCP Registry 元数据
|
|
118
|
+
├── LICENSE # MIT 许可证
|
|
119
|
+
├── src/
|
|
120
|
+
│ └── wechat_devtools_mcp/
|
|
121
|
+
│ ├── server.py # MCP Server 主入口
|
|
122
|
+
│ └── scripts/ # Node.js 辅助脚本
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
## 许可证
|
|
126
|
+
|
|
127
|
+
MIT
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "wechat-devtools-mcp"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "MCP Server for WeChat DevTools CLI - 微信开发者工具 MCP 服务"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
authors = [
|
|
7
|
+
{ name = "WaterTian", email = "changewater@qq.com" }
|
|
8
|
+
]
|
|
9
|
+
requires-python = ">=3.10"
|
|
10
|
+
license = { text = "MIT" }
|
|
11
|
+
dependencies = [
|
|
12
|
+
"mcp[cli]",
|
|
13
|
+
"pydantic>=2.0",
|
|
14
|
+
]
|
|
15
|
+
|
|
16
|
+
[project.urls]
|
|
17
|
+
Homepage = "https://github.com/WaterTian/wechat-devtools"
|
|
18
|
+
Repository = "https://github.com/WaterTian/wechat-devtools.git"
|
|
19
|
+
Issues = "https://github.com/WaterTian/wechat-devtools/issues"
|
|
20
|
+
|
|
21
|
+
[project.scripts]
|
|
22
|
+
wechat-devtools-mcp = "wechat_devtools_mcp.server:main"
|
|
23
|
+
|
|
24
|
+
[build-system]
|
|
25
|
+
requires = ["hatchling"]
|
|
26
|
+
build-backend = "hatchling.build"
|
|
27
|
+
|
|
28
|
+
[tool.hatch.build.targets.wheel]
|
|
29
|
+
packages = ["src/wechat_devtools_mcp"]
|
|
30
|
+
|
|
31
|
+
[tool.hatch.build.targets.sdist]
|
|
32
|
+
include = [
|
|
33
|
+
"/src/wechat_devtools_mcp",
|
|
34
|
+
"/README.md",
|
|
35
|
+
"LICENSE",
|
|
36
|
+
]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.1.0"
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* automation.js
|
|
4
|
+
* 统一的自动化操作脚本,支持 UI 交互、页面状态修改、wx API 调用等。
|
|
5
|
+
*
|
|
6
|
+
* 用法:
|
|
7
|
+
* node automation.js --port <端口> --action <操作> [其他参数]
|
|
8
|
+
*
|
|
9
|
+
* 支持的 action:
|
|
10
|
+
* setData --data <JSON> 设置页面 Data
|
|
11
|
+
* callMethod --method <方法名> [--args <JSON>] 调用页面方法
|
|
12
|
+
* tap --selector <CSS选择器> 点击元素
|
|
13
|
+
* elementInfo --selector <CSS选择器> [--prop <属性>] 获取元素信息
|
|
14
|
+
* input --selector <CSS选择器> --value <值> 输入内容
|
|
15
|
+
* mockWx --method <wx方法> --result <JSON> Mock wx API
|
|
16
|
+
* callWx --method <wx方法> [--args <JSON>] 调用 wx API
|
|
17
|
+
* pageStack 获取页面栈
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
'use strict';
|
|
21
|
+
|
|
22
|
+
const automator = require('miniprogram-automator');
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* 解析命令行参数
|
|
26
|
+
*/
|
|
27
|
+
function parseArgs(argv) {
|
|
28
|
+
const args = { port: 9420, action: '' };
|
|
29
|
+
for (let i = 2; i < argv.length; i++) {
|
|
30
|
+
switch (argv[i]) {
|
|
31
|
+
case '--port': args.port = parseInt(argv[++i], 10); break;
|
|
32
|
+
case '--action': args.action = argv[++i]; break;
|
|
33
|
+
case '--selector': args.selector = argv[++i]; break;
|
|
34
|
+
case '--data': args.data = argv[++i]; break;
|
|
35
|
+
case '--method': args.method = argv[++i]; break;
|
|
36
|
+
case '--args': args.args = argv[++i]; break;
|
|
37
|
+
case '--value': args.value = argv[++i]; break;
|
|
38
|
+
case '--result': args.result = argv[++i]; break;
|
|
39
|
+
case '--prop': args.prop = argv[++i]; break;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return args;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* 安全解析 JSON 字符串
|
|
47
|
+
*/
|
|
48
|
+
function safeParseJson(str, fallback) {
|
|
49
|
+
if (!str) return fallback;
|
|
50
|
+
try { return JSON.parse(str); } catch (e) { return fallback; }
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
async function main() {
|
|
54
|
+
const args = parseArgs(process.argv);
|
|
55
|
+
const { port, action } = args;
|
|
56
|
+
|
|
57
|
+
let miniProgram = null;
|
|
58
|
+
|
|
59
|
+
try {
|
|
60
|
+
miniProgram = await automator.connect({
|
|
61
|
+
wsEndpoint: `ws://localhost:${port}`,
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
let result = { success: true, action };
|
|
65
|
+
|
|
66
|
+
switch (action) {
|
|
67
|
+
|
|
68
|
+
// ── 设置页面 Data ──
|
|
69
|
+
case 'setData': {
|
|
70
|
+
const data = safeParseJson(args.data, null);
|
|
71
|
+
if (!data) throw new Error('缺失 --data 参数或 JSON 格式错误');
|
|
72
|
+
const page = await miniProgram.currentPage();
|
|
73
|
+
await page.setData(data);
|
|
74
|
+
result.path = page.path;
|
|
75
|
+
result.updatedKeys = Object.keys(data);
|
|
76
|
+
break;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// ── 调用页面方法 ──
|
|
80
|
+
case 'callMethod': {
|
|
81
|
+
if (!args.method) throw new Error('缺失 --method 参数');
|
|
82
|
+
const page = await miniProgram.currentPage();
|
|
83
|
+
const methodArgs = safeParseJson(args.args, []);
|
|
84
|
+
const callResult = await page.callMethod(args.method, ...methodArgs);
|
|
85
|
+
result.path = page.path;
|
|
86
|
+
result.method = args.method;
|
|
87
|
+
result.returnValue = callResult;
|
|
88
|
+
break;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// ── 点击元素 ──
|
|
92
|
+
case 'tap': {
|
|
93
|
+
if (!args.selector) throw new Error('缺失 --selector 参数');
|
|
94
|
+
const page = await miniProgram.currentPage();
|
|
95
|
+
const el = await page.$(args.selector);
|
|
96
|
+
if (!el) throw new Error(`未找到元素: ${args.selector}`);
|
|
97
|
+
await el.tap();
|
|
98
|
+
result.selector = args.selector;
|
|
99
|
+
result.tapped = true;
|
|
100
|
+
break;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// ── 获取元素信息 ──
|
|
104
|
+
case 'elementInfo': {
|
|
105
|
+
if (!args.selector) throw new Error('缺失 --selector 参数');
|
|
106
|
+
const page = await miniProgram.currentPage();
|
|
107
|
+
const el = await page.$(args.selector);
|
|
108
|
+
if (!el) throw new Error(`未找到元素: ${args.selector}`);
|
|
109
|
+
|
|
110
|
+
const info = {
|
|
111
|
+
tagName: el.tagName,
|
|
112
|
+
text: await el.text(),
|
|
113
|
+
wxml: await el.wxml(),
|
|
114
|
+
size: await el.size(),
|
|
115
|
+
offset: await el.offset(),
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
// 如果指定了具体样式属性
|
|
119
|
+
if (args.prop) {
|
|
120
|
+
info.style = {};
|
|
121
|
+
info.style[args.prop] = await el.style(args.prop);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
result.selector = args.selector;
|
|
125
|
+
result.element = info;
|
|
126
|
+
break;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// ── 输入内容 ──
|
|
130
|
+
case 'input': {
|
|
131
|
+
if (!args.selector) throw new Error('缺失 --selector 参数');
|
|
132
|
+
if (args.value === undefined) throw new Error('缺失 --value 参数');
|
|
133
|
+
const page = await miniProgram.currentPage();
|
|
134
|
+
const el = await page.$(args.selector);
|
|
135
|
+
if (!el) throw new Error(`未找到元素: ${args.selector}`);
|
|
136
|
+
await el.input(args.value);
|
|
137
|
+
result.selector = args.selector;
|
|
138
|
+
result.inputValue = args.value;
|
|
139
|
+
break;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// ── Mock wx API ──
|
|
143
|
+
case 'mockWx': {
|
|
144
|
+
if (!args.method) throw new Error('缺失 --method 参数');
|
|
145
|
+
const mockResult = safeParseJson(args.result, null);
|
|
146
|
+
if (mockResult === null) throw new Error('缺失 --result 参数或 JSON 格式错误');
|
|
147
|
+
await miniProgram.mockWxMethod(args.method, mockResult);
|
|
148
|
+
result.method = args.method;
|
|
149
|
+
result.mocked = true;
|
|
150
|
+
break;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// ── 调用 wx API ──
|
|
154
|
+
case 'callWx': {
|
|
155
|
+
if (!args.method) throw new Error('缺失 --method 参数');
|
|
156
|
+
const wxArgs = safeParseJson(args.args, []);
|
|
157
|
+
// callWxMethod 接受展开参数
|
|
158
|
+
const wxResult = await miniProgram.callWxMethod(args.method, ...wxArgs);
|
|
159
|
+
result.method = args.method;
|
|
160
|
+
result.returnValue = wxResult;
|
|
161
|
+
break;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// ── 获取页面栈 ──
|
|
165
|
+
case 'pageStack': {
|
|
166
|
+
const stack = await miniProgram.pageStack();
|
|
167
|
+
result.pages = stack.map(p => ({ path: p.path, query: p.query }));
|
|
168
|
+
result.depth = stack.length;
|
|
169
|
+
break;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
default:
|
|
173
|
+
throw new Error(`未知 action: ${action}`);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
process.stdout.write(JSON.stringify(result, null, 2));
|
|
177
|
+
// 不调用 disconnect(),保持 IDE 自动化会话存活
|
|
178
|
+
process.exit(0);
|
|
179
|
+
|
|
180
|
+
} catch (err) {
|
|
181
|
+
process.stdout.write(JSON.stringify({
|
|
182
|
+
success: false,
|
|
183
|
+
action,
|
|
184
|
+
error: err.message || String(err),
|
|
185
|
+
}, null, 2));
|
|
186
|
+
// 不调用 disconnect(),进程退出时自动清理连接
|
|
187
|
+
process.exit(0);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
main();
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
const WebSocket = require('./node_modules/ws');
|
|
2
|
+
const http = require('http');
|
|
3
|
+
|
|
4
|
+
async function getTargets() {
|
|
5
|
+
return new Promise((resolve, reject) => {
|
|
6
|
+
const req = http.get('http://127.0.0.1:9222/json/list', (res) => {
|
|
7
|
+
let data = '';
|
|
8
|
+
res.on('data', (chunk) => data += chunk);
|
|
9
|
+
res.on('end', () => {
|
|
10
|
+
try {
|
|
11
|
+
resolve(JSON.parse(data));
|
|
12
|
+
} catch (e) {
|
|
13
|
+
resolve([]);
|
|
14
|
+
}
|
|
15
|
+
});
|
|
16
|
+
});
|
|
17
|
+
req.on('error', reject);
|
|
18
|
+
req.end();
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const duration = parseInt(process.argv[2]) || 15;
|
|
23
|
+
const activeConnections = new Map();
|
|
24
|
+
let logs = [];
|
|
25
|
+
|
|
26
|
+
function addLog(type, targetUrl, targetType, data) {
|
|
27
|
+
logs.push({
|
|
28
|
+
timestamp: new Date().toISOString(),
|
|
29
|
+
type: type,
|
|
30
|
+
url: targetUrl,
|
|
31
|
+
targetType: targetType,
|
|
32
|
+
content: data
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
async function monitorTarget(target) {
|
|
37
|
+
if (activeConnections.has(target.id)) return;
|
|
38
|
+
|
|
39
|
+
// 排除 IDE 自身的 UI 界面,采集小程序相关的所有目标
|
|
40
|
+
const isIdeUI = target.url && (target.url.startsWith('devtools://') || target.url.startsWith('chrome-extension://') || target.url === '微信开发者工具');
|
|
41
|
+
if (isIdeUI && target.type !== 'webview') return;
|
|
42
|
+
|
|
43
|
+
activeConnections.set(target.id, true);
|
|
44
|
+
process.stderr.write(`[ATTACHING] ${target.type} - ${target.url}\n`);
|
|
45
|
+
|
|
46
|
+
try {
|
|
47
|
+
const ws = new WebSocket(target.webSocketDebuggerUrl);
|
|
48
|
+
|
|
49
|
+
ws.on('open', () => {
|
|
50
|
+
ws.send(JSON.stringify({ id: 1, method: 'Console.enable' }));
|
|
51
|
+
ws.send(JSON.stringify({ id: 2, method: 'Runtime.enable' }));
|
|
52
|
+
ws.send(JSON.stringify({ id: 3, method: 'Log.enable' }));
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
ws.on('message', (data) => {
|
|
56
|
+
try {
|
|
57
|
+
const msg = JSON.parse(data);
|
|
58
|
+
const method = msg.method;
|
|
59
|
+
const params = msg.params;
|
|
60
|
+
|
|
61
|
+
if (!method) return;
|
|
62
|
+
|
|
63
|
+
if (method === 'Console.messageAdded') {
|
|
64
|
+
addLog('CONSOLE', target.url, target.type, params.message);
|
|
65
|
+
}
|
|
66
|
+
else if (method === 'Runtime.consoleAPICalled') {
|
|
67
|
+
addLog('RUNTIME_CONSOLE', target.url, target.type, {
|
|
68
|
+
type: params.type,
|
|
69
|
+
args: params.args.map(a => a.value || a.description || 'unserializable')
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
else if (method === 'Runtime.exceptionThrown') {
|
|
73
|
+
addLog('EXCEPTION', target.url, target.type, params.exceptionDetails);
|
|
74
|
+
}
|
|
75
|
+
} catch (e) { }
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
ws.on('close', () => activeConnections.delete(target.id));
|
|
79
|
+
ws.on('error', () => activeConnections.delete(target.id));
|
|
80
|
+
} catch (e) {
|
|
81
|
+
activeConnections.delete(target.id);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
async function run() {
|
|
86
|
+
process.stderr.write(`Full capture monitoring for ${duration} seconds...\n`);
|
|
87
|
+
|
|
88
|
+
setTimeout(() => {
|
|
89
|
+
console.log(JSON.stringify(logs, null, 2));
|
|
90
|
+
process.exit(0);
|
|
91
|
+
}, duration * 1000);
|
|
92
|
+
|
|
93
|
+
const interval = setInterval(async () => {
|
|
94
|
+
try {
|
|
95
|
+
const targets = await getTargets();
|
|
96
|
+
for (const target of targets) {
|
|
97
|
+
await monitorTarget(target);
|
|
98
|
+
}
|
|
99
|
+
} catch (e) { }
|
|
100
|
+
}, 1000);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
run();
|