hostpad 0.2.1 → 0.2.2

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
@@ -27,7 +27,7 @@ npx hostpad
27
27
  }
28
28
  ```
29
29
 
30
- ## MCP 工具(10 个)
30
+ ## MCP 工具(12 个)
31
31
 
32
32
  | 工具 | 作用 |
33
33
  |------|------|
@@ -38,8 +38,11 @@ npx hostpad
38
38
  | `reload` | 重推工具(激活) |
39
39
  | `get_logs` | 读手机回流的日志环形缓冲(工具 console.* 与系统事件,游标分页) |
40
40
  | `rpc_call` | 调用手机上运行中工具的 `onMessage` 处理器,返回值回传 |
41
+ | `ui_snapshot` / `ui_screenshot` | UI 调试:拉取手机上**净化后实际渲染**的 DOM 快照(含每元素几何框、用户指认记录、renderSeq)/ 截取当前工具页 JPEG 图直接看效果。需手机上该工具页处于打开状态 |
41
42
 
42
- 典型闭环:`create_tool`(写)→ 手机热加载 → `get_logs` 读 `loaded` / console 输出 → `update_tool` 增量迭代 → `rpc_call` 驱动交互。
43
+ 典型闭环:`create_tool`(写)→ 手机热加载 → `get_logs` 读 `loaded` / console 输出 → `update_tool` 增量迭代 → `rpc_call` 驱动交互 → `ui_snapshot` 指认元素 → `ui_screenshot` 验收视觉。
44
+
45
+ UI 调试循环细节:推送后轮询 `ui_snapshot`,`renderSeq` 变化即新帧已渲染;用户在手机工具页右上角十字准星图标开启**指认模式**后,点选的元素路径会出现在下一次 `ui_snapshot` 的 `picks` 里。截图/指认会把界面内容发到电脑端(App 内有日志留痕)。老版本 App 不支持这两个工具时会有明确的升级提示。
43
46
 
44
47
  ## 命令行
45
48
 
package/bin/hostpad.js CHANGED
@@ -79,6 +79,17 @@ const fileSchema = {
79
79
  required: ['path', 'content'],
80
80
  },
81
81
  };
82
+ // UI 调试保留字(__ui_inspect / __ui_screenshot)响应的形状校验 + 解析。
83
+ // 老 App 没有保留字拦截,会落进工具 onMessage → result 为 nil/杂值,这里转成版本提示。
84
+ function parseUiPayload(r, mustHave) {
85
+ let parsed;
86
+ try { parsed = JSON.parse(r.result); } catch { /* 落到下面的版本提示 */ }
87
+ if (!parsed || typeof parsed !== 'object' || !(mustHave in parsed)) {
88
+ throw new Error('手机端 App 版本过旧(未实现 UI 调试通道),请在 iPhone 上更新 HostPad 后重试');
89
+ }
90
+ return parsed;
91
+ }
92
+
82
93
  const tools = [
83
94
  {
84
95
  name: 'list_devices',
@@ -208,10 +219,49 @@ const tools = [
208
219
  { timeoutMs: timeoutMs ?? 5000 },
209
220
  ),
210
221
  },
222
+ {
223
+ name: 'ui_snapshot',
224
+ description: 'UI 调试:拉取手机上当前工具实际渲染后的 DOM 快照(净化器处理后的真实结果,'
225
+ + '每元素含 tag/class/文本/几何框,能看出哪些写法被剥除)+ 用户指认记录(picks,'
226
+ + '手机上开启指认模式后用户点了哪些元素)+ renderSeq。'
227
+ + '调试循环:update_tool 推送后轮询本工具,renderSeq 变化说明新帧已渲染',
228
+ inputSchema: {
229
+ type: 'object',
230
+ properties: { toolId: { type: 'string' } },
231
+ required: ['toolId'],
232
+ },
233
+ handler: async ({ toolId }) => {
234
+ const r = await bridge.rpcCall(toolId, '__ui_inspect', {}, { timeoutMs: 8000 });
235
+ return parseUiPayload(r, 'dom');
236
+ },
237
+ },
238
+ {
239
+ name: 'ui_screenshot',
240
+ description: 'UI 调试:截取手机上当前工具页面的图(JPEG ~800px,以图片内容返回,直接看视觉效果)。'
241
+ + '需该工具页面在手机上处于打开状态。改完 UI 推送后用它验收',
242
+ inputSchema: {
243
+ type: 'object',
244
+ properties: { toolId: { type: 'string' } },
245
+ required: ['toolId'],
246
+ },
247
+ handler: async ({ toolId }) => {
248
+ const r = await bridge.rpcCall(toolId, '__ui_screenshot', {}, { timeoutMs: 8000 });
249
+ const p = parseUiPayload(r, 'image');
250
+ return {
251
+ // 哨兵键:mcp.js 直接透传为 MCP content(图片不能 stringify 成文本)
252
+ __rawContent: [
253
+ { type: 'text', text: `renderSeq=${p.renderSeq} ${p.width}x${p.height} ${p.mime}` },
254
+ { type: 'image', data: p.image, mimeType: p.mime },
255
+ ],
256
+ };
257
+ },
258
+ },
211
259
  ];
212
260
 
213
261
  const mcp = createMcpServer({
214
262
  tools,
263
+ // serverInfo 版本跟随 package.json,发布升版不用改这里
264
+ serverInfo: { name: 'hostpad-agent', version: require('../package.json').version },
215
265
  write: (line) => process.stdout.write(line + '\n'),
216
266
  });
217
267
 
package/lib/guide.js CHANGED
@@ -61,6 +61,16 @@ ${FENCE}
61
61
  host.ui.onMessage 收到 { name: "load", values: { repo: "apple/swift" } }。
62
62
  注意 img 的 data: src 会被剥(base64 内联图不可用,只能 http(s) 远程图)。
63
63
 
64
+ ## UI 调试循环(不用盲写)
65
+
66
+ 1. ui_snapshot(toolId):拉取手机上**净化后实际渲染**的 DOM 树(每元素含几何框),
67
+ 看元素是否被剥、层级是否符合预期;响应含 renderSeq 与用户指认记录(picks)
68
+ 2. update_tool 推送修改 → 轮询 ui_snapshot 直到 renderSeq 变化(新帧已渲染)
69
+ 3. ui_screenshot(toolId):截取当前工具页(JPEG 图片直接可见),验收视觉效果
70
+ 用户在手机上点工具页右上角十字准星(scope)图标开启**指认模式**后,点哪个元素,
71
+ 下一次 ui_snapshot 的 picks 里就有该元素的选择器路径/outerHTML/几何框——
72
+ "用户说哪里不对"可直接定位到节点。截图/指认会把界面内容发到电脑端(App 内有日志留痕)。
73
+
64
74
  ## 权限模型
65
75
 
66
76
  manifest.permissions 声明 "逃出沙盒" 的能力:http / clipboard / notifications。
package/lib/mcp.js CHANGED
@@ -56,9 +56,14 @@ function createMcpServer({ tools, write, serverInfo }) {
56
56
  let result;
57
57
  try {
58
58
  result = await tool.handler(f.params?.arguments ?? {});
59
+ // 哨兵透传:handler 返回 { __rawContent: [内容块] } 时直接作为 MCP content
60
+ // (ui_screenshot 返回 image 块用;其余 handler 照旧统一 stringify)
61
+ const raw = result && typeof result === 'object' && Array.isArray(result.__rawContent)
62
+ ? result.__rawContent
63
+ : null;
59
64
  respond({
60
65
  jsonrpc: '2.0', id,
61
- result: { content: [{ type: 'text', text: JSON.stringify(result ?? null) }] },
66
+ result: { content: raw ?? [{ type: 'text', text: JSON.stringify(result ?? null) }] },
62
67
  });
63
68
  } catch (e) {
64
69
  respond({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hostpad",
3
- "version": "0.2.1",
3
+ "version": "0.2.2",
4
4
  "description": "HostPad 的 MCP 服务器 + iPhone 同步桥:AI agent 经 MCP 管理与调试 iOS 工具(写→推→读日志闭环)",
5
5
  "license": "UNLICENSED",
6
6
  "bin": {