phone-harnessHarness
§ · Nested detail page

Doctor 与扩展层 深度解析

Doctor 与扩展层 这页聚焦 权限阶梯、CLI 分流与 agent helper 注入。关键证据包括 src/phone_harness/helpers.py:45、src/phone_harness/mirror.py:91、src/phone_harness/ocr.py:19、src/phone_harness/admin.py:12,所有判断都回到真实源码行号。

§ 00 · IMPORT CHAIN

导入链与契约

doctor 先检查运行时依赖而不是直接操作手机。src/phone_harness/admin.py:12、src/phone_harness/admin.py:16、src/phone_harness/admin.py:24、src/phone_harness/admin.py:30 把 PyObjC、Accessibility 和 Screen Recording 缺失变成用户可修复的诊断项。

src/phone_harness/admin.py · L12-22Python
12def run_doctor():
13    print("phone-harness doctor\n")
14    ok = True
15
16    try:
17        import Quartz, Vision, AppKit  # noqa: F401
18        ok &= _check("pyobjc frameworks (Quartz, Vision, AppKit)", True)
19    except ImportError as e:
20        _check("pyobjc frameworks", False,
21               f"pip install pyobjc-framework-Quartz pyobjc-framework-Vision ({e})")
22        return 1
§ 01 · CORE IMPL

核心实现,逐段拆

▸CLI 命令面

run.py 把 doctor、skill 和 stdin 执行分开。src/phone_harness/run.py:16、src/phone_harness/run.py:21、src/phone_harness/run.py:24、src/phone_harness/run.py:30、src/phone_harness/run.py:36 说明 agent 使用时只是喂一段 Python,helper 会被预注入。

src/phone_harness/run.py · L16-36Python
16def main():
17    args = sys.argv[1:]
18    if args and args[0] in {"-h", "--help"}:
19        print(USAGE)
20        return
21    if args and args[0] in {"--doctor", "doctor"}:
22        from .admin import run_doctor
23        sys.exit(run_doctor())
24    if args and args[0] == "skill":
25        repo_root = Path(__file__).resolve().parent.parent.parent
26        print((repo_root / "SKILL.md").read_text(encoding="utf-8"), end="")
27        return
28    if args or sys.stdin.isatty():
29        sys.exit(USAGE)
30    code = sys.stdin.read()
31    if not code.strip():
32        sys.exit(USAGE)
33    from . import helpers
34    g = {k: v for k, v in vars(helpers).items() if not k.startswith("_")}
35    g["__name__"] = "__main__"
36    exec(code, g)

▸可编辑 helper

agent-workspace 不是核心库的一部分,却会在 import helpers 时自动加载。src/phone_harness/helpers.py:332、src/phone_harness/helpers.py:336、src/phone_harness/helpers.py:340、agent-workspace/agent_helpers.py:9、agent-workspace/agent_helpers.py:21 这个设计让任务经验可沉淀,又不污染传输层。

src/phone_harness/helpers.py · L332-346Python
332def _load_agent_helpers():
333    p = AGENT_WORKSPACE / "agent_helpers.py"
334    if not p.exists():
335        return
336    spec = importlib.util.spec_from_file_location("phone_harness_agent_helpers", p)
337    if not spec or not spec.loader:
338        return
339    module = importlib.util.module_from_spec(spec)
340    spec.loader.exec_module(module)
341    for name, value in vars(module).items():
342        if not name.startswith("_"):
343            globals()[name] = value
344
345
346_load_agent_helpers()
§ 02 · KEY FLOW

关键流程

capture / OCR / helper → compute screen-point evidence → post Quartz event or raise user-action error → capture again and compare visible state

这个流程的关键是每一步都有可观察证据:src/phone_harness/mirror.py:84、src/phone_harness/ocr.py:48、src/phone_harness/helpers.py:73、src/phone_harness/helpers.py:316、src/phone_harness/admin.py:65 分别覆盖截图、OCR 坐标、screen_info、稳定等待和 doctor 结果。它不像浏览器自动化那样有 DOM,所以截图就是最终断言。

§ 03 · EDGE CASES

边界 / 失败模式

  • src/phone_harness/helpers.py:67 遇到 blocked interstitial 时要求用户处理,防止 agent 越权。
  • src/phone_harness/mirror.py:251 对无法输入字符抛错,避免用户以为文本已发送。
  • src/phone_harness/helpers.py:280 需要连续不移动才结束列表,避免 lazy-load 误判。
  • src/phone_harness/admin.py:55 用截图字节数识别空 capture,提示重启终端让录屏权限生效。
Sub-page · 2026-08-10 Source · ShawnPana/phone-harness ↑ phone-harness 主页面