@11agents/cli 0.1.27 → 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.27",
3
+ "version": "0.1.29",
4
4
  "description": "11agents local runtime and telemetry CLI",
5
5
  "type": "module",
6
6
  "bin": {
@@ -103,6 +103,9 @@ async function writeCurrentClaim(deps, task, machineKey) {
103
103
  task_id: String(task.id || ''),
104
104
  runtime_id: String(task.runtime_id || task.runtime?.id || ''),
105
105
  machine_key: String(task.runtime?.machine_key || machineKey || ''),
106
+ project_id: String(task.workspace?.id || ''),
107
+ project_slug: String(task.workspace?.slug || task.workspace_slug || ''),
108
+ agent_name: String(task.agent?.name || ''),
106
109
  }
107
110
  await mkdir(claimsDirPath(deps.homeDir), { recursive: true })
108
111
  await writeFile(claimFilePath(deps.homeDir, task.id), JSON.stringify(payload))
@@ -1079,6 +1082,14 @@ function buildCodexPrompt(task) {
1079
1082
  '- aspect alternatives: "1:1", "16:9", "9:16", "4:3", "3:4". Choose the closest ratio to the final placement.',
1080
1083
  '- image_size alternatives: "1K", "2K". For a specific output size, pass aspect and image_size; pass size only when the MCP tool/provider explicitly supports the requested exact dimensions.',
1081
1084
  '- Use model="gpt" with model_id only when the task or current project notes confirm that GPT image generation is available for the hosted MCP key.',
1085
+ '- MCP authentication (agent_planning, database_sync, knowledge_sync, image_generate, etc.):',
1086
+ ' 1. Find your project_slug from the task context (task.workspace.slug).',
1087
+ ' 2. Open ~/.11agents/credentials.yaml and read the value under tokens.<project_slug>.',
1088
+ ' 3. Use that value as the Bearer token in the Authorization header for the 11agents hosted MCP endpoint.',
1089
+ ' 4. If the key is missing from credentials.yaml, add it:',
1090
+ ' tokens:',
1091
+ ' <project_slug>: <your-token>',
1092
+ ' Get the token from the 11agents platform → open your project → Tokens menu.',
1082
1093
  '',
1083
1094
  'Final task comment requirements:',
1084
1095
  '> [!IMPORTANT]',
@@ -1397,6 +1408,7 @@ async function runOneRuntimeTaskSlot(runtime, config, machineKey, registration,
1397
1408
  task_id: runtimeTask.id,
1398
1409
  task_title: String(runtimeTask.issue?.title || runtimeTask.title || ''),
1399
1410
  project_slug: String(runtimeTask.workspace?.slug || runtimeTask.workspace_slug || ''),
1411
+ agent_name: String(runtimeTask.agent?.name || ''),
1400
1412
  runtime_id: runtime.id,
1401
1413
  }, null, 2))
1402
1414
  await writeCurrentClaim(deps, runtimeTask, machineKey)
@@ -47,7 +47,7 @@ export function parseCredentials(content = '') {
47
47
 
48
48
  export async function readCredentials({ homeDir = os.homedir() } = {}) {
49
49
  try {
50
- return parseCredentials(await readFile(path.join(homeDir, '.11agents', 'credentials'), 'utf8'))
50
+ return parseCredentials(await readFile(path.join(homeDir, '.11agents', 'credentials.yaml'), 'utf8'))
51
51
  } catch (error) {
52
52
  if (error?.code === 'ENOENT') return { tokens: {} }
53
53
  throw error