@harmonyos-arkts/d2h 0.1.11 → 0.1.12

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.
Files changed (126) hide show
  1. package/.claude-plugin/marketplace.json +15 -13
  2. package/.claude-plugin/plugin.json +13 -7
  3. package/AGENTS.md +5 -5
  4. package/README.md +212 -212
  5. package/agents/android-to-hmos-00-orchestrator.md +318 -318
  6. package/agents/d2h.md +157 -157
  7. package/dist/bin/install-opencode.js +30 -30
  8. package/docs/environment-check.md +86 -86
  9. package/install-opencode.sh +160 -160
  10. package/opencode/agents.json +16 -16
  11. package/opencode/vscode-d2h-package.json +7 -7
  12. package/opencode/vscode-d2h-plugin.mjs +29 -29
  13. package/package.json +54 -54
  14. package/schemas/android-source-manifest.schema.json +25 -25
  15. package/schemas/checkpoint-provenance.schema.json +67 -67
  16. package/schemas/final-acceptance.schema.json +29 -29
  17. package/schemas/managed-evidence-index.schema.json +15 -15
  18. package/schemas/managed-evidence.schema.json +114 -114
  19. package/schemas/migration-config.schema.json +51 -51
  20. package/schemas/migration-report-index.schema.json +39 -39
  21. package/schemas/migration-report-item.schema.json +51 -51
  22. package/schemas/migration-report-summary.schema.json +25 -25
  23. package/schemas/migration-status.schema.json +202 -202
  24. package/schemas/preflight.schema.json +60 -60
  25. package/schemas/source-order-audit.schema.json +16 -16
  26. package/schemas/source-provenance-event.schema.json +19 -19
  27. package/schemas/spec-app-shard.schema.json +45 -45
  28. package/schemas/spec-fact-corrections-shard.schema.json +13 -13
  29. package/schemas/spec-features-shard.schema.json +43 -43
  30. package/schemas/spec-index.schema.json +26 -26
  31. package/schemas/spec-interactions-shard.schema.json +40 -40
  32. package/schemas/spec-page.schema.json +98 -98
  33. package/schemas/spec-pages-index.schema.json +1 -1
  34. package/schemas/spec-unresolved-shard.schema.json +1 -1
  35. package/schemas/task-envelope.schema.json +109 -109
  36. package/schemas/task-plan.schema.json +341 -341
  37. package/scripts/check-environment.mjs +320 -320
  38. package/skills/android2hmos_resources_convert/SKILL.md +180 -180
  39. package/skills/android2hmos_resources_convert/references/image-conversion-rules.md +230 -230
  40. package/skills/android2hmos_resources_convert/references/svg-fix-patterns.md +175 -175
  41. package/skills/android2hmos_resources_convert/references/xml-drawable-to-svg-rules.md +513 -513
  42. package/skills/arkts-app-identity/SKILL.md +238 -238
  43. package/skills/arkts-i18n/SKILL.md +496 -496
  44. package/skills/arkts-i18n/evals/evals.json +84 -84
  45. package/skills/arkts-i18n/references/code-examples.md +302 -302
  46. package/skills/arkts-i18n/references/common-pitfalls.md +391 -391
  47. package/skills/arkts-i18n/references/dynamic-language-switch.md +604 -604
  48. package/skills/arkts-i18n/references/hardcoded-string-scanner.md +348 -348
  49. package/skills/arkts-i18n/references/language-codes.md +104 -104
  50. package/skills/arkts-i18n/references/resource-file-structure.md +775 -775
  51. package/skills/arkts-i18n/references/static-vs-dynamic.md +242 -242
  52. package/skills/arkts-i18n/references/v1-compat.md +244 -244
  53. package/skills/arkts-i18n/scripts/audit_i18n_completeness.sh +174 -174
  54. package/skills/arkts-icon-sizing/SKILL.md +211 -211
  55. package/skills/arkts-icon-sizing/scripts/icon_audit.py +131 -131
  56. package/skills/arkts-icon-sizing/scripts/icon_autofix.py +88 -88
  57. package/skills/arkts-icon-sizing/scripts/icon_dims.py +179 -179
  58. package/skills/arkts-icon-sizing/scripts/icon_fix.py +119 -119
  59. package/skills/arkts-mvvm-architecture/SKILL.md +613 -613
  60. package/skills/d2h-hypium-forge/SKILL.md +309 -309
  61. package/skills/d2h-hypium-forge/references/functional-spec-template.md +140 -140
  62. package/skills/d2h-hypium-forge/references/official-api.md +135 -135
  63. package/skills/d2h-hypium-forge/references/pitfalls.md +98 -98
  64. package/skills/d2h-hypium-forge/references/testability-template.md +71 -71
  65. package/skills/d2h-hypium-forge/scripts/_common_template.py +659 -659
  66. package/skills/d2h-hypium-forge/scripts/analyze_results.py +165 -165
  67. package/skills/d2h-hypium-forge/scripts/assert_audit.py +107 -107
  68. package/skills/d2h-hypium-forge/scripts/collect_runtime_pages.py +169 -169
  69. package/skills/d2h-hypium-forge/scripts/forge_report.py +325 -325
  70. package/skills/d2h-hypium-forge/scripts/inject_resource_ids.py +114 -114
  71. package/skills/d2h-hypium-forge/scripts/lint_asserts.py +80 -80
  72. package/skills/d2h-hypium-forge/scripts/lint_locate.py +60 -60
  73. package/skills/d2h-hypium-forge/scripts/merge_appgraph.py +249 -249
  74. package/skills/d2h-hypium-forge/scripts/precondition.py +140 -140
  75. package/skills/d2h-hypium-forge/scripts/preinject_ids.py +232 -232
  76. package/skills/d2h-hypium-forge/scripts/probe.py +197 -197
  77. package/skills/d2h-hypium-forge/scripts/run_direct.py +823 -823
  78. package/skills/d2h-hypium-forge/scripts/run_parallel.py +121 -121
  79. package/skills/d2h-hypium-forge/scripts/scan_features.py +255 -255
  80. package/skills/d2h-hypium-forge/scripts/upgrade_asserts.py +66 -66
  81. package/skills/d2h-hypium-forge/scripts/validate_scenarios.py +84 -84
  82. package/skills/d2h-page-develop/SKILL.md +112 -112
  83. package/skills/d2h-page-develop/agents/openai.yaml +7 -7
  84. package/skills/d2h-page-spec-generate/SKILL.md +206 -206
  85. package/skills/d2h-page-spec-generate/scripts/build_batch_report.py +227 -227
  86. package/skills/d2h-utg-ai-generate/SKILL.md +128 -128
  87. package/skills/d2h-utg-ai-generate/references/click-paths-schema.json +76 -76
  88. package/skills/d2h-utg-ai-generate/references/extraction-guide.md +34 -34
  89. package/skills/d2h-utg-ai-generate/references/facts-contract.md +96 -96
  90. package/skills/d2h-utg-ai-generate/references/page-schema.json +61 -61
  91. package/skills/d2h-utg-ai-generate/references/utg-schema.json +89 -89
  92. package/skills/d2h-utg-ai-generate/scripts/build_page_json.py +314 -314
  93. package/skills/d2h-utg-ai-generate/scripts/build_utg.py +123 -123
  94. package/skills/d2h-utg-ai-generate/scripts/capture_from_page_json.ts +280 -280
  95. package/skills/d2h-utg-ai-generate/scripts/utg_common.py +524 -524
  96. package/skills/d2h-utg-ai-generate/scripts/utg_html.py +489 -489
  97. package/skills/d2h-utg-ai-generate/scripts/validate_utg.py +69 -69
  98. package/skills/d2h-utg-ai-generate/scripts/vendor/D3-LICENSE +13 -13
  99. package/skills/d2h-utg-ai-generate/scripts/vendor/d3-7.9.0.min.js +2 -2
  100. package/skills/d2h-utg-ai-generate/tests/test_pipeline.py +369 -369
  101. package/skills/d2h-utg-static-generate/SKILL.md +73 -73
  102. package/skills/d2h-utg-static-generate/references/discoveries.md +38 -38
  103. package/skills/d2h-utg-static-generate/scripts/generate.py +499 -499
  104. package/skills/d2h-utg-static-generate/tests/test_static.py +335 -335
  105. package/skills/d2h-utg-static-generate/tests/verify-offline-html.py +60 -60
  106. package/skills/d2h-utg-static-generate/tests/verify-produced-fixture.py +131 -131
  107. package/skills/harmonyos-migration-playbook/SKILL.md +83 -83
  108. package/skills/harmonyos-migration-playbook/agents/openai.yaml +7 -7
  109. package/skills/harmonyos-migration-playbook/references/arkts-compile.md +24 -24
  110. package/skills/harmonyos-migration-playbook/references/harmony-runtime.md +53 -53
  111. package/skills/harmonyos-migration-playbook/references/lesson-lifecycle.md +45 -45
  112. package/skills/harmonyos-migration-playbook/references/protocol-e2e.md +23 -23
  113. package/skills/harmonyos-migration-playbook/references/ui-automation.md +53 -53
  114. package/skills/harmonyos-migration-playbook/references/windows-environment.md +38 -38
  115. package/skills/maintaining-migration-report/SKILL.md +144 -144
  116. package/skills/maintaining-migration-report/references/final-acceptance.md +48 -48
  117. package/skills/maintaining-migration-report/references/source-provenance.md +66 -66
  118. package/skills/managing-d2h-toolchain/SKILL.md +24 -24
  119. package/skills/managing-d2h-toolchain/environment-requirements.json +16 -16
  120. package/skills/managing-d2h-toolchain/scripts/manage-toolchain.mjs +98 -98
  121. package/skills/managing-d2h-toolchain/toolchain.json +16 -16
  122. package/skills/native-library-substitution/SKILL.md +384 -384
  123. package/skills/native-library-substitution/references/native-library-substitution.json +56905 -56905
  124. package/skills/native-library-substitution/references/native-library-substitution.md +163 -163
  125. package/skills/preparing-migration-workspace/SKILL.md +131 -131
  126. package/skills/reviewing-migration-process/SKILL.md +62 -62
