@11agents/cli 0.1.26 → 0.1.27

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (28) hide show
  1. package/mobile-runtime/configs/platforms/xiaohongshu_d01.json +1 -1
  2. package/mobile-runtime/configs/platforms/xiaohongshu_d02.json +1 -1
  3. package/mobile-runtime/configs/platforms/xiaohongshu_d03.json +1 -1
  4. package/mobile-runtime/python/src/device_control/adb.py +3 -0
  5. package/mobile-runtime/python/src/device_control/cli.py +38 -19
  6. package/mobile-runtime/python/src/device_control/metrics/tiktok_account_adb.py +1 -1
  7. package/mobile-runtime/python/src/device_control/metrics/tiktok_video_adb.py +2 -2
  8. package/mobile-runtime/python/src/device_control/models.py +4 -4
  9. package/mobile-runtime/python/src/device_control/publishers/facebook_adb.py +6 -6
  10. package/mobile-runtime/python/src/device_control/publishers/instagram_adb.py +10 -10
  11. package/mobile-runtime/python/src/device_control/publishers/tiktok_adb.py +5 -5
  12. package/mobile-runtime/python/src/device_control/publishers/x_adb.py +16 -16
  13. package/mobile-runtime/python/src/device_control/publishers/xiaohongshu_adb.py +511 -67
  14. package/mobile-runtime/skills/android-collect-tiktok-metrics/SKILL.md +1 -1
  15. package/mobile-runtime/skills/android-group-control-cli/SKILL.md +1 -1
  16. package/mobile-runtime/skills/android-group-control-cli/references/command-reference.md +1 -1
  17. package/mobile-runtime/skills/android-publish-facebook/SKILL.md +1 -1
  18. package/mobile-runtime/skills/android-publish-instagram/SKILL.md +2 -2
  19. package/mobile-runtime/skills/android-publish-reddit/SKILL.md +1 -1
  20. package/mobile-runtime/skills/android-publish-tiktok/SKILL.md +1 -1
  21. package/mobile-runtime/skills/android-publish-x/SKILL.md +1 -1
  22. package/mobile-runtime/skills/android-publish-xiaohongshu/SKILL.md +8 -6
  23. package/mobile-runtime/skills/mobile-publish-device-health/SKILL.md +3 -3
  24. package/mobile-runtime/skills/mobile-publish-execution/SKILL.md +2 -2
  25. package/mobile-runtime/skills/mobile-publish-records/SKILL.md +1 -1
  26. package/package.json +1 -1
  27. package/src/commands/mobile.js +144 -1
  28. package/src/commands/runtime.js +13 -0
@@ -10,7 +10,7 @@ description: Collect TikTok account and video metrics from the bundled 11agents
10
10
  - Use `11agents mobile ...`; do not call raw ADB/Appium directly.
11
11
  - Collect only metrics visible to an authorized account/device.
12
12
  - Do not automate engagement, browsing, liking, following, sharing, or view inflation.
13
- - If TikTok Studio, login, verification, or app state requires a person, return `needs_human` with screenshot evidence.
13
+ - If TikTok Studio, login, verification, or app state cannot be handled automatically, return `failed` with screenshot evidence.
14
14
  - Always pass `--json` when supported and `--task-id <task_id>` when running for an agent task.
15
15
 
16
16
  ## Required Inputs
@@ -13,7 +13,7 @@ description: Operate the bundled 11agents mobile Android runtime for device-pool
13
13
  - Use only allowlisted high-level commands exposed by `11agents mobile`.
14
14
  - Do not run arbitrary `adb shell`, arbitrary Appium scripts, credential scraping, CAPTCHA bypass, login bypass, or platform verification bypass.
15
15
  - Do not automate likes, comments, follows, shares, views, random browsing, registrations, or fake engagement.
16
- - If a page needs human action, report `needs_human`, include the screenshot path, and stop.
16
+ - If the CLI cannot continue automatically, report `failed`, include the screenshot path, and stop.
17
17
  - Prefer `--json` for machine-readable execution results.
18
18
  - Use `--dry-run` when the user asks for preview/test; use live publish only with explicit approval.
19
19
 
