@buaa_smat/hometrans 0.1.3 → 0.1.5

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
@@ -45,13 +45,15 @@ Requires Node.js **>= 18**.
45
45
  - `OHOS_SDK_PATH` — OpenHarmony SDK ETS path
46
46
  - `HMS_SDK_PATH` — HMS SDK ETS path
47
47
  - `TEST_API_KEY` — API key written into the autotest `config.yaml`
48
- 3. **Seeds/refreshes the autotest config**: if `agents/test-tools/autotest/config.yaml` is missing it is created from `config.yaml.example`, and every `api_key:` line is filled with the supplied `TEST_API_KEY`.
49
- 4. For every selected editor, **copies**:
48
+ These are stored under a single `env` object in `~/.hometrans/config.json` (alongside `TOOL_PATH`, set in the next step).
49
+ 3. **Installs bundled tools** to `~/.hometrans/tools` (the AutoTest toolchain lives at `~/.hometrans/tools/test-tools/autotest`) and records the destination as `env.TOOL_PATH` in `~/.hometrans/config.json`. Agents/skills resolve the AutoTest dir from `env.TOOL_PATH`.
50
+ 4. **Seeds/refreshes the autotest config**: if `<TOOL_PATH>/test-tools/autotest/config.yaml` is missing it is created from `config.yaml.example`, and every `api_key:` line is filled with the supplied `TEST_API_KEY`.
51
+ 5. For every selected editor, **copies**:
50
52
  - every skill folder under `skills/` (those containing a `SKILL.md`) → `<editor>/skills/<skill-name>/` (recursive — preserves subdirectory structure)
51
- - every agent under `agents/` → `<editor>/agents/` (recursive — preserves `<agent>/scripts/` and `test-tools/` subtrees)
52
- 5. **Registers the MCP server** (auto-adapts to editor config format: jsonc-object / jsonc-command-array / codex-cli / toml-section).
53
- 6. Editors whose marker directory is missing are reported as **`skipped`** — nothing is written for them.
54
- 7. The install is **idempotent** — re-running overwrites bundled content only. It never touches files under `<editor>/skills/` or `<editor>/agents/` that don't share a name with a bundled item.
53
+ - every agent under `agents/` → `<editor>/agents/` (recursive — preserves `<agent>/scripts/` subtrees)
54
+ 6. **Registers the MCP server** (auto-adapts to editor config format: jsonc-object / jsonc-command-array / codex-cli / toml-section).
55
+ 7. Editors whose marker directory is missing are reported as **`skipped`** — nothing is written for them.
56
+ 8. The install is **idempotent** — re-running overwrites bundled content only. It never touches files under `<editor>/skills/` or `<editor>/agents/` that don't share a name with a bundled item.
55
57
 
56
58
  ### Target directories
57
59
 
