@11agents/cli 0.1.24 → 0.1.26
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/README.md +52 -0
- package/bin/11agents.js +12 -0
- package/mobile-runtime/README.md +19 -0
- package/mobile-runtime/configs/platforms/xiaohongshu_d01.json +73 -0
- package/mobile-runtime/configs/platforms/xiaohongshu_d02.json +70 -0
- package/mobile-runtime/configs/platforms/xiaohongshu_d03.json +73 -0
- package/mobile-runtime/configs/publish_policy.json +40 -0
- package/mobile-runtime/data-templates/README.md +4 -0
- package/mobile-runtime/data-templates/accounts.example.csv +6 -0
- package/mobile-runtime/data-templates/devices.example.csv +2 -0
- package/mobile-runtime/data-templates/publish_records.example.jsonl +2 -0
- package/mobile-runtime/data-templates/tasks.example.jsonl +5 -0
- package/mobile-runtime/data-templates/video_metrics.example.jsonl +1 -0
- package/mobile-runtime/python/pyproject.toml +34 -0
- package/mobile-runtime/python/src/device_control/__init__.py +5 -0
- package/mobile-runtime/python/src/device_control/adapters/__init__.py +31 -0
- package/mobile-runtime/python/src/device_control/adapters/base.py +43 -0
- package/mobile-runtime/python/src/device_control/adapters/facebook.py +30 -0
- package/mobile-runtime/python/src/device_control/adapters/instagram.py +25 -0
- package/mobile-runtime/python/src/device_control/adapters/reddit.py +29 -0
- package/mobile-runtime/python/src/device_control/adapters/tiktok.py +25 -0
- package/mobile-runtime/python/src/device_control/adapters/x.py +29 -0
- package/mobile-runtime/python/src/device_control/adapters/xiaohongshu.py +26 -0
- package/mobile-runtime/python/src/device_control/adb.py +161 -0
- package/mobile-runtime/python/src/device_control/appium_client.py +131 -0
- package/mobile-runtime/python/src/device_control/appium_manager.py +403 -0
- package/mobile-runtime/python/src/device_control/cli.py +1608 -0
- package/mobile-runtime/python/src/device_control/entrypoints.py +60 -0
- package/mobile-runtime/python/src/device_control/locks.py +162 -0
- package/mobile-runtime/python/src/device_control/metrics/__init__.py +33 -0
- package/mobile-runtime/python/src/device_control/metrics/collectors.py +320 -0
- package/mobile-runtime/python/src/device_control/metrics/tiktok_account_adb.py +367 -0
- package/mobile-runtime/python/src/device_control/metrics/tiktok_video_adb.py +714 -0
- package/mobile-runtime/python/src/device_control/models.py +439 -0
- package/mobile-runtime/python/src/device_control/publish_policy.py +173 -0
- package/mobile-runtime/python/src/device_control/publishers/__init__.py +24 -0
- package/mobile-runtime/python/src/device_control/publishers/facebook_adb.py +494 -0
- package/mobile-runtime/python/src/device_control/publishers/instagram_adb.py +663 -0
- package/mobile-runtime/python/src/device_control/publishers/reddit_adb.py +595 -0
- package/mobile-runtime/python/src/device_control/publishers/tiktok_adb.py +477 -0
- package/mobile-runtime/python/src/device_control/publishers/tiktok_appium.py +259 -0
- package/mobile-runtime/python/src/device_control/publishers/ui_helpers.py +372 -0
- package/mobile-runtime/python/src/device_control/publishers/x_adb.py +636 -0
- package/mobile-runtime/python/src/device_control/publishers/xiaohongshu_adb.py +1143 -0
- package/mobile-runtime/python/src/device_control/store.py +137 -0
- package/mobile-runtime/scripts/appium_smoke.py +71 -0
- package/mobile-runtime/skills/android-collect-tiktok-metrics/SKILL.md +60 -0
- package/mobile-runtime/skills/android-collect-tiktok-metrics/agents/openai.yaml +4 -0
- package/mobile-runtime/skills/android-group-control-cli/SKILL.md +76 -0
- package/mobile-runtime/skills/android-group-control-cli/agents/openai.yaml +4 -0
- package/mobile-runtime/skills/android-group-control-cli/references/command-reference.md +122 -0
- package/mobile-runtime/skills/android-publish-facebook/SKILL.md +41 -0
- package/mobile-runtime/skills/android-publish-facebook/agents/openai.yaml +4 -0
- package/mobile-runtime/skills/android-publish-instagram/SKILL.md +45 -0
- package/mobile-runtime/skills/android-publish-instagram/agents/openai.yaml +4 -0
- package/mobile-runtime/skills/android-publish-reddit/SKILL.md +41 -0
- package/mobile-runtime/skills/android-publish-reddit/agents/openai.yaml +4 -0
- package/mobile-runtime/skills/android-publish-tiktok/SKILL.md +43 -0
- package/mobile-runtime/skills/android-publish-tiktok/agents/openai.yaml +4 -0
- package/mobile-runtime/skills/android-publish-x/SKILL.md +40 -0
- package/mobile-runtime/skills/android-publish-x/agents/openai.yaml +4 -0
- package/mobile-runtime/skills/android-publish-xiaohongshu/SKILL.md +50 -0
- package/mobile-runtime/skills/android-publish-xiaohongshu/agents/openai.yaml +4 -0
- package/mobile-runtime/skills/mobile-publish-data-collection/SKILL.md +49 -0
- package/mobile-runtime/skills/mobile-publish-device-health/SKILL.md +47 -0
- package/mobile-runtime/skills/mobile-publish-execution/SKILL.md +57 -0
- package/mobile-runtime/skills/mobile-publish-records/SKILL.md +29 -0
- package/package.json +4 -1
- package/scripts/mobile-postinstall.js +26 -0
- package/src/commands/mobile.js +695 -0
- package/src/commands/runtime.js +21 -5
package/README.md
CHANGED
|
@@ -10,6 +10,14 @@ npm install -g @11agents/cli@latest
|
|
|
10
10
|
|
|
11
11
|
The CLI requires Node.js 22 or newer.
|
|
12
12
|
|
|
13
|
+
Android publishing machines can opt into mobile runtime setup during install:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
ELEVENAGENTS_INSTALL_MOBILE=1 npm install -g @11agents/cli@latest
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
If setup must fail the npm install when Python/Appium prerequisites cannot be prepared, use `ELEVENAGENTS_REQUIRE_MOBILE=1`.
|
|
20
|
+
|
|
13
21
|
## Configure
|
|
14
22
|
|
|
15
23
|
```bash
|
|
@@ -125,6 +133,50 @@ export async function handleRuntimeTask(task) {
|
|
|
125
133
|
}
|
|
126
134
|
```
|
|
127
135
|
|
|
136
|
+
## Mobile Runtime
|
|
137
|
+
|
|
138
|
+
`@11agents/cli` bundles the Android device-pool runtime that used to live in `android-group-control-mvp`: Python `device_control`, platform configs, mobile publish skills, data templates, and the Appium smoke script. Runtime state is kept outside the npm package:
|
|
139
|
+
|
|
140
|
+
- Mobile home: `~/.11agents/mobile` or `ELEVENAGENTS_MOBILE_HOME`
|
|
141
|
+
- Python venv: `~/.11agents/mobile/.venv`
|
|
142
|
+
- Device/account data: `~/.11agents/mobile/data`
|
|
143
|
+
- Screenshots/UI dumps: `~/.11agents/mobile/artifacts`
|
|
144
|
+
- Task logs: `~/.11agents/mobile/runs/<task_id>/log`
|
|
145
|
+
|
|
146
|
+
Prepare a publishing machine:
|
|
147
|
+
|
|
148
|
+
```bash
|
|
149
|
+
11agents mobile setup
|
|
150
|
+
11agents mobile doctor --json
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
`setup` creates the Python venv, installs the bundled Python runtime, installs local Appium npm dependencies, installs UiAutomator2 into `APPIUM_HOME`, and seeds example data/config files without overwriting existing runtime data.
|
|
154
|
+
|
|
155
|
+
Core commands:
|
|
156
|
+
|
|
157
|
+
```bash
|
|
158
|
+
11agents mobile list-adb
|
|
159
|
+
11agents mobile list-devices --health --json
|
|
160
|
+
11agents mobile ensure-appium --device D03 --json --task-id TASK_123
|
|
161
|
+
11agents mobile publish-tiktok --device D03 --video /path/video.mp4 --caption "..." --json --task-id TASK_123
|
|
162
|
+
11agents mobile publish-instagram --device D03 --post-type reel --video /path/video.mp4 --caption "..." --json --task-id TASK_123
|
|
163
|
+
11agents mobile publish-facebook --device D03 --post-type text --text "..." --json --task-id TASK_123
|
|
164
|
+
11agents mobile publish-reddit --device D02 --post-type text --subreddit test --title "..." --body "..." --json --task-id TASK_123
|
|
165
|
+
11agents mobile publish-x --device D03 --post-type text --body "..." --json --task-id TASK_123
|
|
166
|
+
11agents mobile publish-xiaohongshu --device D03 --publish-package /path/publish_package.json --json --task-id TASK_123
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
Post-publish data recovery:
|
|
170
|
+
|
|
171
|
+
```bash
|
|
172
|
+
11agents mobile list-publish-records --json --task-id TASK_123
|
|
173
|
+
11agents mobile data collect --platform reddit --record-id pub_123 --json --task-id TASK_123
|
|
174
|
+
11agents mobile data collect --platform tiktok --scope video --device D03 --record-id pub_123 --json --task-id TASK_123
|
|
175
|
+
11agents mobile data collect --platform xiaohongshu --record-id pub_123 --copy-link --json --task-id TASK_123
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
Unsupported collectors return `missing_access`; provide an API token, export, or manual values and append them with `11agents mobile add-metrics`.
|
|
179
|
+
|
|
128
180
|
## Hosted MCP
|
|
129
181
|
|
|
130
182
|
Runtime agents and MCP clients should use the hosted project MCP endpoint:
|
package/bin/11agents.js
CHANGED
|
@@ -4,6 +4,7 @@ import { fileURLToPath } from 'node:url'
|
|
|
4
4
|
import { parseArgs } from '../src/args.js'
|
|
5
5
|
import { knowledgeStatus, syncKnowledge } from '../src/commands/knowledge.js'
|
|
6
6
|
import { showDaemonLog, showTaskLog } from '../src/commands/logs.js'
|
|
7
|
+
import { runMobile } from '../src/commands/mobile.js'
|
|
7
8
|
import { runNode } from '../src/commands/node.js'
|
|
8
9
|
import { pushArtifact, pushBatch, pushObservation } from '../src/commands/push.js'
|
|
9
10
|
import { registerRuntime, scanRuntime, startRuntimeDaemon } from '../src/commands/runtime.js'
|
|
@@ -33,6 +34,11 @@ Usage:
|
|
|
33
34
|
11agents push observation --workspace <slug> --agent <key> --platform x --type post --external-id <id> --metric views=123
|
|
34
35
|
11agents knowledge sync --project <slug> [--mode pull|push]
|
|
35
36
|
11agents knowledge status --project <slug>
|
|
37
|
+
11agents mobile setup
|
|
38
|
+
11agents mobile doctor [--json]
|
|
39
|
+
11agents mobile <device|publish|records|metrics command> [args...]
|
|
40
|
+
11agents mobile data collect --platform <x|reddit|instagram|facebook|tiktok|xiaohongshu>
|
|
41
|
+
11agents mobile ... --task-id <task-id> # logs under ~/.11agents/mobile/runs/<task-id>/log
|
|
36
42
|
11agents node run --workspace <slug> --agent <key> --node <node-id> --handler ./collect-x.js [--once]
|
|
37
43
|
|
|
38
44
|
Environment:
|
|
@@ -42,6 +48,7 @@ Environment:
|
|
|
42
48
|
ELEVENAGENTS_MACHINE stable machine key, defaults to hostname
|
|
43
49
|
GTM_SWARM_TOKEN project swarm token for push/node commands
|
|
44
50
|
~/.11agents/credentials project token map for hosted MCP and sync commands
|
|
51
|
+
ELEVENAGENTS_MOBILE_HOME optional mobile runtime state dir, defaults ~/.11agents/mobile
|
|
45
52
|
|
|
46
53
|
Runtime task workspace:
|
|
47
54
|
Daemon project headquarters live under ~/.11agents/<project>/.
|
|
@@ -135,6 +142,11 @@ async function main() {
|
|
|
135
142
|
return
|
|
136
143
|
}
|
|
137
144
|
|
|
145
|
+
if (command === 'mobile') {
|
|
146
|
+
await runMobile(argv.slice(1))
|
|
147
|
+
return
|
|
148
|
+
}
|
|
149
|
+
|
|
138
150
|
if (command === 'validate') {
|
|
139
151
|
const file = subcommand
|
|
140
152
|
if (!file) throw new Error('validate requires a file')
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# 11agents Mobile Runtime
|
|
2
|
+
|
|
3
|
+
This directory is bundled into `@11agents/cli` and contains the Android device-pool runtime:
|
|
4
|
+
|
|
5
|
+
- Python package: `python/src/device_control`
|
|
6
|
+
- Platform configs: `configs`
|
|
7
|
+
- Mobile skills: `skills`
|
|
8
|
+
- Data templates: `data-templates`
|
|
9
|
+
- Diagnostic scripts: `scripts`
|
|
10
|
+
|
|
11
|
+
Mutable state must not be written here. `11agents mobile setup` seeds state under `${ELEVENAGENTS_MOBILE_HOME:-~/.11agents/mobile}`.
|
|
12
|
+
|
|
13
|
+
Agent task logs are written to:
|
|
14
|
+
|
|
15
|
+
```text
|
|
16
|
+
~/.11agents/mobile/runs/<task_id>/log
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Use `11agents mobile skills list` and `11agents mobile skills show <skill-name>` to inspect bundled mobile skill guidance.
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
{
|
|
2
|
+
"package": "com.xingin.xhs",
|
|
3
|
+
"screen": {
|
|
4
|
+
"width": 1080,
|
|
5
|
+
"height": 2400
|
|
6
|
+
},
|
|
7
|
+
"coords": {
|
|
8
|
+
"unlock_swipe": [540, 1850, 540, 550, 350],
|
|
9
|
+
"home_plus": [540, 2230],
|
|
10
|
+
"album_drawer_first": [540, 1695],
|
|
11
|
+
"latest_video_select": [1025, 425],
|
|
12
|
+
"latest_image_select": [305, 425],
|
|
13
|
+
"multi_image_select": [
|
|
14
|
+
[305, 425],
|
|
15
|
+
[665, 425],
|
|
16
|
+
[1025, 425],
|
|
17
|
+
[305, 787],
|
|
18
|
+
[665, 787],
|
|
19
|
+
[1025, 787],
|
|
20
|
+
[305, 1150]
|
|
21
|
+
],
|
|
22
|
+
"gallery_next": [800, 2240],
|
|
23
|
+
"editor_next": [885, 2220],
|
|
24
|
+
"first_publish": [945, 180],
|
|
25
|
+
"confirm_publish": [725, 2200],
|
|
26
|
+
"bottom_me": [972, 2230],
|
|
27
|
+
"profile_latest_note": [270, 1220],
|
|
28
|
+
"post_publish_prompt_close": [1020, 1200],
|
|
29
|
+
"note_share": [988, 186],
|
|
30
|
+
"copy_link": [137, 2142],
|
|
31
|
+
"comment_input": [421, 374]
|
|
32
|
+
},
|
|
33
|
+
"selectors": {
|
|
34
|
+
"title": "com.xingin.xhs:id/editTitle",
|
|
35
|
+
"caption": "com.xingin.xhs:id/postNoteEditContentView"
|
|
36
|
+
},
|
|
37
|
+
"waits": {
|
|
38
|
+
"after_wake": 1,
|
|
39
|
+
"after_unlock": 1,
|
|
40
|
+
"after_launch": 5,
|
|
41
|
+
"after_plus": 1,
|
|
42
|
+
"after_album_entry": 3,
|
|
43
|
+
"after_latest_video_select": 1,
|
|
44
|
+
"after_gallery_next": 4,
|
|
45
|
+
"after_editor_next": 4,
|
|
46
|
+
"after_title_click": 1,
|
|
47
|
+
"after_caption_click": 1,
|
|
48
|
+
"after_caption_input": 1,
|
|
49
|
+
"after_first_publish": 2,
|
|
50
|
+
"after_confirm_publish": 2,
|
|
51
|
+
"after_prompt_dismiss": 1,
|
|
52
|
+
"after_me_tab": 3,
|
|
53
|
+
"after_latest_note_tap": 3,
|
|
54
|
+
"after_share_tap": 1,
|
|
55
|
+
"after_copy_link": 1,
|
|
56
|
+
"after_comment_input_tap": 1,
|
|
57
|
+
"after_paste_probe": 1,
|
|
58
|
+
"detail_load_timeout": 12,
|
|
59
|
+
"publish_poll_interval": 2,
|
|
60
|
+
"publish_timeout": 45
|
|
61
|
+
},
|
|
62
|
+
"success_focus_keywords": [
|
|
63
|
+
"IndexActivityV2"
|
|
64
|
+
],
|
|
65
|
+
"detail_focus_keywords": [
|
|
66
|
+
"NoteDetailActivity"
|
|
67
|
+
],
|
|
68
|
+
"in_progress_focus_keywords": [
|
|
69
|
+
"CapaPostNotePlatformActivity",
|
|
70
|
+
"VideoEditActivity",
|
|
71
|
+
"CapaAlbumActivity"
|
|
72
|
+
]
|
|
73
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
{
|
|
2
|
+
"package": "com.xingin.xhs",
|
|
3
|
+
"screen": {
|
|
4
|
+
"width": 1080,
|
|
5
|
+
"height": 2280
|
|
6
|
+
},
|
|
7
|
+
"coords": {
|
|
8
|
+
"unlock_swipe": [540, 1758, 540, 523, 350],
|
|
9
|
+
"home_plus": [540, 2119],
|
|
10
|
+
"album_drawer_first": [540, 1610],
|
|
11
|
+
"latest_video_select": [1025, 404],
|
|
12
|
+
"latest_image_select": [305, 404],
|
|
13
|
+
"multi_image_select": [
|
|
14
|
+
[305, 404],
|
|
15
|
+
[665, 404],
|
|
16
|
+
[1025, 404],
|
|
17
|
+
[305, 748],
|
|
18
|
+
[665, 748],
|
|
19
|
+
[1025, 748],
|
|
20
|
+
[305, 1092]
|
|
21
|
+
],
|
|
22
|
+
"gallery_next": [800, 2128],
|
|
23
|
+
"editor_next": [885, 2109],
|
|
24
|
+
"first_publish": [945, 180],
|
|
25
|
+
"confirm_publish": [725, 2090],
|
|
26
|
+
"bottom_me": [972, 2119],
|
|
27
|
+
"profile_latest_note": [270, 1135],
|
|
28
|
+
"post_publish_prompt_close": [1020, 1140],
|
|
29
|
+
"note_share": [988, 186],
|
|
30
|
+
"copy_link": [137, 2035],
|
|
31
|
+
"comment_input": [421, 355]
|
|
32
|
+
},
|
|
33
|
+
"selectors": {
|
|
34
|
+
"title": "com.xingin.xhs:id/editTitle",
|
|
35
|
+
"caption": "com.xingin.xhs:id/postNoteEditContentView"
|
|
36
|
+
},
|
|
37
|
+
"waits": {
|
|
38
|
+
"after_wake": 1,
|
|
39
|
+
"after_unlock": 1,
|
|
40
|
+
"after_launch": 5,
|
|
41
|
+
"after_plus": 1,
|
|
42
|
+
"after_album_entry": 3,
|
|
43
|
+
"after_latest_video_select": 1,
|
|
44
|
+
"after_gallery_next": 4,
|
|
45
|
+
"after_editor_next": 4,
|
|
46
|
+
"after_title_click": 1,
|
|
47
|
+
"after_caption_click": 1,
|
|
48
|
+
"after_caption_input": 1,
|
|
49
|
+
"after_first_publish": 2,
|
|
50
|
+
"after_confirm_publish": 2,
|
|
51
|
+
"after_prompt_dismiss": 1,
|
|
52
|
+
"after_me_tab": 3,
|
|
53
|
+
"after_latest_note_tap": 3,
|
|
54
|
+
"after_share_tap": 1,
|
|
55
|
+
"after_copy_link": 1,
|
|
56
|
+
"after_comment_input_tap": 1,
|
|
57
|
+
"after_paste_probe": 1,
|
|
58
|
+
"detail_load_timeout": 12,
|
|
59
|
+
"publish_poll_interval": 2,
|
|
60
|
+
"publish_timeout": 45
|
|
61
|
+
},
|
|
62
|
+
"success_focus_keywords": ["IndexActivityV2"],
|
|
63
|
+
"detail_focus_keywords": ["NoteDetailActivity"],
|
|
64
|
+
"in_progress_focus_keywords": [
|
|
65
|
+
"CapaPostNotePlatformActivity",
|
|
66
|
+
"VideoEditActivity",
|
|
67
|
+
"ImageEditActivity",
|
|
68
|
+
"CapaAlbumActivity"
|
|
69
|
+
]
|
|
70
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
{
|
|
2
|
+
"package": "com.xingin.xhs",
|
|
3
|
+
"screen": {
|
|
4
|
+
"width": 1080,
|
|
5
|
+
"height": 2400
|
|
6
|
+
},
|
|
7
|
+
"coords": {
|
|
8
|
+
"unlock_swipe": [540, 1850, 540, 550, 350],
|
|
9
|
+
"home_plus": [540, 2230],
|
|
10
|
+
"album_drawer_first": [540, 1695],
|
|
11
|
+
"latest_video_select": [1025, 425],
|
|
12
|
+
"latest_image_select": [1025, 425],
|
|
13
|
+
"multi_image_select": [
|
|
14
|
+
[1028, 424],
|
|
15
|
+
[665, 424],
|
|
16
|
+
[302, 424],
|
|
17
|
+
[1028, 787],
|
|
18
|
+
[665, 787],
|
|
19
|
+
[302, 787],
|
|
20
|
+
[1028, 1150]
|
|
21
|
+
],
|
|
22
|
+
"gallery_next": [800, 2240],
|
|
23
|
+
"editor_next": [885, 2220],
|
|
24
|
+
"first_publish": [945, 180],
|
|
25
|
+
"confirm_publish": [725, 2200],
|
|
26
|
+
"bottom_me": [972, 2230],
|
|
27
|
+
"profile_latest_note": [270, 1220],
|
|
28
|
+
"post_publish_prompt_close": [1020, 1200],
|
|
29
|
+
"note_share": [988, 186],
|
|
30
|
+
"copy_link": [137, 2142],
|
|
31
|
+
"comment_input": [421, 374]
|
|
32
|
+
},
|
|
33
|
+
"selectors": {
|
|
34
|
+
"title": "com.xingin.xhs:id/editTitle",
|
|
35
|
+
"caption": "com.xingin.xhs:id/postNoteEditContentView"
|
|
36
|
+
},
|
|
37
|
+
"waits": {
|
|
38
|
+
"after_wake": 1,
|
|
39
|
+
"after_unlock": 1,
|
|
40
|
+
"after_launch": 5,
|
|
41
|
+
"after_plus": 1,
|
|
42
|
+
"after_album_entry": 3,
|
|
43
|
+
"after_latest_video_select": 1,
|
|
44
|
+
"after_gallery_next": 4,
|
|
45
|
+
"after_editor_next": 4,
|
|
46
|
+
"after_title_click": 1,
|
|
47
|
+
"after_caption_click": 1,
|
|
48
|
+
"after_caption_input": 1,
|
|
49
|
+
"after_first_publish": 2,
|
|
50
|
+
"after_confirm_publish": 2,
|
|
51
|
+
"after_prompt_dismiss": 1,
|
|
52
|
+
"after_me_tab": 3,
|
|
53
|
+
"after_latest_note_tap": 3,
|
|
54
|
+
"after_share_tap": 1,
|
|
55
|
+
"after_copy_link": 1,
|
|
56
|
+
"after_comment_input_tap": 1,
|
|
57
|
+
"after_paste_probe": 1,
|
|
58
|
+
"detail_load_timeout": 12,
|
|
59
|
+
"publish_poll_interval": 2,
|
|
60
|
+
"publish_timeout": 45
|
|
61
|
+
},
|
|
62
|
+
"success_focus_keywords": [
|
|
63
|
+
"IndexActivityV2"
|
|
64
|
+
],
|
|
65
|
+
"detail_focus_keywords": [
|
|
66
|
+
"NoteDetailActivity"
|
|
67
|
+
],
|
|
68
|
+
"in_progress_focus_keywords": [
|
|
69
|
+
"CapaPostNotePlatformActivity",
|
|
70
|
+
"VideoEditActivity",
|
|
71
|
+
"CapaAlbumActivity"
|
|
72
|
+
]
|
|
73
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"global": {
|
|
3
|
+
"explicit_live_publish_auto_confirm": true
|
|
4
|
+
},
|
|
5
|
+
"instagram": {
|
|
6
|
+
"auto_confirm_reels_public": true
|
|
7
|
+
},
|
|
8
|
+
"xiaohongshu": {
|
|
9
|
+
"approval_bridge": {
|
|
10
|
+
"enabled": true,
|
|
11
|
+
"explicit_publish_states": [
|
|
12
|
+
"approved_for_publish",
|
|
13
|
+
"approved_to_publish",
|
|
14
|
+
"approved_to_publish_by_request",
|
|
15
|
+
"publish_requested_by_member_comment",
|
|
16
|
+
"publish_requested_for_series_by_member_comment",
|
|
17
|
+
"publish_requested_for_single_post_by_member_comment"
|
|
18
|
+
]
|
|
19
|
+
},
|
|
20
|
+
"commercial_disclosure": {
|
|
21
|
+
"auto_confirm_no_disclosure_needed": true,
|
|
22
|
+
"default_decision": "no_disclosure_needed",
|
|
23
|
+
"trust_explicit_publish_request": true,
|
|
24
|
+
"strict_package_disclosure_decision": false,
|
|
25
|
+
"unresolved_decisions_follow_default": true,
|
|
26
|
+
"no_disclosure_decisions": [
|
|
27
|
+
"no_disclosure_needed",
|
|
28
|
+
"not_required",
|
|
29
|
+
"not_needed",
|
|
30
|
+
"none"
|
|
31
|
+
],
|
|
32
|
+
"requires_disclosure_decisions": [
|
|
33
|
+
"disclosure_required",
|
|
34
|
+
"commercial_disclosure_required",
|
|
35
|
+
"content_declaration_required",
|
|
36
|
+
"required"
|
|
37
|
+
]
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
account_id,platform,username,bound_device_id,login_status,status,notes
|
|
2
|
+
RD_001,reddit,company_reddit_001,D01,logged_in,active,example account
|
|
3
|
+
TT_001,tiktok,company_tiktok_001,D01,logged_in,active,example account
|
|
4
|
+
IG_001,instagram,company_instagram_001,D02,unknown,paused,example account
|
|
5
|
+
FB_001,facebook,company_facebook_001,D03,unknown,paused,example account
|
|
6
|
+
XHS_001,xiaohongshu,company_xiaohongshu_001,D04,unknown,paused,example account
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
{"account_id":"acct_tiktok_001","caption":"demo caption","created_at":"2026-05-27T09:00:00+00:00","device_id":"D01","local_media_path":"/Users/edy/Desktop/demo.mp4","platform":"tiktok","platform_permalink":"https://www.tiktok.com/@example/video/1234567890","platform_post_id":"1234567890","post_type":"video","published_at":"2026-05-27T09:00:00+00:00","record_id":"pub_demo_tiktok_001","remote_media_path":"/sdcard/DCIM/Camera/demo.mp4","result_screenshot_path":"artifacts/screenshots/demo-after-post/D01.png","status":"published","task_id":"task_demo_001"}
|
|
2
|
+
{"account_id":"acct_reddit_001","caption":"","created_at":"2026-05-27T09:05:00+00:00","device_id":"D02","local_media_path":"","platform":"reddit","platform_permalink":"https://www.reddit.com/r/test/comments/abc123/demo/","platform_post_id":"abc123","post_type":"text","published_at":"2026-05-27T09:05:00+00:00","record_id":"pub_demo_reddit_001","remote_media_path":"","result_screenshot_path":"artifacts/screenshots/demo-reddit-after-post/D02.png","status":"published","task_id":"task_demo_002"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
{"task_id":"TASK_RD_001","action":"publish_content","platform":"reddit","post_type":"text","account_id":"RD_001","device_id":"D01","target":"r/test","title":"Dry-run test post","body":"This is a dry-run test post.","link_url":null,"media_path":null,"caption":null,"hashtags":[],"dry_run":true,"require_human_confirm":true}
|
|
2
|
+
{"task_id":"TASK_TT_001","action":"publish_content","platform":"tiktok","post_type":"video","account_id":"TT_001","device_id":"D01","target":null,"title":null,"body":null,"link_url":null,"media_path":"media/video-001.mp4","caption":"New product demo","hashtags":["demo","product"],"dry_run":true,"require_human_confirm":true}
|
|
3
|
+
{"task_id":"TASK_IG_001","action":"publish_content","platform":"instagram","post_type":"video","account_id":"IG_001","device_id":"D02","target":null,"title":null,"body":null,"link_url":null,"media_path":"media/video-001.mp4","caption":"New product demo","hashtags":["demo","product"],"dry_run":true,"require_human_confirm":true}
|
|
4
|
+
{"task_id":"TASK_FB_001","action":"publish_content","platform":"facebook","post_type":"text","account_id":"FB_001","device_id":"D03","target":null,"title":null,"body":"Dry-run Facebook post.","link_url":null,"media_path":null,"caption":null,"hashtags":[],"dry_run":true,"require_human_confirm":true}
|
|
5
|
+
{"task_id":"TASK_XHS_001","action":"publish_content","platform":"xiaohongshu","post_type":"image","account_id":"XHS_001","device_id":"D04","target":null,"title":"Dry-run Xiaohongshu note","body":null,"link_url":null,"media_path":"media/image-001.jpg","caption":"New product note","hashtags":["demo","product"],"dry_run":true,"require_human_confirm":true}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"account_id":"acct_tiktok_001","comments":3,"fetched_at":"2026-05-27T10:00:00+00:00","likes":42,"platform":"tiktok","platform_permalink":"https://www.tiktok.com/@example/video/1234567890","platform_post_id":"1234567890","raw":{"view_count":1200},"record_id":"pub_demo_tiktok_001","saves":null,"score":null,"shares":5,"snapshot_id":"met_demo_tiktok_001","source":"tiktok_display_api","views":1200}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "elevenagents-mobile-runtime"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "11agents Android device-pool runtime for mobile publishing, records, and data collection."
|
|
5
|
+
requires-python = ">=3.10"
|
|
6
|
+
dependencies = [
|
|
7
|
+
"Pillow>=10",
|
|
8
|
+
]
|
|
9
|
+
|
|
10
|
+
[project.optional-dependencies]
|
|
11
|
+
appium = [
|
|
12
|
+
"Appium-Python-Client>=4.0",
|
|
13
|
+
]
|
|
14
|
+
|
|
15
|
+
[project.scripts]
|
|
16
|
+
groupctl = "device_control.cli:main"
|
|
17
|
+
publish-facebook = "device_control.entrypoints:publish_facebook"
|
|
18
|
+
publish-instagram = "device_control.entrypoints:publish_instagram"
|
|
19
|
+
publish-reddit = "device_control.entrypoints:publish_reddit"
|
|
20
|
+
publish-tiktok = "device_control.entrypoints:publish_tiktok"
|
|
21
|
+
publish-x = "device_control.entrypoints:publish_x"
|
|
22
|
+
publish-xiaohongshu = "device_control.entrypoints:publish_xiaohongshu"
|
|
23
|
+
copy-xiaohongshu-link = "device_control.entrypoints:copy_xiaohongshu_link"
|
|
24
|
+
ensure-appium = "device_control.entrypoints:ensure_appium"
|
|
25
|
+
stop-appium = "device_control.entrypoints:stop_appium"
|
|
26
|
+
collect-tiktok-account-metrics = "device_control.entrypoints:collect_tiktok_account_metrics"
|
|
27
|
+
collect-tiktok-video-metrics = "device_control.entrypoints:collect_tiktok_video_metrics"
|
|
28
|
+
|
|
29
|
+
[build-system]
|
|
30
|
+
requires = ["setuptools>=68"]
|
|
31
|
+
build-backend = "setuptools.build_meta"
|
|
32
|
+
|
|
33
|
+
[tool.setuptools.packages.find]
|
|
34
|
+
where = ["src"]
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
from .base import AdapterRegistry, PlatformAdapter
|
|
2
|
+
from .facebook import FacebookAdapter
|
|
3
|
+
from .instagram import InstagramAdapter
|
|
4
|
+
from .reddit import RedditAdapter
|
|
5
|
+
from .tiktok import TikTokAdapter
|
|
6
|
+
from .x import XAdapter
|
|
7
|
+
from .xiaohongshu import XiaohongshuAdapter
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def default_registry() -> AdapterRegistry:
|
|
11
|
+
registry = AdapterRegistry()
|
|
12
|
+
registry.register(RedditAdapter())
|
|
13
|
+
registry.register(TikTokAdapter())
|
|
14
|
+
registry.register(InstagramAdapter())
|
|
15
|
+
registry.register(FacebookAdapter())
|
|
16
|
+
registry.register(XiaohongshuAdapter())
|
|
17
|
+
registry.register(XAdapter())
|
|
18
|
+
return registry
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
__all__ = [
|
|
22
|
+
"AdapterRegistry",
|
|
23
|
+
"PlatformAdapter",
|
|
24
|
+
"FacebookAdapter",
|
|
25
|
+
"RedditAdapter",
|
|
26
|
+
"TikTokAdapter",
|
|
27
|
+
"InstagramAdapter",
|
|
28
|
+
"XiaohongshuAdapter",
|
|
29
|
+
"XAdapter",
|
|
30
|
+
"default_registry",
|
|
31
|
+
]
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from abc import ABC, abstractmethod
|
|
4
|
+
|
|
5
|
+
from device_control.models import PublishTask
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class PlatformAdapter(ABC):
|
|
9
|
+
platform: str
|
|
10
|
+
package_names: tuple[str, ...]
|
|
11
|
+
supported_post_types: tuple[str, ...]
|
|
12
|
+
|
|
13
|
+
def validate(self, task: PublishTask) -> list[str]:
|
|
14
|
+
errors: list[str] = []
|
|
15
|
+
if task.platform != self.platform:
|
|
16
|
+
errors.append(f"Task platform {task.platform!r} does not match adapter {self.platform!r}")
|
|
17
|
+
if task.post_type not in self.supported_post_types:
|
|
18
|
+
errors.append(
|
|
19
|
+
f"Post type {task.post_type!r} is not supported by {self.platform}; "
|
|
20
|
+
f"supported={self.supported_post_types}"
|
|
21
|
+
)
|
|
22
|
+
return errors
|
|
23
|
+
|
|
24
|
+
@abstractmethod
|
|
25
|
+
def plan(self, task: PublishTask) -> list[str]:
|
|
26
|
+
"""Return human-readable execution steps for dry planning."""
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class AdapterRegistry:
|
|
30
|
+
def __init__(self) -> None:
|
|
31
|
+
self._adapters: dict[str, PlatformAdapter] = {}
|
|
32
|
+
|
|
33
|
+
def register(self, adapter: PlatformAdapter) -> None:
|
|
34
|
+
self._adapters[adapter.platform] = adapter
|
|
35
|
+
|
|
36
|
+
def get(self, platform: str) -> PlatformAdapter:
|
|
37
|
+
key = platform.lower()
|
|
38
|
+
if key not in self._adapters:
|
|
39
|
+
raise KeyError(f"No adapter registered for platform {platform!r}")
|
|
40
|
+
return self._adapters[key]
|
|
41
|
+
|
|
42
|
+
def platforms(self) -> list[str]:
|
|
43
|
+
return sorted(self._adapters)
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from device_control.models import PublishTask
|
|
4
|
+
|
|
5
|
+
from .base import PlatformAdapter
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class FacebookAdapter(PlatformAdapter):
|
|
9
|
+
platform = "facebook"
|
|
10
|
+
package_names = ("com.facebook.katana", "com.facebook.lite")
|
|
11
|
+
supported_post_types = ("text", "link", "image", "video")
|
|
12
|
+
|
|
13
|
+
def plan(self, task: PublishTask) -> list[str]:
|
|
14
|
+
steps = [
|
|
15
|
+
"wake device",
|
|
16
|
+
"open Facebook app",
|
|
17
|
+
f"choose content flow for post_type={task.post_type}",
|
|
18
|
+
]
|
|
19
|
+
if task.media_path:
|
|
20
|
+
steps.append(f"push/select media: {task.media_path}")
|
|
21
|
+
if task.title:
|
|
22
|
+
steps.append(f"fill title: {task.title}")
|
|
23
|
+
if task.body:
|
|
24
|
+
steps.append("fill body")
|
|
25
|
+
if task.caption:
|
|
26
|
+
steps.append(f"fill caption: {task.caption}")
|
|
27
|
+
if task.link_url:
|
|
28
|
+
steps.append(f"fill link: {task.link_url}")
|
|
29
|
+
steps.extend(["capture pre-publish screenshot", "publish or stop at dry-run", "capture result screenshot"])
|
|
30
|
+
return steps
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from device_control.models import PublishTask
|
|
4
|
+
|
|
5
|
+
from .base import PlatformAdapter
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class InstagramAdapter(PlatformAdapter):
|
|
9
|
+
platform = "instagram"
|
|
10
|
+
package_names = ("com.instagram.android",)
|
|
11
|
+
supported_post_types = ("image", "video")
|
|
12
|
+
|
|
13
|
+
def plan(self, task: PublishTask) -> list[str]:
|
|
14
|
+
return [
|
|
15
|
+
"wake device",
|
|
16
|
+
"push media to gallery directory",
|
|
17
|
+
"open Instagram app",
|
|
18
|
+
f"choose content flow for post_type={task.post_type}",
|
|
19
|
+
f"select media: {task.media_path}",
|
|
20
|
+
f"fill caption: {task.caption or ''}",
|
|
21
|
+
f"fill hashtags: {' '.join('#' + tag for tag in task.hashtags)}",
|
|
22
|
+
"capture pre-publish screenshot",
|
|
23
|
+
"publish or stop at dry-run",
|
|
24
|
+
"capture result screenshot",
|
|
25
|
+
]
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from device_control.models import PublishTask
|
|
4
|
+
|
|
5
|
+
from .base import PlatformAdapter
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class RedditAdapter(PlatformAdapter):
|
|
9
|
+
platform = "reddit"
|
|
10
|
+
package_names = ("com.reddit.frontpage",)
|
|
11
|
+
supported_post_types = ("text", "link", "image", "video")
|
|
12
|
+
|
|
13
|
+
def plan(self, task: PublishTask) -> list[str]:
|
|
14
|
+
steps = [
|
|
15
|
+
"wake device",
|
|
16
|
+
"open Reddit app",
|
|
17
|
+
f"select target community: {task.target}",
|
|
18
|
+
f"choose post type: {task.post_type}",
|
|
19
|
+
]
|
|
20
|
+
if task.media_path:
|
|
21
|
+
steps.append(f"push/select media: {task.media_path}")
|
|
22
|
+
if task.title:
|
|
23
|
+
steps.append(f"fill title: {task.title}")
|
|
24
|
+
if task.body:
|
|
25
|
+
steps.append("fill body")
|
|
26
|
+
if task.link_url:
|
|
27
|
+
steps.append(f"fill link: {task.link_url}")
|
|
28
|
+
steps.extend(["capture pre-publish screenshot", "publish or stop at dry-run", "capture result screenshot"])
|
|
29
|
+
return steps
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from device_control.models import PublishTask
|
|
4
|
+
|
|
5
|
+
from .base import PlatformAdapter
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class TikTokAdapter(PlatformAdapter):
|
|
9
|
+
platform = "tiktok"
|
|
10
|
+
package_names = ("com.zhiliaoapp.musically", "com.ss.android.ugc.trill")
|
|
11
|
+
supported_post_types = ("video",)
|
|
12
|
+
|
|
13
|
+
def plan(self, task: PublishTask) -> list[str]:
|
|
14
|
+
return [
|
|
15
|
+
"wake device",
|
|
16
|
+
"push video to gallery directory",
|
|
17
|
+
"open TikTok app",
|
|
18
|
+
"enter create flow",
|
|
19
|
+
f"select video: {task.media_path}",
|
|
20
|
+
f"fill caption: {task.caption or ''}",
|
|
21
|
+
f"fill hashtags: {' '.join('#' + tag for tag in task.hashtags)}",
|
|
22
|
+
"capture pre-publish screenshot",
|
|
23
|
+
"publish or stop at dry-run",
|
|
24
|
+
"capture result screenshot",
|
|
25
|
+
]
|