@@ -118,5 +118,5 @@ Use `--no-ensure-appium` or `--no-appium-smoke` only for controlled debugging ag
118
118
 
119
119
  - `published`: report success, duration, record id, permalink/post id when available, screenshot, and run log path.
120
120
  - `dry_run`: report the post-form screenshot and say no publish was clicked.
121
- - `needs_human`: report screenshot and the reason; do not continue.
121
+ - `failed`: report screenshot and the reason; do not continue.
122
122
  - `failed`: report screenshot and error; do not retry unless the user asks or the failure is a transient device/offline issue.
@@ -34,7 +34,7 @@ Stop on login, restriction, verification, unexpected composer, or publish confir
34
34
 
35
35
  ## Output
36
36
 
37
- Return publish status, record id, account/device, screenshot path, permalink or platform post id when available, duration, error or human action, and the data-collection handoff:
37
+ Return publish status, record id, account/device, screenshot path, permalink or platform post id when available, duration, error, and the data-collection handoff:
38
38
 
39
39
  ```bash
40
40
  11agents mobile data collect --platform facebook --record-id <record_id> --json --task-id TASK_123
@@ -34,11 +34,11 @@ Feed image or video:
34
34
  11agents mobile publish-instagram --device D03 --post-type post-video --video "/path/video.mp4" --caption "caption" --json --task-id TASK_123
35
35
  ```
36
36
 
37
- Do not auto-confirm unexpected login, verification, restriction, or account prompts. Stop on `needs_human` and return screenshot evidence.
37
+ Do not auto-confirm unexpected login, verification, restriction, or account prompts. Stop on `failed` and return screenshot evidence.
38
38
 
39
39
  ## Output
40
40
 
41
- Return publish status, record id, account/device, screenshot path, permalink or platform post id when available, duration, error or human action, and the data-collection handoff:
41
+ Return publish status, record id, account/device, screenshot path, permalink or platform post id when available, duration, error, and the data-collection handoff:
42
42
 
43
43
  ```bash
44
44
  11agents mobile data collect --platform instagram --record-id <record_id> --json --task-id TASK_123
@@ -34,7 +34,7 @@ Use `--no-fetch-permalink` when the public username is unknown and app-level pub
34
34
 
35
35
  ## Output
36
36
 
37
- Return publish status, record id, account/subreddit/device, screenshot path, permalink or platform post id when available, duration, error or human action, and the data-collection handoff:
37
+ Return publish status, record id, account/subreddit/device, screenshot path, permalink or platform post id when available, duration, error, and the data-collection handoff:
38
38
 
39
39
  ```bash
40
40
  11agents mobile data collect --platform reddit --record-id <record_id> --json --task-id TASK_123
@@ -36,7 +36,7 @@ Use `--range`, `--parallel`, and `--max-concurrency N` only when the approved pl
36
36
 
37
37
  ## Output
38
38
 
39
- Return publish status, record id, account/device, screenshot path, permalink or platform post id when available, duration, error or `needs_human` action, and the data-collection handoff:
39
+ Return publish status, record id, account/device, screenshot path, permalink or platform post id when available, duration, error action, and the data-collection handoff:
40
40
 
41
41
  ```bash
42
42
  11agents mobile data collect --platform tiktok --scope video --record-id <record_id> --json --task-id TASK_123
@@ -33,7 +33,7 @@ Do not automate replies, follows, likes, reposts, random browsing, or DM actions
33
33
 
34
34
  ## Output
35
35
 
36
- Return publish status, record id, account/device, screenshot path, permalink or platform post id when available, duration, error or human action, and the data-collection handoff:
36
+ Return publish status, record id, account/device, screenshot path, permalink or platform post id when available, duration, error, and the data-collection handoff:
37
37
 
38
38
  ```bash
39
39
  11agents mobile data collect --platform x --record-id <record_id> --json --task-id TASK_123
@@ -23,8 +23,8 @@ If approval, account lifecycle, media/package path, text fields, or device readi
23
23
  Image/video note:
24
24
 
