@buaa_smat/hometrans 0.1.13 → 0.1.14
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 +164 -112
- package/agents/build-fixer.md +384 -394
- package/agents/code-reviewer.md +240 -240
- package/agents/logic-coder.md +199 -199
- package/agents/logic-context-builder.md +194 -194
- package/agents/review-fixer.md +405 -405
- package/agents/self-test-fixer.md +296 -296
- package/agents/self-tester.md +393 -392
- package/agents/spec-generator.md +540 -540
- package/dist/cli/config-store.js +84 -8
- package/dist/cli/config.js +3 -3
- package/dist/cli/env-vars.js +129 -0
- package/dist/cli/init.js +272 -272
- package/dist/cli/uninstall.js +152 -17
- package/dist/context/index.js +10 -197
- package/env-requirements.json +3 -3
- package/package.json +1 -1
- package/resource/choose_editor.png +0 -0
- 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/skills/hmos-batch-ui-align/SKILL.md +108 -98
- package/skills/hmos-batch-ui-align/references/conversion-procedure.md +180 -180
- package/skills/hmos-batch-ui-align/references/mappings/android-to-harmonyOS-ui-atomic-component-mapping-reference.md +2533 -2533
- package/skills/hmos-batch-ui-align/references/mappings/android-to-harmonyOS-ui-interaction-mapping-reference.md +555 -555
- package/skills/hmos-batch-ui-align/references/mappings/android-to-harmonyOS-ui-layout-mapping-reference.md +117 -117
- package/skills/hmos-batch-ui-align/references/mvvm/@Link/350/243/205/351/245/260/345/231/250/357/274/232/347/210/266/345/255/220/345/217/214/345/220/221/345/220/214/346/255/245.md +648 -648
- package/skills/hmos-batch-ui-align/references/mvvm/@Observed/350/243/205/351/245/260/345/231/250/345/222/214@ObjectLink/350/243/205/351/245/260/345/231/250/357/274/232/345/265/214/345/245/227/347/261/273/345/257/271/350/261/241/345/261/236/346/200/247/345/217/230/345/214/226.md +2088 -2088
- package/skills/hmos-batch-ui-align/references/mvvm/@Prop/350/243/205/351/245/260/345/231/250/357/274/232/347/210/266/345/255/220/345/215/225/345/220/221/345/220/214/346/255/245.md +1033 -1033
- package/skills/hmos-batch-ui-align/references/mvvm/@Provide/350/243/205/351/245/260/345/231/250/345/222/214@Consume/350/243/205/351/245/260/345/231/250/357/274/232/344/270/216/345/220/216/344/273/243/347/273/204/344/273/266/345/217/214/345/220/221/345/220/214/346/255/245.md +1183 -1183
- package/skills/hmos-batch-ui-align/references/mvvm/@State/350/243/205/351/245/260/345/231/250/357/274/232/347/273/204/344/273/266/345/206/205/347/212/266/346/200/201.md +576 -576
- package/skills/hmos-batch-ui-align/references/mvvm/@Track/350/243/205/351/245/260/345/231/250/357/274/232class/345/257/271/350/261/241/345/261/236/346/200/247/347/272/247/346/233/264/346/226/260.md +297 -297
- package/skills/hmos-batch-ui-align/references/mvvm/@Watch/350/243/205/351/245/260/345/231/250/357/274/232/347/212/266/346/200/201/345/217/230/351/207/217/346/233/264/346/224/271/351/200/232/347/237/245.md +395 -395
- package/skills/hmos-batch-ui-align/references/mvvm/AppStorage/357/274/232/345/272/224/347/224/250/345/205/250/345/261/200/347/232/204UI/347/212/266/346/200/201/345/255/230/345/202/250.md +902 -902
- package/skills/hmos-batch-ui-align/references/mvvm/Environment/357/274/232/350/256/276/345/244/207/347/216/257/345/242/203/346/237/245/350/257/242.md +106 -106
- package/skills/hmos-batch-ui-align/references/mvvm/LocalStorage/357/274/232/351/241/265/351/235/242/347/272/247UI/347/212/266/346/200/201/345/255/230/345/202/250.md +1178 -1178
- package/skills/hmos-batch-ui-align/references/mvvm/MVVM/346/250/241/345/274/217/357/274/210V1/357/274/211.md +911 -911
- package/skills/hmos-batch-ui-align/references/mvvm/PersistentStorage/357/274/232/346/214/201/344/271/205/345/214/226/345/255/230/345/202/250UI/347/212/266/346/200/201.md +354 -354
- package/skills/hmos-batch-ui-align/references/mvvm//347/256/241/347/220/206/345/272/224/347/224/250/346/213/245/346/234/211/347/232/204/347/212/266/346/200/201/346/246/202/350/277/260.md +11 -11
- package/skills/hmos-convert-pipeline/SKILL.md +429 -415
- package/skills/hmos-fix-build-errors/SKILL.md +272 -273
- package/skills/hmos-fix-build-errors/references/arkts-strict-patterns.md +219 -219
- package/skills/hmos-fix-build-errors/references/known-patterns.md +157 -157
- package/skills/hmos-fix-build-errors/references/rdb-entity-pattern.md +131 -131
- package/skills/hmos-incremental-ui-align/SKILL.md +219 -200
- package/skills/hmos-incremental-ui-align/diff_analysis.md +52 -52
- package/skills/hmos-incremental-ui-align/page_align.md +62 -62
- package/skills/hmos-incremental-ui-align/readme.md +237 -230
- package/skills/hmos-incremental-ui-align/references/Comparison_Template.md +2 -2
- package/skills/hmos-incremental-ui-align/references/MVVM/345/274/200/345/217/221/346/226/207/346/241/243/@Link/350/243/205/351/245/260/345/231/250/357/274/232/347/210/266/345/255/220/345/217/214/345/220/221/345/220/214/346/255/245.md +648 -648
- package/skills/hmos-incremental-ui-align/references/MVVM/345/274/200/345/217/221/346/226/207/346/241/243/@Observed/350/243/205/351/245/260/345/231/250/345/222/214@ObjectLink/350/243/205/351/245/260/345/231/250/357/274/232/345/265/214/345/245/227/347/261/273/345/257/271/350/261/241/345/261/236/346/200/247/345/217/230/345/214/226.md +2088 -2088
- package/skills/hmos-incremental-ui-align/references/MVVM/345/274/200/345/217/221/346/226/207/346/241/243/@Prop/350/243/205/351/245/260/345/231/250/357/274/232/347/210/266/345/255/220/345/215/225/345/220/221/345/220/214/346/255/245.md +1033 -1033
- package/skills/hmos-incremental-ui-align/references/MVVM/345/274/200/345/217/221/346/226/207/346/241/243/@Provide/350/243/205/351/245/260/345/231/250/345/222/214@Consume/350/243/205/351/245/260/345/231/250/357/274/232/344/270/216/345/220/216/344/273/243/347/273/204/344/273/266/345/217/214/345/220/221/345/220/214/346/255/245.md +1183 -1183
- package/skills/hmos-incremental-ui-align/references/MVVM/345/274/200/345/217/221/346/226/207/346/241/243/@State/350/243/205/351/245/260/345/231/250/357/274/232/347/273/204/344/273/266/345/206/205/347/212/266/346/200/201.md +576 -576
- package/skills/hmos-incremental-ui-align/references/MVVM/345/274/200/345/217/221/346/226/207/346/241/243/@Track/350/243/205/351/245/260/345/231/250/357/274/232class/345/257/271/350/261/241/345/261/236/346/200/247/347/272/247/346/233/264/346/226/260.md +297 -297
- package/skills/hmos-incremental-ui-align/references/MVVM/345/274/200/345/217/221/346/226/207/346/241/243/@Watch/350/243/205/351/245/260/345/231/250/357/274/232/347/212/266/346/200/201/345/217/230/351/207/217/346/233/264/346/224/271/351/200/232/347/237/245.md +395 -395
- package/skills/hmos-incremental-ui-align/references/MVVM/345/274/200/345/217/221/346/226/207/346/241/243/AppStorage/357/274/232/345/272/224/347/224/250/345/205/250/345/261/200/347/232/204UI/347/212/266/346/200/201/345/255/230/345/202/250.md +902 -902
- package/skills/hmos-incremental-ui-align/references/MVVM/345/274/200/345/217/221/346/226/207/346/241/243/Environment/357/274/232/350/256/276/345/244/207/347/216/257/345/242/203/346/237/245/350/257/242.md +106 -106
- package/skills/hmos-incremental-ui-align/references/MVVM/345/274/200/345/217/221/346/226/207/346/241/243/LocalStorage/357/274/232/351/241/265/351/235/242/347/272/247UI/347/212/266/346/200/201/345/255/230/345/202/250.md +1178 -1178
- package/skills/hmos-incremental-ui-align/references/MVVM/345/274/200/345/217/221/346/226/207/346/241/243/MVVM/346/250/241/345/274/217V1.md +911 -911
- package/skills/hmos-incremental-ui-align/references/MVVM/345/274/200/345/217/221/346/226/207/346/241/243/PersistentStorage/357/274/232/346/214/201/344/271/205/345/214/226/345/255/230/345/202/250UI/347/212/266/346/200/201.md +354 -354
- package/skills/hmos-incremental-ui-align/references/MVVM/345/274/200/345/217/221/346/226/207/346/241/243//347/256/241/347/220/206/345/272/224/347/224/250/346/213/245/346/234/211/347/232/204/347/212/266/346/200/201/346/246/202/350/277/260.md +11 -11
- package/skills/hmos-incremental-ui-align/references/UI_Analysis_Template.md +3 -3
- package/skills/hmos-incremental-ui-align/references/android-to-harmonyOS-ui-atomic-component-mapping-reference.md +2533 -2533
- package/skills/hmos-incremental-ui-align/references/android-to-harmonyOS-ui-interaction-mapping-reference.md +555 -555
- package/skills/hmos-incremental-ui-align/references/android-to-harmonyOS-ui-layout-mapping-reference.md +117 -117
- package/skills/hmos-incremental-ui-align/scripts/navigation-capure.md +37 -37
- package/skills/hmos-integration-test/SKILL.md +380 -369
- package/skills/hmos-integration-test/readme.md +309 -309
- package/skills/hmos-resources-convert/SKILL.md +623 -623
- package/skills/hmos-resources-convert/references/conversion-rules.md +663 -663
- package/skills/hmos-resources-convert/references/dependency-analysis-rules.md +388 -388
- package/skills/hmos-resources-convert/references/resource-mapping-rules.md +457 -457
- package/skills/hmos-resources-convert/references/xml-drawable-to-svg-rules.md +513 -513
- package/skills/hmos-spec-generate/SKILL.md +331 -331
- package/skills/hmos-spec-generate/references/android-platform-tokens.md +105 -105
- package/skills/hmos-spec-generate/references/spec-sample-1.md +78 -78
- package/skills/hmos-spec-generate/references/spec-sample-2.md +58 -58
- package/skills/hmos-spec-generate/references/spec-sample-3.md +116 -116
- package/skills/hmos-spec-generate/references/step4-report-template.md +33 -33
- package/tools/test-tools/autotest/README.md +33 -17
- package/tools/test-tools/autotest/self_test_runner.py +109 -15
- package/resource/hometrans_config.png +0 -0
- package/skills/hmos-incremental-ui-align/config-example.json +0 -11
- package/tools/test-tools/autotest/config.yaml.example +0 -58
package/dist/cli/init.js
CHANGED
|
@@ -12,9 +12,9 @@ import { fileURLToPath } from 'node:url';
|
|
|
12
12
|
import chalk from 'chalk';
|
|
13
13
|
import figlet from 'figlet';
|
|
14
14
|
import inquirer from 'inquirer';
|
|
15
|
-
import { parseTree, modify, applyEdits } from 'jsonc-parser';
|
|
16
15
|
import { setupMcpForAllEditors } from './mcp-setup.js';
|
|
17
16
|
import { deriveSdkPaths, expandHome, getConfigDir, getConfigPath, getToolsDir, loadHomeTransConfig, saveHomeTransConfig, } from './config-store.js';
|
|
17
|
+
import { persistEnvVars } from './env-vars.js';
|
|
18
18
|
function ensureChalkColor() {
|
|
19
19
|
if (process.stdout.isTTY && chalk.level === 0) {
|
|
20
20
|
chalk.level = 1;
|
|
@@ -70,7 +70,7 @@ function resolveToolsRoot() {
|
|
|
70
70
|
}
|
|
71
71
|
/**
|
|
72
72
|
* Copy the bundled `tools/` folder into ~/.hometrans/tools and record the
|
|
73
|
-
* destination in config.env.
|
|
73
|
+
* destination in config.env.HOMETRANS_TOOL_PATH. Returns the destination path, or null if
|
|
74
74
|
* the package ships no tools/ folder.
|
|
75
75
|
*/
|
|
76
76
|
async function installTools(toolsRoot, config) {
|
|
@@ -78,239 +78,187 @@ async function installTools(toolsRoot, config) {
|
|
|
78
78
|
return null;
|
|
79
79
|
const toolsDest = getToolsDir();
|
|
80
80
|
await copyDirRecursive(toolsRoot, toolsDest);
|
|
81
|
-
config.env.
|
|
81
|
+
config.env.HOMETRANS_TOOL_PATH = toolsDest;
|
|
82
82
|
await saveHomeTransConfig(config);
|
|
83
83
|
return toolsDest;
|
|
84
84
|
}
|
|
85
|
-
/**
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
function resolveAutotestDir(toolPath) {
|
|
91
|
-
return path.join(toolPath, 'test-tools', 'autotest');
|
|
85
|
+
/** Mask an api_key for display; placeholders/empty shown as-is. */
|
|
86
|
+
function maskKey(key) {
|
|
87
|
+
if (!key || key === 'YOUR_API_KEY_HERE')
|
|
88
|
+
return key || '';
|
|
89
|
+
return key.length <= 8 ? '***' : `${key.slice(0, 4)}***${key.slice(-4)}`;
|
|
92
90
|
}
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
91
|
+
/** Inline notes for each autotest field, shown in the table's Description column. */
|
|
92
|
+
const AUTOTEST_FIELD_NOTES = {
|
|
93
|
+
name: '多模态(视觉)模型的名字',
|
|
94
|
+
base_url: '模型API服务的基础地址',
|
|
95
|
+
api_key: '用于模型API请求认证的令牌',
|
|
96
|
+
provider: '模型API服务的提供方标识,如openai',
|
|
97
|
+
temperature: '采样温度',
|
|
98
|
+
top_p: '核采样阈值',
|
|
99
|
+
frequency_penalty: '重复惩罚',
|
|
100
|
+
device_sn: '设备序列号;null 表示自动选用当前连接的设备',
|
|
101
|
+
ip: '设备连接 IP(hdc)',
|
|
102
|
+
port: '设备连接端口(hdc)',
|
|
103
|
+
device_log_level: '设备日志级别:debug/info/...',
|
|
104
|
+
max_steps: '单条用例最大执行步数',
|
|
105
|
+
verbose: '是否输出详细日志',
|
|
106
|
+
lang: '输出语言:zh/en',
|
|
107
|
+
enable_preprocess: '执行前是否先用 unified_model 把用例拆成步骤序列(多一次 LLM 调用,默认关)',
|
|
108
|
+
debug_mode: '调试会话:null 关闭 / memory 内存 / file 存盘并跑完后起调试服务(:5000)',
|
|
109
|
+
};
|
|
110
|
+
/** Visual width of a string in a monospace terminal (CJK / fullwidth glyphs = 2). */
|
|
111
|
+
function displayWidth(s) {
|
|
112
|
+
let w = 0;
|
|
113
|
+
for (const ch of s) {
|
|
114
|
+
const cp = ch.codePointAt(0) ?? 0;
|
|
115
|
+
const wide = (cp >= 0x1100 && cp <= 0x115f) ||
|
|
116
|
+
(cp >= 0x2e80 && cp <= 0xa4cf) ||
|
|
117
|
+
(cp >= 0xac00 && cp <= 0xd7a3) ||
|
|
118
|
+
(cp >= 0xf900 && cp <= 0xfaff) ||
|
|
119
|
+
(cp >= 0xfe30 && cp <= 0xfe4f) ||
|
|
120
|
+
(cp >= 0xff00 && cp <= 0xff60) ||
|
|
121
|
+
(cp >= 0xffe0 && cp <= 0xffe6) ||
|
|
122
|
+
(cp >= 0x20000 && cp <= 0x3fffd);
|
|
123
|
+
w += wide ? 2 : 1;
|
|
124
|
+
}
|
|
125
|
+
return w;
|
|
100
126
|
}
|
|
101
|
-
/**
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
return false;
|
|
105
|
-
const norm = (s) => s.replace(/\r\n/g, '\n');
|
|
106
|
-
return norm(a) !== norm(b);
|
|
127
|
+
/** padEnd by visual width so columns with CJK content still line up. */
|
|
128
|
+
function padEndDisplay(s, width) {
|
|
129
|
+
return s + ' '.repeat(Math.max(0, width - displayWidth(s)));
|
|
107
130
|
}
|
|
108
|
-
/**
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
131
|
+
/**
|
|
132
|
+
* Render the current/default autotest block as a table so the user sees every
|
|
133
|
+
* field, its current/default value, and a short description. Section label shows
|
|
134
|
+
* only on the first row of each group for readability.
|
|
135
|
+
*/
|
|
136
|
+
function printAutotestTable(at) {
|
|
137
|
+
const fmt = (v) => (v === null ? 'null' : String(v));
|
|
138
|
+
const um = at.unified_model;
|
|
139
|
+
const data = [
|
|
140
|
+
['unified_model', 'name', um.name],
|
|
141
|
+
['unified_model', 'base_url', um.base_url],
|
|
142
|
+
['unified_model', 'provider', um.provider],
|
|
143
|
+
['unified_model', 'api_key', maskKey(um.api_key)],
|
|
144
|
+
['unified_model', 'temperature', fmt(um.temperature)],
|
|
145
|
+
['unified_model', 'top_p', fmt(um.top_p)],
|
|
146
|
+
['unified_model', 'frequency_penalty', fmt(um.frequency_penalty)],
|
|
147
|
+
['device', 'device_sn', fmt(at.device.device_sn)],
|
|
148
|
+
['device', 'ip', at.device.ip],
|
|
149
|
+
['device', 'port', fmt(at.device.port)],
|
|
150
|
+
['device', 'device_log_level', at.device.device_log_level],
|
|
151
|
+
['agent', 'max_steps', fmt(at.agent.max_steps)],
|
|
152
|
+
['agent', 'verbose', fmt(at.agent.verbose)],
|
|
153
|
+
['agent', 'lang', at.agent.lang],
|
|
154
|
+
['agent', 'enable_preprocess', fmt(at.agent.enable_preprocess)],
|
|
155
|
+
['agent', 'debug_mode', fmt(at.agent.debug_mode)],
|
|
156
|
+
];
|
|
157
|
+
const header = ['Section', 'Field', 'Current/Default', 'Description'];
|
|
158
|
+
// Section label only on the first row of each group.
|
|
159
|
+
const rows = [];
|
|
160
|
+
let prevSection = '';
|
|
161
|
+
for (const [s, f, v] of data) {
|
|
162
|
+
rows.push([s === prevSection ? '' : s, f, v, AUTOTEST_FIELD_NOTES[f] ?? '']);
|
|
163
|
+
prevSection = s;
|
|
164
|
+
}
|
|
165
|
+
const cols = header.map((h) => displayWidth(h));
|
|
166
|
+
for (const r of rows) {
|
|
167
|
+
for (let i = 0; i < cols.length; i++) {
|
|
168
|
+
cols[i] = Math.max(cols[i], displayWidth(r[i]));
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
const border = (l, m, r) => ` ${l}` + cols.map((w) => '─'.repeat(w + 2)).join(m) + r;
|
|
172
|
+
const row = (cells) => ' │ ' + cells.map((c, i) => padEndDisplay(c, cols[i])).join(' │ ') + ' │';
|
|
173
|
+
console.log(chalk.gray(border('┌', '┬', '┐')));
|
|
174
|
+
console.log(chalk.gray(row(header)));
|
|
175
|
+
console.log(chalk.gray(border('├', '┼', '┤')));
|
|
176
|
+
for (const r of rows)
|
|
177
|
+
console.log(chalk.gray(row(r)));
|
|
178
|
+
console.log(chalk.gray(border('└', '┴', '┘')));
|
|
114
179
|
}
|
|
115
180
|
/**
|
|
116
|
-
*
|
|
117
|
-
*
|
|
118
|
-
*
|
|
119
|
-
*
|
|
120
|
-
*
|
|
181
|
+
* Test Configuration step — configures the `autotest` block in
|
|
182
|
+
* ~/.hometrans/config.json (used by the on-device self-test agent). Shows the
|
|
183
|
+
* default content, then prompts for the model essentials. The api_key (formerly
|
|
184
|
+
* TEST_API_KEY) is stored here and NOT written to the system environment;
|
|
185
|
+
* self_test_runner.py reads it from config.json at run time.
|
|
186
|
+
*
|
|
187
|
+
* device / agent stay at their defaults (shown above) — edit config.json to tune.
|
|
121
188
|
*/
|
|
122
|
-
async function
|
|
123
|
-
|
|
189
|
+
async function promptTestConfig(config) {
|
|
190
|
+
const at = config.autotest;
|
|
191
|
+
const um = at.unified_model;
|
|
192
|
+
console.log('');
|
|
193
|
+
console.log(chalk.blue(' Test Configuration'));
|
|
194
|
+
printAutotestTable(at);
|
|
195
|
+
console.log(chalk.gray(' This step configures only the parameters prompted below; configure all other'));
|
|
196
|
+
console.log(chalk.gray(` parameters in config.json: ${getConfigPath()}`));
|
|
197
|
+
console.log('');
|
|
124
198
|
try {
|
|
125
199
|
const answers = await inquirer.prompt([
|
|
126
200
|
{
|
|
127
|
-
type: '
|
|
128
|
-
name: '
|
|
129
|
-
message:
|
|
130
|
-
default:
|
|
201
|
+
type: 'input',
|
|
202
|
+
name: 'api_key',
|
|
203
|
+
message: 'autotest api_key (LLM key the self-test agent uses to run test cases):',
|
|
204
|
+
default: um.api_key && um.api_key !== 'YOUR_API_KEY_HERE' ? um.api_key : undefined,
|
|
205
|
+
},
|
|
206
|
+
{ type: 'input', name: 'name', message: 'model name:', default: um.name },
|
|
207
|
+
{ type: 'input', name: 'base_url', message: 'base_url:', default: um.base_url },
|
|
208
|
+
{
|
|
209
|
+
type: 'input',
|
|
210
|
+
name: 'provider',
|
|
211
|
+
message: 'provider (openai / azure / ...):',
|
|
212
|
+
default: um.provider,
|
|
131
213
|
},
|
|
132
214
|
]);
|
|
133
|
-
|
|
215
|
+
if (answers.api_key.trim())
|
|
216
|
+
um.api_key = answers.api_key.trim();
|
|
217
|
+
if (answers.name.trim())
|
|
218
|
+
um.name = answers.name.trim();
|
|
219
|
+
if (answers.base_url.trim())
|
|
220
|
+
um.base_url = answers.base_url.trim();
|
|
221
|
+
if (answers.provider.trim())
|
|
222
|
+
um.provider = answers.provider.trim();
|
|
223
|
+
await saveHomeTransConfig(config);
|
|
224
|
+
const note = um.api_key && um.api_key !== 'YOUR_API_KEY_HERE'
|
|
225
|
+
? `api_key ${maskKey(um.api_key)}`
|
|
226
|
+
: 'api_key left as placeholder — self-test will not run until it is set';
|
|
227
|
+
console.log(chalk.green(` + autotest config saved (${note})`));
|
|
134
228
|
}
|
|
135
229
|
catch (err) {
|
|
136
230
|
if (isPromptAbort(err))
|
|
137
231
|
abortInit();
|
|
138
|
-
console.log(chalk.yellow('
|
|
139
|
-
return false;
|
|
232
|
+
console.log(chalk.yellow(' Test configuration skipped (non-interactive mode).'));
|
|
140
233
|
}
|
|
141
234
|
}
|
|
142
235
|
/**
|
|
143
|
-
*
|
|
144
|
-
*
|
|
145
|
-
*
|
|
146
|
-
*
|
|
147
|
-
* Returns a status string for the result summary, or null if nothing was done
|
|
148
|
-
* (e.g., the autotest folder isn't present in this package).
|
|
236
|
+
* UI Migration Configuration step — configures the GLM key used by the
|
|
237
|
+
* incremental UI-alignment skill's phone-agent. GLM_API_KEY is read from the OS
|
|
238
|
+
* environment at run time (app_feature_verify.py), so it stays in config.env and
|
|
239
|
+
* is persisted as a machine env var by maybePersistMachineEnv.
|
|
149
240
|
*/
|
|
150
|
-
async function
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
.
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
.
|
|
164
|
-
|
|
165
|
-
.catch(() => false);
|
|
166
|
-
if (!hasConfig) {
|
|
167
|
-
await fs.copyFile(examplePath, configPath);
|
|
168
|
-
seeded = true;
|
|
169
|
-
}
|
|
170
|
-
else {
|
|
171
|
-
// Template drift: the bundled example changed since the local config was
|
|
172
|
-
// created (prevExampleContent = local example snapshot taken before the
|
|
173
|
-
// tools copy). Y regenerates from the new template after a timestamped
|
|
174
|
-
// backup; N keeps the local file (key-only refresh below) — but saves the
|
|
175
|
-
// old template alongside, since the new one just overwrote it on disk
|
|
176
|
-
// and the local config is still based on the old one.
|
|
177
|
-
const newExample = await fs.readFile(examplePath, 'utf-8');
|
|
178
|
-
if (templatesDiffer(prevExampleContent, newExample)) {
|
|
179
|
-
if (await confirmRegenerateFromTemplate('autotest config.yaml')) {
|
|
180
|
-
backupName = `config.yaml.${timestampSuffix()}.bak`;
|
|
181
|
-
await fs.copyFile(configPath, path.join(autotestDir, backupName));
|
|
182
|
-
await fs.copyFile(examplePath, configPath);
|
|
183
|
-
}
|
|
184
|
-
else {
|
|
185
|
-
templateBackupName = `config.yaml.example.${timestampSuffix()}.bak`;
|
|
186
|
-
await fs.writeFile(path.join(autotestDir, templateBackupName), prevExampleContent, 'utf-8');
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
if (!apiKey) {
|
|
191
|
-
if (backupName) {
|
|
192
|
-
return `autotest config.yaml regenerated from new template (backup: ${backupName}; api_key left as placeholder)`;
|
|
193
|
-
}
|
|
194
|
-
if (templateBackupName) {
|
|
195
|
-
return `autotest config.yaml kept (old template backed up: ${templateBackupName})`;
|
|
196
|
-
}
|
|
197
|
-
return seeded
|
|
198
|
-
? `autotest config.yaml seeded (api_key left as placeholder)`
|
|
199
|
-
: null;
|
|
200
|
-
}
|
|
201
|
-
const original = await fs.readFile(configPath, 'utf-8');
|
|
202
|
-
// Match `api_key: <value>` with optional quoting, preserve indent + key.
|
|
203
|
-
const updated = original.replace(/^(\s*api_key\s*:\s*)(?:"[^"]*"|'[^']*'|\S+)\s*$/gm, (_m, prefix) => `${prefix}"${apiKey}"`);
|
|
204
|
-
if (updated !== original) {
|
|
205
|
-
await fs.writeFile(configPath, updated, 'utf-8');
|
|
206
|
-
}
|
|
207
|
-
if (backupName) {
|
|
208
|
-
return `autotest config.yaml regenerated from new template + api_key filled (backup: ${backupName})`;
|
|
209
|
-
}
|
|
210
|
-
if (templateBackupName) {
|
|
211
|
-
return `autotest config.yaml api_key refreshed (kept local file; old template backed up: ${templateBackupName})`;
|
|
212
|
-
}
|
|
213
|
-
return seeded
|
|
214
|
-
? `autotest config.yaml seeded + api_key filled`
|
|
215
|
-
: `autotest config.yaml api_key refreshed`;
|
|
216
|
-
}
|
|
217
|
-
const UI_ALIGN_SKILL = 'hmos-incremental-ui-align';
|
|
218
|
-
/**
|
|
219
|
-
* Initialize / refresh `<skillsDir>/hmos-incremental-ui-align/config.json`
|
|
220
|
-
* in an editor's installed skills dir — same semantics as the autotest
|
|
221
|
-
* config.yaml handling:
|
|
222
|
-
* - If config.json does not exist, seed it from config-example.json.
|
|
223
|
-
* - If it exists, surgically overwrite ONLY the `glm_api_key` and
|
|
224
|
-
* `hmos_sdk_dir` values (via jsonc edits), preserving every other
|
|
225
|
-
* field the user has set. `hmos_sdk_dir` = `<DEVECO_SDK_HOME>/default`.
|
|
226
|
-
*
|
|
227
|
-
* Returns a status string for the result summary, or null if nothing was
|
|
228
|
-
* done (skill not installed there, or no values to write into an existing file).
|
|
229
|
-
*/
|
|
230
|
-
export async function refreshUiAlignConfig(skillsDir, glmApiKey, hmosSdkDir, prevExampleContent = null) {
|
|
231
|
-
const skillDir = path.join(skillsDir, UI_ALIGN_SKILL);
|
|
232
|
-
const examplePath = path.join(skillDir, 'config-example.json');
|
|
233
|
-
const configPath = path.join(skillDir, 'config.json');
|
|
234
|
-
const hasExample = await fs
|
|
235
|
-
.access(examplePath)
|
|
236
|
-
.then(() => true)
|
|
237
|
-
.catch(() => false);
|
|
238
|
-
if (!hasExample)
|
|
239
|
-
return null;
|
|
240
|
-
let seeded = false;
|
|
241
|
-
let backupName = null;
|
|
242
|
-
let templateBackupName = null;
|
|
243
|
-
const hasConfig = await fs
|
|
244
|
-
.access(configPath)
|
|
245
|
-
.then(() => true)
|
|
246
|
-
.catch(() => false);
|
|
247
|
-
if (!hasConfig) {
|
|
248
|
-
await fs.copyFile(examplePath, configPath);
|
|
249
|
-
seeded = true;
|
|
250
|
-
}
|
|
251
|
-
else {
|
|
252
|
-
// Template drift: bundled config-example.json changed since the local
|
|
253
|
-
// config.json was created (prevExampleContent = snapshot taken before the
|
|
254
|
-
// skills copy). Y regenerates from the new template after a timestamped
|
|
255
|
-
// backup; N keeps the local file (key-only update below) — but saves the
|
|
256
|
-
// old template alongside, since the new one just overwrote it on disk
|
|
257
|
-
// and the local config is still based on the old one.
|
|
258
|
-
const newExample = await fs.readFile(examplePath, 'utf-8');
|
|
259
|
-
if (templatesDiffer(prevExampleContent, newExample)) {
|
|
260
|
-
if (await confirmRegenerateFromTemplate(`${UI_ALIGN_SKILL}/config.json`)) {
|
|
261
|
-
backupName = `config.json.${timestampSuffix()}.bak`;
|
|
262
|
-
await fs.copyFile(configPath, path.join(skillDir, backupName));
|
|
263
|
-
await fs.copyFile(examplePath, configPath);
|
|
264
|
-
}
|
|
265
|
-
else {
|
|
266
|
-
templateBackupName = `config-example.json.${timestampSuffix()}.bak`;
|
|
267
|
-
await fs.writeFile(path.join(skillDir, templateBackupName), prevExampleContent, 'utf-8');
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
const updates = [];
|
|
272
|
-
if (glmApiKey)
|
|
273
|
-
updates.push(['glm_api_key', glmApiKey]);
|
|
274
|
-
if (hmosSdkDir)
|
|
275
|
-
updates.push(['hmos_sdk_dir', hmosSdkDir]);
|
|
276
|
-
if (updates.length === 0) {
|
|
277
|
-
if (backupName) {
|
|
278
|
-
return `${UI_ALIGN_SKILL}/config.json regenerated from new template (backup: ${backupName}; glm_api_key / hmos_sdk_dir left empty)`;
|
|
279
|
-
}
|
|
280
|
-
if (templateBackupName) {
|
|
281
|
-
return `${UI_ALIGN_SKILL}/config.json kept (old template backed up: ${templateBackupName})`;
|
|
282
|
-
}
|
|
283
|
-
return seeded
|
|
284
|
-
? `${UI_ALIGN_SKILL}/config.json seeded (glm_api_key / hmos_sdk_dir left empty)`
|
|
285
|
-
: null;
|
|
286
|
-
}
|
|
287
|
-
let raw = await fs.readFile(configPath, 'utf-8');
|
|
288
|
-
const parseErrors = [];
|
|
289
|
-
const tree = parseTree(raw, parseErrors);
|
|
290
|
-
if (!tree || tree.type !== 'object' || parseErrors.length > 0) {
|
|
291
|
-
// Corrupt config — never regenerate over user content; just report.
|
|
292
|
-
return `${UI_ALIGN_SKILL}/config.json NOT updated (file is not valid JSON — fix it manually)`;
|
|
293
|
-
}
|
|
294
|
-
const original = raw;
|
|
295
|
-
for (const [key, value] of updates) {
|
|
296
|
-
const edits = modify(raw, [key], value, {
|
|
297
|
-
formattingOptions: { tabSize: 2, insertSpaces: true },
|
|
298
|
-
});
|
|
299
|
-
raw = applyEdits(raw, edits);
|
|
300
|
-
}
|
|
301
|
-
if (raw !== original) {
|
|
302
|
-
await fs.writeFile(configPath, raw, 'utf-8');
|
|
303
|
-
}
|
|
304
|
-
const what = updates.map(([k]) => k).join(' + ');
|
|
305
|
-
if (backupName) {
|
|
306
|
-
return `${UI_ALIGN_SKILL}/config.json regenerated from new template + ${what} filled (backup: ${backupName})`;
|
|
241
|
+
async function promptUiAlignConfig(config) {
|
|
242
|
+
console.log('');
|
|
243
|
+
console.log(chalk.blue(' UI Migration Configuration'));
|
|
244
|
+
console.log(chalk.gray(' GLM_API_KEY: Zhipu GLM key for the UI-alignment phone-agent (read from the OS env at run time).'));
|
|
245
|
+
try {
|
|
246
|
+
const answers = await inquirer.prompt([
|
|
247
|
+
{
|
|
248
|
+
type: 'input',
|
|
249
|
+
name: 'GLM_API_KEY',
|
|
250
|
+
message: 'GLM_API_KEY (LLM API key for the GLM phone-agent used in UI alignment):',
|
|
251
|
+
default: config.env.GLM_API_KEY || undefined,
|
|
252
|
+
},
|
|
253
|
+
]);
|
|
254
|
+
config.env.GLM_API_KEY = answers.GLM_API_KEY.trim();
|
|
255
|
+
await saveHomeTransConfig(config);
|
|
307
256
|
}
|
|
308
|
-
|
|
309
|
-
|
|
257
|
+
catch (err) {
|
|
258
|
+
if (isPromptAbort(err))
|
|
259
|
+
abortInit();
|
|
260
|
+
console.log(chalk.yellow(' UI migration configuration skipped (non-interactive mode).'));
|
|
310
261
|
}
|
|
311
|
-
return seeded
|
|
312
|
-
? `${UI_ALIGN_SKILL}/config.json seeded + ${what} filled`
|
|
313
|
-
: `${UI_ALIGN_SKILL}/config.json ${what} refreshed`;
|
|
314
262
|
}
|
|
315
263
|
async function installSkillsTo(skillsRoot, targetDir, skipNames = new Set()) {
|
|
316
264
|
let entries;
|
|
@@ -438,7 +386,7 @@ async function confirmOverwrite(editorName, skillConflicts, agentConflicts) {
|
|
|
438
386
|
return true;
|
|
439
387
|
}
|
|
440
388
|
}
|
|
441
|
-
async function installForEditor(editor, skillsRoot, agentsRoot,
|
|
389
|
+
async function installForEditor(editor, skillsRoot, agentsRoot, result) {
|
|
442
390
|
const marker = expandHome(editor.markerDir);
|
|
443
391
|
if (marker && !(await dirExists(marker))) {
|
|
444
392
|
result.skipped.push(`${editor.name} (not installed)`);
|
|
@@ -461,9 +409,6 @@ async function installForEditor(editor, skillsRoot, agentsRoot, glmApiKey, hmosS
|
|
|
461
409
|
if (skillConflicts.length > 0 || agentConflicts.length > 0) {
|
|
462
410
|
overwrite = await confirmOverwrite(editor.name, skillConflicts, agentConflicts);
|
|
463
411
|
}
|
|
464
|
-
// Snapshot the installed ui-align template BEFORE the skills copy updates
|
|
465
|
-
// it — refreshUiAlignConfig uses it to detect template drift.
|
|
466
|
-
const prevUiAlignExample = await readFileIfExists(path.join(skillsDir, UI_ALIGN_SKILL, 'config-example.json'));
|
|
467
412
|
// N = incremental install: keep the conflicting items untouched, still copy
|
|
468
413
|
// everything that doesn't exist in the target dirs yet.
|
|
469
414
|
const skipSkills = overwrite ? new Set() : new Set(skillConflicts);
|
|
@@ -478,19 +423,7 @@ async function installForEditor(editor, skillsRoot, agentsRoot, glmApiKey, hmosS
|
|
|
478
423
|
result.errors.push(`${editor.name} skills: ${err.message}`);
|
|
479
424
|
}
|
|
480
425
|
if (!overwrite) {
|
|
481
|
-
result.skipped.push(`${editor.name} existing kept: ${skipSkills.size} skills, ${skipAgents.size} agents (new items installed
|
|
482
|
-
}
|
|
483
|
-
// Seed / refresh the incremental-ui-align per-skill config.json with the
|
|
484
|
-
// GLM key + SDK dir from ~/.hometrans/config.json (existing files: key-only
|
|
485
|
-
// update). Runs regardless of the overwrite decision above.
|
|
486
|
-
try {
|
|
487
|
-
const status = await refreshUiAlignConfig(skillsDir, glmApiKey, hmosSdkDir, prevUiAlignExample);
|
|
488
|
-
if (status) {
|
|
489
|
-
result.configured.push(`${editor.name} ${status}`);
|
|
490
|
-
}
|
|
491
|
-
}
|
|
492
|
-
catch (err) {
|
|
493
|
-
result.errors.push(`${editor.name} ${UI_ALIGN_SKILL}/config.json: ${err.message}`);
|
|
426
|
+
result.skipped.push(`${editor.name} existing kept: ${skipSkills.size} skills, ${skipAgents.size} agents (new items installed)`);
|
|
494
427
|
}
|
|
495
428
|
try {
|
|
496
429
|
const agents = await installAgentsTo(agentsRoot, agentsDir, skipAgents);
|
|
@@ -654,8 +587,8 @@ async function detectEnvironment(spec, env) {
|
|
|
654
587
|
const isWin = process.platform === 'win32';
|
|
655
588
|
const baseDir = (key) => key === 'DEVECO_SDK_HOME'
|
|
656
589
|
? env.DEVECO_SDK_HOME
|
|
657
|
-
: key === '
|
|
658
|
-
? env.
|
|
590
|
+
: key === 'DEVECO_HOME'
|
|
591
|
+
? env.DEVECO_HOME
|
|
659
592
|
: '';
|
|
660
593
|
for (const [id, tool] of Object.entries(spec.tools)) {
|
|
661
594
|
if (tool.source === 'env-dir') {
|
|
@@ -815,6 +748,82 @@ async function detectInstalledEditors(editors) {
|
|
|
815
748
|
}
|
|
816
749
|
return status;
|
|
817
750
|
}
|
|
751
|
+
/**
|
|
752
|
+
* Config env keys that are useful as real OS environment variables (the SDK
|
|
753
|
+
* paths + tool dir + GLM key). PATH entries for node/java/hdc are not included
|
|
754
|
+
* here — those are documented as manual PATH setup.
|
|
755
|
+
*
|
|
756
|
+
* TEST_API_KEY is intentionally absent: the self-test api_key now lives in the
|
|
757
|
+
* `autotest` block of config.json (configured by the Test Configuration step)
|
|
758
|
+
* and is read from there by self_test_runner.py — never from the system env.
|
|
759
|
+
* GLM_API_KEY stays, because the UI-alignment phone-agent reads it from the OS
|
|
760
|
+
* environment at run time.
|
|
761
|
+
*/
|
|
762
|
+
export const MACHINE_ENV_KEYS = [
|
|
763
|
+
'DEVECO_SDK_HOME',
|
|
764
|
+
'DEVECO_HOME',
|
|
765
|
+
'OHOS_SDK_PATH',
|
|
766
|
+
'HMS_SDK_PATH',
|
|
767
|
+
'HOMETRANS_TOOL_PATH',
|
|
768
|
+
'GLM_API_KEY',
|
|
769
|
+
];
|
|
770
|
+
/** Mask API-key values for display; show paths in full. */
|
|
771
|
+
function maskEnvValue(name, value) {
|
|
772
|
+
if (/_API_KEY$/.test(name) && value.length > 0) {
|
|
773
|
+
return value.length <= 8 ? '***' : `${value.slice(0, 4)}***${value.slice(-4)}`;
|
|
774
|
+
}
|
|
775
|
+
return value;
|
|
776
|
+
}
|
|
777
|
+
/**
|
|
778
|
+
* Show the resolved config values that would become machine environment
|
|
779
|
+
* variables, then ask Y/N before writing them (Windows: setx; macOS/Linux:
|
|
780
|
+
* shell rc block). Non-interactive shells skip without changing anything.
|
|
781
|
+
* Re-throws ExitPromptError so the caller can abort init on Ctrl-C.
|
|
782
|
+
*/
|
|
783
|
+
async function maybePersistMachineEnv(env) {
|
|
784
|
+
const vars = MACHINE_ENV_KEYS.map((k) => [k, (env[k] ?? '').trim()]).filter(([, value]) => value.length > 0);
|
|
785
|
+
if (vars.length === 0)
|
|
786
|
+
return;
|
|
787
|
+
console.log('');
|
|
788
|
+
console.log(chalk.blue(' Add these to your machine environment variables?'));
|
|
789
|
+
console.log(chalk.gray(process.platform === 'win32'
|
|
790
|
+
? ' (Windows: persisted via setx to your user environment)'
|
|
791
|
+
: ' (macOS/Linux: written as an export block to your shell profile)'));
|
|
792
|
+
for (const [name, value] of vars) {
|
|
793
|
+
console.log(` ${chalk.cyan(name)} = ${maskEnvValue(name, value)}`);
|
|
794
|
+
}
|
|
795
|
+
console.log('');
|
|
796
|
+
console.log(chalk.yellow(' Recommended: skills and MCP tools read these from the OS environment at runtime.'));
|
|
797
|
+
console.log(chalk.gray(' If you choose No, they are NOT set as system variables — skills/tools that rely on\n' +
|
|
798
|
+
' them (self-test, UI alignment, commit-context analysis) may fail until you set them\n' +
|
|
799
|
+
' yourself or re-run `ht init`.'));
|
|
800
|
+
let confirmed;
|
|
801
|
+
try {
|
|
802
|
+
const answers = await inquirer.prompt([
|
|
803
|
+
{
|
|
804
|
+
type: 'confirm',
|
|
805
|
+
name: 'persist',
|
|
806
|
+
message: 'Write these environment variables to this machine?',
|
|
807
|
+
default: true,
|
|
808
|
+
},
|
|
809
|
+
]);
|
|
810
|
+
confirmed = answers.persist;
|
|
811
|
+
}
|
|
812
|
+
catch (err) {
|
|
813
|
+
if (isPromptAbort(err))
|
|
814
|
+
throw err;
|
|
815
|
+
console.log(chalk.yellow(' Non-interactive: skipped (set them manually if needed).'));
|
|
816
|
+
return;
|
|
817
|
+
}
|
|
818
|
+
if (!confirmed) {
|
|
819
|
+
console.log(chalk.yellow(' Skipped — no system environment variables were set.'));
|
|
820
|
+
console.log(chalk.gray(' Skills/MCP tools that need these (e.g. SDK paths, API keys) will not find them\n' +
|
|
821
|
+
' automatically. Set them manually or re-run `ht init` to enable those features.'));
|
|
822
|
+
return;
|
|
823
|
+
}
|
|
824
|
+
const note = await persistEnvVars(vars);
|
|
825
|
+
console.log(chalk.green(` + Environment variables ${note}`));
|
|
826
|
+
}
|
|
818
827
|
export async function initCommand(options = {}) {
|
|
819
828
|
ensureChalkColor();
|
|
820
829
|
const banner = figlet.textSync('HomeTrans', { font: 'Standard' });
|
|
@@ -833,7 +842,16 @@ export async function initCommand(options = {}) {
|
|
|
833
842
|
process.exitCode = 1;
|
|
834
843
|
return;
|
|
835
844
|
}
|
|
845
|
+
// Generate an initial ~/.hometrans/config.json up front (first-use scenario):
|
|
846
|
+
// loadHomeTransConfig writes a complete default file (editors + env + autotest)
|
|
847
|
+
// when none exists, and saveHomeTransConfig flushes it again to be certain it is
|
|
848
|
+
// on disk before any later step (e.g. Test Configuration) points the user at it.
|
|
849
|
+
const configPath = getConfigPath();
|
|
850
|
+
const configExisted = await pathExists(configPath);
|
|
836
851
|
const config = await loadHomeTransConfig();
|
|
852
|
+
await saveHomeTransConfig(config);
|
|
853
|
+
console.log(chalk.green(` ${configExisted ? 'Using' : 'Initialized'} config: ${configPath}`));
|
|
854
|
+
console.log('');
|
|
837
855
|
const { editors } = config;
|
|
838
856
|
const installedStatus = await detectInstalledEditors(editors);
|
|
839
857
|
let selectedEditors;
|
|
@@ -889,7 +907,7 @@ export async function initCommand(options = {}) {
|
|
|
889
907
|
console.log(chalk.yellow('\n No editors selected. Nothing to do.\n'));
|
|
890
908
|
return;
|
|
891
909
|
}
|
|
892
|
-
// --- User parameters (SDK paths
|
|
910
|
+
// --- User parameters (SDK paths) ---
|
|
893
911
|
console.log('');
|
|
894
912
|
console.log(chalk.blue(' Parameter Configuration'));
|
|
895
913
|
console.log(chalk.gray(' (press Enter to keep current value or leave empty)\n'));
|
|
@@ -901,33 +919,19 @@ export async function initCommand(options = {}) {
|
|
|
901
919
|
message: 'DEVECO_SDK_HOME (DevEco Studio SDK dir):',
|
|
902
920
|
default: config.env.DEVECO_SDK_HOME || undefined,
|
|
903
921
|
},
|
|
904
|
-
{
|
|
905
|
-
type: 'input',
|
|
906
|
-
name: 'TEST_API_KEY',
|
|
907
|
-
message: 'TEST_API_KEY (LLM API key used by the integration-test agent to run test cases):',
|
|
908
|
-
default: config.env.TEST_API_KEY || undefined,
|
|
909
|
-
},
|
|
910
|
-
{
|
|
911
|
-
type: 'input',
|
|
912
|
-
name: 'GLM_API_KEY',
|
|
913
|
-
message: 'GLM_API_KEY (LLM API key for the GLM phone-agent used in UI alignment):',
|
|
914
|
-
default: config.env.GLM_API_KEY || undefined,
|
|
915
|
-
},
|
|
916
922
|
]);
|
|
917
923
|
const sdk = deriveSdkPaths(answers.DEVECO_SDK_HOME);
|
|
918
924
|
config.env.DEVECO_SDK_HOME = sdk.DEVECO_SDK_HOME;
|
|
919
|
-
config.env.
|
|
925
|
+
config.env.DEVECO_HOME = sdk.DEVECO_HOME;
|
|
920
926
|
config.env.OHOS_SDK_PATH = sdk.OHOS_SDK_PATH;
|
|
921
927
|
config.env.HMS_SDK_PATH = sdk.HMS_SDK_PATH;
|
|
922
|
-
config.env.TEST_API_KEY = answers.TEST_API_KEY.trim();
|
|
923
|
-
config.env.GLM_API_KEY = answers.GLM_API_KEY.trim();
|
|
924
928
|
await saveHomeTransConfig(config);
|
|
925
929
|
// Echo the derived paths and validate each exists on disk; missing ones are
|
|
926
930
|
// a strong signal of a typo'd DEVECO_SDK_HOME or a broken DevEco install.
|
|
927
931
|
if (sdk.DEVECO_SDK_HOME) {
|
|
928
932
|
const checks = [
|
|
929
933
|
['DEVECO_SDK_HOME', sdk.DEVECO_SDK_HOME],
|
|
930
|
-
['
|
|
934
|
+
['DEVECO_HOME', sdk.DEVECO_HOME],
|
|
931
935
|
['OHOS_SDK_PATH', sdk.OHOS_SDK_PATH],
|
|
932
936
|
['HMS_SDK_PATH', sdk.HMS_SDK_PATH],
|
|
933
937
|
];
|
|
@@ -955,6 +959,13 @@ export async function initCommand(options = {}) {
|
|
|
955
959
|
abortInit();
|
|
956
960
|
console.log(chalk.yellow(' Parameter prompts skipped (non-interactive mode).'));
|
|
957
961
|
}
|
|
962
|
+
// UI Migration Configuration — configure the GLM key for the UI-alignment
|
|
963
|
+
// phone-agent. Stays in config.env (read from the OS env at run time).
|
|
964
|
+
await promptUiAlignConfig(config);
|
|
965
|
+
// Test Configuration — configure the autotest block (self-test api_key etc.)
|
|
966
|
+
// in config.json. Replaces the old TEST_API_KEY env prompt; never written to
|
|
967
|
+
// the system environment.
|
|
968
|
+
await promptTestConfig(config);
|
|
958
969
|
// Detect external tools (adb / hdc / python / uv / java / gitnexus + DevEco)
|
|
959
970
|
// and report which skills/agents are impacted by anything missing.
|
|
960
971
|
try {
|
|
@@ -963,19 +974,15 @@ export async function initCommand(options = {}) {
|
|
|
963
974
|
catch (err) {
|
|
964
975
|
console.log(chalk.yellow(` ! environment check skipped: ${err.message}`));
|
|
965
976
|
}
|
|
966
|
-
//
|
|
967
|
-
//
|
|
968
|
-
//
|
|
969
|
-
const prevAutotestExample = await readFileIfExists(path.join(resolveAutotestDir(config.env.TOOL_PATH || getToolsDir()), 'config.yaml.example'));
|
|
970
|
-
// Copy bundled tools/ into ~/.hometrans/tools and record env.TOOL_PATH.
|
|
971
|
-
// Must run before the autotest config step below, which seeds config.yaml
|
|
972
|
-
// into the installed tools dir (the location agents read via env.TOOL_PATH).
|
|
977
|
+
// Copy bundled tools/ into ~/.hometrans/tools and record env.HOMETRANS_TOOL_PATH.
|
|
978
|
+
// self_test_runner.py (under the installed tools dir) reads the autotest
|
|
979
|
+
// config from ~/.hometrans/config.json at run time — see promptTestConfig.
|
|
973
980
|
let installedToolPath = null;
|
|
974
981
|
try {
|
|
975
982
|
installedToolPath = await installTools(toolsRoot, config);
|
|
976
983
|
if (installedToolPath) {
|
|
977
984
|
console.log(chalk.green(` + tools copied -> ${prettyHome(installedToolPath)}`));
|
|
978
|
-
console.log(chalk.gray(`
|
|
985
|
+
console.log(chalk.gray(` HOMETRANS_TOOL_PATH set in ${prettyHome(getConfigPath())}`));
|
|
979
986
|
}
|
|
980
987
|
}
|
|
981
988
|
catch (err) {
|
|
@@ -992,32 +999,25 @@ export async function initCommand(options = {}) {
|
|
|
992
999
|
catch (err) {
|
|
993
1000
|
console.log(chalk.red(` ! env-requirements.json copy: ${err.message}`));
|
|
994
1001
|
}
|
|
995
|
-
//
|
|
996
|
-
//
|
|
1002
|
+
// (The autotest block in config.json was already configured by the Test
|
|
1003
|
+
// Configuration step above; self_test_runner.py reads it at run time.)
|
|
1004
|
+
// Offer to persist the resolved config values as real OS environment
|
|
1005
|
+
// variables (Windows: setx; macOS/Linux: shell rc block). Shows what will be
|
|
1006
|
+
// written and asks Y/N first; runs after HOMETRANS_TOOL_PATH is finalized above.
|
|
997
1007
|
try {
|
|
998
|
-
|
|
999
|
-
if (toolPath) {
|
|
1000
|
-
const autotestDir = resolveAutotestDir(toolPath);
|
|
1001
|
-
const status = await refreshAutotestConfig(autotestDir, config.env.TEST_API_KEY, prevAutotestExample);
|
|
1002
|
-
if (status) {
|
|
1003
|
-
console.log(chalk.green(` + ${status}`));
|
|
1004
|
-
console.log(chalk.gray(` ${path.join(autotestDir, 'config.yaml')}`));
|
|
1005
|
-
}
|
|
1006
|
-
}
|
|
1008
|
+
await maybePersistMachineEnv(config.env);
|
|
1007
1009
|
}
|
|
1008
1010
|
catch (err) {
|
|
1009
|
-
|
|
1011
|
+
if (isPromptAbort(err))
|
|
1012
|
+
abortInit();
|
|
1013
|
+
console.log(chalk.red(` ! environment variables: ${err.message}`));
|
|
1010
1014
|
}
|
|
1011
1015
|
console.log('');
|
|
1012
1016
|
const editorsToSetup = editors.filter((e) => selectedEditors.includes(e.name));
|
|
1013
1017
|
const result = { configured: [], skipped: [], errors: [] };
|
|
1014
|
-
// hmos_sdk_dir consumed by incremental-ui-align = <DEVECO_SDK_HOME>/default.
|
|
1015
|
-
const hmosSdkDir = config.env.DEVECO_SDK_HOME
|
|
1016
|
-
? path.join(config.env.DEVECO_SDK_HOME, 'default')
|
|
1017
|
-
: '';
|
|
1018
1018
|
for (const editor of editorsToSetup) {
|
|
1019
1019
|
console.log(chalk.blue(` Configuring ${editor.name}...`));
|
|
1020
|
-
await installForEditor(editor, skillsRoot, agentsRoot,
|
|
1020
|
+
await installForEditor(editor, skillsRoot, agentsRoot, result);
|
|
1021
1021
|
}
|
|
1022
1022
|
await setupMcpForAllEditors(editorsToSetup, result);
|
|
1023
1023
|
console.log('');
|