fix(windows): force UTF-8 in channel subprocesses and MCP server env (#345)

Extracted from #318 (the UTF-8/doctor core, minus the env-wrapper feature):

- new agent_reach/utils/process.py: utf8_subprocess_env() +
  mcporter_utf8_env_args() — Windows GBK consoles otherwise corrupt
  Chinese output from mcporter/MCP child processes
- weibo/douyin/linkedin checks and weibo install/registration now pass
  the UTF-8 env (and register the MCP server with --env PYTHONUTF8=1)
- youtube: extract _has_js_runtime_config() with an OSError guard so an
  unreadable yt-dlp config can't crash doctor
- test_skill_command: open SKILL.md with explicit utf-8 (Windows GBK
  default broke these tests)

Co-authored-by: chidao <2980933590@qq.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Pnant
2026-06-10 15:09:42 +08:00
committed by GitHub
parent 4ca570a39f
commit b0e010c6cc
8 changed files with 93 additions and 24 deletions
+18
View File
@@ -0,0 +1,18 @@
from agent_reach.utils.process import mcporter_utf8_env_args, utf8_subprocess_env
def test_utf8_subprocess_env_forces_python_utf8():
env = utf8_subprocess_env({"PYTHONUTF8": "0", "OTHER": "value"})
assert env["PYTHONUTF8"] == "1"
assert env["PYTHONIOENCODING"] == "utf-8"
assert env["OTHER"] == "value"
def test_mcporter_utf8_env_args():
assert mcporter_utf8_env_args() == [
"--env",
"PYTHONUTF8=1",
"--env",
"PYTHONIOENCODING=utf-8",
]
+4 -7
View File
@@ -39,14 +39,11 @@ class TestSkillCommand(unittest.TestCase):
with patch.dict(os.environ, env, clear=True):
_install_skill()
target = os.path.join(skill_dir, "agent-reach", "SKILL.md")
# Check at least one known skill dir pattern
found = False
for dirpath, _, filenames in os.walk(tmpdir):
if "SKILL.md" in filenames:
found = True
# Verify content is non-empty
with open(os.path.join(dirpath, "SKILL.md")) as f:
with open(os.path.join(dirpath, "SKILL.md"), encoding="utf-8") as f:
content = f.read()
self.assertIn("Agent Reach", content)
# _install_skill may or may not find dirs depending on mock; just ensure no crash
@@ -58,7 +55,7 @@ class TestSkillCommand(unittest.TestCase):
# Create a fake skill installation
skill_path = os.path.join(tmpdir, ".openclaw", "skills", "agent-reach")
os.makedirs(skill_path)
with open(os.path.join(skill_path, "SKILL.md"), "w") as f:
with open(os.path.join(skill_path, "SKILL.md"), "w", encoding="utf-8") as f:
f.write("test")
self.assertTrue(os.path.exists(skill_path))
@@ -92,7 +89,7 @@ class TestSkillCommand(unittest.TestCase):
target = os.path.join(skill_parent, "agent-reach", "SKILL.md")
self.assertTrue(os.path.exists(target))
with open(target) as f:
with open(target, encoding="utf-8") as f:
content = f.read()
self.assertIn("Agent Reach", content)
@@ -114,7 +111,7 @@ class TestSkillCommand(unittest.TestCase):
target = os.path.join(skill_parent, "agent-reach", "SKILL.md")
self.assertTrue(os.path.exists(target))
with open(target) as f:
with open(target, encoding="utf-8") as f:
content = f.read()
self.assertTrue(content.strip())
self.assertIn("WeChat Articles, Xiaoyuzhou Podcast", content)