25
25
  ```bash
26
- 11agents mobile publish-xiaohongshu --device D03 --post-type image --image "/path/image.jpg" --title "title" --caption "body" --json --task-id TASK_123
27
- 11agents mobile publish-xiaohongshu --device D03 --post-type video --video "/path/video.mp4" --title "title" --caption "body" --json --task-id TASK_123
26
+ 11agents mobile publish-xiaohongshu --device D03 --post-type image --image "/path/image.jpg" --title "title" --caption "body" --copy-link-after-publish --json --task-id TASK_123
27
+ 11agents mobile publish-xiaohongshu --device D03 --post-type video --video "/path/video.mp4" --title "title" --caption "body" --copy-link-after-publish --json --task-id TASK_123
28
28
  ```
29
29
 
30
30
  Structured package:
@@ -33,17 +33,19 @@ Structured package:
33
33
  11agents mobile publish-xiaohongshu --device D03 --publish-package "/path/publish_package.json" --json --task-id TASK_123
34
34
  ```
35
35
 
36
- Link copy after publish, when requested:
36
+ Successful live publish is only confirmed after the CLI taps the final publish button, waits without tapping while Xiaohongshu shows upload/progress state, then opens the "我" page, opens the note whose title matches the expected title, and verifies the detail page against the expected title/body. If that evidence does not match, treat the result as `failed`; do not claim publish success from the publish button tap alone.
37
+
38
+ Link copy after publish or during the recovery skill:
37
39
 
38
40
  ```bash
39
- 11agents mobile copy-xiaohongshu-link --device D03 --record-id <record_id> --json --task-id TASK_123
41
+ 11agents mobile copy-xiaohongshu-link --record-id <record_id> --json --task-id TASK_123
40
42
  ```
41
43
 
42
- Use `--confirm-no-disclosure-needed` only when the approved package/policy explicitly allows it. Stop on login, restriction, verification, disclosure ambiguity, or unexpected UI state.
44
+ Use `--confirm-no-disclosure-needed` only when the approved package/policy explicitly allows it. Return `failed` with screenshot evidence on login, restriction, verification, disclosure ambiguity, or unexpected UI state.
43
45
 
44
46
  ## Output
45
47
 
46
- Return publish status, record id, account/device, screenshot path, note link or platform post id when available, duration, error or human action, and the data-collection handoff:
48
+ Return publish status, record id, account/device, screenshot path, note link or platform post id when available, duration, error, and the data-collection handoff:
47
49
 
48
50
  ```bash
49
51
  11agents mobile data collect --platform xiaohongshu --record-id <record_id> --copy-link --json --task-id TASK_123
@@ -24,7 +24,7 @@ If mobile setup, device data, or target device information is missing, ask the u
24
24
 
25
25
  3. Select target devices only from current health output where the managed device is active and online.
26
26
  4. If account inventory was requested and `~/.11agents/mobile/data/accounts.csv` exists, check account binding through documented local commands or records. If devices are already logged in and no account registry was requested, verify app/login readiness from device state instead of requiring account metadata.
27
- 5. Capture screenshots through high-level CLI when a device/account needs human attention:
27
+ 5. Capture screenshots through high-level CLI when a device/account needs inspection:
28
28
 
29
29
  ```bash
30
30
  11agents mobile screenshot Dxx --task-id TASK_ID
@@ -39,8 +39,8 @@ If mobile setup, device data, or target device information is missing, ask the u
39
39
  ```
40
40
 
41
41
  7. `tap`, `keyevent`, and `input-text` are allowed only through `11agents mobile` for explicit diagnostics, documented recovery steps, or human handoff assistance. Do not run raw `adb shell`.
42
- 8. Do not return `needs_human` only for camera, microphone, gallery/media, or notification permission prompts when the publish CLI can auto-grant or tap allow for publish-required permissions.
43
- 9. Return `needs_human` for login prompts, captcha, account restrictions, verification challenges, or unexpected app states that are not permission prompts.
42
+ 8. Do not fail only for camera, microphone, gallery/media, or notification permission prompts when the publish CLI can auto-grant or tap allow for publish-required permissions.
43
+ 9. Return `failed` for login prompts, captcha, account restrictions, verification challenges, or unexpected app states that are not permission prompts.
44
44
  10. Do not retry blindly after a restricted or unknown state; preserve screenshot evidence and stop.
45
45
 
46
46
  ## Output
