agent-miniprogram 0.1.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 +21 -0
- package/README.md +128 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +386 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/close.d.ts +3 -0
- package/dist/commands/close.js +6 -0
- package/dist/commands/close.js.map +1 -0
- package/dist/commands/connect.d.ts +4 -0
- package/dist/commands/connect.js +8 -0
- package/dist/commands/connect.js.map +1 -0
- package/dist/commands/data.d.ts +3 -0
- package/dist/commands/data.js +6 -0
- package/dist/commands/data.js.map +1 -0
- package/dist/commands/eval.d.ts +3 -0
- package/dist/commands/eval.js +6 -0
- package/dist/commands/eval.js.map +1 -0
- package/dist/commands/input.d.ts +3 -0
- package/dist/commands/input.js +6 -0
- package/dist/commands/input.js.map +1 -0
- package/dist/commands/launch.d.ts +4 -0
- package/dist/commands/launch.js +7 -0
- package/dist/commands/launch.js.map +1 -0
- package/dist/commands/mock.d.ts +3 -0
- package/dist/commands/mock.js +13 -0
- package/dist/commands/mock.js.map +1 -0
- package/dist/commands/navigate.d.ts +7 -0
- package/dist/commands/navigate.js +10 -0
- package/dist/commands/navigate.js.map +1 -0
- package/dist/commands/screenshot.d.ts +4 -0
- package/dist/commands/screenshot.js +6 -0
- package/dist/commands/screenshot.js.map +1 -0
- package/dist/commands/scroll.d.ts +5 -0
- package/dist/commands/scroll.js +6 -0
- package/dist/commands/scroll.js.map +1 -0
- package/dist/commands/snapshot.d.ts +4 -0
- package/dist/commands/snapshot.js +6 -0
- package/dist/commands/snapshot.js.map +1 -0
- package/dist/commands/tap.d.ts +3 -0
- package/dist/commands/tap.js +6 -0
- package/dist/commands/tap.js.map +1 -0
- package/dist/commands/wait.d.ts +5 -0
- package/dist/commands/wait.js +20 -0
- package/dist/commands/wait.js.map +1 -0
- package/dist/daemon/client.d.ts +6 -0
- package/dist/daemon/client.js +55 -0
- package/dist/daemon/client.js.map +1 -0
- package/dist/daemon/server.d.ts +1 -0
- package/dist/daemon/server.js +369 -0
- package/dist/daemon/server.js.map +1 -0
- package/dist/daemon/session.d.ts +14 -0
- package/dist/daemon/session.js +65 -0
- package/dist/daemon/session.js.map +1 -0
- package/dist/replay/player.d.ts +3 -0
- package/dist/replay/player.js +41 -0
- package/dist/replay/player.js.map +1 -0
- package/dist/replay/recorder.d.ts +15 -0
- package/dist/replay/recorder.js +39 -0
- package/dist/replay/recorder.js.map +1 -0
- package/dist/snapshot/builder.d.ts +16 -0
- package/dist/snapshot/builder.js +371 -0
- package/dist/snapshot/builder.js.map +1 -0
- package/dist/snapshot/refs.d.ts +17 -0
- package/dist/snapshot/refs.js +68 -0
- package/dist/snapshot/refs.js.map +1 -0
- package/package.json +44 -0
- package/skills/agent-miniprogram/SKILL.md +163 -0
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
import * as os from 'os';
|
|
4
|
+
const STATE_DIR = path.join(os.homedir(), '.agent-miniprogram');
|
|
5
|
+
const REFS_FILE = path.join(STATE_DIR, 'refs.json');
|
|
6
|
+
let registry = new Map();
|
|
7
|
+
let counter = 0;
|
|
8
|
+
function ensureStateDir() {
|
|
9
|
+
if (!fs.existsSync(STATE_DIR)) {
|
|
10
|
+
fs.mkdirSync(STATE_DIR, { recursive: true });
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
export function loadRefs() {
|
|
14
|
+
if (!fs.existsSync(REFS_FILE))
|
|
15
|
+
return;
|
|
16
|
+
try {
|
|
17
|
+
const data = JSON.parse(fs.readFileSync(REFS_FILE, 'utf-8'));
|
|
18
|
+
registry = new Map(Object.entries(data.registry || {}));
|
|
19
|
+
counter = data.counter || 0;
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
registry = new Map();
|
|
23
|
+
counter = 0;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
export function saveRefs() {
|
|
27
|
+
ensureStateDir();
|
|
28
|
+
const obj = {};
|
|
29
|
+
for (const [k, v] of registry)
|
|
30
|
+
obj[k] = v;
|
|
31
|
+
fs.writeFileSync(REFS_FILE, JSON.stringify({ registry: obj, counter }, null, 2));
|
|
32
|
+
}
|
|
33
|
+
export function clearRefs() {
|
|
34
|
+
registry.clear();
|
|
35
|
+
counter = 0;
|
|
36
|
+
saveRefs();
|
|
37
|
+
}
|
|
38
|
+
export function registerRef(selector, tag, text) {
|
|
39
|
+
// Count how many times this selector has already been registered (for $$()[N] resolution)
|
|
40
|
+
let matchIndex = 0;
|
|
41
|
+
for (const [, entry] of registry) {
|
|
42
|
+
if (entry.selector === selector)
|
|
43
|
+
matchIndex++;
|
|
44
|
+
}
|
|
45
|
+
counter++;
|
|
46
|
+
const ref = `@e${counter}`;
|
|
47
|
+
registry.set(ref, { selector, tag, text, matchIndex });
|
|
48
|
+
return ref;
|
|
49
|
+
}
|
|
50
|
+
export function resolveRef(ref) {
|
|
51
|
+
if (!ref?.startsWith('@'))
|
|
52
|
+
return undefined;
|
|
53
|
+
loadRefs();
|
|
54
|
+
return registry.get(ref)?.selector;
|
|
55
|
+
}
|
|
56
|
+
export function resolveRefFull(ref) {
|
|
57
|
+
if (!ref?.startsWith('@'))
|
|
58
|
+
return undefined;
|
|
59
|
+
loadRefs();
|
|
60
|
+
const entry = registry.get(ref);
|
|
61
|
+
if (!entry)
|
|
62
|
+
return undefined;
|
|
63
|
+
return { selector: entry.selector, matchIndex: entry.matchIndex ?? 0 };
|
|
64
|
+
}
|
|
65
|
+
export function getRefInfo(ref) {
|
|
66
|
+
return registry.get(ref);
|
|
67
|
+
}
|
|
68
|
+
//# sourceMappingURL=refs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"refs.js","sourceRoot":"","sources":["../../src/snapshot/refs.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "agent-miniprogram",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "CLI tool for AI-driven WeChat miniprogram automation testing",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"bin": {
|
|
8
|
+
"agent-mp": "./dist/cli.js"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"dist",
|
|
12
|
+
"skills"
|
|
13
|
+
],
|
|
14
|
+
"engines": {
|
|
15
|
+
"node": ">=18.0.0"
|
|
16
|
+
},
|
|
17
|
+
"keywords": [
|
|
18
|
+
"wechat",
|
|
19
|
+
"miniprogram",
|
|
20
|
+
"automation",
|
|
21
|
+
"testing",
|
|
22
|
+
"cli",
|
|
23
|
+
"ai",
|
|
24
|
+
"agent"
|
|
25
|
+
],
|
|
26
|
+
"scripts": {
|
|
27
|
+
"build": "tsc",
|
|
28
|
+
"start": "tsx src/cli.ts",
|
|
29
|
+
"cli": "tsx src/cli.ts",
|
|
30
|
+
"daemon": "tsx src/daemon/server.ts",
|
|
31
|
+
"test": "vitest run",
|
|
32
|
+
"prepublishOnly": "npm run build && npm test"
|
|
33
|
+
},
|
|
34
|
+
"dependencies": {
|
|
35
|
+
"commander": "^12.0.0",
|
|
36
|
+
"miniprogram-automator": "^0.12.1"
|
|
37
|
+
},
|
|
38
|
+
"devDependencies": {
|
|
39
|
+
"@types/node": "^20.0.0",
|
|
40
|
+
"tsx": "^4.7.0",
|
|
41
|
+
"typescript": "^5.4.0",
|
|
42
|
+
"vitest": "^1.4.0"
|
|
43
|
+
}
|
|
44
|
+
}
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
# Agent Miniprogram — Claude Code Skill
|
|
2
|
+
|
|
3
|
+
## Trigger Conditions
|
|
4
|
+
|
|
5
|
+
Use this skill when asked to:
|
|
6
|
+
- Test, explore, or debug a WeChat miniprogram
|
|
7
|
+
- Automate interactions in the WeChat Developer Tools simulator
|
|
8
|
+
- Verify miniprogram UI behavior or page data
|
|
9
|
+
- Run regression tests on a miniprogram
|
|
10
|
+
|
|
11
|
+
## Setup
|
|
12
|
+
|
|
13
|
+
The tool requires:
|
|
14
|
+
1. WeChat Developer Tools installed, with "Security Settings → Enable HTTP Calls" turned on
|
|
15
|
+
2. The daemon running: `agent-mp daemon`
|
|
16
|
+
- Or if running from source: `npm run daemon`
|
|
17
|
+
|
|
18
|
+
All commands communicate with the daemon via HTTP at `localhost:9430`.
|
|
19
|
+
|
|
20
|
+
## CLI Reference
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
# Session
|
|
24
|
+
agent-mp launch <project-path> # Launch DevTools + connect
|
|
25
|
+
agent-mp connect [--ws <endpoint>] # Connect to already-running DevTools
|
|
26
|
+
agent-mp status # Show connection status
|
|
27
|
+
agent-mp close # Close connection
|
|
28
|
+
|
|
29
|
+
# Inspection
|
|
30
|
+
agent-mp snapshot # WXML tree with @eN refs (~200-400 tokens)
|
|
31
|
+
agent-mp snapshot --data # Also shows page.data
|
|
32
|
+
agent-mp screenshot [--path <f>] # Save screenshot
|
|
33
|
+
agent-mp data [<dot.path>] # Read page.data
|
|
34
|
+
agent-mp diff snapshot # Diff vs last snapshot
|
|
35
|
+
|
|
36
|
+
# Navigation
|
|
37
|
+
agent-mp navigate <url> # Navigate to page
|
|
38
|
+
agent-mp navigate <url> --type reLaunch|navigateTo|switchTab
|
|
39
|
+
agent-mp back # Navigate back
|
|
40
|
+
|
|
41
|
+
# Interaction (use @eN refs from snapshot)
|
|
42
|
+
agent-mp tap <@ref|selector>
|
|
43
|
+
agent-mp input <@ref|selector> <text>
|
|
44
|
+
agent-mp scroll <@ref|selector> --x <n> --y <n>
|
|
45
|
+
agent-mp long-press <@ref|selector>
|
|
46
|
+
agent-mp swipe <@ref|selector> <up|down|left|right>
|
|
47
|
+
|
|
48
|
+
# Advanced
|
|
49
|
+
agent-mp wait <@ref|ms> # Wait for element or duration
|
|
50
|
+
agent-mp wait --text "some text" # Wait for text to appear
|
|
51
|
+
agent-mp eval "<js code>" # Run JS in AppService
|
|
52
|
+
agent-mp mock <api> '<json>' # Mock wx.* API
|
|
53
|
+
|
|
54
|
+
# Record / Replay
|
|
55
|
+
agent-mp record start
|
|
56
|
+
agent-mp record stop [--output <file.amp>]
|
|
57
|
+
agent-mp replay <file.amp>
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
All commands support `--json` for structured output.
|
|
61
|
+
|
|
62
|
+
## Workflow 1: Exploratory Testing
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
# 1. Start session
|
|
66
|
+
agent-mp launch /path/to/miniprogram
|
|
67
|
+
|
|
68
|
+
# 2. Observe current state
|
|
69
|
+
agent-mp snapshot
|
|
70
|
+
|
|
71
|
+
# 3. Interact using refs from snapshot
|
|
72
|
+
agent-mp tap @e5 # tap the login button (ref from snapshot)
|
|
73
|
+
agent-mp snapshot # re-snapshot after action
|
|
74
|
+
|
|
75
|
+
# 4. Fill in forms
|
|
76
|
+
agent-mp input @e3 "13800138000" # phone input
|
|
77
|
+
agent-mp input @e4 "password123" # password input
|
|
78
|
+
agent-mp tap @e7 # tap submit button
|
|
79
|
+
|
|
80
|
+
# 5. Verify result
|
|
81
|
+
agent-mp snapshot --data
|
|
82
|
+
|
|
83
|
+
# 6. Close
|
|
84
|
+
agent-mp close
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## Workflow 2: Debug Workflow
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
agent-mp launch /path/to/miniprogram
|
|
91
|
+
agent-mp navigate /pages/product/detail?id=123
|
|
92
|
+
|
|
93
|
+
# Check page data
|
|
94
|
+
agent-mp data
|
|
95
|
+
|
|
96
|
+
# Execute JS in page context
|
|
97
|
+
agent-mp eval "getCurrentPages()[0].data"
|
|
98
|
+
|
|
99
|
+
# Take screenshot for visual inspection
|
|
100
|
+
agent-mp screenshot --path /tmp/debug.png
|
|
101
|
+
|
|
102
|
+
# Mock an API to test edge case
|
|
103
|
+
agent-mp mock request '{"statusCode":200,"data":{"items":[]}}'
|
|
104
|
+
agent-mp navigate /pages/list/index --type reLaunch
|
|
105
|
+
agent-mp snapshot
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## Workflow 3: Regression Testing (Record & Replay)
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
# Record a user flow
|
|
112
|
+
agent-mp launch /path/to/miniprogram
|
|
113
|
+
agent-mp record start
|
|
114
|
+
agent-mp navigate /pages/index/index
|
|
115
|
+
agent-mp tap @e3
|
|
116
|
+
agent-mp input @e5 "test@example.com"
|
|
117
|
+
agent-mp tap @e7
|
|
118
|
+
agent-mp record stop --output tests/login-flow.amp
|
|
119
|
+
|
|
120
|
+
# Later: replay to verify
|
|
121
|
+
agent-mp launch /path/to/miniprogram
|
|
122
|
+
agent-mp replay tests/login-flow.amp
|
|
123
|
+
agent-mp snapshot --data
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
## Snapshot Format
|
|
127
|
+
|
|
128
|
+
The `snapshot` command outputs a compact WXML tree:
|
|
129
|
+
|
|
130
|
+
```
|
|
131
|
+
Page: /pages/index/index
|
|
132
|
+
|
|
133
|
+
[view @e1]
|
|
134
|
+
[image @e2] src="/img/logo.png"
|
|
135
|
+
[text @e3] "欢迎登录"
|
|
136
|
+
[form @e4]
|
|
137
|
+
[input @e5] placeholder="手机号" (bindinput=onPhoneInput)
|
|
138
|
+
[input @e6] type="password" placeholder="密码" (bindinput=onPasswordInput)
|
|
139
|
+
[button @e7] "登录" (bindtap=onLogin)
|
|
140
|
+
[navigator @e8] → /pages/register/index "注册账号"
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
Key rules:
|
|
144
|
+
- `@eN` refs are stable within a session — use them instead of selectors
|
|
145
|
+
- **After every navigation, run `snapshot` again** — refs reset on each snapshot call
|
|
146
|
+
- Elements with `(bindtap)` are tappable
|
|
147
|
+
- Elements with `(bindinput)` accept text input
|
|
148
|
+
|
|
149
|
+
## Best Practices
|
|
150
|
+
|
|
151
|
+
1. **Always snapshot after navigation** — page content changes, refs reset
|
|
152
|
+
2. **Prefer refs over selectors** — `@e5` is cleaner than `.form > input:nth-child(2)`
|
|
153
|
+
3. **Use `--data` to debug state** — `agent-mp snapshot --data` shows both UI and data
|
|
154
|
+
4. **Use `diff snapshot` for assertions** — run before and after an action to see what changed
|
|
155
|
+
5. **Use `eval` for complex assertions** — `agent-mp eval "getCurrentPages()[0].data.loginStatus"`
|
|
156
|
+
6. **Mock network calls for deterministic tests** — `agent-mp mock request '...'`
|
|
157
|
+
7. **Add `wait` after async operations** — e.g., `agent-mp wait 500` after a form submit
|
|
158
|
+
|
|
159
|
+
## Error Handling
|
|
160
|
+
|
|
161
|
+
- `Daemon not running` → run `agent-mp daemon` first
|
|
162
|
+
- `Not connected` → run `agent-mp launch <path>` or `agent-mp connect`
|
|
163
|
+
- `Element not found: @e5` → snapshot is stale, run `agent-mp snapshot` again
|