@buaa_smat/hometrans 0.1.15 → 0.1.16
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 +54 -89
- package/agents/build-fixer.md +15 -14
- package/agents/code-reviewer.md +3 -3
- package/agents/logic-coder.md +1 -1
- package/agents/logic-context-builder.md +1 -1
- package/agents/self-tester.md +16 -13
- package/dist/cli/config-store.js +127 -29
- package/dist/cli/config.js +9 -2
- package/dist/cli/init.js +367 -248
- package/dist/cli/uninstall.js +12 -7
- package/dist/context/index.js +19 -2
- package/env-requirements.json +19 -23
- package/package.json +1 -1
- package/resource/choose_editor.png +0 -0
- package/resource/set_environment.png +0 -0
- package/resource/set_multimodel.png +0 -0
- package/resource/set_sdk.png +0 -0
- package/skills/hmos-batch-ui-align/SKILL.md +6 -6
- package/skills/hmos-convert-pipeline/SKILL.md +7 -7
- package/skills/hmos-fix-build-errors/SKILL.md +4 -3
- package/skills/hmos-incremental-ui-align/README.md +9 -12
- package/skills/hmos-incremental-ui-align/SKILL.md +9 -9
- package/skills/hmos-incremental-ui-align/scripts/__pycache__/app_feature_verify.cpython-314.pyc +0 -0
- package/skills/hmos-incremental-ui-align/scripts/app_feature_verify.py +128 -29
- package/skills/hmos-incremental-ui-align/scripts/navigation-capure.md +37 -37
- package/skills/hmos-incremental-ui-align/scripts/page_capture.py +7 -2
- package/skills/hmos-integration-test/README.md +3 -3
- package/skills/hmos-integration-test/SKILL.md +7 -7
- package/skills/hmos-spec-generate/SKILL.md +45 -52
- package/tools/test-tools/autotest/README.md +10 -11
- package/tools/test-tools/autotest/self_test_runner.py +40 -12
- package/resource/common_config.png +0 -0
- package/resource/integration_test_config.png +0 -0
- package/resource/set_env.png +0 -0
- package/resource/ui_align_config.png +0 -0
package/dist/cli/uninstall.js
CHANGED
|
@@ -22,9 +22,13 @@ import { removeEnvVars } from './env-vars.js';
|
|
|
22
22
|
const execFileAsync = promisify(execFile);
|
|
23
23
|
const __filename = fileURLToPath(import.meta.url);
|
|
24
24
|
const __dirname = path.dirname(__filename);
|
|
25
|
-
/**
|
|
26
|
-
|
|
27
|
-
|
|
25
|
+
/**
|
|
26
|
+
* Machine env var names that `ht init` may have set and that have a non-empty
|
|
27
|
+
* value in config.env: the SDK paths / tool dir plus the mirrored unified model
|
|
28
|
+
* fields (HOMETRANS_MODEL_*, kept in sync with autotest.unified_model on load).
|
|
29
|
+
*/
|
|
30
|
+
function plannedEnvVarNames(config) {
|
|
31
|
+
return MACHINE_ENV_KEYS.filter((k) => (config.env[k] ?? '').trim().length > 0);
|
|
28
32
|
}
|
|
29
33
|
/* ------------------------------------------------------------------ */
|
|
30
34
|
/* Bundled content discovery */
|
|
@@ -398,7 +402,8 @@ export async function uninstallCommand() {
|
|
|
398
402
|
console.log(chalk.gray(' Reinstall hometrans or manually remove skill/agent directories from your editors.\n'));
|
|
399
403
|
return;
|
|
400
404
|
}
|
|
401
|
-
const
|
|
405
|
+
const config = await loadHomeTransConfig();
|
|
406
|
+
const { editors } = config;
|
|
402
407
|
const plan = {
|
|
403
408
|
deletions: [],
|
|
404
409
|
modifications: [],
|
|
@@ -409,9 +414,9 @@ export async function uninstallCommand() {
|
|
|
409
414
|
for (const editor of editors) {
|
|
410
415
|
await buildPlanForEditor(editor, skillNames, agentEntries, plan);
|
|
411
416
|
}
|
|
412
|
-
// Env vars to remove come from config.json `env
|
|
413
|
-
// (which holds config.json) is deleted last.
|
|
414
|
-
plan.envVars = plannedEnvVarNames(
|
|
417
|
+
// Env vars to remove come from config.json (`env` + the unified model
|
|
418
|
+
// fields); the ~/.hometrans dir (which holds config.json) is deleted last.
|
|
419
|
+
plan.envVars = plannedEnvVarNames(config);
|
|
415
420
|
const configDir = getConfigDir();
|
|
416
421
|
if (await dirExists(configDir)) {
|
|
417
422
|
plan.configDir = configDir;
|
package/dist/context/index.js
CHANGED
|
@@ -10,8 +10,25 @@ import {
|
|
|
10
10
|
} from "arkanalyzer";
|
|
11
11
|
|
|
12
12
|
// src/cli/config-store.ts
|
|
13
|
+
import { readFileSync } from "node:fs";
|
|
13
14
|
import path from "node:path";
|
|
14
15
|
import os from "node:os";
|
|
16
|
+
function getConfigDir() {
|
|
17
|
+
return path.join(os.homedir(), ".hometrans");
|
|
18
|
+
}
|
|
19
|
+
function getConfigPath() {
|
|
20
|
+
return path.join(getConfigDir(), "config.json");
|
|
21
|
+
}
|
|
22
|
+
function readConfigEnvFieldSync(key) {
|
|
23
|
+
try {
|
|
24
|
+
const raw = readFileSync(getConfigPath(), "utf-8");
|
|
25
|
+
const parsed = JSON.parse(raw);
|
|
26
|
+
const v = parsed?.env?.[key];
|
|
27
|
+
return typeof v === "string" ? v : "";
|
|
28
|
+
} catch {
|
|
29
|
+
return "";
|
|
30
|
+
}
|
|
31
|
+
}
|
|
15
32
|
function expandHome(p) {
|
|
16
33
|
if (!p) return p;
|
|
17
34
|
if (p === "~") return os.homedir();
|
|
@@ -594,10 +611,10 @@ async function extractCommitContext(input) {
|
|
|
594
611
|
const { projectPath, commitId } = input;
|
|
595
612
|
const mode = input.mode ?? "default";
|
|
596
613
|
const ohosSdkPath = expandHome(
|
|
597
|
-
input.ohosSdkPath
|
|
614
|
+
input.ohosSdkPath || process.env.OHOS_SDK_PATH || readConfigEnvFieldSync("OHOS_SDK_PATH")
|
|
598
615
|
);
|
|
599
616
|
const hmsSdkPath = expandHome(
|
|
600
|
-
input.hmsSdkPath
|
|
617
|
+
input.hmsSdkPath || process.env.HMS_SDK_PATH || readConfigEnvFieldSync("HMS_SDK_PATH")
|
|
601
618
|
);
|
|
602
619
|
if (!projectPath) {
|
|
603
620
|
throw new Error("extractCommitContext: projectPath is required");
|
package/env-requirements.json
CHANGED
|
@@ -27,6 +27,7 @@
|
|
|
27
27
|
|
|
28
28
|
"tools": {
|
|
29
29
|
"deveco": {
|
|
30
|
+
"label": "harmony sdk",
|
|
30
31
|
"source": "env-dir",
|
|
31
32
|
"envVar": "DEVECO_SDK_HOME",
|
|
32
33
|
"hint": "set DEVECO_SDK_HOME via `ht init` (DevEco Studio install)"
|
|
@@ -46,20 +47,12 @@
|
|
|
46
47
|
"fallback": { "base": "DEVECO_SDK_HOME", "segments": ["default", "openharmony", "toolchains"], "exe": "hdc" },
|
|
47
48
|
"hint": "ships with DevEco: <sdk>/default/openharmony/toolchains"
|
|
48
49
|
},
|
|
49
|
-
"python": {
|
|
50
|
-
"source": "path",
|
|
51
|
-
"aliases": ["python", "python3", "py"],
|
|
52
|
-
"verify": ["--version"],
|
|
53
|
-
"versionRegex": "(\\d+\\.\\d+(?:\\.\\d+)?)",
|
|
54
|
-
"version": { "min": "3.10.0", "max": null },
|
|
55
|
-
"hint": "install Python >= 3.10 and add to PATH (UI-align capture/parse scripts)"
|
|
56
|
-
},
|
|
57
50
|
"uv": {
|
|
58
51
|
"source": "path",
|
|
59
52
|
"aliases": ["uv"],
|
|
60
53
|
"verify": ["--version"],
|
|
61
54
|
"versionRegex": "(\\d+\\.\\d+(?:\\.\\d+)?)",
|
|
62
|
-
"hint": "https://docs.astral.sh/uv (AutoTest
|
|
55
|
+
"hint": "https://docs.astral.sh/uv — runs every Python script (AutoTest, UI-align, logic) via `uv run` and provides Python ≥ 3.10 on demand (no standalone Python needed)"
|
|
63
56
|
},
|
|
64
57
|
"java": {
|
|
65
58
|
"source": "path",
|
|
@@ -70,12 +63,12 @@
|
|
|
70
63
|
"fallback": { "base": "DEVECO_HOME", "segments": ["jbr", "bin"], "exe": "java" },
|
|
71
64
|
"hint": "any JDK 17+, or reuse DevEco jbr: <deveco>/jbr/bin"
|
|
72
65
|
},
|
|
73
|
-
"
|
|
66
|
+
"homegraph": {
|
|
74
67
|
"source": "path",
|
|
75
|
-
"aliases": ["
|
|
68
|
+
"aliases": ["homegraph"],
|
|
76
69
|
"verify": ["--version"],
|
|
77
70
|
"versionRegex": "(\\d+\\.\\d+(?:\\.\\d+)?)",
|
|
78
|
-
"hint": "npm
|
|
71
|
+
"hint": "npm install -g homegraph arkanalyzer (code-graph backend for spec generation)"
|
|
79
72
|
}
|
|
80
73
|
},
|
|
81
74
|
|
|
@@ -83,7 +76,7 @@
|
|
|
83
76
|
{
|
|
84
77
|
"name": "hmos-spec-generate",
|
|
85
78
|
"kind": "skill",
|
|
86
|
-
"dependencies": [{ "tool": "
|
|
79
|
+
"dependencies": [{ "tool": "homegraph", "level": "required" }]
|
|
87
80
|
},
|
|
88
81
|
{
|
|
89
82
|
"name": "hmos-resources-convert",
|
|
@@ -95,20 +88,21 @@
|
|
|
95
88
|
"name": "hmos-batch-ui-align",
|
|
96
89
|
"kind": "skill",
|
|
97
90
|
"dependencies": [
|
|
98
|
-
{ "tool": "
|
|
91
|
+
{ "tool": "uv", "level": "required" },
|
|
99
92
|
{ "tool": "adb", "level": "optional" }
|
|
100
93
|
],
|
|
101
|
-
"note": "adb only for page exploration"
|
|
94
|
+
"note": "android_parse_fast.py runs via `uv run` (uv provides Python); adb only for page exploration"
|
|
102
95
|
},
|
|
103
96
|
{
|
|
104
97
|
"name": "hmos-incremental-ui-align",
|
|
105
98
|
"kind": "skill",
|
|
106
99
|
"dependencies": [
|
|
107
100
|
{ "tool": "deveco", "level": "required" },
|
|
108
|
-
{ "tool": "
|
|
101
|
+
{ "tool": "uv", "level": "required" },
|
|
109
102
|
{ "tool": "hdc", "level": "required" },
|
|
110
103
|
{ "tool": "adb", "level": "required" }
|
|
111
|
-
]
|
|
104
|
+
],
|
|
105
|
+
"note": "app_feature_verify.py / page_capture.py run via `uv run` (PEP 723 inline deps; uv provides Python)"
|
|
112
106
|
},
|
|
113
107
|
{
|
|
114
108
|
"name": "hmos-integration-test",
|
|
@@ -117,7 +111,8 @@
|
|
|
117
111
|
{ "tool": "deveco", "level": "required" },
|
|
118
112
|
{ "tool": "uv", "level": "required" },
|
|
119
113
|
{ "tool": "hdc", "level": "required" }
|
|
120
|
-
]
|
|
114
|
+
],
|
|
115
|
+
"note": "self_test_runner.py runs via `uv run --no-project python` (uv provides Python) and then `uv sync`s the AutoTest venv"
|
|
121
116
|
},
|
|
122
117
|
{
|
|
123
118
|
"name": "hmos-fix-build-errors",
|
|
@@ -132,22 +127,22 @@
|
|
|
132
127
|
{ "tool": "uv", "level": "optional" },
|
|
133
128
|
{ "tool": "hdc", "level": "optional" }
|
|
134
129
|
],
|
|
135
|
-
"note": "test stage skippable via skip_test"
|
|
130
|
+
"note": "test stage skippable via skip_test; when run it needs uv + hdc"
|
|
136
131
|
},
|
|
137
132
|
{
|
|
138
133
|
"name": "logic-context-builder",
|
|
139
134
|
"kind": "agent",
|
|
140
|
-
"dependencies": [{ "tool": "
|
|
135
|
+
"dependencies": [{ "tool": "uv", "level": "optional" }]
|
|
141
136
|
},
|
|
142
137
|
{
|
|
143
138
|
"name": "logic-coder",
|
|
144
139
|
"kind": "agent",
|
|
145
|
-
"dependencies": [{ "tool": "
|
|
140
|
+
"dependencies": [{ "tool": "uv", "level": "optional" }]
|
|
146
141
|
},
|
|
147
142
|
{
|
|
148
143
|
"name": "spec-generator",
|
|
149
144
|
"kind": "agent",
|
|
150
|
-
"dependencies": [{ "tool": "
|
|
145
|
+
"dependencies": [{ "tool": "homegraph", "level": "required" }]
|
|
151
146
|
},
|
|
152
147
|
{
|
|
153
148
|
"name": "build-fixer",
|
|
@@ -170,7 +165,8 @@
|
|
|
170
165
|
"dependencies": [
|
|
171
166
|
{ "tool": "uv", "level": "required" },
|
|
172
167
|
{ "tool": "hdc", "level": "required" }
|
|
173
|
-
]
|
|
168
|
+
],
|
|
169
|
+
"note": "invokes `uv run --no-project python self_test_runner.py run/status/kill` (uv provides Python; --no-project keeps the interpreter out of the .venv it rebuilds)"
|
|
174
170
|
},
|
|
175
171
|
{
|
|
176
172
|
"name": "self-test-fixer",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@buaa_smat/hometrans",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.16",
|
|
4
4
|
"description": "HomeTrans (Android-to-HarmonyOS) skill + agent installer. Run `ht init` to distribute conversion skills and subagents into AI editors.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -9,13 +9,13 @@ Batch-convert a set of Android page snapshots (each snapshot is a `page_NNNN_Act
|
|
|
9
9
|
|
|
10
10
|
## Step 0 — Environment Variables Check (run first)
|
|
11
11
|
|
|
12
|
-
The final build (Step 6, via `hmos_fix_build_errors`)
|
|
12
|
+
The final build (Step 6, via `hmos_fix_build_errors`) resolves the DevEco location along the standard chain — **OS environment variable first, then `~/.hometrans/config.json`, then ask the user** (read env vars with `echo "$VAR"` on macOS/Linux; `$env:VAR` in PowerShell on Windows):
|
|
13
13
|
|
|
14
|
-
| Variable | Needed for | Valid when |
|
|
15
|
-
|
|
16
|
-
| `DEVECO_HOME` *(or `DEVECO_SDK_HOME`)* | Step 6 unified build | resolves to a valid DevEco install |
|
|
14
|
+
| Variable | config.json fallback | Needed for | Valid when |
|
|
15
|
+
|---|---|---|---|
|
|
16
|
+
| `DEVECO_HOME` *(or `DEVECO_SDK_HOME`)* | `env.DEVECO_HOME` / `env.DEVECO_SDK_HOME` | Step 6 unified build | resolves to a valid DevEco install |
|
|
17
17
|
|
|
18
|
-
If neither
|
|
18
|
+
If neither the env var nor `~/.hometrans/config.json` yields a valid DevEco install, **ask the user** for the DevEco Studio install path before proceeding (suggest running `ht init` to persist it as a machine environment variable).
|
|
19
19
|
|
|
20
20
|
## Step 1 — Parse User Request
|
|
21
21
|
|
|
@@ -50,7 +50,7 @@ This skill batch-converts Android resources (strings, colors, drawables, images,
|
|
|
50
50
|
2. Otherwise, determine the target App package name (`package`). If the user didn't provide it, extract the `package` attribute from `AndroidManifest.xml` in `android_project_dir`; if still unavailable, ask the user.
|
|
51
51
|
3. Execute with Bash:
|
|
52
52
|
```
|
|
53
|
-
python ./scripts/android_parse_fast.py --package <package_name> --output <ui_info_root_absolute_path>
|
|
53
|
+
uv run --no-project python ./scripts/android_parse_fast.py --package <package_name> --output <ui_info_root_absolute_path>
|
|
54
54
|
```
|
|
55
55
|
This script connects to the Android emulator via ADB, BFS-traverses all reachable pages in the App, and for each page saves `screenshot.png`, `view.xml`, `meta.json` into `page_NNNN_ActivityName/` subdirectories. It also generates `index.json` and `report.html` under `ui_info_root`.
|
|
56
56
|
4. After execution, confirm that `index.json` has been generated under `ui_info_root`. If not, report an error and abort.
|
|
@@ -47,15 +47,15 @@ Define shorthand variables for the instructions below:
|
|
|
47
47
|
|
|
48
48
|
## Environment Variables Check (run before Stage 1)
|
|
49
49
|
|
|
50
|
-
The pipeline's sub-agents
|
|
50
|
+
The pipeline's sub-agents resolve each setting along the standard chain — **OS environment variable first, then `~/.hometrans/config.json`, then ask the user**. Verify upfront so the long pipeline fails fast (read env vars with `echo "$VAR"` on macOS/Linux; `$env:VAR` in PowerShell on Windows):
|
|
51
51
|
|
|
52
|
-
| Variable | Needed for | Valid when |
|
|
53
|
-
|
|
54
|
-
| `DEVECO_HOME` *(or `DEVECO_SDK_HOME`)* | Stage 2 build + Stage 3a/4b rebuilds | resolves to a valid DevEco install |
|
|
55
|
-
| `TEST_API_KEY` | Stage 4 self-test (only when `SKIP_TEST=false`) | non-empty |
|
|
56
|
-
| `HOMETRANS_TOOL_PATH` | Stage 4 self-test AutoTest dir
|
|
52
|
+
| Variable | config.json fallback | Needed for | Valid when |
|
|
53
|
+
|---|---|---|---|
|
|
54
|
+
| `DEVECO_HOME` *(or `DEVECO_SDK_HOME`)* | `env.DEVECO_HOME` / `env.DEVECO_SDK_HOME` | Stage 2 build + Stage 3a/4b rebuilds | resolves to a valid DevEco install |
|
|
55
|
+
| `HOMETRANS_MODEL_API_KEY` *(legacy `TEST_API_KEY` still honored as a fallback)* | `autotest.unified_model.api_key` | Stage 4 self-test (only when `SKIP_TEST=false`) | non-empty / not the placeholder |
|
|
56
|
+
| `HOMETRANS_TOOL_PATH` | `env.HOMETRANS_TOOL_PATH` (defaults to `~/.hometrans/tools`) | Stage 4 self-test AutoTest dir | path exists on disk |
|
|
57
57
|
|
|
58
|
-
|
|
58
|
+
For each value, if the env var is unset, fall back to the listed `~/.hometrans/config.json` field. If neither yields a value: for the DevEco path / model api_key, **ask the user** (for the api_key they may instead pass `skip_test=true`). Suggest running `ht init` to persist these as machine environment variables.
|
|
59
59
|
|
|
60
60
|
---
|
|
61
61
|
|
|
@@ -21,10 +21,11 @@ Automatically build a HarmonyOS NEXT project from the command line, parse compil
|
|
|
21
21
|
|
|
22
22
|
1. **Verify project exists** — Check that `$ARGUMENTS[0]` contains a valid HarmonyOS project (look for `build-profile.json5`, `entry/src` directory, `oh-package.json5`).
|
|
23
23
|
|
|
24
|
-
2. **Resolve `<deveco-path>`** —
|
|
24
|
+
2. **Resolve `<deveco-path>`** — follow the standard config chain, stopping at the first source that yields a valid path:
|
|
25
25
|
1. `$ARGUMENTS[1]` if provided (and not `--signed`).
|
|
26
|
-
2.
|
|
27
|
-
3.
|
|
26
|
+
2. **OS environment variables** (`echo "$VAR"` on macOS/Linux; `$env:VAR` in PowerShell on Windows): `DEVECO_HOME` → parent of `DEVECO_SDK_HOME` → strip the trailing `/sdk/default/openharmony/ets` from `OHOS_SDK_PATH` (legacy).
|
|
27
|
+
3. **`~/.hometrans/config.json`** — read `env.DEVECO_HOME`, else parent of `env.DEVECO_SDK_HOME` (written by `ht init`).
|
|
28
|
+
4. **Ask the user** for the DevEco Studio install path (suggest running `ht init` to persist it as an environment variable).
|
|
28
29
|
|
|
29
30
|
**Verify the resolved `<deveco-path>`** contains:
|
|
30
31
|
- `tools/node/node.exe`
|
|
@@ -13,7 +13,7 @@ HarmonyOS-Android UI 自动对齐流水线。
|
|
|
13
13
|
每多一个页面/弹窗/tab,上述步骤都要重复一遍,非常费时。
|
|
14
14
|
|
|
15
15
|
本 skill 做了三件事:
|
|
16
|
-
-
|
|
16
|
+
- 用 phone-agent 自动**寻路**(根据自然语言点到指定页面,模型复用 `~/.hometrans/config.json` 统一配置)
|
|
17
17
|
- 自动**采集** view tree + 截图(安卓走 adb、鸿蒙走 hdc)
|
|
18
18
|
- 自动**对比 + 改码**,并按 MVVM 模式把 mock 数据放到 Model 层
|
|
19
19
|
|
|
@@ -31,12 +31,10 @@ HarmonyOS-Android UI 自动对齐流水线。
|
|
|
31
31
|
- 两台设备建议都保持亮屏解锁
|
|
32
32
|
|
|
33
33
|
2. **Python 依赖**
|
|
34
|
-
|
|
35
|
-
pip install openpyxl phone-agent
|
|
36
|
-
```
|
|
34
|
+
无需手动安装。脚本通过 `uv run` 调用,`openpyxl` / `phone-agent` 已在 `app_feature_verify.py` 顶部的 PEP 723 内联元数据里声明,uv 首次运行时自动解析安装(走清华镜像)。只需确保已安装 `uv`(`uv --version`)。
|
|
37
35
|
|
|
38
|
-
3.
|
|
39
|
-
|
|
36
|
+
3. **模型配置**(phone-agent 导航用)
|
|
37
|
+
与自测共用同一个多模态模型:`ht init` 把模型四要素(api_key / name / base_url / provider)写入 `~/.hometrans/config.json` 的 `autotest.unified_model`,并导出为 `HOMETRANS_MODEL_*` 环境变量。脚本优先读环境变量,缺失时回落 config.json;都没有时兼容老版本的 `GLM_API_KEY`(走智谱 GLM 端点)。无需单独设置 API Key。
|
|
40
38
|
|
|
41
39
|
4. **鸿蒙 SDK 路径**(给改码阶段查 API 用)
|
|
42
40
|
取自 OS 环境变量 `OHOS_SDK_PATH` / `HMS_SDK_PATH`(为空时由 `DEVECO_SDK_HOME` 派生),由 `ht init` 写入机器环境变量;例如 DevEco Studio 自带的 `E:\DevEco Studio\sdk\default\openharmony\ets`。
|
|
@@ -55,9 +53,9 @@ HarmonyOS-Android UI 自动对齐流水线。
|
|
|
55
53
|
| `harmony_project_dir` | 是 | 鸿蒙工程根目录(会被直接修改) |
|
|
56
54
|
| `capture_output_dir` | 否 | 采集产物输出目录(截图/view tree/分析报告)。默认 `<harmony_project_dir>/.hometrans/capture_output` |
|
|
57
55
|
|
|
58
|
-
**②
|
|
56
|
+
**② 全局配置**(统一链路「环境变量 → `~/.hometrans/config.json` → 询问用户」,由 `ht init` 同时写入机器环境变量与 config.json,无需每次传):
|
|
59
57
|
|
|
60
|
-
-
|
|
58
|
+
- 模型配置 ← `HOMETRANS_MODEL_*` 环境变量(缺失时回落 `~/.hometrans/config.json` 的 `autotest.unified_model`;与自测共用同一个模型)
|
|
61
59
|
- 鸿蒙 SDK ETS API 目录 ← 环境变量 `OHOS_SDK_PATH` / `HMS_SDK_PATH`(为空时由 `DEVECO_SDK_HOME` 派生为 `<DEVECO_SDK_HOME>/default/openharmony/ets`、`.../hms/ets`)
|
|
62
60
|
|
|
63
61
|
> **Step 0.0** 会在执行前检查这些环境变量是否存在;缺失时会要求用户输入。
|
|
@@ -120,7 +118,7 @@ HarmonyOS-Android UI 自动对齐流水线。
|
|
|
120
118
|
skill 会严格按 `SKILL.md` 里的 5 步流水线跑:
|
|
121
119
|
|
|
122
120
|
### Step 0 · 解析入参
|
|
123
|
-
读取调用入参 `android_project_dir` / `harmony_project_dir` / `capture_output_dir
|
|
121
|
+
读取调用入参 `android_project_dir` / `harmony_project_dir` / `capture_output_dir`,并按统一链路「环境变量 → config.json → 询问」解析 `HOMETRANS_MODEL_*`(统一模型,回落 `~/.hometrans/config.json` 的 `autotest.unified_model`)与 `OHOS_SDK_PATH` / `HMS_SDK_PATH`(回落 config.json 的 `env.*`;**Step 0.0** 会先做存在性检查,两层都缺失才要求用户输入)。随后 **Step 0.1** 按两个工程目录自动解析两端的 App 显示名与包名(无需手动配置)。
|
|
124
122
|
|
|
125
123
|
### Step 1 · 双端页面采集
|
|
126
124
|
1.1 解析你的需求,拆成一组「基础页」,每个基础页有 `android_nav_path` 和 `hmos_nav_path`
|
|
@@ -193,16 +191,15 @@ Agents/hmos-ui-align/
|
|
|
193
191
|
```powershell
|
|
194
192
|
# 安卓寻路
|
|
195
193
|
$env:PYTHONIOENCODING="utf-8"
|
|
196
|
-
|
|
194
|
+
uv run Agents/hmos-ui-align/scripts/app_feature_verify.py `
|
|
197
195
|
--device adb `
|
|
198
196
|
--app "Salt Player" `
|
|
199
197
|
--package "com.salt.music" `
|
|
200
198
|
--prompt "进入首页-点击AI智能填报-点击专业筛选按钮" `
|
|
201
|
-
--api-key "$env:GLM_API_KEY" `
|
|
202
199
|
--max-steps 15
|
|
203
200
|
|
|
204
201
|
# 安卓采集
|
|
205
|
-
|
|
202
|
+
uv run Agents/hmos-ui-align/scripts/page_capture.py --device adb -o ./tmp/android_page_1_xxx
|
|
206
203
|
|
|
207
204
|
# 鸿蒙同理,把 --device adb 换成 --device hdc
|
|
208
205
|
```
|
|
@@ -18,7 +18,7 @@ You are writing ArkTS codes.
|
|
|
18
18
|
|
|
19
19
|
## Step 0: Resolve Inputs
|
|
20
20
|
|
|
21
|
-
This skill takes three path inputs from the invocation and
|
|
21
|
+
This skill takes three path inputs from the invocation and resolves its other settings along the standard chain — **OS environment variable → `~/.hometrans/config.json` → ask the user** (see Step 0.0).
|
|
22
22
|
|
|
23
23
|
### Skill input arguments (provided by the user when invoking the skill)
|
|
24
24
|
|
|
@@ -32,17 +32,17 @@ The user may pass them as named args (e.g. `android_project_dir: D:\path\to\andr
|
|
|
32
32
|
|
|
33
33
|
### Step 0.0: Environment Variables Check (run first)
|
|
34
34
|
|
|
35
|
-
|
|
35
|
+
Resolve each value along the standard chain — **OS environment variable first, then `~/.hometrans/config.json`, then ask the user** (read env vars with `echo "$VAR"` on macOS/Linux; `$env:VAR` in PowerShell / `echo %VAR%` in cmd on Windows).
|
|
36
36
|
|
|
37
|
-
| Variable | Purpose | Valid when |
|
|
38
|
-
|
|
39
|
-
| `
|
|
40
|
-
| `OHOS_SDK_PATH` | HarmonyOS SDK ETS API reference (Step 3) | path exists on disk |
|
|
41
|
-
| `HMS_SDK_PATH` | HMS SDK ETS API reference (Step 3) | path exists on disk |
|
|
37
|
+
| Variable | config.json fallback | Purpose | Valid when |
|
|
38
|
+
|---|---|---|---|
|
|
39
|
+
| `HOMETRANS_MODEL_*` (`HOMETRANS_MODEL_API_KEY` / `HOMETRANS_MODEL_NAME` / `HOMETRANS_MODEL_BASE_URL`) | `autotest.unified_model.{api_key,name,base_url}`(provider 默认 openai,不导出环境变量;再兜底老版本 `GLM_API_KEY` → 智谱 GLM 端点) | 统一多模态模型(UI 对齐 + 自测共用),由 `ht init` 导出 | `HOMETRANS_MODEL_API_KEY` 非空(或 config.json 已配置,或老版本 `GLM_API_KEY` 已设置) |
|
|
40
|
+
| `OHOS_SDK_PATH` | `env.OHOS_SDK_PATH` | HarmonyOS SDK ETS API reference (Step 3) | path exists on disk |
|
|
41
|
+
| `HMS_SDK_PATH` | `env.HMS_SDK_PATH` | HMS SDK ETS API reference (Step 3) | path exists on disk |
|
|
42
42
|
|
|
43
|
-
For `OHOS_SDK_PATH` / `HMS_SDK_PATH`, if
|
|
43
|
+
The model config is resolved automatically by `scripts/app_feature_verify.py` (env → config.json), so you don't read it by hand — just confirm `HOMETRANS_MODEL_API_KEY` or `autotest.unified_model.api_key` is set. For `OHOS_SDK_PATH` / `HMS_SDK_PATH`, if both the env var and `~/.hometrans/config.json` are empty but `DEVECO_SDK_HOME` is set (env or `env.DEVECO_SDK_HOME` in config.json), derive them as `<DEVECO_SDK_HOME>/default/openharmony/ets` and `<DEVECO_SDK_HOME>/default/hms/ets`.
|
|
44
44
|
|
|
45
|
-
For each
|
|
45
|
+
For each value that is still missing/empty (or a path that does not exist) after both the env var and `~/.hometrans/config.json`, **stop and ask the user to provide a value**, then use what they give for this run. Suggest running `ht init` to persist them as machine environment variables for next time.
|
|
46
46
|
|
|
47
47
|
### Step 0.1: Auto-derive App Names & Package Names
|
|
48
48
|
|
package/skills/hmos-incremental-ui-align/scripts/__pycache__/app_feature_verify.cpython-314.pyc
ADDED
|
Binary file
|
|
@@ -1,22 +1,32 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
2
|
# -*- coding: utf-8 -*-
|
|
3
|
+
# /// script
|
|
4
|
+
# requires-python = ">=3.12"
|
|
5
|
+
# dependencies = [
|
|
6
|
+
# "openpyxl",
|
|
7
|
+
# "phone-agent",
|
|
8
|
+
# ]
|
|
9
|
+
# [[tool.uv.index]]
|
|
10
|
+
# url = "https://pypi.tuna.tsinghua.edu.cn/simple"
|
|
11
|
+
# ///
|
|
3
12
|
"""
|
|
4
13
|
应用页面导航工具 —— 使用 PhoneAgent 按指定路径操作设备,导航到目标页面。
|
|
5
14
|
|
|
6
15
|
支持 Android (ADB) 和 HarmonyOS (HDC) 两种设备类型。
|
|
7
16
|
|
|
8
|
-
依赖:
|
|
17
|
+
依赖: openpyxl、phone-agent —— 已在文件顶部 PEP 723 元数据声明,`uv run` 自动安装(无需手动 pip install)
|
|
9
18
|
运行前需确保: 设备已连接 (HDC/ADB)、模型 API 可用
|
|
10
19
|
|
|
11
20
|
用法:
|
|
12
21
|
# 自由 prompt 导航
|
|
13
|
-
|
|
22
|
+
uv run app_feature_verify.py --device adb --app Gallery --package com.example.gallery --prompt "打开相册,进入设置页面"
|
|
14
23
|
|
|
15
24
|
# 按功能路径导航
|
|
16
|
-
|
|
25
|
+
uv run app_feature_verify.py --app 简单图库 --package com.simplemobiletools.gallery.pro --task "L1相册详情L2更多L3筛选媒体文件"
|
|
17
26
|
"""
|
|
18
27
|
|
|
19
28
|
import argparse
|
|
29
|
+
import json
|
|
20
30
|
import re
|
|
21
31
|
import sys
|
|
22
32
|
import os
|
|
@@ -267,25 +277,62 @@ def _apply_phone_agent_patches():
|
|
|
267
277
|
_swipe._patched = True
|
|
268
278
|
_hdc.swipe = _swipe
|
|
269
279
|
|
|
270
|
-
# Patch 2:
|
|
280
|
+
# Patch 2: _parse_response — strip <think>/<answer> tags before splitting by do()/finish()
|
|
281
|
+
import phone_agent.model.client as _client
|
|
282
|
+
if not getattr(_client.ModelClient._parse_response, '_patched', False):
|
|
283
|
+
_orig_parse_response = _client.ModelClient._parse_response
|
|
284
|
+
|
|
285
|
+
def _patched_parse_response(self, content: str):
|
|
286
|
+
# Strip <think>...</think> and <answer>...</answer> wrappers first
|
|
287
|
+
cleaned = re.sub(r'<think>.*?</think>\s*', '', content, flags=re.DOTALL)
|
|
288
|
+
m = re.search(r'<answer>\s*(.*?)\s*</answer>', cleaned, re.DOTALL)
|
|
289
|
+
if m:
|
|
290
|
+
cleaned = m.group(1)
|
|
291
|
+
# Also strip trailing </answer> if only opening was consumed by rule 2
|
|
292
|
+
cleaned = cleaned.replace('</answer>', '').strip()
|
|
293
|
+
if cleaned:
|
|
294
|
+
return _orig_parse_response(self, cleaned)
|
|
295
|
+
return _orig_parse_response(self, content)
|
|
296
|
+
|
|
297
|
+
_patched_parse_response._patched = True
|
|
298
|
+
_client.ModelClient._parse_response = _patched_parse_response
|
|
299
|
+
|
|
300
|
+
# Patch 3: parse_action — strip thinking tags + regex fallback for malformed do()
|
|
271
301
|
import phone_agent.actions.handler as _handler
|
|
272
302
|
if not getattr(_handler.parse_action, '_patched', False):
|
|
273
303
|
_orig_parse = _handler.parse_action
|
|
304
|
+
_THINK_RE = re.compile(r'<think>.*?</think>\s*', re.DOTALL)
|
|
305
|
+
_ANSWER_RE = re.compile(r'<answer>\s*(.*?)\s*</answer>', re.DOTALL)
|
|
306
|
+
|
|
307
|
+
def _strip_llm_tags(text: str) -> str:
|
|
308
|
+
text = _THINK_RE.sub('', text)
|
|
309
|
+
m = _ANSWER_RE.search(text)
|
|
310
|
+
if m:
|
|
311
|
+
text = m.group(1)
|
|
312
|
+
return text.strip()
|
|
313
|
+
|
|
274
314
|
def _parse_action(response):
|
|
315
|
+
cleaned = _strip_llm_tags(response)
|
|
275
316
|
try:
|
|
276
|
-
return _orig_parse(
|
|
277
|
-
except ValueError
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
)
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
317
|
+
return _orig_parse(cleaned)
|
|
318
|
+
except ValueError:
|
|
319
|
+
pass
|
|
320
|
+
# regex fallback for do() with unescaped quotes
|
|
321
|
+
for text in (cleaned, response.strip()):
|
|
322
|
+
m = re.search(
|
|
323
|
+
r'do\s*\(\s*action\s*=\s*"([^"]*)"\s*,\s*message\s*="(.*)"\s*\)\s*$',
|
|
324
|
+
text, re.DOTALL,
|
|
325
|
+
)
|
|
326
|
+
if m:
|
|
327
|
+
msg = m.group(2)
|
|
328
|
+
msg = msg.replace("\\n", "\x0a").replace("\\t", "\x09").replace("\\r", "\x0d")
|
|
329
|
+
msg = msg.replace('\\"', '"')
|
|
330
|
+
return {"_metadata": "do", "action": m.group(1), "message": msg}
|
|
331
|
+
# fallback: extract bare do() from anywhere in the text
|
|
332
|
+
m = re.search(r'do\s*\(\s*action\s*=\s*"([^"]*)".*?\)', cleaned)
|
|
333
|
+
if m:
|
|
334
|
+
return _orig_parse(m.group(0))
|
|
335
|
+
return _orig_parse(response)
|
|
289
336
|
_parse_action._patched = True
|
|
290
337
|
_handler.parse_action = _parse_action
|
|
291
338
|
|
|
@@ -374,6 +421,63 @@ def navigate(
|
|
|
374
421
|
agent.reset()
|
|
375
422
|
|
|
376
423
|
|
|
424
|
+
# ---------------------------------------------------------------------------
|
|
425
|
+
# 模型配置加载:UI 对齐与自测共用同一个多模态模型。
|
|
426
|
+
# 优先级:CLI --api-key > HOMETRANS_MODEL_* 环境变量 > ~/.hometrans/config.json
|
|
427
|
+
# 的 autotest.unified_model(由 `ht init` 写入,两端共用)> 老版本 GLM_API_KEY(兼容)。
|
|
428
|
+
# ---------------------------------------------------------------------------
|
|
429
|
+
|
|
430
|
+
def _load_model_config(cli_api_key: str = ""):
|
|
431
|
+
"""加载统一多模态模型配置(UI 对齐 + 自测共用)。
|
|
432
|
+
|
|
433
|
+
`ht init` 把模型四要素(api_key / name / base_url / provider)写入
|
|
434
|
+
`autotest.unified_model`,并导出为 HOMETRANS_MODEL_* 环境变量。这里优先读
|
|
435
|
+
环境变量,缺字段时回落到 config.json,单个字段可被 CLI/环境变量覆盖;
|
|
436
|
+
都没有时再兼容老版本的 GLM_API_KEY(走智谱 GLM 端点)。
|
|
437
|
+
"""
|
|
438
|
+
from phone_agent.model import ModelConfig
|
|
439
|
+
|
|
440
|
+
# 1) 环境变量(ht init 导出)+ CLI 覆盖
|
|
441
|
+
api_key = (cli_api_key or os.environ.get("HOMETRANS_MODEL_API_KEY", "")).strip()
|
|
442
|
+
base_url = os.environ.get("HOMETRANS_MODEL_BASE_URL", "").strip()
|
|
443
|
+
model_name = os.environ.get("HOMETRANS_MODEL_NAME", "").strip()
|
|
444
|
+
temperature = 0.1
|
|
445
|
+
|
|
446
|
+
# 2) 回落 ~/.hometrans/config.json 的 autotest.unified_model(补齐缺失字段)
|
|
447
|
+
config_path = Path.home() / ".hometrans" / "config.json"
|
|
448
|
+
if config_path.exists():
|
|
449
|
+
try:
|
|
450
|
+
cfg = json.loads(config_path.read_text(encoding="utf-8"))
|
|
451
|
+
um = cfg.get("autotest", {}).get("unified_model", {})
|
|
452
|
+
api_key = api_key or str(um.get("api_key", "")).strip()
|
|
453
|
+
base_url = base_url or str(um.get("base_url", "")).strip()
|
|
454
|
+
model_name = model_name or str(um.get("name", "")).strip()
|
|
455
|
+
if um.get("temperature") is not None:
|
|
456
|
+
temperature = um.get("temperature")
|
|
457
|
+
except (json.JSONDecodeError, KeyError):
|
|
458
|
+
pass
|
|
459
|
+
|
|
460
|
+
if api_key:
|
|
461
|
+
return ModelConfig(
|
|
462
|
+
base_url=base_url or "http://localhost:8000/v1",
|
|
463
|
+
api_key=api_key,
|
|
464
|
+
model_name=model_name,
|
|
465
|
+
temperature=temperature,
|
|
466
|
+
)
|
|
467
|
+
|
|
468
|
+
# 3) 向后兼容老版本:仅设置了 GLM_API_KEY 时,回落到智谱 GLM 端点。
|
|
469
|
+
glm_key = os.environ.get("GLM_API_KEY", "").strip()
|
|
470
|
+
if glm_key:
|
|
471
|
+
return ModelConfig(
|
|
472
|
+
base_url="https://open.bigmodel.cn/api/paas/v4",
|
|
473
|
+
api_key=glm_key,
|
|
474
|
+
model_name="autoglm-phone",
|
|
475
|
+
temperature=0.1,
|
|
476
|
+
)
|
|
477
|
+
|
|
478
|
+
return ModelConfig(temperature=temperature)
|
|
479
|
+
|
|
480
|
+
|
|
377
481
|
# ---------------------------------------------------------------------------
|
|
378
482
|
# CLI 入口
|
|
379
483
|
# ---------------------------------------------------------------------------
|
|
@@ -395,7 +499,7 @@ def main():
|
|
|
395
499
|
parser.add_argument("--limit", type=int, default=None, help="仅导航前 N 条")
|
|
396
500
|
parser.add_argument("--offset", type=int, default=1, help="从第几条开始(1-based)")
|
|
397
501
|
parser.add_argument("--sheet", type=int, default=0, help="Excel 工作表索引")
|
|
398
|
-
parser.add_argument("--api-key", type=str, default="", help="
|
|
502
|
+
parser.add_argument("--api-key", type=str, default="", help="API Key(可选,不传则读取 HOMETRANS_MODEL_* 环境变量 / ~/.hometrans/config.json)")
|
|
399
503
|
parser.add_argument("--max-steps", type=int, default=30, help="Agent 最大步数")
|
|
400
504
|
parser.add_argument("--app-hints", type=str, default=None, help="应用上下文提示")
|
|
401
505
|
parser.add_argument("-q", "--quiet", action="store_true", help="减少输出")
|
|
@@ -404,19 +508,14 @@ def main():
|
|
|
404
508
|
if not args.prompt and not args.task and not args.feature:
|
|
405
509
|
parser.error("必须指定 --feature、--task 或 --prompt 之一")
|
|
406
510
|
|
|
407
|
-
api_key = args.api_key or os.environ.get("GLM_API_KEY", "")
|
|
408
|
-
if not api_key and not args.quiet:
|
|
409
|
-
print("提示: 未设置 API Key,请通过 --api-key 或环境变量 GLM_API_KEY 配置")
|
|
410
|
-
print("继续运行可能因鉴权失败而报错\n")
|
|
411
|
-
|
|
412
511
|
from phone_agent.model import ModelConfig
|
|
413
512
|
|
|
414
|
-
model_config =
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
513
|
+
model_config = _load_model_config(args.api_key)
|
|
514
|
+
if not model_config.api_key or model_config.api_key == "EMPTY":
|
|
515
|
+
if not args.quiet:
|
|
516
|
+
print("提示: 未找到可用的模型配置。请运行 `ht init` 配置统一模型(UI 对齐 + 自测共用),")
|
|
517
|
+
print("它会写入 ~/.hometrans/config.json 的 autotest.unified_model 并导出 HOMETRANS_MODEL_* 环境变量;")
|
|
518
|
+
print("或通过 --api-key 传入 API Key。\n")
|
|
420
519
|
|
|
421
520
|
device_label = "ADB (Android)" if args.device == "adb" else "HDC (HarmonyOS)"
|
|
422
521
|
print(f"设备: {device_label}, 应用: {args.app} ({args.package})")
|