@@ -47,11 +47,11 @@ If mobile setup, device online status, media path, app login readiness, or appro
47
47
  7. Validate the selected platform command supports the requested post type and text fields.
48
48
  8. If no explicit `--account-id` is provided, rely on the command's `--account-prefix` fallback account id for publish records when supported.
49
49
  9. Use `--dry-run` only for preview/test requests; do not add it to explicit live-publish requests.
50
- 10. Do not stop solely for Android camera, microphone, gallery/media, notification, Reels public-sharing, or Xiaohongshu disclosure prompts when the CLI policy handles them for explicit live publish. Let the CLI return `needs_human` when it cannot safely continue.
50
+ 10. Do not stop solely for Android camera, microphone, gallery/media, notification, Reels public-sharing, or Xiaohongshu disclosure prompts when the CLI policy handles them for explicit live publish. Let the CLI return `failed` with screenshot evidence when it cannot safely continue automatically.
51
51
  11. Use `11agents mobile publish-tiktok`, `publish-instagram`, `publish-facebook`, `publish-reddit`, `publish-x`, and `publish-xiaohongshu`.
52
52
  12. Prefer `--json` and parse status, duration, record id, screenshot path, permalink, platform post id, and error.
53
53
  13. Pass `--task-id <task_id>` so logs land at `~/.11agents/mobile/runs/<task_id>/log`.
54
- 14. Stop on `needs_human` or `failed`; surface screenshot and error instead of repeated retries.
54
+ 14. Stop on `failed`; surface screenshot and error instead of repeated retries.
55
55
 
56
56
  ## Output
57
57
  Return readiness status, dry-run result or execution preparation summary, device/app health notes, optional account-registry notes, command family selected, exact safe `11agents mobile` command shape, log path, and missing env placeholders.