@@ -1,197 +1,197 @@
1
- #!/usr/bin/env python3
2
- """probe.py — 单设备快速取证探针(替代手写 UiDriver 内联脚本)。
3
-
4
- 背景(Grit 实证):测试阶段 69 次手工探针 + 35 次单例调试里,agent 反复手写
5
- `python3 - <<EOF from hypium import ...` 取证——每次 10-20 行代码生成 + connect 样板,
6
- 占探针时间的一半以上。本工具把高频取证动作固化为一条命令,输出紧凑可直接读。
7
-
8
- 用法(--device 缺省取 env FORGE_DEVICE,再缺省 127.0.0.1:5555):
9
- python3 probe.py texts # 当前页可见 Text/Button 文案
10
- python3 probe.py ids # 当前页全部 key/id
11
- python3 probe.py find <id> # 定位: 属性+bounds+可见性
12
- python3 probe.py tree [--grep x] [--depth 6] # 紧凑组件树(含 type/key/text/bounds)
13
- python3 probe.py click <id|text> # 点击: id 优先,文本兜底(uinput 坐标, Toggle 实证可靠)
14
- python3 probe.py tap <x> <y> # 原始坐标点击 (uinput -T -c)
15
- python3 probe.py shot [out.jpeg] # 截图
16
- python3 probe.py hilog [tag] [n] # hilog 过滤 (缺省 JSAPP/400)
17
- python3 probe.py db-pull <package> # 拉取应用 rdb 目录到 ./db_probe/
18
- python3 probe.py db-sql <db> <sql> # 本地 sqlite 查询(db-pull 之后)
19
-
20
- 除 click/shot 外全部基于单次 uitest dumpLayout 本地解析(1 次 RPC 取全量证据)。
21
- """
22
- import argparse
23
- import json
24
- import os
25
- import re
26
- import subprocess
27
- import sys
28
- import tempfile
29
-
30
- LAYOUT_REMOTE = '/data/local/tmp/_probe_layout.json'
31
-
32
-
33
- def hdc_bin():
34
- return os.environ.get('FORGE_HDC', 'hdc')
35
-
36
-
37
- def hdc(dev, args, timeout=25):
38
- r = subprocess.run([hdc_bin(), '-t', dev] + args,
39
- capture_output=True, timeout=timeout)
40
- return (r.stdout + r.stderr).decode('utf-8', 'ignore')
41
-
42
-
43
- def dump_layout(dev, timeout=25):
44
- """单次 RPC: uitest dumpLayout → 本地 JSON dict(失败返回 {})。"""
45
- local = os.path.join(tempfile.gettempdir(), '_probe_layout.json')
46
- hdc(dev, ['shell', 'uitest', 'dumpLayout', '-p', LAYOUT_REMOTE], timeout)
47
- hdc(dev, ['file', 'recv', LAYOUT_REMOTE, local], timeout)
48
- try:
49
- return json.load(open(local, encoding='utf-8'))
50
- except Exception:
51
- return {}
52
-
53
-
54
- def walk(node):
55
- yield node
56
- for ch in node.get('children') or []:
57
- yield from walk(ch)
58
-
59
-
60
- def attrs(n):
61
- a = n.get('attributes') or {}
62
- return a.get('type', '?'), a.get('key', '') or '', a.get('text', '') or '', a.get('bounds', '')
63
-
64
-
65
- def cmd_texts(dev, _a):
66
- seen, out = set(), []
67
- for n in walk(dump_layout(dev)):
68
- t, k, txt, b = attrs(n)
69
- if txt and txt not in seen:
70
- seen.add(txt)
71
- out.append(f'{txt} [{b}]')
72
- print('\n'.join(out[:80]) or '(无文本)')
73
-
74
-
75
- def cmd_ids(dev, _a):
76
- out = []
77
- for n in walk(dump_layout(dev)):
78
- t, k, txt, b = attrs(n)
79
- if k:
80
- out.append(f'{k} ({t}{"/" + txt if txt else ""}) [{b}]')
81
- print('\n'.join(out[:120]) or '(无 id)')
82
-
83
-
84
- def cmd_find(dev, a):
85
- target = a.target
86
- for n in walk(dump_layout(dev)):
87
- t, k, txt, b = attrs(n)
88
- if k == target or txt == target:
89
- print(f'type={t} key={k} text={txt} bounds={b} visible={bool(b)}')
90
- return
91
- print(f'not found: {target}')
92
-
93
-
94
- def cmd_tree(dev, a):
95
- root = dump_layout(dev)
96
- lines = []
97
-
98
- def rec(n, depth):
99
- if depth > a.depth:
100
- return
101
- t, k, txt, b = attrs(n)
102
- tag = t + (f'#{k}' if k else '') + (f' "{txt[:14]}"' if txt else '')
103
- if (not a.grep or a.grep in k or a.grep in txt or a.grep in t):
104
- lines.append(' ' * depth + f'{tag} {b}')
105
- for ch in n.get('children') or []:
106
- rec(ch, depth + 1)
107
-
108
- rec(root, 0)
109
- print('\n'.join(lines[:200]) or '(空树)')
110
-
111
-
112
- def _bounds_center(b):
113
- # uitest 布局 bounds 兼容两种格式: '[l,t][r,b]'(标准)与 '[l,t,r,b]'
114
- nums = [int(x) for x in re.findall(r'-?\d+', str(b))]
115
- if len(nums) != 4:
116
- return None
117
- l, t, r, bt = nums
118
- return (l + r) // 2, (t + bt) // 2
119
-
120
-
121
- def cmd_click(dev, a):
122
- # 1) id 命中 → 取中心坐标(uinput 通道,Toggle 等 hypium touch 失效场景实证可靠)
123
- for n in walk(dump_layout(dev)):
124
- t, k, txt, b = attrs(n)
125
- if k == a.target or txt == a.target:
126
- c = _bounds_center(b)
127
- if c:
128
- print(hdc(dev, ['shell', 'uinput', '-T', '-c', str(c[0]), str(c[1])]).strip() or
129
- f'clicked {a.target} @ {c}')
130
- return
131
- print(f'not found: {a.target}')
132
-
133
-
134
- def cmd_tap(dev, a):
135
- print(hdc(dev, ['shell', 'uinput', '-T', '-c', str(a.x), str(a.y)]).strip() or f'tapped {a.x},{a.y}')
136
-
137
-
138
- def cmd_shot(dev, a):
139
- out = a.path or 'probe_shot.jpeg'
140
- try:
141
- from hypium import UiDriver
142
- UiDriver.connect(device_sn=dev).screenshot(out)
143
- print(out)
144
- return
145
- except Exception:
146
- pass
147
- remote = '/data/local/tmp/_probe_shot.jpeg'
148
- hdc(dev, ['shell', 'snapshot_display', '-f', remote])
149
- hdc(dev, ['file', 'recv', remote, out])
150
- hdc(dev, ['shell', 'rm', '-f', remote])
151
- print(out)
152
-
153
-
154
- def cmd_hilog(dev, a):
155
- print(hdc(dev, ['shell', 'hilog', '-z', str(a.n), '-T', a.tag])[:8000])
156
-
157
-
158
- def cmd_db_pull(dev, a):
159
- outdir = 'db_probe'
160
- os.makedirs(outdir, exist_ok=True)
161
- listing = hdc(dev, ['shell', f'ls /data/app/el2/*/database/{a.package}/entry/rdb/ 2>/dev/null'])
162
- got = []
163
- for fn in [x.strip() for x in listing.splitlines() if x.strip().endswith(('.db', '.db-wal', '.db-shm'))]:
164
- hdc(dev, ['file', 'recv', f'/data/app/el2/100/database/{a.package}/entry/rdb/{fn}',
165
- os.path.join(outdir, fn)])
166
- got.append(fn)
167
- print(f'pulled {len(got)} files -> {outdir}/: {" ".join(got) or "(空,检查包名)" }')
168
-
169
-
170
- def cmd_db_sql(_dev, a):
171
- r = subprocess.run(['sqlite3', a.db, a.sql], capture_output=True, text=True)
172
- print(r.stdout or r.stderr)
173
-
174
-
175
- def main():
176
- ap = argparse.ArgumentParser(add_help=True)
177
- ap.add_argument('--device', default=os.environ.get('FORGE_DEVICE', '127.0.0.1:5555'))
178
- sub = ap.add_subparsers(dest='cmd', required=True)
179
- sub.add_parser('texts')
180
- sub.add_parser('ids')
181
- p = sub.add_parser('find'); p.add_argument('target')
182
- p = sub.add_parser('tree'); p.add_argument('--grep'); p.add_argument('--depth', type=int, default=6)
183
- p = sub.add_parser('click'); p.add_argument('target')
184
- p = sub.add_parser('tap'); p.add_argument('x', type=int); p.add_argument('y', type=int)
185
- p = sub.add_parser('shot'); p.add_argument('path', nargs='?')
186
- p = sub.add_parser('hilog'); p.add_argument('tag', nargs='?', default='JSAPP'); p.add_argument('n', nargs='?', type=int, default=400)
187
- p = sub.add_parser('db-pull'); p.add_argument('package')
188
- p = sub.add_parser('db-sql'); p.add_argument('db'); p.add_argument('sql')
189
- a = ap.parse_args()
190
- dev = a.device
191
- {'texts': cmd_texts, 'ids': cmd_ids, 'find': cmd_find, 'tree': cmd_tree,
192
- 'click': cmd_click, 'tap': cmd_tap, 'shot': cmd_shot, 'hilog': cmd_hilog,
193
- 'db-pull': cmd_db_pull, 'db-sql': cmd_db_sql}[a.cmd](dev, a)
194
-
195
-
196
- if __name__ == '__main__':
197
- main()
1
+ #!/usr/bin/env python3
2
+ """probe.py — 单设备快速取证探针(替代手写 UiDriver 内联脚本)。
3
+
4
+ 背景(Grit 实证):测试阶段 69 次手工探针 + 35 次单例调试里,agent 反复手写
5
+ `python3 - <<EOF from hypium import ...` 取证——每次 10-20 行代码生成 + connect 样板,
6
+ 占探针时间的一半以上。本工具把高频取证动作固化为一条命令,输出紧凑可直接读。
7
+
8
+ 用法(--device 缺省取 env FORGE_DEVICE,再缺省 127.0.0.1:5555):
9
+ python3 probe.py texts # 当前页可见 Text/Button 文案
10
+ python3 probe.py ids # 当前页全部 key/id
11
+ python3 probe.py find <id> # 定位: 属性+bounds+可见性
12
+ python3 probe.py tree [--grep x] [--depth 6] # 紧凑组件树(含 type/key/text/bounds)
13
+ python3 probe.py click <id|text> # 点击: id 优先,文本兜底(uinput 坐标, Toggle 实证可靠)
14
+ python3 probe.py tap <x> <y> # 原始坐标点击 (uinput -T -c)
15
+ python3 probe.py shot [out.jpeg] # 截图
16
+ python3 probe.py hilog [tag] [n] # hilog 过滤 (缺省 JSAPP/400)
17
+ python3 probe.py db-pull <package> # 拉取应用 rdb 目录到 ./db_probe/
18
+ python3 probe.py db-sql <db> <sql> # 本地 sqlite 查询(db-pull 之后)
19
+
20
+ 除 click/shot 外全部基于单次 uitest dumpLayout 本地解析(1 次 RPC 取全量证据)。
21
+ """
22
+ import argparse
23
+ import json
24
+ import os
25
+ import re
26
+ import subprocess
27
+ import sys
28
+ import tempfile
29
+
30
+ LAYOUT_REMOTE = '/data/local/tmp/_probe_layout.json'
31
+
32
+
33
+ def hdc_bin():
34
+ return os.environ.get('FORGE_HDC', 'hdc')
35
+
36
+
37
+ def hdc(dev, args, timeout=25):
38
+ r = subprocess.run([hdc_bin(), '-t', dev] + args,
39
+ capture_output=True, timeout=timeout)
40
+ return (r.stdout + r.stderr).decode('utf-8', 'ignore')
41
+
42
+
43
+ def dump_layout(dev, timeout=25):
44
+ """单次 RPC: uitest dumpLayout → 本地 JSON dict(失败返回 {})。"""
45
+ local = os.path.join(tempfile.gettempdir(), '_probe_layout.json')
46
+ hdc(dev, ['shell', 'uitest', 'dumpLayout', '-p', LAYOUT_REMOTE], timeout)
47
+ hdc(dev, ['file', 'recv', LAYOUT_REMOTE, local], timeout)
48
+ try:
49
+ return json.load(open(local, encoding='utf-8'))
50
+ except Exception:
51
+ return {}
52
+
53
+
54
+ def walk(node):
55
+ yield node
56
+ for ch in node.get('children') or []:
57
+ yield from walk(ch)
58
+
59
+
60
+ def attrs(n):
61
+ a = n.get('attributes') or {}
62
+ return a.get('type', '?'), a.get('key', '') or '', a.get('text', '') or '', a.get('bounds', '')
63
+
64
+
65
+ def cmd_texts(dev, _a):
66
+ seen, out = set(), []
67
+ for n in walk(dump_layout(dev)):
68
+ t, k, txt, b = attrs(n)
69
+ if txt and txt not in seen:
70
+ seen.add(txt)
71
+ out.append(f'{txt} [{b}]')
72
+ print('\n'.join(out[:80]) or '(无文本)')
73
+
74
+
75
+ def cmd_ids(dev, _a):
76
+ out = []
77
+ for n in walk(dump_layout(dev)):
78
+ t, k, txt, b = attrs(n)
79
+ if k:
80
+ out.append(f'{k} ({t}{"/" + txt if txt else ""}) [{b}]')
81
+ print('\n'.join(out[:120]) or '(无 id)')
82
+
83
+
84
+ def cmd_find(dev, a):
85
+ target = a.target
86
+ for n in walk(dump_layout(dev)):
87
+ t, k, txt, b = attrs(n)
88
+ if k == target or txt == target:
89
+ print(f'type={t} key={k} text={txt} bounds={b} visible={bool(b)}')
90
+ return
91
+ print(f'not found: {target}')
92
+
93
+
94
+ def cmd_tree(dev, a):
95
+ root = dump_layout(dev)
96
+ lines = []
97
+
98
+ def rec(n, depth):
99
+ if depth > a.depth:
100
+ return
101
+ t, k, txt, b = attrs(n)
102
+ tag = t + (f'#{k}' if k else '') + (f' "{txt[:14]}"' if txt else '')
103
+ if (not a.grep or a.grep in k or a.grep in txt or a.grep in t):
104
+ lines.append(' ' * depth + f'{tag} {b}')
105
+ for ch in n.get('children') or []:
106
+ rec(ch, depth + 1)
107
+
108
+ rec(root, 0)
109
+ print('\n'.join(lines[:200]) or '(空树)')
110
+
111
+
112
+ def _bounds_center(b):
113
+ # uitest 布局 bounds 兼容两种格式: '[l,t][r,b]'(标准)与 '[l,t,r,b]'
114
+ nums = [int(x) for x in re.findall(r'-?\d+', str(b))]
115
+ if len(nums) != 4:
116
+ return None
117
+ l, t, r, bt = nums
118
+ return (l + r) // 2, (t + bt) // 2
119
+
120
+
121
+ def cmd_click(dev, a):
122
+ # 1) id 命中 → 取中心坐标(uinput 通道,Toggle 等 hypium touch 失效场景实证可靠)
123
+ for n in walk(dump_layout(dev)):
124
+ t, k, txt, b = attrs(n)
125
+ if k == a.target or txt == a.target:
126
+ c = _bounds_center(b)
127
+ if c:
128
+ print(hdc(dev, ['shell', 'uinput', '-T', '-c', str(c[0]), str(c[1])]).strip() or
129
+ f'clicked {a.target} @ {c}')
130
+ return
131
+ print(f'not found: {a.target}')
132
+
133
+
134
+ def cmd_tap(dev, a):
135
+ print(hdc(dev, ['shell', 'uinput', '-T', '-c', str(a.x), str(a.y)]).strip() or f'tapped {a.x},{a.y}')
136
+
137
+
138
+ def cmd_shot(dev, a):
139
+ out = a.path or 'probe_shot.jpeg'
140
+ try:
141
+ from hypium import UiDriver
142
+ UiDriver.connect(device_sn=dev).screenshot(out)
143
+ print(out)
144
+ return
145
+ except Exception:
146
+ pass
147
+ remote = '/data/local/tmp/_probe_shot.jpeg'
148
+ hdc(dev, ['shell', 'snapshot_display', '-f', remote])
149
+ hdc(dev, ['file', 'recv', remote, out])
150
+ hdc(dev, ['shell', 'rm', '-f', remote])
151
+ print(out)
152
+
153
+
154
+ def cmd_hilog(dev, a):
155
+ print(hdc(dev, ['shell', 'hilog', '-z', str(a.n), '-T', a.tag])[:8000])
156
+
157
+
158
+ def cmd_db_pull(dev, a):
159
+ outdir = 'db_probe'
160
+ os.makedirs(outdir, exist_ok=True)
161
+ listing = hdc(dev, ['shell', f'ls /data/app/el2/*/database/{a.package}/entry/rdb/ 2>/dev/null'])
162
+ got = []
163
+ for fn in [x.strip() for x in listing.splitlines() if x.strip().endswith(('.db', '.db-wal', '.db-shm'))]:
164
+ hdc(dev, ['file', 'recv', f'/data/app/el2/100/database/{a.package}/entry/rdb/{fn}',
165
+ os.path.join(outdir, fn)])
166
+ got.append(fn)
167
+ print(f'pulled {len(got)} files -> {outdir}/: {" ".join(got) or "(空,检查包名)" }')
168
+
169
+
170
+ def cmd_db_sql(_dev, a):
171
+ r = subprocess.run(['sqlite3', a.db, a.sql], capture_output=True, text=True)
172
+ print(r.stdout or r.stderr)
173
+
174
+
175
+ def main():
176
+ ap = argparse.ArgumentParser(add_help=True)
177
+ ap.add_argument('--device', default=os.environ.get('FORGE_DEVICE', '127.0.0.1:5555'))
178
+ sub = ap.add_subparsers(dest='cmd', required=True)
179
+ sub.add_parser('texts')
180
+ sub.add_parser('ids')
181
+ p = sub.add_parser('find'); p.add_argument('target')
182
+ p = sub.add_parser('tree'); p.add_argument('--grep'); p.add_argument('--depth', type=int, default=6)
183
+ p = sub.add_parser('click'); p.add_argument('target')
184
+ p = sub.add_parser('tap'); p.add_argument('x', type=int); p.add_argument('y', type=int)
185
+ p = sub.add_parser('shot'); p.add_argument('path', nargs='?')
186
+ p = sub.add_parser('hilog'); p.add_argument('tag', nargs='?', default='JSAPP'); p.add_argument('n', nargs='?', type=int, default=400)
187
+ p = sub.add_parser('db-pull'); p.add_argument('package')
188
+ p = sub.add_parser('db-sql'); p.add_argument('db'); p.add_argument('sql')
189
+ a = ap.parse_args()
190
+ dev = a.device
191
+ {'texts': cmd_texts, 'ids': cmd_ids, 'find': cmd_find, 'tree': cmd_tree,
192
+ 'click': cmd_click, 'tap': cmd_tap, 'shot': cmd_shot, 'hilog': cmd_hilog,
193
+ 'db-pull': cmd_db_pull, 'db-sql': cmd_db_sql}[a.cmd](dev, a)
194
+
195
+
196
+ if __name__ == '__main__':
197
+ main()