@@ -36,15 +36,16 @@ Read the config file written by `ht init` at `~/.hometrans/config.json` (`$HOME/
36
36
  ```jsonc
37
37
  {
38
38
  "editors": [ /* ... */ ],
39
- "params": {
39
+ "env": {
40
40
  "OHOS_SDK_PATH": "...", // OpenHarmony SDK ETS path, e.g. "D:/DevEco Studio/sdk/default/openharmony/ets"
41
41
  "HMS_SDK_PATH": "...", // HMS SDK ETS path, e.g. "D:/DevEco Studio/sdk/default/hms/ets"
42
- "TEST_API_KEY": "..."
42
+ "TEST_API_KEY": "...",
43
+ "TOOL_PATH": "..." // HomeTrans tools dir, e.g. "C:/Users/<you>/.hometrans/tools"
43
44
  }
44
45
  }
45
46
  ```
46
47
 
47
- Read `params.OHOS_SDK_PATH` from this file.
48
+ Read `env.OHOS_SDK_PATH` from this file.
48
49
 
49
50
  ### 0b. Derive the DevEco Studio root and tool paths
50
51
 
@@ -58,11 +59,11 @@ From `<deveco>`, derive:
58
59
  - **ohpm**: `<deveco>/tools/ohpm/bin/ohpm`
59
60
  - **SDK directory**: `<deveco>/sdk`
60
61
 
61
- Verify these files exist. If they do, use them and skip auto-detection. (Fall back to `params.HMS_SDK_PATH` — stripping `/sdk/default/hms/ets` — if `OHOS_SDK_PATH` is absent but `HMS_SDK_PATH` is present.)
62
+ Verify these files exist. If they do, use them and skip auto-detection. (Fall back to `env.HMS_SDK_PATH` — stripping `/sdk/default/hms/ets` — if `OHOS_SDK_PATH` is absent but `HMS_SDK_PATH` is present.)
62
63
 
63
64
  ### 0c. Auto-detect if config is missing or invalid
64
65
 
65
- If `~/.hometrans/config.json` is missing, has empty `params.OHOS_SDK_PATH`/`HMS_SDK_PATH`, or the derived paths don't point to real files, auto-detect:
66
+ If `~/.hometrans/config.json` is missing, has empty `env.OHOS_SDK_PATH`/`HMS_SDK_PATH`, or the derived paths don't point to real files, auto-detect:
66
67
 
67
68
  1. **Find DevEco Studio installation**:
68
69
  - Search common locations: `D:\DevEco Studio`, `C:\DevEco Studio`, `C:\Program Files\DevEco Studio`
@@ -41,7 +41,7 @@ If `commit-id` is provided and is not `none`:
41
41
  - `projectPath`: `<harmonyos-project-path>`
42
42
  - `commitId`: `<commit-id>`
43
43
  - `mode`: `"default"`
44
- - `ohosSdkPath` / `hmsSdkPath`: omit to let the tool read `params.OHOS_SDK_PATH` / `params.HMS_SDK_PATH` from the `ht init` config (`~/.hometrans/config.json`), falling back to the `OHOS_SDK_PATH` / `HMS_SDK_PATH` env vars
44
+ - `ohosSdkPath` / `hmsSdkPath`: omit to let the tool read `env.OHOS_SDK_PATH` / `env.HMS_SDK_PATH` from the `ht init` config (`~/.hometrans/config.json`), falling back to the `OHOS_SDK_PATH` / `HMS_SDK_PATH` env vars
45
45
 
46
46
  2. **On failure**, fall back to:
47
47
  ```bash
@@ -231,14 +231,14 @@ Classify: **CONFIRMED** (fix) | **FALSE_POSITIVE** (skip, record reason) | **UNC
231
231
 
232
232
  ### Step 3.0: Resolve Build Environment
233
233
 
234
- 1. Read the `ht init` config at `~/.hometrans/config.json` (Windows: `%USERPROFILE%\.hometrans\config.json`). Take `params.OHOS_SDK_PATH` (e.g. `D:/DevEco Studio/sdk/default/openharmony/ets`) and strip the trailing `/sdk/default/openharmony/ets` to get the DevEco Studio root `<deveco>`. Fall back to `params.HMS_SDK_PATH` (strip `/sdk/default/hms/ets`) if needed.
234
+ 1. Read the `ht init` config at `~/.hometrans/config.json` (Windows: `%USERPROFILE%\.hometrans\config.json`). Take `env.OHOS_SDK_PATH` (e.g. `D:/DevEco Studio/sdk/default/openharmony/ets`) and strip the trailing `/sdk/default/openharmony/ets` to get the DevEco Studio root `<deveco>`. Fall back to `env.HMS_SDK_PATH` (strip `/sdk/default/hms/ets`) if needed.
235
235
 
236
236
  2. Derive tool paths:
237
237
  - `NODE_EXE`: `<deveco>/tools/node/node.exe`
238
238
  - `HVIGORW_JS`: `<deveco>/tools/hvigor/bin/hvigorw.js`
239
239
  - `OHPM`: `<deveco>/tools/ohpm/bin/ohpm`
240
240
 
241
- 3. If the config is missing or `params.OHOS_SDK_PATH`/`HMS_SDK_PATH` are empty, auto-detect: search `D:\DevEco Studio`, `C:\DevEco Studio`, `C:\Program Files\DevEco Studio` for `tools/hvigor/bin/hvigorw.js`.
241
+ 3. If the config is missing or `env.OHOS_SDK_PATH`/`HMS_SDK_PATH` are empty, auto-detect: search `D:\DevEco Studio`, `C:\DevEco Studio`, `C:\Program Files\DevEco Studio` for `tools/hvigor/bin/hvigorw.js`.
242
242
 
243
243
  4. If detection fails, stop and report clearly what is missing so the user can run `ht init` to configure it.
244
244
 
@@ -38,25 +38,36 @@ You are a self-tester for HarmonyOS applications. You run a single pipeline: par
38
38
 
39
39
  ### AutoTest Directory Locator
40
40
 
41
- Used independently by S5 and T4. **Anchor the search at `<output-path>` sub-agent cwd is not guaranteed to sit anywhere in particular.** Walk up from `<output-path>` looking for a sibling `agents/test-tools/autotest`:
41
+ Used independently by S5 and T4. The AutoTest tools are installed by `ht init` under the HomeTrans tools dir; resolve their location from config rather than searching the filesystem.
42
+
43
+ **Primary — read `env.TOOL_PATH` from the HomeTrans config** at `~/.hometrans/config.json` (`$HOME/.hometrans/config.json`; on Windows `%USERPROFILE%\.hometrans\config.json`). The autotest dir is `<TOOL_PATH>/test-tools/autotest`:
44
+
45
+ ```bash
46
+ cfg="$HOME/.hometrans/config.json"; [ -n "$USERPROFILE" ] && [ -f "$USERPROFILE/.hometrans/config.json" ] && cfg="$USERPROFILE/.hometrans/config.json"
47
+ tool_path="$(python -c "import json,os;print(json.load(open(os.path.expanduser(r'$cfg'),encoding='utf-8')).get('env',{}).get('TOOL_PATH',''))" 2>/dev/null)"
48
+ if [ -n "$tool_path" ] && [ -d "$tool_path/test-tools/autotest" ]; then echo "$tool_path/test-tools/autotest"; fi
49
+ ```
50
+
51
+ If config is missing or `TOOL_PATH` is empty/invalid, **fall back** to walking up from `<output-path>` for a sibling `test-tools/autotest` (then `agents/test-tools/autotest` for legacy layouts) — **anchor the search at `<output-path>`, the sub-agent cwd is not guaranteed**:
42
52
 
43
53
  ```bash
44
54
  d="<output-path>"; while [ "$d" != "/" ] && [ "$d" != "" ]; do \
55
+ if [ -d "$d/test-tools/autotest" ]; then echo "$d/test-tools/autotest"; break; fi; \
45
56
  if [ -d "$d/agents/test-tools/autotest" ]; then echo "$d/agents/test-tools/autotest"; break; fi; \
46
57
  d="$(dirname "$d")"; \
47
58
  done
48
59
  ```
49
60
 
50
- If the walk-up yields nothing, fall back to a `$(pwd)`-anchored search **only as a last resort**:
61
+ As a last resort, a `$(pwd)`-anchored search:
51
62
 
52
63
  ```bash
53
- find "$(pwd)" -type d -path "*/agents/test-tools/autotest" -print -quit 2>/dev/null
64
+ find "$(pwd)" -type d -path "*/test-tools/autotest" -print -quit 2>/dev/null
54
65
  ```
55
66
 
56
67
  | Caller | Purpose | On failure |
57
68
  |--------|---------|------------|
58
- | S5 | Locate then run testcases_tool.py | Emit `Error: cannot locate agents/test-tools/autotest from <output-path>` and exit |
59
- | T4 | Locate then verify config.yaml | Write `self-test-report.md` with status **FAIL** and reason "AutoTest directory not found from output-path", then exit |
69
+ | S5 | Locate then run testcases_tool.py | Emit `Error: cannot locate test-tools/autotest from config env.TOOL_PATH or <output-path>` and exit |
70
+ | T4 | Locate then verify config.yaml | Write `self-test-report.md` with status **FAIL** and reason "AutoTest directory not found from config TOOL_PATH or output-path", then exit |
60
71
 
61
72
  ### Input Validation Template
62
73
 
@@ -173,7 +184,7 @@ Write to `<output-path>/_extracted.json`:
173
184
 
174
185
  ### S5 — Generate testcases.json via Python script
175
186
 
176
- Find the `agents/test-tools/autotest` directory using the **AutoTest Directory Locator** in Shared Utilities (walk up from `<output-path>`). Set `<autotest_dir>` to the resolved path. If not found, emit the documented error and exit. Run:
187
+ Find the `test-tools/autotest` directory using the **AutoTest Directory Locator** in Shared Utilities (prefer config `env.TOOL_PATH`, fall back to walking up from `<output-path>`). Set `<autotest_dir>` to the resolved path. If not found, emit the documented error and exit. Run:
177
188
 
178
189
  ```bash
179
190
  cd "<autotest_dir>" && uv run python testcases_tool.py generate "<output-path>/_extracted.json" "<output-path>/testcases.json" --validate
@@ -236,7 +247,7 @@ Record the device serial number.
236
247
 
237
248
  ### T4 — Locate AutoTest Directory & Verify config.yaml
238
249
 
239
- Find the `agents/test-tools/autotest` directory using the **AutoTest Directory Locator** in Shared Utilities (walk up from `<output-path>`). If not found, write a `self-test-report.md` with the sentinel format (`status: FAIL` / `reason: AutoTest directory not found from output-path`), then stop.
250
+ Find the `test-tools/autotest` directory using the **AutoTest Directory Locator** in Shared Utilities (prefer config `env.TOOL_PATH`, fall back to walking up from `<output-path>`). If not found, write a `self-test-report.md` with the sentinel format (`status: FAIL` / `reason: AutoTest directory not found from config TOOL_PATH or output-path`), then stop.
240
251
 
241
252
  Set `<autotest_dir>` to the found path. Check config.yaml:
242
253
 
@@ -370,7 +381,7 @@ cd "<autotest_dir>" && uv run python report_tool.py validate "<output-path>/self
370
381
  - **Two-phase architecture**: When the parse phase runs, the LLM writes `_extracted.json` → `testcases_tool.py` writes `testcases.json`. Never write `testcases.json` directly. If `testcases.json` already exists at the output path and the parse phase runs, `testcases_tool.py` overwrites it — never skip the script.
371
382
  - **App name → bundle_name is critical**: `test_steps` must use `bundle_name` (e.g., `com.example.tuku`), NOT the display name. AutoTest uses this to launch the correct app. Using the display name instead may launch the wrong app entirely.
372
383
  - **JSON contract**: `app-metadata.json` and `testcases.json` always live at `<output-path>/app-metadata.json` and `<output-path>/testcases.json`. T2 reads from the file (not from memory).
373
- - **AutoTest directory locator**: Walk up from `<output-path>` (see Shared Utilities). Do not assume `$(pwd)` is anchored anywhere in particular.
384
+ - **AutoTest directory locator**: Resolve `<TOOL_PATH>/test-tools/autotest` from `~/.hometrans/config.json` (`env.TOOL_PATH`); fall back to walking up from `<output-path>` (see Shared Utilities). Do not assume `$(pwd)` is anchored anywhere in particular.
374
385
  - **Sentinel-format early-exit reports**: When T1 / T3 / T4 / T7 write a degraded FAIL report (config error, no device, missing autotest dir, crashed batch, timeout, or `setup=false` precondition failure), the report's first non-frontmatter line MUST be `status: FAIL` and the second MUST be `reason: <one-line reason>`. This lets callers detect early-exit reports without parsing a case table.
375
386
  - **Quote all paths** — paths may contain spaces.
376
387
  - **Capture complete output** — never truncate command output; it is essential for diagnosis.
@@ -14,6 +14,10 @@ export function getConfigDir() {
14
14
  export function getConfigPath() {
15
15
  return path.join(getConfigDir(), 'config.json');
16
16
  }
17
+ /** 工具目录:~/.hometrans/tools,`ht init` 把打包的 tools/ 拷贝到此。 */
18
+ export function getToolsDir() {
19
+ return path.join(getConfigDir(), 'tools');
20
+ }
17
21
  /** 把 ~/foo 展开为 $HOME/foo;非 ~ 开头原样返回。 */
18
22
  export function expandHome(p) {
19
23
  if (!p)
@@ -87,11 +91,12 @@ async function fileExists(p) {
87
91
  * 加载 editors 配置。文件不存在时写入默认值再返回。
88
92
  * 解析失败时抛错,避免静默覆盖用户手动修改。
89
93
  */
90
- export function defaultUserParams() {
94
+ export function defaultEnv() {
91
95
  return {
92
96
  OHOS_SDK_PATH: '',
93
97
  HMS_SDK_PATH: '',
94
98
  TEST_API_KEY: '',
99
+ TOOL_PATH: '',
95
100
  };
96
101
  }
97
102
  export async function loadHomeTransConfig() {
@@ -99,7 +104,7 @@ export async function loadHomeTransConfig() {
99
104
  if (!(await fileExists(configPath))) {
100
105
  const config = {
101
106
  editors: defaultEditors(),
102
- params: defaultUserParams(),
107
+ env: defaultEnv(),
103
108
  };
104
109
  await fs.mkdir(getConfigDir(), { recursive: true });
105
110
  await fs.writeFile(configPath, JSON.stringify(config, null, 2) + '\n', 'utf-8');
@@ -117,15 +122,23 @@ export async function loadHomeTransConfig() {
117
122
  throw new Error(`Invalid config.json shape at ${configPath}: top-level must be { "editors": [...] }.`);
118
123
  }
119
124
  const config = parsed;
120
- // Migrate legacy `sdkPaths` field, fill in any missing params keys.
121
- const legacy = parsed.sdkPaths;
122
- if (!config.params) {
123
- config.params = { ...defaultUserParams(), ...(legacy ?? {}) };
124
- }
125
- else {
126
- config.params = { ...defaultUserParams(), ...config.params };
125
+ const anyParsed = parsed;
126
+ // Migrate legacy fields into `env`:
127
+ // - `sdkPaths` / `params` held OHOS_SDK_PATH / HMS_SDK_PATH / TEST_API_KEY.
128
+ // - `tool_path` held the tools dir, now env.TOOL_PATH.
129
+ const legacyParams = (anyParsed.params ?? anyParsed.sdkPaths);
130
+ const legacyToolPath = typeof anyParsed.tool_path === 'string' ? anyParsed.tool_path : undefined;
131
+ config.env = {
132
+ ...defaultEnv(),
133
+ ...(legacyParams ?? {}),
134
+ ...(config.env ?? {}),
135
+ };
136
+ if (legacyToolPath && !config.env.TOOL_PATH) {
137
+ config.env.TOOL_PATH = legacyToolPath;
127
138
  }
128
- delete config.sdkPaths;
139
+ delete anyParsed.sdkPaths;
140
+ delete anyParsed.params;
141
+ delete anyParsed.tool_path;
129
142
  return config;
130
143
  }
131
144
  export async function saveHomeTransConfig(config) {
@@ -17,15 +17,16 @@ export async function configCommand() {
17
17
  console.log(` Path: ${configPath}`);
18
18
  console.log('');
19
19
  // User parameters
20
- const maskedKey = config.params.TEST_API_KEY
21
- ? config.params.TEST_API_KEY.slice(0, 4) +
20
+ const maskedKey = config.env.TEST_API_KEY
21
+ ? config.env.TEST_API_KEY.slice(0, 4) +
22
22
  '***' +
23
- config.params.TEST_API_KEY.slice(-4)
23
+ config.env.TEST_API_KEY.slice(-4)
24
24
  : '(not set)';
25
25
  console.log(' Parameters:');
26
- console.log(` OHOS_SDK_PATH : ${config.params.OHOS_SDK_PATH || '(not set)'}`);
27
- console.log(` HMS_SDK_PATH : ${config.params.HMS_SDK_PATH || '(not set)'}`);
26
+ console.log(` OHOS_SDK_PATH : ${config.env.OHOS_SDK_PATH || '(not set)'}`);
27
+ console.log(` HMS_SDK_PATH : ${config.env.HMS_SDK_PATH || '(not set)'}`);
28
28
  console.log(` TEST_API_KEY : ${maskedKey}`);
29
+ console.log(` TOOL_PATH : ${config.env.TOOL_PATH || '(not set)'}`);
29
30
  console.log('');
30
31
  // Full config content
31
32
  console.log(' Full Content:');
package/dist/cli/init.js CHANGED
@@ -13,7 +13,7 @@ import chalk from 'chalk';
13
13
  import figlet from 'figlet';
14
14
  import inquirer from 'inquirer';
15
15
  import { setupMcpForAllEditors } from './mcp-setup.js';
16
- import { expandHome, loadHomeTransConfig, saveHomeTransConfig, } from './config-store.js';
16
+ import { expandHome, getConfigPath, getToolsDir, loadHomeTransConfig, saveHomeTransConfig, } from './config-store.js';
17
17
  function ensureChalkColor() {
18
18
  if (process.stdout.isTTY && chalk.level === 0) {
19
19
  chalk.level = 1;
@@ -50,8 +50,30 @@ function resolveSkillsRoot() {
50
50
  function resolveAgentsRoot() {
51
51
  return path.resolve(__dirname, '..', '..', 'agents');
52
52
  }
53
- function resolveAutotestDir(agentsRoot) {
54
- return path.join(agentsRoot, 'test-tools', 'autotest');
53
+ function resolveToolsRoot() {
54
+ return path.resolve(__dirname, '..', '..', 'tools');
55
+ }
56
+ /**
57
+ * Copy the bundled `tools/` folder into ~/.hometrans/tools and record the
58
+ * destination in config.env.TOOL_PATH. Returns the destination path, or null if
59
+ * the package ships no tools/ folder.
60
+ */
61
+ async function installTools(toolsRoot, config) {
62
+ if (!(await dirExists(toolsRoot)))
63
+ return null;
64
+ const toolsDest = getToolsDir();
65
+ await copyDirRecursive(toolsRoot, toolsDest);
66
+ config.env.TOOL_PATH = toolsDest;
67
+ await saveHomeTransConfig(config);
68
+ return toolsDest;
69
+ }
70
+ /**
71
+ * The AutoTest tools live under the installed HomeTrans tools dir
72
+ * (~/.hometrans/tools/test-tools/autotest) once `installTools` has run.
73
+ * This is the same location the self-tester agent resolves via config.env.TOOL_PATH.
74
+ */
75
+ function resolveAutotestDir(toolPath) {
76
+ return path.join(toolPath, 'test-tools', 'autotest');
55
77
  }
56
78
  /**
57
79
  * Initialize / refresh `agents/test-tools/autotest/config.yaml`:
@@ -198,6 +220,7 @@ export async function initCommand(options = {}) {
198
220
  console.log(chalk.gray('\n Android-to-HarmonyOS skill & agent installer for AI editors\n'));
199
221
  const skillsRoot = resolveSkillsRoot();
200
222
  const agentsRoot = resolveAgentsRoot();
223
+ const toolsRoot = resolveToolsRoot();
201
224
  const hasSkills = await dirExists(skillsRoot);
202
225
  const hasAgents = await dirExists(agentsRoot);
203
226
  if (!hasSkills && !hasAgents) {
@@ -261,36 +284,54 @@ export async function initCommand(options = {}) {
261
284
  type: 'input',
262
285
  name: 'OHOS_SDK_PATH',
263
286
  message: 'OHOS_SDK_PATH:',
264
- default: config.params.OHOS_SDK_PATH || undefined,
287
+ default: config.env.OHOS_SDK_PATH || undefined,
265
288
  },
266
289
  {
267
290
  type: 'input',
268
291
  name: 'HMS_SDK_PATH',
269
292
  message: 'HMS_SDK_PATH:',
270
- default: config.params.HMS_SDK_PATH || undefined,
293
+ default: config.env.HMS_SDK_PATH || undefined,
271
294
  },
272
295
  {
273
296
  type: 'input',
274
297
  name: 'TEST_API_KEY',
275
298
  message: 'TEST_API_KEY (for autotest config.yaml):',
276
- default: config.params.TEST_API_KEY || undefined,
299
+ default: config.env.TEST_API_KEY || undefined,
277
300
  },
278
301
  ]);
279
- config.params.OHOS_SDK_PATH = answers.OHOS_SDK_PATH.trim();
280
- config.params.HMS_SDK_PATH = answers.HMS_SDK_PATH.trim();
281
- config.params.TEST_API_KEY = answers.TEST_API_KEY.trim();
302
+ config.env.OHOS_SDK_PATH = answers.OHOS_SDK_PATH.trim();
303
+ config.env.HMS_SDK_PATH = answers.HMS_SDK_PATH.trim();
304
+ config.env.TEST_API_KEY = answers.TEST_API_KEY.trim();
282
305
  await saveHomeTransConfig(config);
283
306
  }
284
307
  catch {
285
308
  console.log(chalk.yellow(' Parameter prompts skipped (non-interactive mode).'));
286
309
  }
287
- // Initialize / refresh autotest config.yaml from the example template.
310
+ // Copy bundled tools/ into ~/.hometrans/tools and record env.TOOL_PATH.
311
+ // Must run before the autotest config step below, which seeds config.yaml
312
+ // into the installed tools dir (the location agents read via env.TOOL_PATH).
313
+ let installedToolPath = null;
288
314
  try {
289
- const autotestDir = resolveAutotestDir(agentsRoot);
290
- const status = await refreshAutotestConfig(autotestDir, config.params.TEST_API_KEY);
291
- if (status) {
292
- console.log(chalk.green(` + ${status}`));
293
- console.log(chalk.gray(` ${path.join(autotestDir, 'config.yaml')}`));
315
+ installedToolPath = await installTools(toolsRoot, config);
316
+ if (installedToolPath) {
317
+ console.log(chalk.green(` + tools copied -> ${prettyHome(installedToolPath)}`));
318
+ console.log(chalk.gray(` TOOL_PATH set in ${prettyHome(getConfigPath())}`));
319
+ }
320
+ }
321
+ catch (err) {
322
+ console.log(chalk.red(` ! tools copy: ${err.message}`));
323
+ }
324
+ // Initialize / refresh autotest config.yaml from the example template,
325
+ // inside the installed tools dir (<TOOL_PATH>/test-tools/autotest).
326
+ try {
327
+ const toolPath = installedToolPath ?? config.env.TOOL_PATH;
328
+ if (toolPath) {
329
+ const autotestDir = resolveAutotestDir(toolPath);
330
+ const status = await refreshAutotestConfig(autotestDir, config.env.TEST_API_KEY);
331
+ if (status) {
332
+ console.log(chalk.green(` + ${status}`));
333
+ console.log(chalk.gray(` ${path.join(autotestDir, 'config.yaml')}`));
334
+ }
294
335
  }
295
336
  }
296
337
  catch (err) {
@@ -84,11 +84,12 @@ async function fileExists(p) {
84
84
  return false;
85
85
  }
86
86
  }
87
- function defaultUserParams() {
87
+ function defaultEnv() {
88
88
  return {
89
89
  OHOS_SDK_PATH: "",
90
90
  HMS_SDK_PATH: "",
91
- TEST_API_KEY: ""
91
+ TEST_API_KEY: "",
92
+ TOOL_PATH: ""
92
93
  };
93
94
  }
94
95
  async function loadHomeTransConfig() {
@@ -96,7 +97,7 @@ async function loadHomeTransConfig() {
96
97
  if (!await fileExists(configPath)) {
97
98
  const config2 = {
98
99
  editors: defaultEditors(),
99
- params: defaultUserParams()
100
+ env: defaultEnv()
100
101
  };
101
102
  await fs.mkdir(getConfigDir(), { recursive: true });
102
103
  await fs.writeFile(configPath, JSON.stringify(config2, null, 2) + "\n", "utf-8");
@@ -117,13 +118,20 @@ async function loadHomeTransConfig() {
117
118
  );
118
119
  }
119
120
  const config = parsed;
120
- const legacy = parsed.sdkPaths;
121
- if (!config.params) {
122
- config.params = { ...defaultUserParams(), ...legacy ?? {} };
123
- } else {
124
- config.params = { ...defaultUserParams(), ...config.params };
121
+ const anyParsed = parsed;
122
+ const legacyParams = anyParsed.params ?? anyParsed.sdkPaths;
123
+ const legacyToolPath = typeof anyParsed.tool_path === "string" ? anyParsed.tool_path : void 0;
124
+ config.env = {
125
+ ...defaultEnv(),
126
+ ...legacyParams ?? {},
127
+ ...config.env ?? {}
128
+ };
129
+ if (legacyToolPath && !config.env.TOOL_PATH) {
130
+ config.env.TOOL_PATH = legacyToolPath;
125
131
  }
126
- delete config.sdkPaths;
132
+ delete anyParsed.sdkPaths;
133
+ delete anyParsed.params;
134
+ delete anyParsed.tool_path;
127
135
  return config;
128
136
  }
129
137
 
@@ -701,10 +709,10 @@ async function extractCommitContext(input) {
701
709
  const mode = input.mode ?? "default";
702
710
  const config = await loadHomeTransConfig();
703
711
  const ohosSdkPath = expandHome(
704
- input.ohosSdkPath ?? config.params.OHOS_SDK_PATH ?? process.env.OHOS_SDK_PATH ?? ""
712
+ input.ohosSdkPath ?? config.env.OHOS_SDK_PATH ?? process.env.OHOS_SDK_PATH ?? ""
705
713
  );
706
714
  const hmsSdkPath = expandHome(
707
- input.hmsSdkPath ?? config.params.HMS_SDK_PATH ?? process.env.HMS_SDK_PATH ?? ""
715
+ input.hmsSdkPath ?? config.env.HMS_SDK_PATH ?? process.env.HMS_SDK_PATH ?? ""
708
716
  );
709
717
  if (!projectPath) {
710
718
  throw new Error("extractCommitContext: projectPath is required");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@buaa_smat/hometrans",
3
- "version": "0.1.3",
3
+ "version": "0.1.5",
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
  "type": "module",
@@ -11,7 +11,8 @@
11
11
  "files": [
12
12
  "dist",
13
13
  "skills",
14
- "agents"
14
+ "agents",
15
+ "tools"
15
16
  ],
16
17
  "scripts": {
17
18
  "build": "node scripts/build.js",
@@ -159,7 +159,7 @@
159
159
  | 达到最大轮数 | `max_rounds_reached` | 已完成配置的最大轮数循环(`max-rounds`,默认 3),仍有失败未解决 |
160
160
  | 编译未产出 HAP(异常) | `no_signed_hap` | build-fixer 未产出签名 HAP,无法继续测试 |
161
161
  | 用例列表为空(异常) | `no_testcases` | `testcases.json` 里 0 条用例,没有可跑的内容。常见原因:解析阶段没识别到 `### Scenario:` 区块 |
162
- | 自测 agent 早退(异常) | `agent_early_exit` | `self-tester` 在跑用例之前就退出了(设备没连、`config.yaml` 没填、`agents/test-tools/autotest` 目录找不到、batch 启动失败 / 超时 / 崩溃、`setup=false` 但所需 JSON 缺失等)。报告首行会是 `status: FAIL`,第二行 `reason: <原因>`。**这种情况下不会进入 fix 循环**——这些是环境/前置条件问题,不是应用缺陷 |
162
+ | 自测 agent 早退(异常) | `agent_early_exit` | `self-tester` 在跑用例之前就退出了(设备没连、`config.yaml` 没填、`test-tools/autotest` 目录找不到(`env.TOOL_PATH` 未配置或失效)、batch 启动失败 / 超时 / 崩溃、`setup=false` 但所需 JSON 缺失等)。报告首行会是 `status: FAIL`,第二行 `reason: <原因>`。**这种情况下不会进入 fix 循环**——这些是环境/前置条件问题,不是应用缺陷 |
163
163
 
164
164
  ---
165
165
 
@@ -242,7 +242,7 @@ build-fixer 的输出摘要:编译状态、签名类型、迭代次数、修
242
242
  - 操作系统:**Windows** 是主要测试目标;macOS/Linux 上的核心命令(`hdc`、`uv`、POSIX 工具链)也能跑,Skill 里涉及到的复制操作会按可用 shell 自适应
243
243
  - `hdc` 已安装并在 PATH 中
244
244
  - `uv` 已安装(Python 包管理)
245
- - `agents/test-tools/autotest/config.yaml` 已配置真实 api_key
245
+ - `<TOOL_PATH>/test-tools/autotest/config.yaml` 已配置真实 api_key(`TOOL_PATH` 取自 `~/.hometrans/config.json` 的 `env.TOOL_PATH`,默认即 `~/.hometrans/tools`;`ht init` 填好 `TEST_API_KEY` 后会自动写入)
246
246
  - 鸿蒙真机通过 USB 连接,`hdc list targets` 能看到设备
247
247
  - 签名后的 `.hap` 文件
248
248
 
@@ -262,7 +262,7 @@ A: 检查 USB 连接,跑 `hdc list targets` 看看有没有设备 SN。重新
262
262
 
263
263
  **Q: 报 config.yaml missing 或 api_key 没填?**
264
264
 
265
- A: 复制 `agents/test-tools/autotest/config.yaml.example` 为 `config.yaml`,把 `YOUR_API_KEY_HERE` 替换成真实的 API Key
265
+ A: AutoTest 目录 `<TOOL_PATH>/test-tools/autotest`(`TOOL_PATH` 取自 `~/.hometrans/config.json` 的 `env.TOOL_PATH`,默认 `~/.hometrans/tools`)下,复制 `config.yaml.example` 为 `config.yaml`,把 `YOUR_API_KEY_HERE` 替换成真实的 API Key。或者重新跑 `ht init` 并填入 `TEST_API_KEY`,它会自动生成。
266
266
 
267
267
  **Q: 前置用例失败了要不要修代码?**
268
268
 
File without changes
File without changes