@11agents/cli 0.1.28 → 0.1.29

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.
@@ -319,7 +319,13 @@ class XiaohongshuAdbPublisher:
319
319
  )
320
320
  status = "published"
321
321
  if copy_link_after_publish:
322
- link_result = self.copy_current_note_link(device, prepare=False, run_dir=run_dir / "link")
322
+ link_result = self.copy_current_note_link(
323
+ device,
324
+ prepare=False,
325
+ run_dir=run_dir / "link",
326
+ expected_title=title,
327
+ expected_caption=composed_caption,
328
+ )
323
329
 
324
330
  ended_epoch = int(time.time())
325
331
  if status == "published":
@@ -757,6 +763,7 @@ class XiaohongshuAdbPublisher:
757
763
  expected_caption: str,
758
764
  ) -> Path:
759
765
  self._wait_for_publish_exit_after_confirm(device, run_dir)
766
+ self._restart_xiaohongshu_for_link_recovery(device, run_dir)
760
767
  return self._open_latest_own_note_for_link(
761
768
  device,
762
769
  run_dir,
@@ -797,7 +804,13 @@ class XiaohongshuAdbPublisher:
797
804
 
798
805
  self._open_me_tab(device, run_dir)
799
806
  self._dismiss_link_recovery_prompts(device, run_dir)
800
- self._tap_latest_profile_note(device, run_dir, expected_title=expected_title)
807
+ self._tap_latest_profile_note(
808
+ device,
809
+ run_dir,
810
+ expected_title=expected_title,
811
+ require_expected_match=bool(expected_title or expected_caption),
812
+ allow_first_card_fallback=bool(expected_title or expected_caption),
813
+ )
801
814
  self._wait_for_detail_screen(device, run_dir)
802
815
  return self._verify_current_note_detail(
803
816
  device,
@@ -807,6 +820,13 @@ class XiaohongshuAdbPublisher:
807
820
  prefix="published-note-detail",
808
821
  )
809
822
 
823
+ def _restart_xiaohongshu_for_link_recovery(self, device: Device, run_dir: Path) -> None:
824
+ self._close_xiaohongshu(device)
825
+ time.sleep(self._wait("after_prompt_dismiss"))
826
+ self.adb.screenshot(device.adb_serial, run_dir / "after-publish-app-closed.png")
827
+ self._launch_xiaohongshu(device)
828
+ self.adb.screenshot(device.adb_serial, run_dir / "after-publish-relaunch.png")
829
+
810
830
  def _dismiss_link_recovery_prompts(self, device: Device, run_dir: Path) -> None:
811
831
  for idx in range(2):
812
832
  try:
@@ -844,7 +864,22 @@ class XiaohongshuAdbPublisher:
844
864
  self._tap_coord_or_default(device, "bottom_me", self._relative_coord(0.9, 0.93), self._wait("after_me_tab"))
845
865
  self.adb.screenshot(device.adb_serial, run_dir / "profile.png")
846
866
 
847
- def _tap_latest_profile_note(self, device: Device, run_dir: Path, *, expected_title: str = "") -> None:
867
+ def _tap_latest_profile_note(
868
+ self,
869
+ device: Device,
870
+ run_dir: Path,
871
+ *,
872
+ expected_title: str = "",
873
+ require_expected_match: bool = False,
874
+ allow_first_card_fallback: bool = False,
875
+ ) -> None:
876
+ if require_expected_match and not expected_title:
877
+ not_identifiable_shot = run_dir / "expected-profile-note-not-identifiable.png"
878
+ self.adb.screenshot(device.adb_serial, not_identifiable_shot)
879
+ raise XiaohongshuAutomationError(
880
+ "expected Xiaohongshu profile note cannot be selected safely without a title",
881
+ not_identifiable_shot,
882
+ )
848
883
  attempts = 8 if expected_title else 3
849
884
  for attempt in range(attempts):
850
885
  root = ui.dump_ui(self.adb, device, run_dir / f"profile-before-latest-note-{attempt + 1}.xml")
@@ -857,7 +892,14 @@ class XiaohongshuAdbPublisher:
857
892
  self._scroll_profile_notes_down_half_screen(device)
858
893
  self.adb.screenshot(device.adb_serial, run_dir / f"profile-after-scroll-{attempt + 1}.png")
859
894
  time.sleep(2 if expected_title else 1)
860
- if expected_title:
895
+ if allow_first_card_fallback:
896
+ root = ui.dump_ui(self.adb, device, run_dir / "profile-before-first-card-fallback.xml")
897
+ node = self._find_latest_profile_note_node(root, expected_title="")
898
+ if node is not None:
899
+ ui.tap_node(self.adb, device, node, sleep_s=self._wait("after_latest_note_tap"))
900
+ self.adb.screenshot(device.adb_serial, run_dir / "first-card-fallback-opened.png")
901
+ return
902
+ if expected_title or require_expected_match:
861
903
  not_found_shot = run_dir / "expected-profile-note-not-found.png"
862
904
  self.adb.screenshot(device.adb_serial, not_found_shot)
863
905
  raise XiaohongshuAutomationError(
@@ -966,7 +1008,8 @@ class XiaohongshuAdbPublisher:
966
1008
  continue
967
1009
  note_text = f"{text}\n{desc}"
968
1010
  if expected:
969
- if not _looks_like_profile_note_card(node) or expected not in _normalize_match_text(note_text):
1011
+ note_match = _normalize_match_text(note_text)
1012
+ if not _looks_like_profile_note_card(node) or not _profile_card_matches_expected_title(note_match, expected):
970
1013
  continue
971
1014
  return node
972
1015
  item = (top, left, node)
@@ -1462,7 +1505,7 @@ def _note_text_matches_expected(visible_text: str, expected_title: str, expected
1462
1505
  title = _normalize_match_text(expected_title)
1463
1506
  caption_fragment = _verification_fragment(expected_caption)
1464
1507
 
1465
- if title and title not in haystack:
1508
+ if title and title not in haystack and not _visible_text_matches_truncated_title(haystack, title):
1466
1509
  return False, f"title not visible: {expected_title!r}"
1467
1510
  if caption_fragment and caption_fragment not in haystack:
1468
1511
  return False, f"caption fragment not visible: {caption_fragment!r}"
@@ -1471,6 +1514,13 @@ def _note_text_matches_expected(visible_text: str, expected_title: str, expected
1471
1514
  return True, ""
1472
1515
 
1473
1516
 
1517
+ def _visible_text_matches_truncated_title(visible_text: str, expected_title: str) -> bool:
1518
+ if not visible_text or not expected_title:
1519
+ return False
1520
+ min_prefix = min(18, len(expected_title))
1521
+ return len(expected_title) >= min_prefix and expected_title[:min_prefix] in visible_text
1522
+
1523
+
1474
1524
  def _looks_like_profile_note_card(node) -> bool:
1475
1525
  text = node.attrib.get("text", "")
1476
1526
  desc = node.attrib.get("content-desc", "")
@@ -1570,6 +1620,19 @@ def _verification_fragment(text: str) -> str:
1570
1620
  return normalized[:18]
1571
1621
 
1572
1622
 
1623
+ def _profile_card_matches_expected_title(card_text: str, expected_title: str) -> bool:
1624
+ if not card_text or not expected_title:
1625
+ return False
1626
+ if expected_title in card_text:
1627
+ return True
1628
+ min_prefix = min(18, len(expected_title))
1629
+ if len(card_text) >= min_prefix and card_text in expected_title:
1630
+ return True
1631
+ if len(card_text) >= min_prefix and expected_title.startswith(card_text):
1632
+ return True
1633
+ return expected_title[:min_prefix] in card_text
1634
+
1635
+
1573
1636
  def _strip_hashtags(text: str) -> str:
1574
1637
  return re.sub(r"#[^\s#]+", "", text or "").strip()
1575
1638
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@11agents/cli",
3
- "version": "0.1.28",
3
+ "version": "0.1.29",
4
4
  "description": "11agents local runtime and telemetry CLI",
5
5
  "type": "module",
6
6
  "bin": {