给 WorkBuddy 排了个定时任务:早上出门前启动,等你到公司就该出结果。结果中午掏手机一看——任务停在第 3 步,原因是某个站点登录态过期,弹了个窗口等你扫码。
这类中断的共同点是:卡住的只有一步,其余九十九步都跑得好好的。但在远程控制出来之前,你只能干等到回到工位,整套任务白等大半天。
10 月 28 日 WorkBuddy 上线了「远程控制」(由腾讯 Edge One 团队提供技术支持):手机能实时看到电脑桌面,还能直接上手接管。据官方说明,连接全程走加密通道,不记录输入内容、不保存画面,只消耗流量,不消耗 Token。
远程控制解决的是「怎么点」,但它不解决「点什么」。真正让人抓狂的从来不是连不上,而是连上之后不知道该点哪儿、还要不要重跑。
所以这篇文章分两半:
适用边界:目前仅支持 Mac,Windows 待上线;同一台电脑同一时间只允许一部已认证的手机连接。
门槛是 桌面端 ≥ 5.9.3、手机端 App ≥ 2.7.0。别凭记忆判断,直接读安装包里的 Info.plist:
defaults read /Applications/WorkBuddy.app/Contents/Info.plist CFBundleShortVersionString
# 实测输出:5.9.3我在自己机器上跑出来就是 5.9.3,刚好踩在门槛上。
打开 WorkBuddy 桌面端 → 设置 → 设备连接:
macOS 会弹两次系统授权:屏幕录制与系统录音、辅助功能。两项都要给,缺一项就只能看不能点。
第一次授予后建议重启一次客户端,避免组件没被系统登记。
手机 App 更新到 v2.7.0 以上,在首页或会话页右上角菜单选「远程控制」,会自动直连当前这台电脑。前提是电脑开机且客户端在线。
这是我实际改完之后收益最大的一步。给自动化任务加一条硬性输出要求——凡是任务需要人工介入,必须按这个模板输出,不许只写一句「需要你处理」:
## 需要你出手(约 30 秒)
**卡在第几步**:第 3 步 · 读取外部数据源
**卡的原因**:登录态过期,需要扫一次码
**你要做的动作**:打开 Safari → 登录 → 回到窗口点「继续」
**做完之后**:任务从断点继续,不用重跑。
> 不方便就直接关掉,任务停在原地,不会自动重试烧额度。四个字段各有分工:
字段 | 解决什么 |
|---|---|
卡在第几步 | 手机上不用翻历史,一眼知道进度 |
卡的原因 | 判断值不值得现在远程接管 |
你要做的动作 | 手指点哪儿不用猜 |
做完之后 | 消除「是不是要重跑」的焦虑 |
最后那句提示是故意加的——它把「不处理」也变成一个可选项,避免你为了一个不紧急的弹窗专门开一次远程。
开通条件可能悄悄失效(客户端升级后版本回退、系统权限被重置)。我写了个零依赖脚本做前置体检:
python3 remote_handoff.py --probe # 体检
python3 remote_handoff.py --selftest # 验证判定逻辑
python3 remote_handoff.py --pack --step "第3步" --need "登录态过期" --action "扫码登录" --eta "约30秒"脚本本体(Python 3.9+,零第三方依赖,macOS):
import os, re, plistlib, subprocess, sqlite3
APP = "/Applications/WorkBuddy.app"
MIN_DESKTOP = (5, 9, 3) # 官方门槛
TCC = ["/Library/Application Support/com.apple.TCC/TCC.db",
os.path.expanduser("~/Library/Application Support/com.apple.TCC/TCC.db")]
def parse_version(t):
m = re.match(r"^(\d+)\.(\d+)(?:\.(\d+))?", str(t).strip())
return tuple(int(x) if x else 0 for x in m.groups()) if m else None
def cmp_version(a, b): # 必须元组比较,字符串比较会把 5.9.10 判成小于 5.9.3
return (a > b) - (a < b)
def version_ok(path=APP + "/Contents/Info.plist"):
with open(path, "rb") as fh:
raw = str(plistlib.load(fh).get("CFBundleShortVersionString", ""))
v = parse_version(raw)
return v is not None and cmp_version(v, MIN_DESKTOP) >= 0, raw
def online():
out = subprocess.run(["ps", "-eo", "args="], capture_output=True,
text=True, timeout=5).stdout
return "WorkBuddy.app" in out
def tcc_granted():
"""读不到不等于没授权——这是 macOS 的保护机制,必须降级而不是报错。"""
for db in TCC:
if not os.path.exists(db):
continue
try:
con = sqlite3.connect("file:%s?mode=ro" % db, uri=True, timeout=2)
rows = con.execute(
"SELECT service, client, auth_value FROM access "
"WHERE service IN ('kTCCServiceScreenCapture','kTCCServiceAccessibility')"
).fetchall()
con.close()
except Exception:
return None # 无 Full Disk Access,未知
hit = {s: a for s, c, a in rows if "workbuddy" in c.lower()}
if hit:
return all(v == 2 for v in hit.values()) # 0=拒绝 2=允许
return None
if __name__ == "__main__":
ok, raw = version_ok()
print(f"版本 {raw} → {'达标' if ok else '不达标'}")
print(f"客户端在线 → {'是' if online() else '否'}")
g = tcc_granted()
print(f"系统权限 → {'已授权' if g else ('未授权' if g is False else '未知,请目视确认')}")本机实测(macOS,WorkBuddy 5.9.3,脚本零第三方依赖):
远程控制前置体检 · 2026-10-30 10:01:35
桌面端版本 5.9.3 [PASS]
客户端在线 运行中 [PASS]
系统权限(屏幕录制 / 辅助功能) 屏幕录制=是 辅助功能=是 [PASS]
自动化任务数(仅计数,不含名称) 4 个 [INFO]
结论:前置条件满足,可以去手机端发起连接自检集 8 条全通过(--selftest,8/8):
[PASS] 版本号 5.9.3 应判为达标
[PASS] 版本号 5.9.2 应判为不达标
[PASS] 版本号 6.0.0 应判为达标
[PASS] 字符串比较会把 5.9.10 判成小于 5.9.3(反例)
[PASS] 元组比较下 5.9.10 正确大于 5.9.3
[PASS] 畸形版本 'beta' 解析为 None
[PASS] 交接包含四要素
[PASS] 版本号带后缀 5.9.3-beta.1 可解析脚本整体耗时在毫秒级(纯本地读 plist + ps + 一次 sqlite 只读查询)。
以下三项我没有实测,不做数字承诺,发布前请自行补:手机端首次连接耗时、单次远程接管的流量消耗、弱网下切低画质后的可辨识程度 —— 【待本人实测补充】。
"5.9.10" > "5.9.3" 在 Python 里是 False,逐字符比到 1 < 3 就停了。等哪天出到 5.9.10,你的体检脚本会把新版本判成不达标。必须拆成整数元组再比。TCC.db 保护起来了,没给 Full Disk Access 的进程一律读不到。脚本必须返回「未知」而不是「未授权」,否则会把人吓得反复去拨系统开关。这是我在脚本里专门写了降级分支的原因。远程控制把「回到工位才能继续」变成了「掏手机 30 秒」。但真正省时间的不是连接本身,是你在任务里提前埋好的那四行字——远程控制解决怎么点,交接包解决点什么,两个都要配。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。