@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 +33 -17
- package/README.zh-CN.md +22 -12
- package/lib/client.js +297 -105
- package/lib/index.js +15 -2
- package/lib/providers/avd.js +53 -4
- package/lib/providers/build.js +1 -1
- package/lib/providers/preflight.js +31 -4
- package/lib/providers/project-template.js +53 -0
- package/lib/providers/project.js +10 -2
- package/lib/providers/ui.js +68 -28
- package/lib/settings.js +56 -16
- package/package.json +1 -1
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
|
-
| | `
|
|
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 | `
|
|
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 #
|
|
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
|
|
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
|
-
|
|
|
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
|
-
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
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
|
-
| | `
|
|
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
|
-
| 构建 | `
|
|
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 #
|
|
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
|
|
186
|
-
|
|
|
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
|
-
-
|
|
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。
|