wechatbridge-cli 1.4.7__tar.gz → 1.4.8__tar.gz
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.
- {wechatbridge_cli-1.4.7/wechatbridge_cli.egg-info → wechatbridge_cli-1.4.8}/PKG-INFO +1 -1
- {wechatbridge_cli-1.4.7 → wechatbridge_cli-1.4.8}/tests/test_codex.py +28 -0
- {wechatbridge_cli-1.4.7 → wechatbridge_cli-1.4.8}/wechatbridge/__init__.py +1 -1
- {wechatbridge_cli-1.4.7 → wechatbridge_cli-1.4.8}/wechatbridge/codex.py +59 -23
- {wechatbridge_cli-1.4.7 → wechatbridge_cli-1.4.8/wechatbridge_cli.egg-info}/PKG-INFO +1 -1
- {wechatbridge_cli-1.4.7 → wechatbridge_cli-1.4.8}/LICENSE +0 -0
- {wechatbridge_cli-1.4.7 → wechatbridge_cli-1.4.8}/README.md +0 -0
- {wechatbridge_cli-1.4.7 → wechatbridge_cli-1.4.8}/pyproject.toml +0 -0
- {wechatbridge_cli-1.4.7 → wechatbridge_cli-1.4.8}/setup.cfg +0 -0
- {wechatbridge_cli-1.4.7 → wechatbridge_cli-1.4.8}/tests/test_hardening.py +0 -0
- {wechatbridge_cli-1.4.7 → wechatbridge_cli-1.4.8}/wechatbridge/__main__.py +0 -0
- {wechatbridge_cli-1.4.7 → wechatbridge_cli-1.4.8}/wechatbridge/agy.py +0 -0
- {wechatbridge_cli-1.4.7 → wechatbridge_cli-1.4.8}/wechatbridge/config.py +0 -0
- {wechatbridge_cli-1.4.7 → wechatbridge_cli-1.4.8}/wechatbridge/grok.py +0 -0
- {wechatbridge_cli-1.4.7 → wechatbridge_cli-1.4.8}/wechatbridge/ilink.py +0 -0
- {wechatbridge_cli-1.4.7 → wechatbridge_cli-1.4.8}/wechatbridge/main.py +0 -0
- {wechatbridge_cli-1.4.7 → wechatbridge_cli-1.4.8}/wechatbridge/runner_common.py +0 -0
- {wechatbridge_cli-1.4.7 → wechatbridge_cli-1.4.8}/wechatbridge/update_check.py +0 -0
- {wechatbridge_cli-1.4.7 → wechatbridge_cli-1.4.8}/wechatbridge_cli.egg-info/SOURCES.txt +0 -0
- {wechatbridge_cli-1.4.7 → wechatbridge_cli-1.4.8}/wechatbridge_cli.egg-info/dependency_links.txt +0 -0
- {wechatbridge_cli-1.4.7 → wechatbridge_cli-1.4.8}/wechatbridge_cli.egg-info/entry_points.txt +0 -0
- {wechatbridge_cli-1.4.7 → wechatbridge_cli-1.4.8}/wechatbridge_cli.egg-info/requires.txt +0 -0
- {wechatbridge_cli-1.4.7 → wechatbridge_cli-1.4.8}/wechatbridge_cli.egg-info/top_level.txt +0 -0
|
@@ -935,6 +935,34 @@ class TestCodexRunFakeCli(unittest.IsolatedAsyncioTestCase):
|
|
|
935
935
|
result_entries = [a for a in artifacts if a[0] == "result.txt"]
|
|
936
936
|
self.assertEqual(len(result_entries), 1, "no duplicate from fallback")
|
|
937
937
|
|
|
938
|
+
def test_shell_pdf_merged_with_file_change(self):
|
|
939
|
+
"""file_change for helper.py plus a shell-written report.pdf — both return."""
|
|
940
|
+
display, artifacts = self._run("hi", "u-shell-mix", mode="ok_shell_mixed_docs")
|
|
941
|
+
self.assertEqual(display, "done")
|
|
942
|
+
names = {n for n, _ in artifacts}
|
|
943
|
+
self.assertIn("helper.py", names)
|
|
944
|
+
self.assertIn("report.pdf", names)
|
|
945
|
+
self.assertEqual(len([a for a in artifacts if a[0] == "helper.py"]), 1)
|
|
946
|
+
self.assertEqual(len([a for a in artifacts if a[0] == "report.pdf"]), 1)
|
|
947
|
+
|
|
948
|
+
def test_shell_overwrite_existing_pdf(self):
|
|
949
|
+
"""Rewriting a pre-existing pdf must still collect it."""
|
|
950
|
+
from wechatbridge.runner_common import sanitize_user_id
|
|
951
|
+
|
|
952
|
+
uid = "u-shell-ow"
|
|
953
|
+
sd = os.path.join(self.td, sanitize_user_id(uid))
|
|
954
|
+
os.makedirs(sd)
|
|
955
|
+
pdf = os.path.join(sd, "report.pdf")
|
|
956
|
+
with open(pdf, "wb") as f:
|
|
957
|
+
f.write(b"%PDF-old")
|
|
958
|
+
os.utime(pdf, (1_000_000, 1_000_000))
|
|
959
|
+
display, artifacts = self._run("hi", uid, mode="ok_shell_mixed_docs")
|
|
960
|
+
self.assertEqual(display, "done")
|
|
961
|
+
names = {n for n, _ in artifacts}
|
|
962
|
+
self.assertIn("report.pdf", names)
|
|
963
|
+
with open(pdf, "rb") as f:
|
|
964
|
+
self.assertEqual(f.read(), b"%PDF-1.4")
|
|
965
|
+
|
|
938
966
|
def test_shell_file_fallback_old_files_not_collected(self):
|
|
939
967
|
"""ok_shell_old_files: files with old mtimes are NOT collected."""
|
|
940
968
|
display, artifacts = self._run("hi", "u-shell-old", mode="ok_shell_old_files")
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
"""WeChatBridge — bridge WeChat messages to agy, Grok Build, or Codex CLIs."""
|
|
2
|
-
__version__ = "1.4.
|
|
2
|
+
__version__ = "1.4.8"
|
|
@@ -633,26 +633,52 @@ def _extract_codex_error_messages(stdout_text: str) -> list:
|
|
|
633
633
|
|
|
634
634
|
|
|
635
635
|
def _collect_fallback_artifacts(session_dir: str, snapshot_before: dict, t0: float) -> list:
|
|
636
|
-
"""Scan session_dir for
|
|
636
|
+
"""Scan session_dir for files created or overwritten during this turn.
|
|
637
637
|
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
638
|
+
Codex often emits a ``file_change`` for a helper script and then writes
|
|
639
|
+
the real pdf/docx via the shell. Always merge this list with structured
|
|
640
|
+
artifacts (dedupe by realpath). Scans only session_dir (never add_dirs).
|
|
641
|
+
Excludes internal metadata dirs. Each candidate is verified under session_dir.
|
|
641
642
|
"""
|
|
642
643
|
_internal_names = {".codex_thread_id"}
|
|
643
644
|
fallback = []
|
|
644
645
|
fallback_seen = set()
|
|
645
646
|
for fp, fmtime in _snapshot_regular_files([session_dir]).items():
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
if
|
|
647
|
+
prev = snapshot_before.get(fp)
|
|
648
|
+
if prev is None:
|
|
649
|
+
if fmtime < t0:
|
|
649
650
|
continue
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
651
|
+
elif fmtime <= prev:
|
|
652
|
+
continue
|
|
653
|
+
name = os.path.basename(fp)
|
|
654
|
+
if name in _internal_names or name.startswith(".initialized."):
|
|
655
|
+
continue
|
|
656
|
+
key = (name, fp)
|
|
657
|
+
if key not in fallback_seen:
|
|
658
|
+
fallback_seen.add(key)
|
|
659
|
+
fallback.append(key)
|
|
654
660
|
return fallback
|
|
655
661
|
|
|
662
|
+
|
|
663
|
+
def _merge_codex_artifacts(*groups) -> list:
|
|
664
|
+
"""Dedupe (name, path) tuples by realpath, preserve first-seen order."""
|
|
665
|
+
merged = []
|
|
666
|
+
seen = set()
|
|
667
|
+
for group in groups:
|
|
668
|
+
for item in group or []:
|
|
669
|
+
if not isinstance(item, (tuple, list)) or len(item) != 2:
|
|
670
|
+
continue
|
|
671
|
+
name, path = item
|
|
672
|
+
try:
|
|
673
|
+
key = os.path.realpath(path)
|
|
674
|
+
except OSError:
|
|
675
|
+
key = path
|
|
676
|
+
if key in seen:
|
|
677
|
+
continue
|
|
678
|
+
seen.add(key)
|
|
679
|
+
merged.append((name, path))
|
|
680
|
+
return merged
|
|
681
|
+
|
|
656
682
|
async def run_codex(prompt: str, user_id: str, timeout: int = None) -> tuple:
|
|
657
683
|
"""Execute codex CLI for a given user message.
|
|
658
684
|
|
|
@@ -831,12 +857,18 @@ async def run_codex(prompt: str, user_id: str, timeout: int = None) -> tuple:
|
|
|
831
857
|
mark_initialized(session_dir, backend="codex")
|
|
832
858
|
if r_thread_id:
|
|
833
859
|
_write_codex_thread_id(session_dir, r_thread_id)
|
|
834
|
-
# Fallback:
|
|
835
|
-
if not r_failed
|
|
836
|
-
|
|
837
|
-
if
|
|
838
|
-
|
|
839
|
-
|
|
860
|
+
# Fallback: 重试成功也扫本轮新建/覆盖的文件,与 file_change 合并
|
|
861
|
+
if not r_failed:
|
|
862
|
+
extra = _collect_fallback_artifacts(session_dir, snapshot_before, t0)
|
|
863
|
+
if extra:
|
|
864
|
+
before = len(r_artifacts)
|
|
865
|
+
r_artifacts = _merge_codex_artifacts(r_artifacts, extra)
|
|
866
|
+
added = len(r_artifacts) - before
|
|
867
|
+
if added:
|
|
868
|
+
logger.info(
|
|
869
|
+
"Fallback scan collected %d additional files on retry",
|
|
870
|
+
added,
|
|
871
|
+
)
|
|
840
872
|
elapsed = time.time() - t0
|
|
841
873
|
logger.info(
|
|
842
874
|
"codex retry done: user=%s elapsed=%.1fs artifacts=%d output=%d chars failed=%s",
|
|
@@ -850,12 +882,16 @@ async def run_codex(prompt: str, user_id: str, timeout: int = None) -> tuple:
|
|
|
850
882
|
if parsed_thread_id:
|
|
851
883
|
_write_codex_thread_id(session_dir, parsed_thread_id)
|
|
852
884
|
|
|
853
|
-
# Fallback:
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
885
|
+
# Fallback: 成功就扫本轮新建/覆盖的文件,与 file_change 合并去重。
|
|
886
|
+
# 不能再要求 artifacts 为空——先写出脚本再终端生成 pdf 时会漏文档。
|
|
887
|
+
if not failed:
|
|
888
|
+
extra = _collect_fallback_artifacts(session_dir, snapshot_before, t0)
|
|
889
|
+
if extra:
|
|
890
|
+
before = len(artifacts)
|
|
891
|
+
artifacts = _merge_codex_artifacts(artifacts, extra)
|
|
892
|
+
added = len(artifacts) - before
|
|
893
|
+
if added:
|
|
894
|
+
logger.info("Fallback scan collected %d additional files", added)
|
|
859
895
|
|
|
860
896
|
elapsed = time.time() - t0
|
|
861
897
|
logger.info(
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{wechatbridge_cli-1.4.7 → wechatbridge_cli-1.4.8}/wechatbridge_cli.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
{wechatbridge_cli-1.4.7 → wechatbridge_cli-1.4.8}/wechatbridge_cli.egg-info/entry_points.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|