@@ -21,7 +21,7 @@ If record path, record id, publish result, or update intent is missing, ask the
21
21
  ```
22
22
 
23
23
  3. Update records only through high-level local commands such as `11agents mobile update-publish-record`.
24
- 4. Preserve screenshot paths, errors, `needs_human` reasons, duration, account/device, media path, platform post id, permalink, and command family.
24
+ 4. Preserve screenshot paths, errors, failure reasons, duration, account/device, media path, platform post id, permalink, and command family.
25
25
  5. For Reddit permalink lookup, avoid marking app-level success as failed only because public lookup lacks a matching username.
26
26
  6. Hand successful records to Mobile Publish Data Recovery.
27
27
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@11agents/cli",
3
- "version": "0.1.26",
3
+ "version": "0.1.27",
4
4
  "description": "11agents local runtime and telemetry CLI",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1,4 +1,5 @@
1
1
  import { spawn } from 'node:child_process'
2
+ import { createHmac } from 'node:crypto'
2
3
  import { constants } from 'node:fs'
3
4
  import { access, copyFile, mkdir, readdir, readFile, writeFile } from 'node:fs/promises'
4
5
  import os from 'node:os'
@@ -65,6 +66,7 @@ const COMMAND_ALIASES = new Map([
65
66
  const WRAPPER_FLAG_NAMES = new Set([
66
67
  'mobile-home',
67
68
  'task-id',
69
+ 'task-title',
68
70
  ])
69
71
 
70
72
  const EMPTY_DATA_FILES = [
@@ -96,7 +98,9 @@ Examples:
96
98
  Runtime:
97
99
  Python/device-control code is bundled with @11agents/cli.
98
100
  Mutable runtime state lives in ~/.11agents/mobile by default.
99
- Every command writes logs under ~/.11agents/mobile/runs/<task_id>/log.`)
101
+ Every command writes logs under ~/.11agents/mobile/runs/<task_id>/log.
102
+ Publish-success DingTalk notification is enabled by ELEVENAGENTS_PUBLISH_DINGTALK_WEBHOOK
103
+ and optional ELEVENAGENTS_PUBLISH_DINGTALK_SECRET.`)
100
104
  }
101
105
 
102
106
  function mobileHome(flags = {}, env = process.env) {
@@ -492,11 +496,19 @@ function buildRunContext(rawArgs, deps = {}) {
492
496
  const env = deps.env || process.env
493
497
  const home = mobileHome(flags, env)
494
498
  const taskId = taskIdFrom(flags, env)
499
+ const taskTitle = String(
500
+ flag(flags, 'task-title')
501
+ || env.ELEVENAGENTS_TASK_TITLE
502
+ || env.ELEVENAGENTS_TASK_NAME
503
+ || env.ELEVENAGENTS_ISSUE_TITLE
504
+ || taskId
505
+ ).trim()
495
506
  const runDir = path.join(home, 'runs', taskId)
496
507
  return {
497
508
  flags,
498
509
  home,
499
510
  taskId,
511
+ taskTitle,
500
512
  runDir,
501
513
  logPath: path.join(runDir, 'log'),
502
514
  }
@@ -526,6 +538,14 @@ async function dispatchPython(rawArgs, deps = {}) {
526
538
  exit_code: result.code,
527
539
  parsed,
528
540
  }, null, 2))
541
+ await notifyMobilePublishSuccess({
542
+ command,
543
+ context,
544
+ parsed,
545
+ env: deps.env || process.env,
546
+ logPath: context.logPath,
547
+ deps,
548
+ })
529
549
  if (result.stdout) process.stdout.write(result.stdout)
530
550
  if (result.stderr) process.stderr.write(result.stderr)
531
551
  if (result.code !== 0) process.exitCode = result.code
@@ -573,6 +593,124 @@ function parseJsonResult(text) {
573
593
  return rows.length ? rows : null
574
594
  }
575
595
 
596
+ async function notifyMobilePublishSuccess({ command, context, parsed, env = process.env, logPath, deps = {} }) {
597
+ const config = dingTalkConfig(env)
598
+ if (!config.webhook || !Array.isArray(parsed)) return
599
+ const platform = platformFromMobileCommand(command)
600
+ if (!platform) return
601
+ for (const row of parsed) {
602
+ if (!isPublishedResult(row)) continue
603
+ const link = publishResultLink(row)
604
+ if (!link) continue
605
+ const content = buildPublishNotificationContent({
606
+ platform,
607
+ task: context.taskTitle || context.taskId,
608
+ deviceId: row.device_id || row.device || '',
609
+ link,
610
+ })
611
+ try {
612
+ const response = await (deps.sendDingTalkText || sendDingTalkText)({
613
+ ...config,
614
+ content,
615
+ })
616
+ await appendLog(logPath, `notification: ${content}\n`)
617
+ if (response) await appendLog(logPath, `notification_response: ${response}\n`)
618
+ } catch (error) {
619
+ await appendLog(logPath, `notification_error: ${errorMessage(error)}\n`)
620
+ }
621
+ }
622
+ }
623
+
624
+ function dingTalkConfig(env = process.env) {
625
+ return {
626
+ webhook: env.ELEVENAGENTS_PUBLISH_DINGTALK_WEBHOOK || env.ELEVENAGENTS_DINGTALK_WEBHOOK || '',
627
+ secret: env.ELEVENAGENTS_PUBLISH_DINGTALK_SECRET || env.ELEVENAGENTS_DINGTALK_SECRET || '',
628
+ }
629
+ }
630
+
631
+ function platformFromMobileCommand(command = '') {
632
+ const text = String(command || '').toLowerCase()
633
+ if (!text.startsWith('publish-')) return ''
634
+ if (text.includes('xiaohongshu')) return 'xiaohongshu'
635
+ if (text.includes('tiktok')) return 'tiktok'
636
+ if (text.includes('instagram')) return 'instagram'
637
+ if (text.includes('facebook')) return 'facebook'
638
+ if (text.includes('reddit')) return 'reddit'
639
+ if (text === 'publish-x' || text.includes('publish-x-')) return 'x'
640
+ return ''
641
+ }
642
+
643
+ function isPublishedResult(row) {
644
+ return row && typeof row === 'object' && row.status === 'published'
645
+ }
646
+
647
+ function publishResultLink(row) {
648
+ return String(
649
+ row.platform_permalink
650
+ || row.permalink
651
+ || row.url
652
+ || row.link
653
+ || ''
654
+ ).trim()
655
+ }
656
+
657
+ function buildPublishNotificationContent({ platform, task, deviceId = '', link }) {
658
+ const label = platformLabel(platform)
659
+ const taskText = String(task || 'task').trim()
660
+ const deviceText = deviceId ? `(设备 ${deviceId})` : ''
661
+ return `${label} ${taskText}${deviceText}已发布,链接:${link}`
662
+ }
663
+
664
+ function platformLabel(platform) {
665
+ const labels = {
666
+ xiaohongshu: '小红书',
667
+ tiktok: 'TikTok',
668
+ instagram: 'Instagram',
669
+ facebook: 'Facebook',
670
+ reddit: 'Reddit',
671
+ x: 'X',
672
+ }
673
+ return labels[platform] || platform
674
+ }
675
+
676
+ async function sendDingTalkText({ webhook, secret = '', content }) {
677
+ const url = signedDingTalkUrl(webhook, secret)
678
+ const response = await fetch(url, {
679
+ method: 'POST',
680
+ headers: { 'Content-Type': 'application/json;charset=utf-8' },
681
+ body: JSON.stringify({
682
+ msgtype: 'text',
683
+ text: { content },
684
+ }),
685
+ })
686
+ const text = await response.text()
687
+ if (!response.ok) throw new Error(`DingTalk webhook failed with HTTP ${response.status}: ${text}`)
688
+ try {
689
+ const parsed = JSON.parse(text)
690
+ if (parsed?.errcode) throw new Error(`DingTalk webhook failed: ${text}`)
691
+ } catch (error) {
692
+ if (error instanceof SyntaxError) return text
693
+ throw error
694
+ }
695
+ return text
696
+ }
697
+
698
+ function signedDingTalkUrl(webhook, secret = '', now = Date.now()) {
699
+ if (!secret) return webhook
700
+ const timestamp = String(now)
701
+ const sign = encodeURIComponent(
702
+ createHmac('sha256', secret)
703
+ .update(`${timestamp}\n${secret}`)
704
+ .digest('base64')
705
+ )
706
+ const sep = webhook.includes('?') ? '&' : '?'
707
+ return `${webhook}${sep}timestamp=${timestamp}&sign=${sign}`
708
+ }
709
+
710
+ function errorMessage(error) {
711
+ return error instanceof Error ? error.message : String(error)
712
+ }
713
+
576
714
  function removeFlags(args, names) {
577
715
  const out = []
578
716
  for (let i = 0; i < args.length; i += 1) {
@@ -686,9 +824,14 @@ function restAndFlagsAfter(argv, positionalCount) {
686
824
  }
687
825
 
688
826
  export const mobileInternals = {
827
+ buildPublishNotificationContent,
689
828
  dataCollectCommand,
829
+ dingTalkConfig,
690
830
  mobileHome,
691
831
  parseRawArgs,
832
+ platformFromMobileCommand,
833
+ publishResultLink,
834
+ signedDingTalkUrl,
692
835
  stripWrapperFlags,
693
836
  taskIdFrom,
694
837
  venvPython,
@@ -843,6 +843,18 @@ function agentNameForTask(task) {
843
843
  return task.agent?.name || task.agent?.id || task.agent_id || task.assignee_id || 'agent'
844
844
  }
845
845
 
846
+ function taskTitleForEnv(task) {
847
+ return String(
848
+ task.title
849
+ || task.name
850
+ || task.issue?.title
851
+ || task.queue_event?.trigger_title
852
+ || task.queue_event?.trigger_summary
853
+ || task.id
854
+ || ''
855
+ )
856
+ }
857
+
846
858
  function normalizeSkillBundle(skill = {}) {
847
859
  return {
848
860
  id: String(skill.id || ''),
@@ -1015,6 +1027,7 @@ async function prepareRuntimeTask(task, flags, deps, config) {
1015
1027
  ELEVENAGENTS_AGENT_RESULTS_DIR: agentResultsDir,
1016
1028
  ELEVENAGENTS_TASK_TMP: tmpDir,
1017
1029
  ELEVENAGENTS_TASK_ID: String(task.id || ''),
1030
+ ELEVENAGENTS_TASK_TITLE: taskTitleForEnv(task),
1018
1031
  ...(projectToken ? { GTM_SWARM_TOKEN: projectToken } : {}),
1019
1032
  }
1020
1033