Releases: ZSeven-W/dsh-ios
Release list
dsh-ios v0.1.0-rc.7
dsh-ios v0.1.0-rc.7
签名团队要选 Xcode 账号真能签的那个,而不是钥匙串里排第一的那个。/ The signing team must be one the Xcode account can provision for, not whichever identity sits first in the keychain.
🐛 修复 / Fixed
- rc.6 的团队自动检测会选错 / rc.6 auto-detection could pick a team Xcode cannot sign for(#4 后续)—— rc.6 用登录钥匙串里第一个 Apple Development 身份的团队作为
DEVELOPMENT_TEAM。在维护者的真机上实测,它选中了一个 Xcode 账号并不拥有的团队,WebDriverAgentRunner 构建报No Account for Team。用-allowProvisioningUpdates自动签名时,决定成败的是 Xcode 账号是否拥有该团队,钥匙串里有没有证书并不算数。/ rc.6 took the team of the first Apple Development identity in the keychain. On a real Mac that was a team the Xcode account does not own, and the WDA build failed withNo Account for Team.
🔧 改进 / Changed
- 统一的签名团队解析 / One signing-team resolver —— WDA runner 与真机 app 构建共用一套顺序:显式选项 >
DSH_IOS_TEAM_ID> Xcode 偏好IDEProvisioningTeams里账号能签的团队(优先有匹配钥匙串证书的,其次个人团队) > 钥匙串身份 > 旧默认值。日志会写明来源与理由。/ WDA and device app builds share: option >DSH_IOS_TEAM_ID> a team from Xcode'sIDEProvisioningTeams(a team with a matching keychain identity first, then the personal team) > keychain identity > legacy default, with the source and reason logged. - 真机 app 构建加
-allowProvisioningUpdates/ Device app builds pass-allowProvisioningUpdates—— 与 WDA 路径一致,Xcode 可以注册设备或为账号团队生成 profile。/ Matching the WDA path, so Xcode can register the device or mint a profile.
🧪 验证 / Verification
新增 scripts/dev-signing-smoke.mjs(8 项,含维护者机器上的真实组合作为回归:必须选中个人团队、不得选中钥匙串团队),并入 pnpm test;把选择逻辑改回 rc.6 的钥匙串优先后 5/8 变红。真机实测:iPhone 17 Pro 解锁后,新逻辑自动选出个人团队,WDA 9 秒内起到 RUNNING(控制 8100 / MJPEG 9100,会话建立)。/ New signing smoke (8 cases, the real-world combination as a named regression); reverting to the rc.6 chooser turns 5/8 red. Verified on a real iPhone 17 Pro: the auto-selected personal team brought WDA to RUNNING in 9 s.
📦 安装 / Install
dsh plugin --profile web add @zseven-w/dsh-ios@latest
dsh webFull Changelog: v0.1.0-rc.6...v0.1.0-rc.7
dsh-ios v0.1.0-rc.6
dsh-ios v0.1.0-rc.6
真机构建的目标设备,从此和 WDA 路径说同一种 id;签名团队不再写死。/ Device builds now name the destination the way the WDA path already did, and the signing team is no longer hardcoded.
🐛 修复 / Fixed
- 真机构建的
-destination传 CoreDevice 标识符 / Device builds passed the CoreDevice identifier as the destination(#4)——ios_sim_build_run的真机分支把 devicectl 的 CoreDevice UUID 交给xcodebuild -destination platform=iOS,id=…,而 WebDriverAgentRunner 构建路径用的是硬件 UDID。现在两条路径一致使用硬件 UDID,只在 devicectl 未报硬件 UDID 时回退。/ The device branch handed xcodebuild the CoreDevice UUID while the WDA runner build used the hardware UDID. Both paths now use the hardware UDID, falling back only when devicectl reports none. - exit 70 的错误信息只有一份设备列表 / An exit-70 failure showed only the destination list(#4)—— xcodebuild 打印 "Available destinations" 时,错误现在带上插件进程实际使用的
id=、scheme 和DEVELOPER_DIR,可以直接和手动跑通的命令逐项对比。/ When xcodebuild dumps "Available destinations", the error now states the id, scheme andDEVELOPER_DIRthe plugin process used.
🔧 改进 / Changed
DEVELOPMENT_TEAM可配置 /DEVELOPMENT_TEAMis configurable(#4)—— WebDriverAgentRunner 构建的签名团队按 显式选项 > 环境变量DSH_IOS_TEAM_ID> 登录钥匙串里第一个 Apple Development 身份 > 旧默认值 解析,日志会写明来源。DSH_IOS_WDA_BUNDLE_ID同样可覆盖 runner 的 bundle id。其他团队不再需要 sed 改 lib。/ The WDA runner's signing team resolves as option >DSH_IOS_TEAM_ID> the first Apple Development identity in the login keychain > the legacy default, and the log names the source.DSH_IOS_WDA_BUNDLE_IDoverrides the runner bundle id the same way.
🧪 验证 / Verification
typecheck、build、devicectl 与 WDA 夹具套件全绿;scripts/dev-realdevice-smoke.mjs 新增回归断言,把硬件 UDID 的选择回退成旧代码时会变红(已实际验证)。
坦白一句:exit 70 未在我们的机器上复现 —— 本机 Xcode 26 对两种 id 都能解析出设备,报告人的双 Xcode 环境(DEVELOPER_DIR / scheme 自动选取)仍是待确认的嫌疑,#4 保持打开。/ Honest caveat: exit 70 did not reproduce here — on Xcode 26 both id forms resolve. The reporter's two-Xcode environment remains the open suspect; #4 stays open.
DSH_IOS_TEAM_ID=5CHT5RB9C3。/ Upgrade note: with an Apple Development identity in the keychain, the WDA signing team switches from the legacy default to that identity's team. Pin the old behaviour with DSH_IOS_TEAM_ID=5CHT5RB9C3.
🙏 致谢 / Credit
感谢 @mikeanan 在 #4 中的详细报告与 DEVELOPMENT_TEAM 的建议。/ Thanks to @mikeanan for the detailed report in #4 and the DEVELOPMENT_TEAM suggestion.
📦 安装 / Install
dsh plugin --profile web add @zseven-w/dsh-ios@latest
dsh webFull Changelog: v0.1.0-rc.5...v0.1.0-rc.6
dsh-ios v0.1.0-rc.5
dsh-ios v0.1.0-rc.5
设备变慢,不该变成插件卡死。/ A busy device should slow us down, not lock us out.
🐛 修复 / Fixed
-
设备繁忙时 WDA 调用持续超时 / WDA calls timing out forever once the device is busy(#2)—— WDA 服务端串行处理请求,而客户端超时不取消设备上已在执行的命令(断开 socket 只断连接)。于是设备一忙(播放视频是报告中的场景),就没有任何背压:新请求继续满速发出、在设备端排更长的队、每个再烧满 30 秒预算。一次"变慢"因此变成
window/size永远超时。现在廉价 GET(window/size、orientation)的超时预算是 5 秒而非 30 秒,且任一超时会开启 10 秒忙冷却,期间新请求立即以[wda-busy]快速失败——"一直超时"变成"这次失败、稍后重试",对无响应设备的在途请求上界压到 1。/ WDA serves requests serially and a client-side timeout does not cancel the command already running on the device, so nothing pushed back once the device got busy. Cheap GETs now time out in 5 s, and any timeout opens a 10-second cooldown in which new requests fail immediately as busy. -
画面定格且不自愈 / The picture froze with no indication and no recovery(#2)—— 真机画面经 MJPEG 代理转发,而代理层没有空闲看门狗:上游一个字节都不来、但连接没断时,浏览器的
<img>永远不会触发onError,最后一帧就永久留在屏幕上。现在 8 秒无上游数据即主动断流,变成面板本就会处理的错误 → 自动重连,设备恢复后自行回到实时画面。/ The MJPEG proxy had no idle watchdog, so an upstream that stops sending bytes without closing the connection left the browser's<img>with no error to fire. Eight seconds of silence now tears the stream down, surfacing as an error the panel already knows how to retry.
🔧 改进 / Changed
window/size跨调用方共享缓存(旋转或会话重建时失效)。该值一个会话内只在旋转时变,此前每条工具路径每次都要重新问设备;卡顿期手势还能回退到几秒内的旧值继续可用。/window/sizegets a cache shared across callers — it only changes on rotation, yet every tool path re-asked the device for it.- 面板控制请求加在途护栏:1 个在途 + 1 个排队(最新手势胜出),点击卡住的画面不再把请求堆到接不住的设备上。/ Control requests gained an in-flight guard — one out, one queued, newest gesture wins.
🧪 验证 / Verification
全套测试 744 步、0 失败;新增测试覆盖快速超时、冷却期快速失败与过期恢复、传输层重置不触发冷却、共享缓存的三条失效路径、尺寸回退落点、停滞流在预算内被断开、以及控制请求队列的最新胜出与失败不破坏链路。
坦白一句:此修复未在真实设备上复现原始故障 —— 我们无法稳定重现"播放视频时 WDA 变慢"。以上是代码层面的缺陷分析与针对性测试,真实环境的验证仍然欢迎(升级后超时消息会带 [wda-busy] 标记或 timed out after 5000 ms)。/ Honest caveat: the original failure was not reproduced on real hardware. The fix rests on code analysis and targeted tests; confirmation from a real device is still welcome.
🙏 致谢 / Credit
感谢 @godsoul 在 #2 中的报告——"window/size 持续超时"与"画面不同步"这两条线索,恰好各自指向一个独立的结构性缺陷。/ Thanks to @godsoul for #2: the two symptoms turned out to be two independent defects.
📦 安装 / Install
dsh plugin --profile web add @zseven-w/dsh-ios@latest
dsh webFull Changelog: v0.1.0-rc.4...v0.1.0-rc.5
dsh-ios v0.1.0-rc.4
dsh-ios v0.1.0-rc.4
devicectl 列出的模拟器,终究还是模拟器。/ A simulator devicectl lists is still a simulator.
🐛 修复 / Fixed
-
模拟器被误判为物理设备 / Simulators misclassified as physical devices — 新版 devicectl 会把 iOS 模拟器和真机一起列出,而
src/devicectl.ts的两个解析器读遍了所有列,唯独没读区分二者的那一列。模拟器因此进了realDevices,它的 udid 被路由到 WebDriverAgent 真机通道:ios_sim_boot以"这是物理设备"为由拒绝启动、实时 serve-sim 面板一直空白、所有基于 udid 的工具(ios_sim_screenshot/ios_sim_interact/ios_sim_ui_tree/ios_sim_tap_element)统一报 WebDriverAgent 不可用。修复后两个解析器只在明确判定为模拟器时才剔除该条:reality为simulated(位于hardwareProperties或扁平properties映射)、visibilityClass为simulators、或文本输出的Reality列读到simulated。/ Newer devicectl builds report iOS Simulators alongside physical devices, and both parsers read every column except the one that tells them apart. The simulator landed inrealDevices, so its udid was routed down the WebDriverAgent path. Both parsers now drop a row only on an explicit simulated verdict.「只保留
physical」是更顺手却更错的写法:一台离线的 iPad 根本不上报reality字段,那样过滤会让真机从设备列表里凭空消失。/ Keeping onlyphysicalwould have been the tempting filter and the wrong one: an offline iPad reports no reality field at all and would vanish from the device list entirely. -
文本回退里设备名混入主机名 / Hostname bled into the device name in the text fallback —
Hostname列从不输出,却也从未参与列边界计算,于是文本解析出的每个设备名后面都拖着它的主机名。该列现在与其他列一同界定切片范围。/Hostnamewas never emitted but never boundedNameeither; it now joins the text parser's column boundaries.
🧪 验证 / Verification
- 新增
scripts/dev-devicectl-smoke.mjs(12/12),纯 fixture 套件,不依赖 devicectl、模拟器或 USB 设备,可在任意机器上跑出相同结果;覆盖三种 JSON 判别形态、无reality字段的离线 iPad 回归防护、含与不含Reality列的两种文本表头。/ A new pure-fixture suite (12/12) that runs identically on any host, covering all three JSON discriminator shapes, the offline-iPad regression guard, and both text-table headers. - 全套测试 719 项通过、0 失败;typecheck 与 build 均绿;真机
listRealDevices复核,iPhone 与离线 iPad 照常列出。/ The full suite passes 719 steps with zero failures; a real-devicelistRealDevicescheck still lists both the iPhone and the offline iPad. - 坦白一句:本次修复未在真实故障环境复现。手头 Xcode 26.6 的 devicectl 不列模拟器(特意启一个 iOS 26 模拟器验证过),正确性由上述 fixture 保证,真实验证有待 Xcode 27 环境。/ Honest caveat: the original failure was not reproduced here — Xcode 26.6's devicectl does not list simulators at all. Correctness rests on the fixtures above; confirmation on an Xcode 27 host is still welcome.
🙏 致谢 / Credit
感谢 @QiShu3 在 #1 中的报告——根因已经追到两个解析器,判别字段与调用链都指得很准。/ Thanks to @QiShu3 for #1, which arrived with the root cause already traced to both parsers.
📦 安装 / Install
dsh plugin --profile web add @zseven-w/dsh-ios@next
dsh web⚠️ 说明 / Notes
- npm dist-tag:
next。 - 仅
src/devicectl.ts的设备列举路径受影响,其余工具行为不变。/ Only the device-listing path insrc/devicectl.tschanged; every other tool behaves as before.
Full Changelog: v0.1.0-rc.3...v0.1.0-rc.4
dsh-ios v0.1.0-rc.3
dsh-ios v0.1.0-rc.3
DSH 0.1.1 适配版:把在新宿主上"侧栏静默死亡"的插件救回来。/ The DSH 0.1.1 adaptation: the release that brings a silently-dead sidebar back to life on the new host.
🐛 修复 / Fixed
- 在 DSH 0.1.1 上客户端永不激活 / The client never activated on DSH 0.1.1 — 0.1.1 从客户端模块图中移除了
@deepseek-ai/dsh-client-ui-slots(slots服务改由dsh-client-runtime提供,槽位声明并入 ui-tool / ui-conversation)。本插件的dsh.client.inject仍然要求这个不存在的模块,浏览器端 loader 会永远等待:卡片、侧边栏面板、状态胶囊全部静默死亡,服务端工具却一切正常——还会把整页拖在 "Loading plugins…" 上。修复:inject 列表删除该包(peer/dev 依赖保留,details-compat 的类型增强仍需要它编译)。/ DSH 0.1.1 dropped thedsh-client-ui-slotsrow from the client module graph (theslotsservice comes fromdsh-client-runtimenow). Ourdsh.client.injectstill demanded the missing module, so the browser loader waited forever: cards, the sidebar panel and the status capsule died silently while the host-side tools kept working — and the whole page stalled on "Loading plugins…". Fixed by dropping the package from the inject list; the peer/dev dependency stays for type-only compilation.
📝 文档 / Docs
- 全部 15 个语言 README 的验证徽标移到 DSH
0.1.1-rc.1(本版实测宿主)。/ All 15 locale READMEs now state DSH0.1.1-rc.1as the tested harness.
📦 安装 / Install
dsh plugin --profile web add @zseven-w/dsh-ios@next
dsh web⚠️ 说明 / Notes
- npm dist-tag:
next。在 rc.6 时代的宿主上本版仍可运行(slots服务同样存在)。/ Published undernext; the build still runs on rc.6-era hosts. - 七件套 smoke(panel 333/333 等)在适配前后各跑一遍,全绿。/ The seven smoke suites ran green before and after the change.
Full Changelog: v0.1.0-rc.2...v0.1.0-rc.3
dsh-ios v0.1.0-rc.2
dsh-ios v0.1.0-rc.2
修复专版:七个真实 bug,外加发布链路首次由 CI 完整走通 / A bug-fix release: seven real defects fixed, and the first release actually published end-to-end by CI.
✨ 新增 / New
ios_sim_wait_for现在真的存在了(第 22 个工具)/ios_sim_wait_fornow actually exists (tool #22) — 工厂早已创建它、skill 剧本也教模型调用它,但插件从未注册过这个工具,模型每次调用都会失败。现已注册,等待文字出现/消失是一次内部轮询的调用,而不是手动循环ios_sim_find_text(真机上每轮 ~1.2 s)。/ The factory built it and the skill playbook told the model to call it, but the plugin never registered it — every call failed. Registered now: waiting for text to appear/disappear is one internally-polling call instead of a hand-rolled find_text loop (~1.2 s per round trip on a phone).- 真机的锁屏 / 解锁 / Siri / Lock, Unlock and Siri on a real phone — 面板设备菜单宣称真机支持这三个动作,实际点击必然 502(只实现了 App Switcher)。现在它们通过 WebDriverAgent 的
/wda/lock、/wda/unlock、/wda/siri/activate真正落到手机上。/ The panel's device menu advertised all three but every click answered 502 (only App Switcher was implemented). They now reach the phone through WDA's documented endpoints.
🐛 修复 / Fixed
- 截图不再互相覆盖 / Screenshots no longer overwrite each other — 工具层两份截图存储与面板截图路由各自维护独立计数器,却写同一个缓存目录:后写者会复用先写者的文件名,已签发的截图 URL 会回放成另一张图。每个写入者现在会跳过磁盘上已存在的文件名。/ Three independent per-udid counters shared one cache directory, so a later capture reused a name whose signed URL was still live and the old capture replayed as a different image.
- 真机上"无标签 → 自动加深 / 转 OCR"终于生效 / Auto-deepen and the OCR-fallback hint now fire on a real device — WDA 把 app 名镜像进 Application 根节点的 label,导致"树里有标签"的判定在真机上恒为真,深度自动重试(WP62)和"该应用几乎无可访问性信息 → 用
ios_sim_find_text"提示(WP63)被静默废掉。应用名不再被当作内容标签。/ WDA mirrors the app NAME into the Application root's label, so every real-device tree "had a label" and both guards were silently dead. An app name no longer counts as content. - 计数解析认识大写 W / The uppercase
Wmultiplier parses — "3.2W 赞" 现在正确解析为 32 000,而不是把 W 折进计数键。/ "3.2W 赞" now reads as 32 000 instead of folding the W into the counter key. ios_sim_logs不再丢最后一行 / The last log line survives — follow 窗口靠 SIGTERM 收尾,捕获几乎必然停在半行,之前尾部残行被静默丢弃——往往正是调用者在等的那一行。/ A follow window ends by killing the log child mid-line; the trailing partial — often the line you were waiting for — was silently dropped.- preview 热重载不再被
.build触发风暴 / Preview hot reload ignores.buildwrites — recursive watch 报告的是相对路径,旧的忽略检查永远匹配不上,用户在包里跑一次swift build会按产物数量触发重建。/ The ignore check compared whole relative paths against directory names, so a user-runswift buildtriggered a rebuild per artifact. - serve-sim 失败会说出原因 / serve-sim failures name their cause — 超时/信号终止不再显示为无用的
failed (null)。/ A timed-out or signalled child no longer reports the uselessfailed (null).
🔧 发布链路 / Release pipeline
- 本版本是首个由 CI 完整发布的版本:rc.1 实为手工发布,当时的 workflow 只命中了"版本已存在→跳过"。publish 步骤现在带上
NPM_TOKEN回退(npm ≥ 11.5 先尝试 OIDC trusted publishing,不可用才读 token),并把 npm 钉在 major 11。/ This is the first version actually published by CI — rc.1 reached the registry by hand and that run only exercised the already-published skip. The publish step now falls back toNPM_TOKEN(npm tries the OIDC exchange first), with npm pinned to major 11. - registry 已存在该版本时发布安全跳过,重推 tag 不再变红。/ Re-pushed tags skip cleanly when the version is already on the registry.
📦 安装 / Install
dsh plugin --profile web add @zseven-w/dsh-ios@next
dsh web⚠️ 说明 / Notes
- npm dist-tag:
next(latest仍指向 rc.1,正式版 0.1.0 发布时才会移动)。/ Published undernext;lateststill points at rc.1 until 0.1.0. - 15 个语言的 README 均已补上
ios_sim_wait_for的工具表行,工具计数更新为 22。/ All 15 locale READMEs gained theios_sim_wait_forrow; the tool count is now 22. - 真机路径的要求与 rc.1 相同:解锁的 iPhone、可传数据的 USB 线、开发者模式,以及本地 WebDriverAgent 源码。/ Real-device requirements are unchanged from rc.1.
Full Changelog: v0.1.0-rc.1...v0.1.0-rc.2
dsh-ios v0.1.0-rc.1
dsh-ios v0.1.0-rc.1
首个 npm 发布 / First npm release.
✨ 能力 / What it does
- 对话内的实时 iOS 模拟器 / A live iOS Simulator in the conversation — serve-sim MJPEG 串流经签名路由进入侧边栏面板,可直接在画面上点按、拖拽、旋转、回桌面。
- USB 真机 / A real iPhone over USB — 插件在手机上构建并启动 WebDriverAgent,用内置 usbmux 客户端把控制端口与画面端口转发到 loopback,进入同一个面板和同一套工具。
- 21 个 agent 工具 / 21 agent tools — 启动构建、按 accessibility 身份或 OCR 文本驱动 UI、读取统一日志、查看进程/backtrace/leaks,以及列表行操作。
- 列表/信息流的行级操作 / Row-level actions for lists and feeds —
ios_sim_ui_rows把深层快照解析成带 frame 和计数的行,ios_sim_tap_row按行内相对坐标操作并用计数 ±1 验证动作是否生效。真机上无法唯一识别的控件会被拒绝点击,而不是试探。 - SwiftUI 预览热重载 / SwiftUI preview hot reload。
📦 安装 / Install
dsh plugin --profile web add @zseven-w/dsh-ios@latest
dsh web⚠️ 说明 / Notes
- npm dist-tag:
next(首发时latest亦指向本版本)。 - 真机路径需要:解锁的 iPhone、可传数据的 USB 线、设备端开启开发者模式,以及本地的 WebDriverAgent 源码;插件不下载也不克隆任何东西。
- CI 的 fixture 套件不接触真实设备——真机行为由人工验证。