@zhz8888/dsh-android-emulator-helper 1.0.0 → 1.1.0

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.
package/README.md CHANGED
@@ -23,6 +23,7 @@ instead of through environment variables.
23
23
  - [Architecture](#architecture)
24
24
  - [Development](#development)
25
25
  - [Known limitations](#known-limitations)
26
+ - [Releases](#releases)
26
27
  - [License](#license)
27
28
 
28
29
  ## Install
@@ -74,7 +75,11 @@ leaves no trace when it does not.
74
75
  The settings namespace is the profile patch row id, `android-emulator-helper`.
75
76
  The page reads and writes through `ctx.configForms`, so staged edits, dirty
76
77
  detection, inline validation, optimistic-concurrency revisions and
77
- "overridden / reset to default" all come from the harness.
78
+ "overridden / reset to default" all come from the harness. It draws its own
79
+ title and one card per group — the card outline is what separates the
80
+ categories — and the save lives in a sticky action bar at the bottom that
81
+ appears only while there is something to act on: a staged edit, a write in
82
+ flight, or a failure worth retrying.
78
83
 
79
84
  | Group | Field | Kind | Default |
80
85
  |---|---|---|---|
@@ -85,12 +90,12 @@ detection, inline validation, optimistic-concurrency revisions and
85
90
  | | `defaultSerial` | text, suggestions | empty |
86
91
  | | `autoStartEmulator` | switch | on |
87
92
  | | `emulatorArgs` | multiline, one argument per line | empty |
88
- | | `bootTimeoutMs` | integer | `180000` |
93
+ | | `bootTimeoutSeconds` | integer | `180` |
89
94
  | API and system image | `apiLevel` | text, suggestions | `35` |
90
95
  | | `systemImageVariant` | select | `default` |
91
96
  | | `systemImageAbi` | select (empty = host architecture) | auto |
92
97
  | | `minSdk` | integer | `23` |
93
- | Build | `buildTimeoutMs` | integer | `600000` |
98
+ | Build | `buildTimeoutSeconds` | integer | `600` |
94
99
  | | `gradleExtraArgs` | multiline | empty |
95
100
  | Output and logs | `dataDir` | text | `$DSH_HOME/android-emulator-helper` |
96
101
  | | `screenshotDir` | text | `<data dir>/screenshots` |
@@ -201,7 +206,7 @@ Three constraints shape everything above:
201
206
 
202
207
  ```bash
203
208
  pnpm install # the @deepseek-ai/* peers plus react/react-dom for the tests
204
- pnpm test # 63 tests, no Android SDK needed
209
+ pnpm test # 145 tests, no Android SDK needed
205
210
  pnpm run check # tests + host/client gate + real-Cordis mount
206
211
  pnpm run verify:mount # real Cordis Context and real ToolRuntime
207
212
  pnpm run smoke # read-only real-machine probes
@@ -224,9 +229,11 @@ Four layers of verification:
224
229
  | Layer | Command | What it proves |
225
230
  |---|---|---|
226
231
  | Unit and parsers | `pnpm test` | Bundle load contract, manifest/patch contract, settings projection (live volatile reads, env fallback, out-of-range fallback), every ported parser, and the lossless-JSON projection |
227
- | Settings page render | `pnpm test` | The whole page rendered with real React and `react-dom/server`: every group, field and dictionary key present, controlled inputs holding live values, switch and select state correct, override badge and reset, both route buttons and the save control |
228
- | Consistency gate | `scripts/check-bundle.mjs` | Bundle id equals the package name, namespace equals the patch row id, the two field tables match key-for-key, both routes agree on both sides, and the bundle is still a classic script |
229
- | Real host mount | `scripts/verify-mount.mjs` | Mounted on a **real** Cordis `Context` with the **real** `ToolRuntime`: 23 tools registered, four read-only tools dispatched through the registry and validated against their argument and result schemas, malformed arguments refused, unknown names unresolved, 17 volatile fields, full unregistration on dispose |
232
+ | Settings page render | `pnpm test` | The whole page rendered with real React and `react-dom/server`: every group, field and dictionary key present, controlled inputs holding live values, switch and select state correct, override badge and reset, both route buttons, and no action bar while nothing is staged |
233
+ | Consistency gate | `scripts/check-bundle.mjs` | Bundle id equals the package name, namespace equals the patch row id, the two field tables and the two group tables match key-for-key and in order, every heading, hint, placeholder and option matches the host **word for word in both languages**, both routes agree on both sides, `locale/*.json` carries the same keys in both languages, and the bundle is still a classic script |
234
+ | Route integration | `test/routes-integration.test.js` | The two settings routes mounted on a **real** Cordis `Context`: each handler invoked and its `Response` read, all 17 fields arriving as live volatile references, a saved setting visible to the very next call, a handler error answered as a `500` body rather than a crash, an absent optional service tolerated, and disposal removing both routes |
235
+ | Real host mount | `scripts/verify-mount.mjs` | Mounted on a **real** Cordis `Context` with the **real** `ToolRuntime`: 23 tools registered, **all 23 dispatched through the registry** and validated against their argument and result schemas, malformed arguments refused, unknown names unresolved, 17 volatile fields, full unregistration on dispose |
236
+ | Real device | `pnpm run smoke` … | What only a machine with an SDK, an emulator and Gradle can prove: discovery, boot and teardown, screenshots, logcat, UI automation, and a real `android_create_app` → build → install → launch cycle |
230
237
 
231
238
  ### Registry constraints that shape the tools
232
239
 
@@ -243,11 +250,6 @@ hand-written test double:
243
250
  value on the single path every tool result travels, instead of 23 providers
244
251
  each handling it.
245
252
 
246
- `docs/research/` documents the DSH plugin contracts this port depends on, with
247
- line-level citations into the shipped packages. It is the closest thing to a
248
- "DSH plugin development spec" that exists, because the application bundle ships
249
- no developer documentation.
250
-
251
253
  ## Known limitations
252
254
 
253
255
  - Linux is unsupported, exactly as in the original; `android_preflight` reports
@@ -259,15 +261,29 @@ no developer documentation.
259
261
  - The settings page's installed-image list comes from scanning the SDK
260
262
  directory, not from `sdkmanager --list_installed`, which fails in restricted
261
263
  environments and prefixes its output with deprecation notices.
262
- - **The settings page has not been visually confirmed in the real GUI**: the
263
- delivery choice was source-only, so it was never installed into the `desktop`
264
- profile. Its render layer is covered by the server-render test above, but
265
- spacing, dark theme and narrow-width wrapping should be checked after
266
- installing.
264
+ - The settings page's two routes are served by the plugin and are mounted in a
265
+ real profile, but their HTTP responses cannot be exercised outside the GUI:
266
+ the desktop app authenticates every request, so a bare `curl` to
267
+ `/api/android-emulator-helper/status` answers `403 forbidden` before the
268
+ handler runs. `test/routes-integration.test.js` therefore invokes the handlers
269
+ directly on a real Cordis context instead. The page's spacing, dark theme and
270
+ narrow-width wrapping still deserve a human look in the GUI.
267
271
  - `@deepseek-ai/dsh-client-ui-primitives` publishes only `0.0.1-rc.1` on npm,
268
272
  which does not match the runtime API, so the render test uses a faithful double
269
273
  for the primitives while React and `react-dom/server` stay real.
270
274
 
275
+ ## Releases
276
+
277
+ | Version | Highlights |
278
+ |---|---|
279
+ | **1.1.0** | Settings page rebuilt around cards with a sticky action bar; both timeouts asked for in seconds; eight defects fixed, among them a generated project that did not compile, `android_ui_type_text` corrupting every `%` into `%25`, `android_ui_describe` serving the previous screen after a failed dump, and `android_start_emulator` silently starting a different AVD than the one requested. All 23 tools were exercised on a real emulator. |
280
+ | 1.0.0 | First release: the 23 native `android_*` tools, the embedded `android-dev` skill and the `/android-dev` command, and the settings page. |
281
+
282
+ **Restart the DSH host process after upgrading.** The package is mounted as a
283
+ `link:` symlink and Node caches ESM modules for the life of the process, so a
284
+ change under `lib/` does not take effect until the host restarts — until then the
285
+ tools keep serving the old code.
286
+
271
287
  ## License
272
288
 
273
289
  [MIT](LICENSE), copyright (c) 2026 zhz8888.
package/README.zh-CN.md CHANGED
@@ -20,6 +20,8 @@
20
20
  - [架构](#架构)
21
21
  - [开发与验证](#开发与验证)
22
22
  - [已知限制](#已知限制)
23
+ - [版本](#版本)
24
+ - [许可](#许可)
23
25
 
24
26
  ## 安装
25
27
 
@@ -54,7 +56,7 @@ dsh plugin --profile <profile> add /absolute/path/to/dsh-android-emulator-helper
54
56
 
55
57
  ## 设置页面
56
58
 
57
- 设置命名空间 = profile patch 行 id = `android-emulator-helper`(同时是 `dsh.name`)。页面通过 `ctx.configForms` 读写,因此具备暂存编辑、脏值检测、内联校验、乐观并发(revision)与「已覆盖/恢复默认」语义。
59
+ 设置命名空间 = profile patch 行 id = `android-emulator-helper`(同时是 `dsh.name`)。页面通过 `ctx.configForms` 读写,因此具备暂存编辑、脏值检测、内联校验、乐观并发(revision)与「已覆盖/恢复默认」语义。页面自绘标题、每个分组一张卡片(卡片边框就是分组之间的分隔),保存按钮位于底部悬浮胶囊操作栏,只在有可操作内容时出现:有暂存改动、正在写入,或一次失败值得重试。
58
60
 
59
61
  | 分组 | 设置项 | 类型 | 默认值 | 说明 |
60
62
  |---|---|---|---|---|
@@ -65,12 +67,12 @@ dsh plugin --profile <profile> add /absolute/path/to/dsh-android-emulator-helper
65
67
  | | `defaultSerial` | 文本(建议来自已连接设备) | 空 | 优先使用的设备序列号 |
66
68
  | | `autoStartEmulator` | 开关 | 开 | 关闭后无可用设备时直接报错,不启动模拟器 |
67
69
  | | `emulatorArgs` | 多行文本 | 空 | 额外 `emulator` 参数,每行一个,`#` 为注释 |
68
- | | `bootTimeoutMs` | 整数 | `180000` | 模拟器启动超时 |
70
+ | | `bootTimeoutSeconds` | 整数 | `180` | 模拟器启动超时(秒) |
69
71
  | API 与系统镜像 | `apiLevel` | 文本(建议来自已装 platform/镜像) | `35` | `compileSdk` 与系统镜像包名使用的 API 级别 |
70
72
  | | `systemImageVariant` | 下拉 | `default` | `default` / `google_apis` / `google_apis_playstore` |
71
73
  | | `systemImageAbi` | 下拉 | 自动 | 留空按宿主架构,Apple Silicon 用 `arm64-v8a` |
72
74
  | | `minSdk` | 整数 | `23` | `android_create_app` 生成项目的默认 minSdk |
73
- | 构建 | `buildTimeoutMs` | 整数 | `600000` | 单次 assemble 的最长运行时间 |
75
+ | 构建 | `buildTimeoutSeconds` | 整数 | `600` | 单次 assemble 的最长运行时间(秒) |
74
76
  | | `gradleExtraArgs` | 多行文本 | 空 | 额外 Gradle 参数,每行一个 |
75
77
  | 输出目录与日志 | `dataDir` | 文本 | 空 | 截图与构建日志根目录;留空用 `$DSH_HOME/android-emulator-helper` |
76
78
  | | `screenshotDir` | 文本 | 空 | 截图目录;留空用 `<数据目录>/screenshots` |
@@ -168,7 +170,7 @@ dsh-android-emulator-helper/
168
170
 
169
171
  ```bash
170
172
  pnpm install # 装官方 peer 包与 react/react-dom(开发与测试用)
171
- pnpm test # 63 个测试(无需 Android SDK)
173
+ pnpm test # 145 个测试(无需 Android SDK)
172
174
  pnpm run check # 测试 + host/client 一致性闸门 + 真实 Cordis 挂载验证
173
175
  pnpm run verify:mount # 真实 Cordis Context + 真实 ToolRuntime 注册表
174
176
  pnpm run smoke # 只读真机探测:preflight / AVD / 设备 / 项目发现 / inventory
@@ -181,11 +183,14 @@ pnpm run link:dsh-deps # 离线开发:把本机 DSH 的 @deepseek-ai 包
181
183
  | 层 | 命令 | 证明了什么 |
182
184
  |---|---|---|
183
185
  | 单元 / 解析器 | `pnpm test` | 客户端 bundle 加载契约与静态模块约束、manifest 与 patch 契约、设置投影(volatile 实时读取、环境变量回落、越界回落)、移植过来的解析器(`adb devices`、`emulator -list-avds`、UI Automator XML、`input text` 转义、APK 选择、JDK 主版本归一、无损 JSON 归一) |
184
- | 设置页渲染 | `pnpm test`(`client-render.test.js`) | 用真实 React + `react-dom/server` 渲染整个设置页:分组/字段/字典键完整、受控输入带真实值、开关与下拉状态正确、「已覆盖/恢复默认」、两个按钮与保存按钮齐全、无未解析字典键 |
185
- | 一致性闸门 | `scripts/check-bundle.mjs` | bundle id = 包名、命名空间 = patch 行 id、host 与 client 两份字段表逐键逐选项一致、两个路由两侧一致、bundle 仍是经典脚本 |
186
- | 真实宿主挂载 | `scripts/verify-mount.mjs` | 在**真实** Cordis `Context` + **真实** `ToolRuntime` 上挂载:23 个工具注册成功、四个只读工具经注册表实际派发并通过参数/结果 schema 校验、错误参数被注册表拒绝、未知工具名不解析、17 个设置字段全部 volatile、dispose 后工具全部注销 |
186
+ | 设置页渲染 | `pnpm test`(`client-render.test.js`) | 用真实 React + `react-dom/server` 渲染整个设置页:分组/字段/字典键完整、受控输入带真实值、开关与下拉状态正确、「已覆盖/恢复默认」、两个按钮齐全、无未解析字典键,且没有暂存改动时不出现操作栏 |
187
+ | 一致性闸门 | `scripts/check-bundle.mjs` | bundle id = 包名、命名空间 = patch 行 id、host 与 client 两份字段表与分组表逐键逐序一致、每个标题/提示/占位符/选项在**两种语言下都逐字一致**、两个路由两侧一致、`locale/*.json` 两种语言键集相同、bundle 仍是经典脚本 |
188
+ | 路由集成 | `test/routes-integration.test.js` | 在**真实** Cordis `Context` 上挂载两条设置路由:真正调用 handler 并读取其 `Response`、17 个字段都以 volatile 引用形式到达、保存的设置对下一次调用立即可见、handler 出错时返回 `500` JSON 体而不是抛出、缺少可选服务时仍能挂载、dispose 后两条路由都被移除 |
189
+ | 真实宿主挂载 | `scripts/verify-mount.mjs` | 在**真实** Cordis `Context` + **真实** `ToolRuntime` 上挂载:23 个工具注册成功、**23 个工具全部经注册表实际派发**并通过参数/结果 schema 校验、错误参数被注册表拒绝、未知工具名不解析、17 个设置字段全部 volatile、dispose 后工具全部注销 |
190
+ | 真机 | `pnpm run smoke`… | 只有带 SDK、模拟器与 Gradle 的机器才能证明的部分:发现、启动与停止、截图、logcat、UI 自动化,以及真实的 `android_create_app` → 构建 → 安装 → 启动闭环 |
187
191
 
188
192
  `pnpm run smoke` 覆盖只有真机才能证明的部分:SDK 发现、`adb`/`emulator` 实际调用、环境拼装、设置投影真正喂给 provider。
193
+ `pnpm run smoke:emulator` 进一步覆盖启动模拟器 → 截图 → 读日志 → 停止的完整链路。
189
194
 
190
195
  ### 注册表给工具层带来的两条约束
191
196
 
@@ -199,10 +204,6 @@ pnpm run link:dsh-deps # 离线开发:把本机 DSH 的 @deepseek-ai 包
199
204
  `bounds`、发现结果没有 Gradle 根),因此在所有工具结果唯一的必经路径上用
200
205
  `toLosslessJson()` 统一归一,而不是在 23 个 provider 里各处理一遍。
201
206
 
202
- `docs/research/` 记录了本次移植所依赖的 DSH 插件契约,全部带指向实际包源码的
203
- 逐行引用;DSH 应用包不提供开发者文档,这是目前最接近「DSH 插件开发规范」的
204
- 可核对材料。
205
-
206
207
  ## 已知限制
207
208
 
208
209
  - **Linux 不支持**,与原插件一致;`android_preflight` 会将其标记为不支持的宿主。
@@ -210,10 +211,19 @@ pnpm run link:dsh-deps # 离线开发:把本机 DSH 的 @deepseek-ai 包
210
211
  - `android_create_avd` 对 `avdmanager` 的确认提示固定回答 `no`,绝不静默触发数 GB 的镜像下载;需要先自行 `sdkmanager --install`。
211
212
  - UI 自动化依赖设备端 `uiautomator`;失败时 `android_ui_status` 会报告不可用,构建/运行/截图链路不受影响。
212
213
  - 设置页的「已安装系统镜像」列表来自 SDK 目录扫描,而不是 `sdkmanager --list_installed`(后者在受限环境下会因创建 bin 目录失败,且输出含大量弃用提示)。
213
- - **设置页尚未在真实 GUI 中人工目视确认**:交付方式选择了「只交付源码包」,未安装进 `desktop` profile。渲染层已由真实 React 的服务端渲染测试覆盖(见上表),但视觉细节(间距、暗色主题、窄屏换行)需要装上后自行核对。
214
+ - 设置页的两条路由由插件提供、并在真实 profile 中挂载,但它们的 HTTP 响应无法在 GUI 之外验证:桌面端对每个请求做鉴权,未带浏览器会话的 `curl` 访问 `/api/android-emulator-helper/status` 会在进入 handler 之前就返回 `403 forbidden`。因此 `test/routes-integration.test.js` 改为在真实 Cordis context 上直接调用 handler。设置页的间距、暗色主题与窄屏换行仍建议在 GUI 中人工核对一次。
214
215
  - `@deepseek-ai/dsh-client-ui-primitives` 在 npm 上只有 `0.0.1-rc.1`,与运行时 API 不一致;因此设置页渲染测试对 primitives 使用了忠实替身,真实 React 与 `react-dom/server` 仍是真件。
215
216
  - 插件不触碰凭据:不读取、不转发任何含 key/token/secret 的环境变量。
216
217
 
218
+ ## 版本
219
+
220
+ | 版本 | 要点 |
221
+ |---|---|
222
+ | **1.1.0** | 设置页按卡片重做并加底部粘性操作栏;两处超时改为按秒填写;修复 8 个缺陷,其中包含生成的新工程编译不过、`android_ui_type_text` 把每个 `%` 变成 `%25`、`android_ui_describe` 在 dump 失败时返回上一屏、`android_start_emulator` 传入不存在的 AVD 名却静默启动另一个模拟器。23 个工具全部在真机上实测过。 |
223
+ | 1.0.0 | 首个版本:23 个原生 `android_*` 工具、内置 `android-dev` skill 与 `/android-dev` 命令,以及设置页。 |
224
+
225
+ **升级后请重启 DSH 宿主进程。** 本包以 `link:` 软链挂载,Node 对 ESM 做进程级缓存,`lib/` 的改动在宿主重启前不会生效——否则工具仍会跑旧代码。
226
+
217
227
  ## 许可
218
228
 
219
229
  [MIT](LICENSE),版权所有 (c) 2026 zhz8888。