android2harmony 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 +1 -407
- package/agents/self-tester.md +376 -339
- package/dist/index.js +131 -586
- package/dist/index.js.map +4 -4
- package/package.json +36 -37
- package/skills/a2h-resource-convert/SKILL.md +902 -0
- package/skills/a2h-resource-convert/references/code-vector-icon-rules.md +335 -0
- package/skills/{hmos-resources-convert → a2h-resource-convert}/references/conversion-rules.md +15 -2
- package/skills/{hmos-resources-convert → a2h-resource-convert}/references/dependency-analysis-rules.md +24 -2
- package/skills/a2h-resource-convert/references/lottie-conversion-rules.md +219 -0
- package/skills/{hmos-resources-convert → a2h-resource-convert}/references/resource-mapping-rules.md +27 -2
- package/skills/{hmos-resources-convert → a2h-resource-convert}/references/xml-drawable-to-svg-rules.md +118 -1
- package/skills/a2h-resource-convert/scripts/a2h_resource_convert.js +2166 -0
- package/skills/a2h-resource-convert/scripts/code_vector_icons.js +607 -0
- package/skills/a2h-resource-convert/scripts/package.json +3 -0
- package/skills/a2h-resource-convert/scripts/svg_fidelity_check.js +632 -0
- package/skills/a2h-ui-transfer/SKILL.md +420 -0
- package/skills/a2h-ui-transfer/references/conversion-procedure.md +572 -0
- package/skills/{hmos-batch-ui-align → a2h-ui-transfer}/references/mvvm-v2/_/350/214/203/345/274/217/351/200/211/346/213/251/350/257/264/346/230/216.md +1 -1
- package/skills/a2h-ui-transfer/references/mvvm-v2/_/350/243/205/351/245/260/345/231/250/351/200/237/346/237/245.md +133 -0
- package/skills/{hmos-batch-ui-align/scripts/android_parse_fast.ts → a2h-ui-transfer/scripts/android_parse_fast.js} +397 -283
- package/skills/a2h-ui-transfer/scripts/arkts_static_check.js +1624 -0
- package/skills/a2h-ui-transfer/scripts/measure_pack.js +1005 -0
- package/skills/a2h-ui-transfer/scripts/package.json +3 -0
- package/skills/hmos-incremental-ui-align/README.md +251 -251
- package/skills/hmos-incremental-ui-align/SKILL.md +364 -364
- package/skills/hmos-incremental-ui-align/references/State_Model_Template.md +2 -2
- package/skills/hmos-incremental-ui-align/scripts/app_feature_verify.js +790 -0
- package/skills/hmos-incremental-ui-align/scripts/extract_checklist.js +285 -0
- package/skills/hmos-incremental-ui-align/scripts/navigation-capure.md +76 -76
- package/skills/hmos-incremental-ui-align/scripts/page_capture.js +756 -0
- package/skills/hmos-incremental-ui-align/scripts/page_capture_burst.js +155 -0
- package/skills/hmos-batch-ui-align/SKILL.md +0 -141
- package/skills/hmos-batch-ui-align/references/conversion-procedure.md +0 -217
- package/skills/hmos-incremental-ui-align/scripts/app_feature_verify.ts +0 -999
- package/skills/hmos-incremental-ui-align/scripts/extract_checklist.ts +0 -343
- package/skills/hmos-incremental-ui-align/scripts/page_capture.ts +0 -977
- package/skills/hmos-incremental-ui-align/scripts/page_capture_burst.ts +0 -188
- package/skills/hmos-resources-convert/SKILL.md +0 -654
- package/skills/hmos-resources-convert/template/AppScope/app.json5 +0 -10
- package/skills/hmos-resources-convert/template/AppScope/resources/base/element/string.json +0 -8
- package/skills/hmos-resources-convert/template/AppScope/resources/base/media/background.png +0 -0
- package/skills/hmos-resources-convert/template/AppScope/resources/base/media/foreground.png +0 -0
- package/skills/hmos-resources-convert/template/AppScope/resources/base/media/layered_image.json +0 -7
- package/skills/hmos-resources-convert/template/build-profile.json5 +0 -42
- package/skills/hmos-resources-convert/template/code-linter.json5 +0 -32
- package/skills/hmos-resources-convert/template/entry/build-profile.json5 +0 -33
- package/skills/hmos-resources-convert/template/entry/hvigorfile.ts +0 -6
- package/skills/hmos-resources-convert/template/entry/obfuscation-rules.txt +0 -23
- package/skills/hmos-resources-convert/template/entry/oh-package.json5 +0 -10
- package/skills/hmos-resources-convert/template/entry/src/main/ets/entryability/EntryAbility.ets +0 -48
- package/skills/hmos-resources-convert/template/entry/src/main/ets/entrybackupability/EntryBackupAbility.ets +0 -16
- package/skills/hmos-resources-convert/template/entry/src/main/ets/pages/Index.ets +0 -23
- package/skills/hmos-resources-convert/template/entry/src/main/module.json5 +0 -55
- package/skills/hmos-resources-convert/template/entry/src/main/resources/base/element/color.json +0 -8
- package/skills/hmos-resources-convert/template/entry/src/main/resources/base/element/float.json +0 -8
- package/skills/hmos-resources-convert/template/entry/src/main/resources/base/element/string.json +0 -16
- package/skills/hmos-resources-convert/template/entry/src/main/resources/base/media/background.png +0 -0
- package/skills/hmos-resources-convert/template/entry/src/main/resources/base/media/foreground.png +0 -0
- package/skills/hmos-resources-convert/template/entry/src/main/resources/base/media/layered_image.json +0 -7
- package/skills/hmos-resources-convert/template/entry/src/main/resources/base/media/startIcon.png +0 -0
- package/skills/hmos-resources-convert/template/entry/src/main/resources/base/profile/backup_config.json +0 -3
- package/skills/hmos-resources-convert/template/entry/src/main/resources/base/profile/main_pages.json +0 -5
- package/skills/hmos-resources-convert/template/entry/src/main/resources/dark/element/color.json +0 -8
- package/skills/hmos-resources-convert/template/entry/src/mock/mock-config.json5 +0 -2
- package/skills/hmos-resources-convert/template/entry/src/ohosTest/ets/test/Ability.test.ets +0 -35
- package/skills/hmos-resources-convert/template/entry/src/ohosTest/ets/test/List.test.ets +0 -5
- package/skills/hmos-resources-convert/template/entry/src/ohosTest/module.json5 +0 -16
- package/skills/hmos-resources-convert/template/entry/src/test/List.test.ets +0 -5
- package/skills/hmos-resources-convert/template/entry/src/test/LocalUnit.test.ets +0 -33
- package/skills/hmos-resources-convert/template/hvigor/hvigor-config.json5 +0 -23
- package/skills/hmos-resources-convert/template/hvigorfile.ts +0 -6
- package/skills/hmos-resources-convert/template/oh-package-lock.json5 +0 -28
- package/skills/hmos-resources-convert/template/oh-package.json5 +0 -10
- package/tools/autotest/deps/autotest-agent-0.1.1.tgz +0 -0
- package/tools/autotest/engine/batch-launcher.ts +0 -326
- package/tools/autotest/engine/report-tool.ts +0 -773
- package/tools/autotest/engine/self-test-runner.ts +0 -1024
- package/tools/autotest/engine/testcases-tool.ts +0 -246
- package/tools/autotest/resolve-metadata-tool.ts +0 -143
- package/tools/autotest/runner/logger.ts +0 -45
- package/tools/autotest/runner/process-utils.ts +0 -41
- package/tools/autotest/validate.ts +0 -115
- /package/skills/{hmos-batch-ui-align → a2h-ui-transfer}/references/mappings/android-to-harmonyOS-ui-atomic-component-mapping-reference.md +0 -0
- /package/skills/{hmos-batch-ui-align → a2h-ui-transfer}/references/mappings/android-to-harmonyOS-ui-interaction-mapping-reference.md +0 -0
- /package/skills/{hmos-batch-ui-align → a2h-ui-transfer}/references/mappings/android-to-harmonyOS-ui-layout-mapping-reference.md +0 -0
- /package/skills/{hmos-batch-ui-align → a2h-ui-transfer}/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" +0 -0
- /package/skills/{hmos-batch-ui-align → a2h-ui-transfer}/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" +0 -0
- /package/skills/{hmos-batch-ui-align → a2h-ui-transfer}/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" +0 -0
- /package/skills/{hmos-batch-ui-align → a2h-ui-transfer}/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" +0 -0
- /package/skills/{hmos-batch-ui-align → a2h-ui-transfer}/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" +0 -0
- /package/skills/{hmos-batch-ui-align → a2h-ui-transfer}/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" +0 -0
- /package/skills/{hmos-batch-ui-align → a2h-ui-transfer}/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" +0 -0
- /package/skills/{hmos-batch-ui-align → a2h-ui-transfer}/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" +0 -0
- /package/skills/{hmos-batch-ui-align → a2h-ui-transfer}/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" +0 -0
- /package/skills/{hmos-batch-ui-align → a2h-ui-transfer}/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" +0 -0
- /package/skills/{hmos-batch-ui-align → a2h-ui-transfer}/references/mvvm/MVVM/346/250/241/345/274/217/357/274/210V1/357/274/211.md" +0 -0
- /package/skills/{hmos-batch-ui-align → a2h-ui-transfer}/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" +0 -0
- /package/skills/{hmos-batch-ui-align → a2h-ui-transfer}/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" +0 -0
- /package/skills/{hmos-batch-ui-align → a2h-ui-transfer}/references/mvvm-v2/!!/350/257/255/346/263/225/357/274/232/345/217/214/345/220/221/347/273/221/345/256/232.md" +0 -0
- /package/skills/{hmos-batch-ui-align → a2h-ui-transfer}/references/mvvm-v2/@Computed/350/243/205/351/245/260/345/231/250/357/274/232/350/256/241/347/256/227/345/261/236/346/200/247.md" +0 -0
- /package/skills/{hmos-batch-ui-align → a2h-ui-transfer}/references/mvvm-v2/@Event/350/243/205/351/245/260/345/231/250/357/274/232/350/247/204/350/214/203/347/273/204/344/273/266/350/276/223/345/207/272.md" +0 -0
- /package/skills/{hmos-batch-ui-align → a2h-ui-transfer}/references/mvvm-v2/@Local/350/243/205/351/245/260/345/231/250/357/274/232/347/273/204/344/273/266/345/206/205/351/203/250/347/212/266/346/200/201.md" +0 -0
- /package/skills/{hmos-batch-ui-align → a2h-ui-transfer}/references/mvvm-v2/@Monitor/350/243/205/351/245/260/345/231/250/357/274/232/347/212/266/346/200/201/345/217/230/351/207/217/344/277/256/346/224/271/345/274/202/346/255/245/347/233/221/345/220/254.md" +0 -0
- /package/skills/{hmos-batch-ui-align → a2h-ui-transfer}/references/mvvm-v2/@ObservedV2/350/243/205/351/245/260/345/231/250/345/222/214@Trace/350/243/205/351/245/260/345/231/250/357/274/232/347/261/273/345/261/236/346/200/247/345/217/230/345/214/226/350/247/202/346/265/213.md" +0 -0
- /package/skills/{hmos-batch-ui-align → a2h-ui-transfer}/references/mvvm-v2/@Once/350/243/205/351/245/260/345/231/250/357/274/232/345/210/235/345/247/213/345/214/226/345/220/214/346/255/245/344/270/200/346/254/241.md" +0 -0
- /package/skills/{hmos-batch-ui-align → a2h-ui-transfer}/references/mvvm-v2/@Param/350/243/205/351/245/260/345/231/250/357/274/232/347/273/204/344/273/266/345/244/226/351/203/250/350/276/223/345/205/245.md" +0 -0
- /package/skills/{hmos-batch-ui-align → a2h-ui-transfer}/references/mvvm-v2/@Provider/350/243/205/351/245/260/345/231/250/345/222/214@Consumer/350/243/205/351/245/260/345/231/250/357/274/232/350/267/250/347/273/204/344/273/266/345/261/202/347/272/247/345/217/214/345/220/221/345/220/214/346/255/245.md" +0 -0
- /package/skills/{hmos-batch-ui-align → a2h-ui-transfer}/references/mvvm-v2/@Type/350/243/205/351/245/260/345/231/250/357/274/232/346/240/207/350/256/260/347/261/273/345/261/236/346/200/247/347/232/204/347/261/273/345/236/213.md" +0 -0
- /package/skills/{hmos-batch-ui-align → a2h-ui-transfer}/references/mvvm-v2/AppStorageV2/357/274/232/345/272/224/347/224/250/345/205/250/345/261/200UI/347/212/266/346/200/201/345/255/230/345/202/250.md" +0 -0
- /package/skills/{hmos-batch-ui-align → a2h-ui-transfer}/references/mvvm-v2/MVVM/346/250/241/345/274/217/357/274/210V2/357/274/211.md" +0 -0
- /package/skills/{hmos-batch-ui-align → a2h-ui-transfer}/references/mvvm-v2/PersistenceV2/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" +0 -0
- /package/skills/{hmos-batch-ui-align → a2h-ui-transfer}/references/mvvm-v2//347/212/266/346/200/201/347/256/241/347/220/206V1/345/220/221V2/350/277/201/347/247/273/344/270/216/346/267/267/347/224/250/346/214/207/345/257/274.md" +0 -0
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// src/index.ts
|
|
2
|
-
import
|
|
2
|
+
import path5 from "node:path";
|
|
3
3
|
|
|
4
4
|
// src/paths.ts
|
|
5
5
|
import path from "node:path";
|
|
@@ -70,22 +70,21 @@ async function buildA2HEnvBase(deps) {
|
|
|
70
70
|
return env;
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
-
// src/tools/a2h-
|
|
73
|
+
// src/tools/a2h-run-model-script.ts
|
|
74
74
|
import { spawn } from "node:child_process";
|
|
75
75
|
import path4 from "node:path";
|
|
76
|
-
var
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
"TEST_API_KEY"
|
|
76
|
+
var MODEL_SCRIPTS = [
|
|
77
|
+
{
|
|
78
|
+
name: "app_feature_verify",
|
|
79
|
+
path: path4.join(
|
|
80
|
+
PKG_ROOT,
|
|
81
|
+
"skills",
|
|
82
|
+
"hmos-incremental-ui-align",
|
|
83
|
+
"scripts",
|
|
84
|
+
"app_feature_verify.js"
|
|
85
|
+
),
|
|
86
|
+
argsHelp: 'UI \u5BFB\u8DEF phone-agent:\u5728 HarmonyOS(hdc)\u6216 Android(adb)\u5DF2\u8FDE\u63A5\u8BBE\u5907\u4E0A,\u6309\u8DEF\u5F84/\u63D0\u793A\u5BFC\u822A\u5230\u76EE\u6807\u9875\u9762\u5E76\u622A\u56FE+\u6A21\u578B\u5224\u5B9A\u662F\u5426\u5230\u8FBE\u3002\n\n| \u5B57\u6BB5 | \u7C7B\u578B | \u7EA6\u675F | \u8BF4\u660E |\n|---|---|---|---|\n| app | string | \u5FC5\u586B | \u76EE\u6807\u5E94\u7528\u663E\u793A\u540D |\n| package | string | \u5FC5\u586B | \u76EE\u6807\u5E94\u7528\u5305\u540D |\n| device | string | \u9ED8\u8BA4 hdc | \u8BBE\u5907\u9A71\u52A8,\u53D6\u503C hdc \u6216 adb |\n| prompt | string | \u4E09\u9009\u4E00 | \u81EA\u7531\u5BFC\u822A\u63CF\u8FF0 |\n| task | string | \u4E09\u9009\u4E00 | L1\u2026>L2\u2026>L3\u2026 \u5C42\u7EA7\u8DEF\u5F84 |\n| feature | string | \u4E09\u9009\u4E00 | features.md \u8DEF\u5F84 |\n| maxSteps | number | \u9ED8\u8BA4 30 | \u6700\u5927 agent \u6B65\u6570 |\n| serial | string | \u53EF\u9009 | \u8BBE\u5907\u5E8F\u5217\u53F7(\u591A\u8BBE\u5907\u65F6) |\n| appHints | string | \u53EF\u9009 | \u5E94\u7528\u5BFC\u822A\u63D0\u793A |\n| limit | number | \u53EF\u9009 | --feature \u6A21\u5F0F\u53D6\u524D N \u6761 |\n| offset | number | \u9ED8\u8BA4 1 | --feature \u6A21\u5F0F\u504F\u79FB |\n| quiet | boolean | \u53EF\u9009 | true \u65F6\u6291\u5236\u63D0\u793A\u6027\u8F93\u51FA |\n\n\u7EA6\u675F:app \u4E0E package \u5FC5\u586B,\u4E14 prompt/task/feature \u4E09\u9009\u4E00\u3002\n\u793A\u4F8B:commandArgs = {"app":"\u56FE\u5E93","package":"com.x.gallery","device":"adb","prompt":"\u8FDB\u5165\u8BBE\u7F6E\u9875\u9762","maxSteps":15}'
|
|
87
|
+
}
|
|
89
88
|
];
|
|
90
89
|
function cap(s, max = 5e4) {
|
|
91
90
|
if (s.length <= max) return s;
|
|
@@ -94,576 +93,126 @@ function cap(s, max = 5e4) {
|
|
|
94
93
|
\u2026[truncated ${s.length - max} chars]\u2026
|
|
95
94
|
` + s.slice(-half);
|
|
96
95
|
}
|
|
97
|
-
function
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
},
|
|
126
|
-
execute: async (args, context) => {
|
|
127
|
-
const app = String(args.app ?? "");
|
|
128
|
-
const pkg = String(args.package ?? "");
|
|
129
|
-
if (!app || !pkg) {
|
|
130
|
-
throw new Error("a2h_app_feature_verify: `app` and `package` are required");
|
|
131
|
-
}
|
|
132
|
-
const task = args.task != null ? String(args.task) : null;
|
|
133
|
-
const prompt = args.prompt != null ? String(args.prompt) : null;
|
|
134
|
-
const feature = args.feature != null ? String(args.feature) : null;
|
|
135
|
-
if (!task && !prompt && !feature) {
|
|
136
|
-
throw new Error("a2h_app_feature_verify: one of `task` / `prompt` / `feature` is required");
|
|
137
|
-
}
|
|
138
|
-
const model = await deps.resolveModelParams(context.worktree);
|
|
139
|
-
if (!model || !model.apiKey) {
|
|
140
|
-
throw new Error("a2h_app_feature_verify: host did not provide model params (apiKey missing)");
|
|
141
|
-
}
|
|
142
|
-
const argv = ["--app", app, "--package", pkg, "--device", String(args.device ?? "hdc")];
|
|
143
|
-
if (task) argv.push("--task", task);
|
|
144
|
-
if (prompt) argv.push("--prompt", prompt);
|
|
145
|
-
if (feature) argv.push("--feature", feature);
|
|
146
|
-
if (args.maxSteps != null) argv.push("--max-steps", String(args.maxSteps));
|
|
147
|
-
if (args.appHints != null) argv.push("--app-hints", String(args.appHints));
|
|
148
|
-
if (args.serial != null) argv.push("--serial", String(args.serial));
|
|
149
|
-
if (args.limit != null) argv.push("--limit", String(args.limit));
|
|
150
|
-
if (args.offset != null) argv.push("--offset", String(args.offset));
|
|
151
|
-
if (args.quiet === true) argv.push("--quiet");
|
|
152
|
-
argv.push("--model-stdin");
|
|
153
|
-
const env = { ...process.env };
|
|
154
|
-
for (const k of SECRET_ENV) delete env[k];
|
|
155
|
-
const child = spawn("node", [SCRIPT_PATH, ...argv], {
|
|
156
|
-
cwd: context.directory,
|
|
157
|
-
env,
|
|
158
|
-
stdio: ["pipe", "pipe", "pipe"]
|
|
159
|
-
});
|
|
160
|
-
child.stdin.end(
|
|
161
|
-
JSON.stringify({ apiKey: model.apiKey, modelName: model.modelName, baseURL: model.baseURL })
|
|
162
|
-
);
|
|
163
|
-
let stdout = "";
|
|
164
|
-
let stderr = "";
|
|
165
|
-
let spawnError = "";
|
|
166
|
-
const stepRe = /执行动作:\s*(.*)/g;
|
|
167
|
-
child.stdout.on("data", (chunk) => {
|
|
168
|
-
const text = chunk.toString("utf8");
|
|
169
|
-
stdout += text;
|
|
170
|
-
const matches = [...text.matchAll(stepRe)];
|
|
171
|
-
if (matches.length) {
|
|
172
|
-
const last = matches[matches.length - 1][1].trim();
|
|
173
|
-
try {
|
|
174
|
-
context.metadata({ title: `UI verify: ${last.slice(0, 80)}` });
|
|
175
|
-
} catch {
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
});
|
|
179
|
-
child.stderr.on("data", (chunk) => {
|
|
180
|
-
stderr += chunk.toString("utf8");
|
|
181
|
-
});
|
|
182
|
-
const onAbort = () => {
|
|
183
|
-
if (!child.killed) child.kill("SIGTERM");
|
|
184
|
-
};
|
|
185
|
-
context.abort.addEventListener("abort", onAbort);
|
|
186
|
-
const code = await new Promise((resolve) => {
|
|
187
|
-
child.on("close", resolve);
|
|
188
|
-
child.on("error", (err) => {
|
|
189
|
-
spawnError = err.message;
|
|
190
|
-
resolve(-1);
|
|
191
|
-
});
|
|
192
|
-
});
|
|
193
|
-
context.abort.removeEventListener("abort", onAbort);
|
|
194
|
-
if (code !== 0) {
|
|
195
|
-
if (spawnError) {
|
|
196
|
-
throw new Error(`a2h_app_feature_verify: failed to spawn node \u2014 ${spawnError}`);
|
|
96
|
+
async function runModelScript(deps, context, entry, argv) {
|
|
97
|
+
const model = await deps.resolveModelParams(context.worktree);
|
|
98
|
+
if (!model || !model.apiKey) {
|
|
99
|
+
throw new Error(
|
|
100
|
+
`a2h_run_model_script (${entry.name}): host did not provide model params (apiKey missing)`
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
const finalArgv = [...argv, "--model-stdin"];
|
|
104
|
+
const child = spawn("node", [entry.path, ...finalArgv], {
|
|
105
|
+
cwd: context.directory,
|
|
106
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
107
|
+
});
|
|
108
|
+
child.stdin.end(
|
|
109
|
+
JSON.stringify({ apiKey: model.apiKey, modelName: model.modelName, baseURL: model.baseURL })
|
|
110
|
+
);
|
|
111
|
+
let stdout = "";
|
|
112
|
+
let stderr = "";
|
|
113
|
+
let spawnError = "";
|
|
114
|
+
child.stdout.on("data", (chunk) => {
|
|
115
|
+
const text = chunk.toString("utf8");
|
|
116
|
+
stdout += text;
|
|
117
|
+
const lines = text.split(/\r?\n/);
|
|
118
|
+
for (let i = lines.length - 1; i >= 0; i--) {
|
|
119
|
+
const line = lines[i].trim();
|
|
120
|
+
if (line) {
|
|
121
|
+
try {
|
|
122
|
+
context.metadata({ title: line.slice(0, 80) });
|
|
123
|
+
} catch {
|
|
197
124
|
}
|
|
198
|
-
|
|
199
|
-
throw new Error(`a2h_app_feature_verify failed (exit ${code}): ${tail || "(no stderr)"}`);
|
|
125
|
+
break;
|
|
200
126
|
}
|
|
201
|
-
return { ok: true, exitCode: code, output: cap(stdout) };
|
|
202
127
|
}
|
|
128
|
+
});
|
|
129
|
+
child.stderr.on("data", (chunk) => {
|
|
130
|
+
stderr += chunk.toString("utf8");
|
|
131
|
+
});
|
|
132
|
+
const onAbort = () => {
|
|
133
|
+
if (!child.killed) child.kill("SIGTERM");
|
|
203
134
|
};
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
import path6 from "node:path";
|
|
208
|
-
|
|
209
|
-
// src/tools/autotest-shared.ts
|
|
210
|
-
import { spawn as spawn2 } from "node:child_process";
|
|
211
|
-
import path5 from "node:path";
|
|
212
|
-
var AUTOTEST_ROOT = path5.join(PKG_ROOT, "tools", "autotest");
|
|
213
|
-
var SECRET_ENV2 = [
|
|
214
|
-
"HOMETRANS_MODEL_API_KEY",
|
|
215
|
-
"HOMETRANS_MODEL_NAME",
|
|
216
|
-
"HOMETRANS_MODEL_BASE_URL",
|
|
217
|
-
"GLM_API_KEY",
|
|
218
|
-
"TEST_API_KEY"
|
|
219
|
-
];
|
|
220
|
-
function cap2(s, max = 5e4) {
|
|
221
|
-
if (s.length <= max) return s;
|
|
222
|
-
const half = Math.floor(max / 2);
|
|
223
|
-
return s.slice(0, half) + `
|
|
224
|
-
\u2026[truncated ${s.length - max} chars]\u2026
|
|
225
|
-
` + s.slice(-half);
|
|
226
|
-
}
|
|
227
|
-
function runAutotestScript(opts) {
|
|
228
|
-
return new Promise((resolve) => {
|
|
229
|
-
const scriptPath = path5.join(AUTOTEST_ROOT, opts.scriptRel);
|
|
230
|
-
const env = { ...process.env };
|
|
231
|
-
for (const k of SECRET_ENV2) delete env[k];
|
|
232
|
-
const child = spawn2("node", [scriptPath, ...opts.argv], {
|
|
233
|
-
cwd: opts.cwd,
|
|
234
|
-
env,
|
|
235
|
-
stdio: ["pipe", "pipe", "pipe"]
|
|
236
|
-
});
|
|
237
|
-
let stdout = "";
|
|
238
|
-
let stderr = "";
|
|
239
|
-
let spawnError = "";
|
|
240
|
-
child.stdout.on("data", (chunk) => {
|
|
241
|
-
const text = chunk.toString("utf8");
|
|
242
|
-
stdout += text;
|
|
243
|
-
opts.onStdoutChunk?.(text);
|
|
244
|
-
});
|
|
245
|
-
child.stderr.on("data", (chunk) => {
|
|
246
|
-
stderr += chunk.toString("utf8");
|
|
247
|
-
});
|
|
248
|
-
child.stdin.end(opts.stdin ?? "");
|
|
249
|
-
const onAbort = () => {
|
|
250
|
-
if (!child.killed) child.kill("SIGTERM");
|
|
251
|
-
};
|
|
252
|
-
opts.abort.addEventListener("abort", onAbort);
|
|
253
|
-
child.on("close", (code) => {
|
|
254
|
-
opts.abort.removeEventListener("abort", onAbort);
|
|
255
|
-
resolve({ ok: code === 0, exitCode: code ?? -1, stdout: cap2(stdout), stderr, spawnError });
|
|
256
|
-
});
|
|
135
|
+
context.abort.addEventListener("abort", onAbort);
|
|
136
|
+
const code = await new Promise((resolve) => {
|
|
137
|
+
child.on("close", resolve);
|
|
257
138
|
child.on("error", (err) => {
|
|
258
|
-
opts.abort.removeEventListener("abort", onAbort);
|
|
259
139
|
spawnError = err.message;
|
|
260
|
-
resolve(
|
|
140
|
+
resolve(-1);
|
|
261
141
|
});
|
|
262
142
|
});
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
return new Error(`${toolId} failed (exit ${r.exitCode}): ${tail || "(no stderr)"}`);
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
// src/tools/a2h-autotest-resolve-metadata.ts
|
|
271
|
-
var SCRIPT_REL = path6.join("resolve-metadata-tool.ts");
|
|
272
|
-
var TOOL_ID = "a2h_autotest_resolve_metadata";
|
|
273
|
-
function makeA2hAutotestResolveMetadataTool(_deps) {
|
|
274
|
-
return {
|
|
275
|
-
description: "Resolve bundle_name / app_name from a HarmonyOS project (reads AppScope/app.json5 and resolves $string refs) and writes app-metadata.json. Pure metadata extraction \u2014 no model, no device. Use this instead of running `node resolve-metadata-tool.ts` directly. Returns the written metadata JSON path + stdout on success.",
|
|
276
|
-
args: {
|
|
277
|
-
type: "object",
|
|
278
|
-
properties: {
|
|
279
|
-
projectDir: {
|
|
280
|
-
type: "string",
|
|
281
|
-
description: "HarmonyOS \u5DE5\u7A0B\u6839\u76EE\u5F55\uFF08\u542B AppScope/app.json5\uFF09"
|
|
282
|
-
},
|
|
283
|
-
output: {
|
|
284
|
-
type: "string",
|
|
285
|
-
description: "app-metadata.json \u8F93\u51FA\u8DEF\u5F84"
|
|
286
|
-
}
|
|
287
|
-
},
|
|
288
|
-
required: ["projectDir", "output"]
|
|
289
|
-
},
|
|
290
|
-
execute: async (args, context) => {
|
|
291
|
-
const projectDir = String(args.projectDir ?? "");
|
|
292
|
-
const output = String(args.output ?? "");
|
|
293
|
-
if (!projectDir || !output) {
|
|
294
|
-
throw new Error(`${TOOL_ID}: \`projectDir\` and \`output\` are required`);
|
|
295
|
-
}
|
|
296
|
-
const argv = ["--project-dir", projectDir, "--output", output];
|
|
297
|
-
const r = await runAutotestScript({
|
|
298
|
-
scriptRel: SCRIPT_REL,
|
|
299
|
-
argv,
|
|
300
|
-
cwd: context.directory,
|
|
301
|
-
abort: context.abort
|
|
302
|
-
});
|
|
303
|
-
if (!r.ok) throw autotestToolError(TOOL_ID, r);
|
|
304
|
-
return { ok: true, exitCode: r.exitCode, output: r.stdout };
|
|
143
|
+
context.abort.removeEventListener("abort", onAbort);
|
|
144
|
+
if (code !== 0) {
|
|
145
|
+
if (spawnError) {
|
|
146
|
+
throw new Error(`a2h_run_model_script (${entry.name}): failed to spawn node \u2014 ${spawnError}`);
|
|
305
147
|
}
|
|
306
|
-
|
|
148
|
+
const tail = stderr.slice(-2e3);
|
|
149
|
+
throw new Error(
|
|
150
|
+
`a2h_run_model_script (${entry.name}) failed (exit ${code}): ${tail || "(no stderr)"}`
|
|
151
|
+
);
|
|
152
|
+
}
|
|
153
|
+
return { ok: true, exitCode: code, output: cap(stdout) };
|
|
307
154
|
}
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
type: "object",
|
|
318
|
-
properties: {
|
|
319
|
-
action: {
|
|
320
|
-
type: "string",
|
|
321
|
-
enum: ["generate", "validate"],
|
|
322
|
-
description: "generate=\u4ECE\u62BD\u53D6\u8F93\u51FA\u5EFA testcases.json\uFF1Bvalidate=\u6821\u9A8C\u5DF2\u6709 testcases.json"
|
|
323
|
-
},
|
|
324
|
-
input: { type: "string", description: "[generate] \u62BD\u53D6\u8F93\u51FA JSON \u8DEF\u5F84" },
|
|
325
|
-
output: { type: "string", description: "[generate] testcases.json \u8F93\u51FA\u8DEF\u5F84" },
|
|
326
|
-
validate: { type: "boolean", default: false, description: "[generate] \u5199\u5B8C\u540E\u5C31\u5730\u6821\u9A8C" },
|
|
327
|
-
path: { type: "string", description: "[validate] \u8981\u6821\u9A8C\u7684 testcases.json \u8DEF\u5F84" }
|
|
328
|
-
},
|
|
329
|
-
required: ["action"]
|
|
330
|
-
},
|
|
331
|
-
execute: async (args, context) => {
|
|
332
|
-
const action = String(args.action ?? "");
|
|
333
|
-
if (action === "generate") {
|
|
334
|
-
const input = String(args.input ?? "");
|
|
335
|
-
const output = String(args.output ?? "");
|
|
336
|
-
if (!input || !output) {
|
|
337
|
-
throw new Error(`${TOOL_ID2}: \`action:"generate"\` requires \`input\` and \`output\``);
|
|
338
|
-
}
|
|
339
|
-
const argv = ["generate", input, output];
|
|
340
|
-
if (args.validate === true) argv.push("--validate");
|
|
341
|
-
const r = await runAutotestScript({
|
|
342
|
-
scriptRel: SCRIPT_REL2,
|
|
343
|
-
argv,
|
|
344
|
-
cwd: context.directory,
|
|
345
|
-
abort: context.abort
|
|
346
|
-
});
|
|
347
|
-
if (!r.ok) throw autotestToolError(TOOL_ID2, r);
|
|
348
|
-
return { ok: true, exitCode: r.exitCode, output: r.stdout };
|
|
349
|
-
}
|
|
350
|
-
if (action === "validate") {
|
|
351
|
-
const p = String(args.path ?? "");
|
|
352
|
-
if (!p) {
|
|
353
|
-
throw new Error(`${TOOL_ID2}: \`action:"validate"\` requires \`path\``);
|
|
354
|
-
}
|
|
355
|
-
const argv = ["validate", p];
|
|
356
|
-
const r = await runAutotestScript({
|
|
357
|
-
scriptRel: SCRIPT_REL2,
|
|
358
|
-
argv,
|
|
359
|
-
cwd: context.directory,
|
|
360
|
-
abort: context.abort
|
|
361
|
-
});
|
|
362
|
-
if (!r.ok) throw autotestToolError(TOOL_ID2, r);
|
|
363
|
-
return { ok: true, exitCode: r.exitCode, output: r.stdout };
|
|
364
|
-
}
|
|
365
|
-
throw new Error(`${TOOL_ID2}: unknown \`action\` "${action}" (expected generate|validate)`);
|
|
155
|
+
var kebab = (s) => "--" + s.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase();
|
|
156
|
+
function flattenParams(obj) {
|
|
157
|
+
const out = [];
|
|
158
|
+
for (const [k, v] of Object.entries(obj)) {
|
|
159
|
+
if (v == null) continue;
|
|
160
|
+
if (typeof v === "boolean") {
|
|
161
|
+
if (v) out.push(kebab(k));
|
|
162
|
+
} else {
|
|
163
|
+
out.push(kebab(k), String(v));
|
|
366
164
|
}
|
|
367
|
-
}
|
|
165
|
+
}
|
|
166
|
+
return out;
|
|
368
167
|
}
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
import path8 from "node:path";
|
|
372
|
-
var SCRIPT_REL3 = path8.join("engine", "report-tool.ts");
|
|
373
|
-
var TOOL_ID3 = "a2h_autotest_report";
|
|
374
|
-
function makeA2hAutotestReportTool(_deps) {
|
|
168
|
+
var DESCRIPTION_PREFIX = "\u8FD0\u884C\u9700\u8981\u591A\u6A21\u6001\u6A21\u578B\u7684 skill \u811A\u672C:\u5728 HarmonyOS \u6216 Android \u8BBE\u5907\u4E0A\u6267\u884C\u7531 `script` \u9009\u5B9A\u7684\u811A\u672C,\u628A\u53C2\u6570\u4F5C\u4E3A\u5BF9\u8C61(camelCase \u5B57\u6BB5)\u4F20\u5165,\u5DE5\u5177\u81EA\u52A8\u8F6C\u4E3A --kebab-case \u547D\u4EE4\u884C\u53C2\u6570\u900F\u4F20\u7ED9 `node` \u8FDB\u7A0B,\u8FD0\u884C\u5B8C\u6BD5\u8FD4\u56DE\u811A\u672C stdout\u3002\u4F60\u53EA\u9700\u63D0\u4F9B `script` \u4E0E `commandArgs`,\u6A21\u578B\u76F8\u5173\u914D\u7F6E(apiKey \u7B49)\u7531\u5DE5\u5177\u5185\u90E8\u7ECF stdin \u6CE8\u5165,\u4E0D\u8981\u4F20 `apiKey`/`modelStdin` \u5B57\u6BB5\u3002\n\n\u53EF\u7528\u811A\u672C\u53CA\u5176\u53C2\u6570:\n";
|
|
169
|
+
function makeA2hRunModelScriptTool(deps) {
|
|
375
170
|
return {
|
|
376
|
-
description:
|
|
171
|
+
description: DESCRIPTION_PREFIX + MODEL_SCRIPTS.map((s) => `${s.name}: ${s.argsHelp}`).join("\n"),
|
|
172
|
+
// `args` is a flat field-name→field-schema MAP (not a full `{type:"object",
|
|
173
|
+
// properties, required}` schema object). The host's `legacyJsonSchema` reads
|
|
174
|
+
// this map and builds the LLM-facing schema itself: `{type:"object",
|
|
175
|
+
// properties:{script, commandArgs}, required:[script, commandArgs]}` (it
|
|
176
|
+
// marks every field required). Declaring the full-schema wrapper here would
|
|
177
|
+
// make `legacyJsonSchema` mis-read `properties` as a literal field name → the
|
|
178
|
+
// model receives a schema with a single `properties` field and wraps its
|
|
179
|
+
// input under it → "unknown script". Keep it a field-map.
|
|
377
180
|
args: {
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
enum: ["generate", "validate"],
|
|
383
|
-
description: "generate=\u4ECE task \u76EE\u5F55\u6E32\u67D3 self-test-report.md\uFF1Bvalidate=\u6821\u9A8C\u5DF2\u6709 report.md \u5E03\u5C40"
|
|
384
|
-
},
|
|
385
|
-
taskSubdir: { type: "string", description: "[generate] task_<ts>/ \u76EE\u5F55\u8DEF\u5F84" },
|
|
386
|
-
appMetadata: { type: "string", description: "[generate] app-metadata.json \u8DEF\u5F84" },
|
|
387
|
-
hap: { type: "string", description: "[generate] HAP \u6587\u4EF6\u8DEF\u5F84" },
|
|
388
|
-
device: { type: "string", description: "[generate] \u8BBE\u5907\u6807\u8BC6\uFF08\u5982 127.0.0.1:5555\uFF09" },
|
|
389
|
-
suite: { type: "string", description: "[generate] \u6D4B\u8BD5\u5957\u4EF6\u6807\u9898" },
|
|
390
|
-
out: { type: "string", description: "[generate] self-test-report.md \u8F93\u51FA\u8DEF\u5F84" },
|
|
391
|
-
validate: { type: "boolean", default: false, description: "[generate] \u5199\u5B8C\u540E\u5C31\u5730\u6821\u9A8C" },
|
|
392
|
-
path: { type: "string", description: "[validate] \u8981\u6821\u9A8C\u7684 report.md \u8DEF\u5F84" }
|
|
181
|
+
script: {
|
|
182
|
+
type: "string",
|
|
183
|
+
enum: MODEL_SCRIPTS.map((s) => s.name),
|
|
184
|
+
description: "\u8981\u8FD0\u884C\u7684\u6A21\u578B\u811A\u672C\u540D,\u89C1\u4E0A\u65B9\u5404\u6761\u76EE"
|
|
393
185
|
},
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
const action = String(args.action ?? "");
|
|
398
|
-
if (action === "generate") {
|
|
399
|
-
const taskSubdir = String(args.taskSubdir ?? "");
|
|
400
|
-
const appMetadata = String(args.appMetadata ?? "");
|
|
401
|
-
const hap = String(args.hap ?? "");
|
|
402
|
-
const device = String(args.device ?? "");
|
|
403
|
-
const suite = String(args.suite ?? "");
|
|
404
|
-
const out = String(args.out ?? "");
|
|
405
|
-
if (!taskSubdir || !appMetadata || !hap || !device || !suite || !out) {
|
|
406
|
-
throw new Error(
|
|
407
|
-
`${TOOL_ID3}: \`action:"generate"\` requires \`taskSubdir\`, \`appMetadata\`, \`hap\`, \`device\`, \`suite\`, \`out\``
|
|
408
|
-
);
|
|
409
|
-
}
|
|
410
|
-
const argv = [
|
|
411
|
-
"generate",
|
|
412
|
-
"--task-subdir",
|
|
413
|
-
taskSubdir,
|
|
414
|
-
"--app-metadata",
|
|
415
|
-
appMetadata,
|
|
416
|
-
"--hap",
|
|
417
|
-
hap,
|
|
418
|
-
"--device",
|
|
419
|
-
device,
|
|
420
|
-
"--suite",
|
|
421
|
-
suite,
|
|
422
|
-
"--out",
|
|
423
|
-
out
|
|
424
|
-
];
|
|
425
|
-
if (args.validate === true) argv.push("--validate");
|
|
426
|
-
const r = await runAutotestScript({
|
|
427
|
-
scriptRel: SCRIPT_REL3,
|
|
428
|
-
argv,
|
|
429
|
-
cwd: context.directory,
|
|
430
|
-
abort: context.abort
|
|
431
|
-
});
|
|
432
|
-
if (!r.ok) throw autotestToolError(TOOL_ID3, r);
|
|
433
|
-
return { ok: true, exitCode: r.exitCode, output: r.stdout };
|
|
434
|
-
}
|
|
435
|
-
if (action === "validate") {
|
|
436
|
-
const p = String(args.path ?? "");
|
|
437
|
-
if (!p) {
|
|
438
|
-
throw new Error(`${TOOL_ID3}: \`action:"validate"\` requires \`path\``);
|
|
439
|
-
}
|
|
440
|
-
const argv = ["validate", p];
|
|
441
|
-
const r = await runAutotestScript({
|
|
442
|
-
scriptRel: SCRIPT_REL3,
|
|
443
|
-
argv,
|
|
444
|
-
cwd: context.directory,
|
|
445
|
-
abort: context.abort
|
|
446
|
-
});
|
|
447
|
-
if (!r.ok) throw autotestToolError(TOOL_ID3, r);
|
|
448
|
-
return { ok: true, exitCode: r.exitCode, output: r.stdout };
|
|
186
|
+
commandArgs: {
|
|
187
|
+
type: "object",
|
|
188
|
+
description: '\u8BE5\u811A\u672C\u7684\u53C2\u6570\u5BF9\u8C61(camelCase \u5B57\u6BB5,\u5DE5\u5177\u81EA\u52A8\u8F6C\u4E3A --kebab-case \u547D\u4EE4\u884C\u53C2\u6570),\u6309\u4E0A\u65B9\u5BF9\u5E94\u6761\u76EE\u8BF4\u660E\u586B\u5199,\u5F62\u5982 {"device":"adb","app":"\u56FE\u5E93","package":"com.x","prompt":"\u8FDB\u5165\u8BBE\u7F6E","maxSteps":15}'
|
|
449
189
|
}
|
|
450
|
-
throw new Error(`${TOOL_ID3}: unknown \`action\` "${action}" (expected generate|validate)`);
|
|
451
|
-
}
|
|
452
|
-
};
|
|
453
|
-
}
|
|
454
|
-
|
|
455
|
-
// src/tools/a2h-autotest-selftest.ts
|
|
456
|
-
import { spawn as spawn3 } from "node:child_process";
|
|
457
|
-
import path9 from "node:path";
|
|
458
|
-
var SCRIPT_REL4 = path9.join("engine", "self-test-runner.ts");
|
|
459
|
-
var TOOL_ID4 = "a2h_autotest_selftest";
|
|
460
|
-
function makeA2hAutotestSelftestTool(deps) {
|
|
461
|
-
return {
|
|
462
|
-
description: "HarmonyOS on-device self-test engine \u2014 grouped by noun, dispatched on `action`. `run`: hdc uninstall+install of HAP, then a detached @autotest/agent batch (multimodal model resolved by the host, piped via stdin; never env/argv/disk). Without `timeout` returns RUNNING immediately (exit 2); with `timeout` blocks until terminal (0 COMPLETED / 1 FAILED / 3 CRASHED / 4 NOT_STARTED / 5 TIMEOUT). `status`: probe on-disk state (0/2/3/4). `kill`: tear down the batch tree. `check_hap`/`check_inputs`/`check_pre`: pre-flight validators (0 = passed). Exit codes are states, not errors \u2014 branch on `status` in the returned JSON. Only `run` needs a model. Use this instead of running `node self-test-runner.ts` directly.",
|
|
463
|
-
args: {
|
|
464
|
-
type: "object",
|
|
465
|
-
properties: {
|
|
466
|
-
action: {
|
|
467
|
-
type: "string",
|
|
468
|
-
enum: ["run", "status", "kill", "check_hap", "check_inputs", "check_pre"],
|
|
469
|
-
description: "run=\u88C5 HAP + \u8DD1 batch\uFF08\u9700\u6A21\u578B\uFF09\uFF1Bstatus=\u63A2\u72B6\u6001\uFF1Bkill=\u7EC8\u6B62 batch\uFF1Bcheck_hap=\u6821\u9A8C hap_path\uFF1Bcheck_inputs=\u6821\u9A8C testcases+metadata\uFF1Bcheck_pre=\u6821\u9A8C [PRE] \u8FDE\u7EED"
|
|
470
|
-
},
|
|
471
|
-
// ── run ──
|
|
472
|
-
testcases: { type: "string", description: "[run/check_inputs/check_pre] \u6D4B\u8BD5\u7528\u4F8B\u6587\u4EF6\uFF08JSON \u6570\u7EC4\u6216 JSONL\uFF09" },
|
|
473
|
-
hap: { type: "string", description: "[run/check_hap] \u7B7E\u540D\u5305\uFF08.hap/.hsp \u6587\u4EF6\u3001\u76EE\u5F55\uFF0C\u6216\u9017\u53F7\u5206\u9694\u591A\u9879\uFF09" },
|
|
474
|
-
bundleName: { type: "string", description: "[run] \u5305\u540D\uFF08bundle_name\uFF09" },
|
|
475
|
-
taskDir: {
|
|
476
|
-
type: "string",
|
|
477
|
-
description: "[run/status/kill] \u4EFB\u52A1\u76EE\u5F55\uFF08run \u5185\u90E8\u5EFA task_<ts> \u5B50\u76EE\u5F55\uFF1B\u4E0D\u4F20\u5219\u9ED8\u8BA4 'task'\uFF09\u3002status/kill \u636E\u6B64\u627E batch.pid"
|
|
478
|
-
},
|
|
479
|
-
outputDir: { type: "string", description: "[run/status] \u65E5\u5FD7\u76EE\u5F55\uFF08\u4E0D\u4F20\u5219\u540C taskDir\uFF09" },
|
|
480
|
-
category: { type: "string", description: "[run] \u6D4B\u8BD5\u5206\u7C7B\u540D\uFF0C\u4F20\u7ED9 batch-launcher" },
|
|
481
|
-
timeout: {
|
|
482
|
-
type: ["number", "string"],
|
|
483
|
-
description: '[run] \u963B\u585E\u81F3\u7EC8\u6001\u6216\u8D85\u65F6\u3002\u6570\u503C=\u79D2\u6570\uFF1B"auto"=\u5F15\u64CE\u89E3\u6790 testcases \u540E\u6309\u7528\u4F8B\u6570\xD7720s \u81EA\u52A8\u63A8\u5BFC\uFF08\u6BCF\u4F8B 10min + 20% \u4F59\u91CF\uFF09\u3002\u4E0D\u4F20\u5219\u7ACB\u5373\u8FD4\u56DE RUNNING\uFF1B\u8D85\u65F6\u540E\u81EA\u52A8 kill batch \u5E76\u8FD4\u56DE TIMEOUT\uFF08exit 5\uFF09'
|
|
484
|
-
},
|
|
485
|
-
deviceSn: { type: "string", description: "[run] \u8BBE\u5907\u5E8F\u5217\u53F7\uFF08\u2192 AutoTestAgent.deviceSn\uFF09" },
|
|
486
|
-
ip: { type: "string", description: "[run] \u8BBE\u5907 IP\uFF08\u2192 AutoTestAgent.ip\uFF09" },
|
|
487
|
-
port: { type: "string", description: "[run] \u8BBE\u5907\u7AEF\u53E3\uFF08\u2192 AutoTestAgent.port\uFF09" },
|
|
488
|
-
agentMode: { type: "string", description: "[run] agent \u6A21\u5F0F\uFF08\u9ED8\u8BA4 single\uFF09" },
|
|
489
|
-
maxSteps: { type: "number", description: "[run] \u5355\u7528\u4F8B\u6700\u5927\u6B65\u6570" },
|
|
490
|
-
promptVersion: { type: "string", description: "[run] prompt \u7248\u672C\uFF08\u9ED8\u8BA4 v2.0\uFF09" },
|
|
491
|
-
// ── check_inputs ──
|
|
492
|
-
metadata: { type: "string", description: "[check_inputs] app-metadata.json \u8DEF\u5F84" }
|
|
493
|
-
},
|
|
494
|
-
required: ["action"]
|
|
495
190
|
},
|
|
496
191
|
execute: async (args, context) => {
|
|
497
|
-
const
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
throw new Error(`${TOOL_ID4}: \`action:"run"\` requires \`testcases\`, \`hap\`, \`bundleName\``);
|
|
504
|
-
}
|
|
505
|
-
const taskDir = String(args.taskDir ?? "task");
|
|
506
|
-
const argv = [
|
|
507
|
-
"run",
|
|
508
|
-
"--testcases",
|
|
509
|
-
testcases,
|
|
510
|
-
"--hap",
|
|
511
|
-
hap,
|
|
512
|
-
"--bundle-name",
|
|
513
|
-
bundleName,
|
|
514
|
-
"--task-dir",
|
|
515
|
-
taskDir,
|
|
516
|
-
"--model-stdin"
|
|
517
|
-
];
|
|
518
|
-
if (args.outputDir) argv.push("--output-dir", String(args.outputDir));
|
|
519
|
-
if (args.category) argv.push("--category", String(args.category));
|
|
520
|
-
if (args.deviceSn) argv.push("--device-sn", String(args.deviceSn));
|
|
521
|
-
if (args.ip) argv.push("--ip", String(args.ip));
|
|
522
|
-
if (args.port) argv.push("--port", String(args.port));
|
|
523
|
-
if (args.agentMode) argv.push("--agent-mode", String(args.agentMode));
|
|
524
|
-
if (args.maxSteps !== void 0 && args.maxSteps !== null) {
|
|
525
|
-
argv.push("--max-steps", String(args.maxSteps));
|
|
526
|
-
}
|
|
527
|
-
if (args.promptVersion) argv.push("--prompt-version", String(args.promptVersion));
|
|
528
|
-
if (args.timeout !== void 0 && args.timeout !== null) {
|
|
529
|
-
argv.push("--timeout", String(args.timeout));
|
|
530
|
-
}
|
|
531
|
-
const model = await deps.resolveModelParams(context.worktree);
|
|
532
|
-
if (!model) {
|
|
533
|
-
throw new Error(
|
|
534
|
-
`${TOOL_ID4}: \`action:"run"\` needs a model but deps.resolveModelParams returned null for worktree ${context.worktree ?? "(none)"} \u2014 configure the host's model provider`
|
|
535
|
-
);
|
|
536
|
-
}
|
|
537
|
-
const stdin = JSON.stringify({
|
|
538
|
-
apiKey: model.apiKey,
|
|
539
|
-
modelName: model.modelName,
|
|
540
|
-
baseURL: model.baseURL
|
|
541
|
-
});
|
|
542
|
-
const onAbortKill = () => {
|
|
543
|
-
try {
|
|
544
|
-
const env = { ...process.env };
|
|
545
|
-
delete env.HOMETRANS_MODEL_API_KEY;
|
|
546
|
-
delete env.HOMETRANS_MODEL_NAME;
|
|
547
|
-
delete env.HOMETRANS_MODEL_BASE_URL;
|
|
548
|
-
delete env.GLM_API_KEY;
|
|
549
|
-
delete env.TEST_API_KEY;
|
|
550
|
-
const k = spawn3(
|
|
551
|
-
"node",
|
|
552
|
-
[path9.join(AUTOTEST_ROOT, SCRIPT_REL4), "kill", "--task-dir", taskDir],
|
|
553
|
-
{ cwd: context.directory, env, stdio: "ignore", detached: true, windowsHide: true }
|
|
554
|
-
);
|
|
555
|
-
k.on("error", () => {
|
|
556
|
-
});
|
|
557
|
-
k.unref();
|
|
558
|
-
} catch {
|
|
559
|
-
}
|
|
560
|
-
};
|
|
561
|
-
if (!context.abort.aborted) {
|
|
562
|
-
context.abort.addEventListener("abort", onAbortKill);
|
|
563
|
-
}
|
|
564
|
-
const r = await runAutotestScript({
|
|
565
|
-
scriptRel: SCRIPT_REL4,
|
|
566
|
-
argv,
|
|
567
|
-
cwd: context.directory,
|
|
568
|
-
abort: context.abort,
|
|
569
|
-
stdin
|
|
570
|
-
});
|
|
571
|
-
context.abort.removeEventListener("abort", onAbortKill);
|
|
572
|
-
if (r.spawnError) {
|
|
573
|
-
throw new Error(`${TOOL_ID4}: failed to spawn node \u2014 ${r.spawnError}`);
|
|
574
|
-
}
|
|
575
|
-
return { ok: r.exitCode === 0, exitCode: r.exitCode, output: r.stdout };
|
|
576
|
-
}
|
|
577
|
-
if (action === "status") {
|
|
578
|
-
const taskDir = String(args.taskDir ?? "");
|
|
579
|
-
if (!taskDir) {
|
|
580
|
-
throw new Error(`${TOOL_ID4}: \`action:"status"\` requires \`taskDir\``);
|
|
581
|
-
}
|
|
582
|
-
const argv = ["status", "--task-dir", taskDir];
|
|
583
|
-
if (args.outputDir) argv.push("--output-dir", String(args.outputDir));
|
|
584
|
-
const r = await runAutotestScript({
|
|
585
|
-
scriptRel: SCRIPT_REL4,
|
|
586
|
-
argv,
|
|
587
|
-
cwd: context.directory,
|
|
588
|
-
abort: context.abort
|
|
589
|
-
});
|
|
590
|
-
if (r.spawnError) {
|
|
591
|
-
throw new Error(`${TOOL_ID4}: failed to spawn node \u2014 ${r.spawnError}`);
|
|
592
|
-
}
|
|
593
|
-
return { ok: r.exitCode === 0, exitCode: r.exitCode, output: r.stdout };
|
|
594
|
-
}
|
|
595
|
-
if (action === "kill") {
|
|
596
|
-
const taskDir = String(args.taskDir ?? "");
|
|
597
|
-
if (!taskDir) {
|
|
598
|
-
throw new Error(`${TOOL_ID4}: \`action:"kill"\` requires \`taskDir\``);
|
|
599
|
-
}
|
|
600
|
-
const argv = ["kill", "--task-dir", taskDir];
|
|
601
|
-
const r = await runAutotestScript({
|
|
602
|
-
scriptRel: SCRIPT_REL4,
|
|
603
|
-
argv,
|
|
604
|
-
cwd: context.directory,
|
|
605
|
-
abort: context.abort
|
|
606
|
-
});
|
|
607
|
-
if (r.spawnError) {
|
|
608
|
-
throw new Error(`${TOOL_ID4}: failed to spawn node \u2014 ${r.spawnError}`);
|
|
609
|
-
}
|
|
610
|
-
return { ok: r.exitCode === 0, exitCode: r.exitCode, output: r.stdout };
|
|
192
|
+
const name = String(args.script ?? "");
|
|
193
|
+
const entry = MODEL_SCRIPTS.find((s) => s.name === name);
|
|
194
|
+
if (!entry) {
|
|
195
|
+
throw new Error(
|
|
196
|
+
`a2h_run_model_script: unknown script "${name}". Registered: ${MODEL_SCRIPTS.map((s) => s.name).join(", ")}`
|
|
197
|
+
);
|
|
611
198
|
}
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
});
|
|
624
|
-
if (r.spawnError) {
|
|
625
|
-
throw new Error(`${TOOL_ID4}: failed to spawn node \u2014 ${r.spawnError}`);
|
|
626
|
-
}
|
|
627
|
-
return { ok: r.exitCode === 0, exitCode: r.exitCode, output: r.stdout };
|
|
628
|
-
}
|
|
629
|
-
if (action === "check_inputs") {
|
|
630
|
-
const testcases = String(args.testcases ?? "");
|
|
631
|
-
const metadata = String(args.metadata ?? "");
|
|
632
|
-
if (!testcases || !metadata) {
|
|
633
|
-
throw new Error(`${TOOL_ID4}: \`action:"check_inputs"\` requires \`testcases\` and \`metadata\``);
|
|
634
|
-
}
|
|
635
|
-
const argv = ["check-inputs", "--testcases", testcases, "--metadata", metadata];
|
|
636
|
-
const r = await runAutotestScript({
|
|
637
|
-
scriptRel: SCRIPT_REL4,
|
|
638
|
-
argv,
|
|
639
|
-
cwd: context.directory,
|
|
640
|
-
abort: context.abort
|
|
641
|
-
});
|
|
642
|
-
if (r.spawnError) {
|
|
643
|
-
throw new Error(`${TOOL_ID4}: failed to spawn node \u2014 ${r.spawnError}`);
|
|
199
|
+
const params = {};
|
|
200
|
+
const a = args.commandArgs;
|
|
201
|
+
if (a && typeof a === "object") {
|
|
202
|
+
Object.assign(params, a);
|
|
203
|
+
} else if (typeof a === "string" && a.trim()) {
|
|
204
|
+
try {
|
|
205
|
+
const parsed = JSON.parse(a);
|
|
206
|
+
if (parsed && typeof parsed === "object") {
|
|
207
|
+
Object.assign(params, parsed);
|
|
208
|
+
}
|
|
209
|
+
} catch {
|
|
644
210
|
}
|
|
645
|
-
return { ok: r.exitCode === 0, exitCode: r.exitCode, output: r.stdout };
|
|
646
211
|
}
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
if (!testcases) {
|
|
650
|
-
throw new Error(`${TOOL_ID4}: \`action:"check_pre"\` requires \`testcases\``);
|
|
651
|
-
}
|
|
652
|
-
const argv = ["check-pre", "--testcases", testcases];
|
|
653
|
-
const r = await runAutotestScript({
|
|
654
|
-
scriptRel: SCRIPT_REL4,
|
|
655
|
-
argv,
|
|
656
|
-
cwd: context.directory,
|
|
657
|
-
abort: context.abort
|
|
658
|
-
});
|
|
659
|
-
if (r.spawnError) {
|
|
660
|
-
throw new Error(`${TOOL_ID4}: failed to spawn node \u2014 ${r.spawnError}`);
|
|
661
|
-
}
|
|
662
|
-
return { ok: r.exitCode === 0, exitCode: r.exitCode, output: r.stdout };
|
|
212
|
+
for (const [k, v] of Object.entries(args)) {
|
|
213
|
+
if (k !== "script" && k !== "commandArgs") params[k] = v;
|
|
663
214
|
}
|
|
664
|
-
|
|
665
|
-
`${TOOL_ID4}: unknown \`action\` "${action}" (expected run|status|kill|check_hap|check_inputs|check_pre)`
|
|
666
|
-
);
|
|
215
|
+
return runModelScript(deps, context, entry, flattenParams(params));
|
|
667
216
|
}
|
|
668
217
|
};
|
|
669
218
|
}
|
|
@@ -671,8 +220,8 @@ function makeA2hAutotestSelftestTool(deps) {
|
|
|
671
220
|
// src/index.ts
|
|
672
221
|
function createServer(deps) {
|
|
673
222
|
return async (input) => {
|
|
674
|
-
const skillsRoot =
|
|
675
|
-
const agentsRoot =
|
|
223
|
+
const skillsRoot = path5.join(PKG_ROOT, "skills");
|
|
224
|
+
const agentsRoot = path5.join(PKG_ROOT, "agents");
|
|
676
225
|
const agentPrompts = discoverA2HAgents(agentsRoot);
|
|
677
226
|
return {
|
|
678
227
|
dispose: async () => {
|
|
@@ -699,33 +248,29 @@ function createServer(deps) {
|
|
|
699
248
|
}
|
|
700
249
|
}
|
|
701
250
|
},
|
|
702
|
-
// ── shell.env hook: inject DevEco Studio
|
|
703
|
-
//
|
|
704
|
-
//
|
|
705
|
-
//
|
|
251
|
+
// ── shell.env hook: inject DevEco Studio / SDK path vars only. The ──
|
|
252
|
+
// multimodal model apiKey is deliberately NOT put in env (it would leak
|
|
253
|
+
// to every adb/hdc descendant); the a2h_run_model_script tool pipes it to
|
|
254
|
+
// the script over stdin instead. See src/env.ts.
|
|
706
255
|
"shell.env": async (shellInput, output) => {
|
|
707
256
|
const a2hEnv = await buildA2HEnvBase(deps);
|
|
708
257
|
Object.assign(output.env, a2hEnv);
|
|
709
258
|
},
|
|
710
|
-
// ── tool hook: run
|
|
711
|
-
//
|
|
712
|
-
//
|
|
259
|
+
// ── tool hook: run skill scripts that need the multimodal model without
|
|
260
|
+
// leaking the apiKey into env/argv. The generic `a2h_run_model_script`
|
|
261
|
+
// tool is driven by a registry of scripts (src/tools/a2h-run-model-
|
|
262
|
+
// script.ts): the agent picks a registered `script` and passes its CLI
|
|
263
|
+
// `commandArgs`; the tool resolves the apiKey from the host and pipes it to the
|
|
264
|
+
// script over stdin. Adding a model script = one registry entry (no new
|
|
265
|
+
// tool file, no index.ts change).
|
|
713
266
|
//
|
|
714
|
-
// The
|
|
715
|
-
//
|
|
716
|
-
//
|
|
717
|
-
//
|
|
718
|
-
//
|
|
719
|
-
// with `action:"run"` needs a model, resolved via deps.resolveModelParams
|
|
720
|
-
// and piped over stdin with the same secret-handling discipline; the
|
|
721
|
-
// rest are pure JSON/Markdown transforms or state probes with no model
|
|
722
|
-
// and no device.
|
|
267
|
+
// The AutoTest engine (selftest / testcases / report / resolve-metadata)
|
|
268
|
+
// is no longer vendored here — it lives in the HomeTrans package and is
|
|
269
|
+
// invoked directly via `node` by agents/self-tester.md (see that file's
|
|
270
|
+
// "Resolving the AutoTest Tools Directory" section). a2h therefore ships
|
|
271
|
+
// only this one plugin tool and carries no autotest runtime dependency.
|
|
723
272
|
tool: {
|
|
724
|
-
|
|
725
|
-
a2h_autotest_resolve_metadata: makeA2hAutotestResolveMetadataTool(deps),
|
|
726
|
-
a2h_autotest_testcases: makeA2hAutotestTestcasesTool(deps),
|
|
727
|
-
a2h_autotest_report: makeA2hAutotestReportTool(deps),
|
|
728
|
-
a2h_autotest_selftest: makeA2hAutotestSelftestTool(deps)
|
|
273
|
+
a2h_run_model_script: makeA2hRunModelScriptTool(deps)
|
|
729
274
|
}
|
|
730
275
|
};
|
|
731
276
|
};
|