android2harmony 0.1.4 → 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.
Files changed (104) hide show
  1. package/README.md +1 -407
  2. package/agents/self-tester.md +376 -376
  3. package/dist/index.js +134 -116
  4. package/dist/index.js.map +3 -3
  5. package/package.json +36 -32
  6. package/skills/a2h-resource-convert/SKILL.md +902 -0
  7. package/skills/a2h-resource-convert/references/code-vector-icon-rules.md +335 -0
  8. package/skills/{hmos-resources-convert → a2h-resource-convert}/references/conversion-rules.md +15 -2
  9. package/skills/{hmos-resources-convert → a2h-resource-convert}/references/dependency-analysis-rules.md +24 -2
  10. package/skills/a2h-resource-convert/references/lottie-conversion-rules.md +219 -0
  11. package/skills/{hmos-resources-convert → a2h-resource-convert}/references/resource-mapping-rules.md +27 -2
  12. package/skills/{hmos-resources-convert → a2h-resource-convert}/references/xml-drawable-to-svg-rules.md +118 -1
  13. package/skills/a2h-resource-convert/scripts/a2h_resource_convert.js +2166 -0
  14. package/skills/a2h-resource-convert/scripts/code_vector_icons.js +607 -0
  15. package/skills/a2h-resource-convert/scripts/package.json +3 -0
  16. package/skills/a2h-resource-convert/scripts/svg_fidelity_check.js +632 -0
  17. package/skills/a2h-ui-transfer/SKILL.md +420 -0
  18. package/skills/a2h-ui-transfer/references/conversion-procedure.md +572 -0
  19. 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
  20. 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
  21. package/skills/{hmos-batch-ui-align/scripts/android_parse_fast.ts → a2h-ui-transfer/scripts/android_parse_fast.js} +397 -283
  22. package/skills/a2h-ui-transfer/scripts/arkts_static_check.js +1624 -0
  23. package/skills/a2h-ui-transfer/scripts/measure_pack.js +1005 -0
  24. package/skills/a2h-ui-transfer/scripts/package.json +3 -0
  25. package/skills/hmos-incremental-ui-align/README.md +251 -251
  26. package/skills/hmos-incremental-ui-align/SKILL.md +364 -364
  27. package/skills/hmos-incremental-ui-align/references/State_Model_Template.md +2 -2
  28. package/skills/hmos-incremental-ui-align/scripts/app_feature_verify.js +790 -0
  29. package/skills/hmos-incremental-ui-align/scripts/extract_checklist.js +285 -0
  30. package/skills/hmos-incremental-ui-align/scripts/navigation-capure.md +76 -76
  31. package/skills/hmos-incremental-ui-align/scripts/page_capture.js +756 -0
  32. package/skills/hmos-incremental-ui-align/scripts/page_capture_burst.js +155 -0
  33. package/skills/hmos-batch-ui-align/SKILL.md +0 -141
  34. package/skills/hmos-batch-ui-align/references/conversion-procedure.md +0 -217
  35. package/skills/hmos-incremental-ui-align/scripts/app_feature_verify.ts +0 -999
  36. package/skills/hmos-incremental-ui-align/scripts/extract_checklist.ts +0 -343
  37. package/skills/hmos-incremental-ui-align/scripts/page_capture.ts +0 -977
  38. package/skills/hmos-incremental-ui-align/scripts/page_capture_burst.ts +0 -188
  39. package/skills/hmos-resources-convert/SKILL.md +0 -654
  40. package/skills/hmos-resources-convert/template/AppScope/app.json5 +0 -10
  41. package/skills/hmos-resources-convert/template/AppScope/resources/base/element/string.json +0 -8
  42. package/skills/hmos-resources-convert/template/AppScope/resources/base/media/background.png +0 -0
  43. package/skills/hmos-resources-convert/template/AppScope/resources/base/media/foreground.png +0 -0
  44. package/skills/hmos-resources-convert/template/AppScope/resources/base/media/layered_image.json +0 -7
  45. package/skills/hmos-resources-convert/template/build-profile.json5 +0 -42
  46. package/skills/hmos-resources-convert/template/code-linter.json5 +0 -32
  47. package/skills/hmos-resources-convert/template/entry/build-profile.json5 +0 -33
  48. package/skills/hmos-resources-convert/template/entry/hvigorfile.ts +0 -6
  49. package/skills/hmos-resources-convert/template/entry/obfuscation-rules.txt +0 -23
  50. package/skills/hmos-resources-convert/template/entry/oh-package.json5 +0 -10
  51. package/skills/hmos-resources-convert/template/entry/src/main/ets/entryability/EntryAbility.ets +0 -48
  52. package/skills/hmos-resources-convert/template/entry/src/main/ets/entrybackupability/EntryBackupAbility.ets +0 -16
  53. package/skills/hmos-resources-convert/template/entry/src/main/ets/pages/Index.ets +0 -23
  54. package/skills/hmos-resources-convert/template/entry/src/main/module.json5 +0 -55
  55. package/skills/hmos-resources-convert/template/entry/src/main/resources/base/element/color.json +0 -8
  56. package/skills/hmos-resources-convert/template/entry/src/main/resources/base/element/float.json +0 -8
  57. package/skills/hmos-resources-convert/template/entry/src/main/resources/base/element/string.json +0 -16
  58. package/skills/hmos-resources-convert/template/entry/src/main/resources/base/media/background.png +0 -0
  59. package/skills/hmos-resources-convert/template/entry/src/main/resources/base/media/foreground.png +0 -0
  60. package/skills/hmos-resources-convert/template/entry/src/main/resources/base/media/layered_image.json +0 -7
  61. package/skills/hmos-resources-convert/template/entry/src/main/resources/base/media/startIcon.png +0 -0
  62. package/skills/hmos-resources-convert/template/entry/src/main/resources/base/profile/backup_config.json +0 -3
  63. package/skills/hmos-resources-convert/template/entry/src/main/resources/base/profile/main_pages.json +0 -5
  64. package/skills/hmos-resources-convert/template/entry/src/main/resources/dark/element/color.json +0 -8
  65. package/skills/hmos-resources-convert/template/entry/src/mock/mock-config.json5 +0 -2
  66. package/skills/hmos-resources-convert/template/entry/src/ohosTest/ets/test/Ability.test.ets +0 -35
  67. package/skills/hmos-resources-convert/template/entry/src/ohosTest/ets/test/List.test.ets +0 -5
  68. package/skills/hmos-resources-convert/template/entry/src/ohosTest/module.json5 +0 -16
  69. package/skills/hmos-resources-convert/template/entry/src/test/List.test.ets +0 -5
  70. package/skills/hmos-resources-convert/template/entry/src/test/LocalUnit.test.ets +0 -33
  71. package/skills/hmos-resources-convert/template/hvigor/hvigor-config.json5 +0 -23
  72. package/skills/hmos-resources-convert/template/hvigorfile.ts +0 -6
  73. package/skills/hmos-resources-convert/template/oh-package-lock.json5 +0 -28
  74. package/skills/hmos-resources-convert/template/oh-package.json5 +0 -10
  75. /package/skills/{hmos-batch-ui-align → a2h-ui-transfer}/references/mappings/android-to-harmonyOS-ui-atomic-component-mapping-reference.md +0 -0
  76. /package/skills/{hmos-batch-ui-align → a2h-ui-transfer}/references/mappings/android-to-harmonyOS-ui-interaction-mapping-reference.md +0 -0
  77. /package/skills/{hmos-batch-ui-align → a2h-ui-transfer}/references/mappings/android-to-harmonyOS-ui-layout-mapping-reference.md +0 -0
  78. /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
  79. /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
  80. /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
  81. /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
  82. /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
  83. /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
  84. /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
  85. /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
  86. /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
  87. /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
  88. /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
  89. /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
  90. /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
  91. /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
  92. /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
  93. /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
  94. /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
  95. /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
  96. /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
  97. /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
  98. /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
  99. /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
  100. /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
  101. /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
  102. /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
  103. /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
  104. /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
@@ -70,22 +70,21 @@ async function buildA2HEnvBase(deps) {
70
70
  return env;
71
71
  }
72
72
 
73
- // src/tools/a2h-app-feature-verify.ts
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 SCRIPT_PATH = path4.join(
77
- PKG_ROOT,
78
- "skills",
79
- "hmos-incremental-ui-align",
80
- "scripts",
81
- "app_feature_verify.ts"
82
- );
83
- var SECRET_ENV = [
84
- "HOMETRANS_MODEL_API_KEY",
85
- "HOMETRANS_MODEL_NAME",
86
- "HOMETRANS_MODEL_BASE_URL",
87
- "GLM_API_KEY",
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,111 +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 makeA2hAppFeatureVerifyTool(deps) {
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 {
124
+ }
125
+ break;
126
+ }
127
+ }
128
+ });
129
+ child.stderr.on("data", (chunk) => {
130
+ stderr += chunk.toString("utf8");
131
+ });
132
+ const onAbort = () => {
133
+ if (!child.killed) child.kill("SIGTERM");
134
+ };
135
+ context.abort.addEventListener("abort", onAbort);
136
+ const code = await new Promise((resolve) => {
137
+ child.on("close", resolve);
138
+ child.on("error", (err) => {
139
+ spawnError = err.message;
140
+ resolve(-1);
141
+ });
142
+ });
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}`);
147
+ }
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) };
154
+ }
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));
164
+ }
165
+ }
166
+ return out;
167
+ }
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) {
98
170
  return {
99
- description: "Run the a2h UI feature-verification phone agent (app_feature_verify.ts) to navigate an app and verify a feature path on a connected device. The multimodal model apiKey is resolved from the host and piped to the script over stdin \u2014 it is never placed in env or argv, so it is not visible to the spawned adb/hdc subprocesses. Use this instead of running `node app_feature_verify.ts` directly. The script runs its internal agent loop (screenshot \u2192 model \u2192 action) up to maxSteps and prints per-step thinking + actions; the full stdout is returned on success. One of task/prompt/feature is required; app and package are required.",
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.
100
180
  args: {
101
- type: "object",
102
- properties: {
103
- app: { type: "string", description: "\u76EE\u6807 app \u663E\u793A\u540D\uFF08required\uFF09" },
104
- package: { type: "string", description: "\u76EE\u6807 app \u5305\u540D\uFF08required\uFF09" },
105
- device: {
106
- type: "string",
107
- enum: ["hdc", "adb"],
108
- default: "hdc",
109
- description: "\u8BBE\u5907\u9A71\u52A8\uFF1Ahdc(HarmonyOS) / adb(Android)"
110
- },
111
- task: {
112
- type: "string",
113
- description: "L1/L2/L3 \u4EFB\u52A1\u8DEF\u5F84\uFF0C\u5982 L1\u76F8\u518C\u8BE6\u60C5L2\u66F4\u591A\uFF08\u4E0E prompt/feature \u4E09\u9009\u4E00\uFF09"
114
- },
115
- prompt: { type: "string", description: "\u81EA\u7531 prompt\uFF08\u4E0E task/feature \u4E09\u9009\u4E00\uFF09" },
116
- feature: { type: "string", description: ".md \u529F\u80FD\u6E05\u5355\u8DEF\u5F84\uFF08\u4E0E task/prompt \u4E09\u9009\u4E00\uFF09" },
117
- maxSteps: { type: "number", default: 30, description: "\u6700\u5927 agent \u6B65\u6570" },
118
- appHints: { type: "string", description: "\u5E94\u7528\u5BFC\u822A\u63D0\u793A" },
119
- serial: { type: "string", description: "\u8BBE\u5907\u5E8F\u5217\u53F7" },
120
- limit: { type: "number", description: "--feature \u6A21\u5F0F\u4E0B\u53D6\u524D N \u6761" },
121
- offset: { type: "number", default: 1, description: "--feature \u6A21\u5F0F\u4E0B\u504F\u79FB" },
122
- quiet: { type: "boolean", default: false, description: "\u6291\u5236\u63D0\u793A\u6027\u8F93\u51FA" }
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"
123
185
  },
124
- required: ["app", "package"]
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}'
189
+ }
125
190
  },
126
191
  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");
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
+ );
131
198
  }
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 {
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);
176
208
  }
209
+ } catch {
177
210
  }
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}`);
197
- }
198
- const tail = stderr.slice(-2e3);
199
- throw new Error(`a2h_app_feature_verify failed (exit ${code}): ${tail || "(no stderr)"}`);
200
211
  }
201
- return { ok: true, exitCode: code, output: cap(stdout) };
212
+ for (const [k, v] of Object.entries(args)) {
213
+ if (k !== "script" && k !== "commandArgs") params[k] = v;
214
+ }
215
+ return runModelScript(deps, context, entry, flattenParams(params));
202
216
  }
203
217
  };
204
218
  }
@@ -236,15 +250,19 @@ function createServer(deps) {
236
250
  },
237
251
  // ── shell.env hook: inject DevEco Studio / SDK path vars only. The ──
238
252
  // multimodal model apiKey is deliberately NOT put in env (it would leak
239
- // to every adb/hdc descendant); the a2h_app_feature_verify tool pipes it
240
- // to the script over stdin instead. See src/env.ts.
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.
241
255
  "shell.env": async (shellInput, output) => {
242
256
  const a2hEnv = await buildA2HEnvBase(deps);
243
257
  Object.assign(output.env, a2hEnv);
244
258
  },
245
- // ── tool hook: run the UI-verification phone agent without leaking the
246
- // model apiKey into env/argv it is resolved from the host and piped to
247
- // the script over stdin. See src/tools/a2h-app-feature-verify.ts.
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).
248
266
  //
249
267
  // The AutoTest engine (selftest / testcases / report / resolve-metadata)
250
268
  // is no longer vendored here — it lives in the HomeTrans package and is
@@ -252,7 +270,7 @@ function createServer(deps) {
252
270
  // "Resolving the AutoTest Tools Directory" section). a2h therefore ships
253
271
  // only this one plugin tool and carries no autotest runtime dependency.
254
272
  tool: {
255
- a2h_app_feature_verify: makeA2hAppFeatureVerifyTool(deps)
273
+ a2h_run_model_script: makeA2hRunModelScriptTool(deps)
256
274
  }
257
275
  };
258
276
  };
package/dist/index.js.map CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
- "sources": ["../src/index.ts", "../src/paths.ts", "../src/discovery.ts", "../src/env.ts", "../src/tools/a2h-app-feature-verify.ts"],
4
- "sourcesContent": ["import path from \"node:path\"\r\nimport type { A2HHostDeps, Plugin, PluginInput, PluginHooks } from \"./types\"\r\nimport { PKG_ROOT } from \"./paths\"\r\nimport { discoverA2HAgents } from \"./discovery\"\r\nimport { buildA2HEnvBase } from \"./env\"\r\nimport { makeA2hAppFeatureVerifyTool } from \"./tools/a2h-app-feature-verify\"\r\n\r\n// Public re-exports \u2014 the package's external surface (dev eco code loads\r\n// dist/index.js at runtime and calls `createServer`; `A2HHostDeps` /\r\n// `PLUGIN_ID` are kept exported for any TS consumers).\r\nexport type { A2HHostDeps } from \"./types\"\r\nexport { PLUGIN_ID } from \"./paths\"\r\n\r\n/**\r\n * Create the A2H DevEco Code plugin.\r\n *\r\n * This is a self-contained plugin - skills/, agents/, and tools/\r\n * are bundled inside the package, NOT referenced from HomeTrans.\r\n * A2H-Plugin is the independently publishable \"plugin edition\" of HomeTrans.\r\n *\r\n * @param deps - host dependencies injected by DevEco Code's thin bridge.\r\n * The A2H Plugin defines what it needs (A2HHostDeps);\r\n * DevEco Code provides how to resolve it.\r\n * @returns A Plugin function ready to be registered as a built-in plugin.\r\n */\r\nexport function createServer(deps: A2HHostDeps): Plugin {\r\n return async (input: PluginInput): Promise<PluginHooks> => {\r\n // All A2H assets live inside this package (self-contained, no HomeTrans dep)\r\n const skillsRoot = path.join(PKG_ROOT, \"skills\")\r\n const agentsRoot = path.join(PKG_ROOT, \"agents\")\r\n\r\n // Auto-discover agents: scan top-level agents/*.md, parse `name:` from\r\n // frontmatter, strip frontmatter for prompt body. Adding/removing/renaming\r\n // an agent is just a file drop \u2014 no code change, no rebuild.\r\n const agentPrompts = discoverA2HAgents(agentsRoot)\r\n\r\n return {\r\n dispose: async () => {\r\n agentPrompts.clear()\r\n },\r\n\r\n // \u2500\u2500 config hook: register skills search paths and agents \u2500\u2500\r\n config: async (cfg: Record<string, unknown>) => {\r\n // --- Skills ---\r\n const skills = (cfg.skills as Record<string, unknown>) || {}\r\n cfg.skills = skills\r\n\r\n // Register the bundled skills/ directory as an additional search path.\r\n // All bundled skills are user-visible \u2014 there is no partial-visibility\r\n // (hidden) layer anymore, so we only register the search path here.\r\n const paths = (skills.paths as string[]) || []\r\n skills.paths = paths\r\n if (!paths.includes(skillsRoot)) {\r\n paths.push(skillsRoot)\r\n }\r\n\r\n // --- Agents ---\r\n const agent = (cfg.agent as Record<string, Record<string, unknown>>) || {}\r\n cfg.agent = agent\r\n\r\n for (const [name, prompt] of agentPrompts) {\r\n // Don't overwrite if the user already configured this agent\r\n if (!agent[name]) {\r\n agent[name] = {\r\n mode: \"subagent\",\r\n hidden: true,\r\n prompt,\r\n }\r\n }\r\n }\r\n\r\n },\r\n\r\n // \u2500\u2500 shell.env hook: inject DevEco Studio / SDK path vars only. The \u2500\u2500\r\n // multimodal model apiKey is deliberately NOT put in env (it would leak\r\n // to every adb/hdc descendant); the a2h_app_feature_verify tool pipes it\r\n // to the script over stdin instead. See src/env.ts.\r\n \"shell.env\": async (\r\n shellInput: { cwd: string; sessionID?: string; callID?: string },\r\n output: { env: Record<string, string> },\r\n ) => {\r\n const a2hEnv = await buildA2HEnvBase(deps)\r\n Object.assign(output.env, a2hEnv)\r\n },\r\n\r\n // \u2500\u2500 tool hook: run the UI-verification phone agent without leaking the\r\n // model apiKey into env/argv \u2014 it is resolved from the host and piped to\r\n // the script over stdin. See src/tools/a2h-app-feature-verify.ts.\r\n //\r\n // The AutoTest engine (selftest / testcases / report / resolve-metadata)\r\n // is no longer vendored here \u2014 it lives in the HomeTrans package and is\r\n // invoked directly via `node` by agents/self-tester.md (see that file's\r\n // \"Resolving the AutoTest Tools Directory\" section). a2h therefore ships\r\n // only this one plugin tool and carries no autotest runtime dependency.\r\n tool: {\r\n a2h_app_feature_verify: makeA2hAppFeatureVerifyTool(deps),\r\n },\r\n }\r\n }\r\n}\r\n", "import path from \"node:path\"\r\nimport { fileURLToPath } from \"node:url\"\r\n\r\n// Resolve package root (one directory above dist/ or src/). Computed once here\r\n// and imported everywhere else so there's a single source of truth. After\r\n// esbuild bundling, import.meta.url points at dist/index.js, so this still\r\n// resolves to the package root at runtime.\r\nconst __filename = fileURLToPath(import.meta.url)\r\nconst __dirname = path.dirname(__filename)\r\n\r\n/** Absolute path to the A2H-Plugin package root. */\r\nexport const PKG_ROOT = path.resolve(__dirname, \"..\")\r\n\r\n/** Public plugin id. */\r\nexport const PLUGIN_ID = \"android2harmony\"\r\n", "import path from \"node:path\"\r\nimport fs from \"node:fs\"\r\n\r\n/**\r\n * Strip YAML frontmatter (--- ... ---) from agent .md files.\r\n * Agent .md files have metadata in frontmatter; only the body\r\n * should be used as the LLM prompt.\r\n */\r\nexport function stripFrontmatter(content: string): string {\r\n const match = content.match(/^---\\s*\\n([\\s\\S]*?)\\n---\\s*\\n/)\r\n return match ? content.slice(match[0].length).trim() : content\r\n}\r\n\r\n/**\r\n * Minimal YAML frontmatter extractor for A2H-owned `.md` files (SKILL.md\r\n * and agent `.md`). A2H controls the frontmatter format, so we only need\r\n * the top-level `name` (a scalar). This avoids pulling a YAML dependency\r\n * into the plugin.\r\n *\r\n * Returns the parsed `name`, or null when the file has no usable frontmatter\r\n * (e.g. a README dropped into a directory that's also scanned for agents).\r\n */\r\nexport function parseFrontmatter(content: string): string | null {\r\n const match = content.match(/^---\\s*\\r?\\n([\\s\\S]*?)\\r?\\n---/)\r\n if (!match) return null\r\n const lines = match[1].split(/\\r?\\n/)\r\n\r\n for (const line of lines) {\r\n if (!line.trim() || line.trim().startsWith(\"#\")) continue\r\n if (line.length - line.trimStart().length !== 0) continue // skip indented (e.g. metadata block) lines\r\n\r\n const kv = line.trim().match(/^([a-zA-Z_][a-zA-Z0-9_-]*):\\s*(.*)$/)\r\n if (!kv) continue\r\n if (kv[1] === \"name\") {\r\n return kv[2].trim().replace(/^[\"']|[\"']$/g, \"\")\r\n }\r\n }\r\n\r\n return null\r\n}\r\n\r\n/**\r\n * Scan the bundled agents/ directory and return a `name \u2192 prompt body` map.\r\n *\r\n * Only top-level `.md` files are considered (subdirectories like `scripts/`\r\n * are skipped via `isFile()`). Files without a `name:` field in frontmatter\r\n * (e.g. a stray README) are silently skipped \u2014 the frontmatter name is the\r\n * source of truth, not the file basename. The body (frontmatter stripped)\r\n * becomes the LLM prompt.\r\n *\r\n * Adding a new agent is therefore a zero-code, zero-rebuild change: just\r\n * drop an `.md` with `name:` frontmatter into `agents/`.\r\n */\r\nexport function discoverA2HAgents(agentsRoot: string): Map<string, string> {\r\n const result = new Map<string, string>()\r\n let entries: fs.Dirent[]\r\n try {\r\n entries = fs.readdirSync(agentsRoot, { withFileTypes: true })\r\n } catch {\r\n return result\r\n }\r\n for (const entry of entries) {\r\n if (!entry.isFile() || !entry.name.endsWith(\".md\")) continue\r\n const file = path.join(agentsRoot, entry.name)\r\n let raw: string\r\n try {\r\n raw = fs.readFileSync(file, \"utf-8\")\r\n } catch {\r\n continue\r\n }\r\n const name = parseFrontmatter(raw)\r\n if (!name) continue // not a valid agent file (no `name:` frontmatter)\r\n result.set(name, stripFrontmatter(raw))\r\n }\r\n return result\r\n}\r\n", "import path from \"node:path\"\nimport type { A2HHostDeps } from \"./types\"\n\n/**\n * Build the A2H env var set from host-injected deps. Shared by the `shell.env`\n * hook (so every shell session sees these). Env vars are the single source of\n * truth in the plugin context for non-secret paths.\n *\n * Contains only DevEco/SDK path vars. The multimodal model apiKey is NOT\n * injected here anymore \u2014 putting it in `process.env` leaked it to every\n * descendant process (adb/hdc, sub-agents), to `/proc/<pid>/environ`, crash\n * dumps, and any `env`/`printenv` output. Instead the `a2h_app_feature_verify`\n * plugin tool resolves the params via `A2HHostDeps.resolveModelParams` and pipes\n * them to the script over stdin (`--model-stdin`), so the key stays in process\n * memory + the stdin pipe and never enters env/argv/disk.\n *\n * Nothing secret is present in this env set, but it is still good hygiene not\n * to dump it into logs verbatim.\n */\nexport async function buildA2HEnvBase(\n deps: A2HHostDeps,\n): Promise<Record<string, string>> {\n const env: Record<string, string> = {}\n\n const home = await deps.resolveDevEcoHome()\n if (home) {\n const sdk = deps.resolveSdkPath(home)\n env[\"DEVECO_HOME\"] = home\n env[\"DEVECO_SDK_HOME\"] = sdk\n env[\"OHOS_SDK_PATH\"] = path.join(sdk, \"default\", \"openharmony\", \"ets\")\n env[\"HMS_SDK_PATH\"] = path.join(sdk, \"default\", \"hms\", \"ets\")\n }\n\n return env\n}\n", "import { spawn } from \"node:child_process\"\nimport path from \"node:path\"\nimport { PKG_ROOT } from \"../paths\"\nimport type { A2HHostDeps, ToolContext, ToolDefinition } from \"../types\"\n\n// The bundled UI-feature-verification script (TypeScript, run directly by\n// Node \u2265 22.18 which strips types natively \u2014 same as when the agent ran\n// `node app_feature_verify.ts` via the shell).\nconst SCRIPT_PATH = path.join(\n PKG_ROOT,\n \"skills\",\n \"hmos-incremental-ui-align\",\n \"scripts\",\n \"app_feature_verify.ts\",\n)\n\n/**\n * Env vars that may carry a model apiKey. Stripped from the child's env so the\n * key is never inherited by the spawned script or its adb/hdc descendants. The\n * script instead receives the key via the stdin pipe (`--model-stdin`).\n */\nconst SECRET_ENV = [\n \"HOMETRANS_MODEL_API_KEY\",\n \"HOMETRANS_MODEL_NAME\",\n \"HOMETRANS_MODEL_BASE_URL\",\n \"GLM_API_KEY\",\n \"TEST_API_KEY\",\n] as const\n\n/** Cap stdout returned to the host so a runaway loop can't overflow the chat. */\nfunction cap(s: string, max = 50000): string {\n if (s.length <= max) return s\n const half = Math.floor(max / 2)\n return s.slice(0, half) + `\\n\u2026[truncated ${s.length - max} chars]\u2026\\n` + s.slice(-half)\n}\n\n/**\n * Build the `a2h_app_feature_verify` plugin tool.\n *\n * Spawns app_feature_verify.ts with the model config piped to its stdin, so the\n * apiKey travels only through the OS pipe + process memory \u2014 never env, argv,\n * or a plugin-written file. The host resolves the params (apiKey) via\n * `deps.resolveModelParams`; the tool is the only thing that ever holds it.\n */\nexport function makeA2hAppFeatureVerifyTool(deps: A2HHostDeps): ToolDefinition {\n return {\n description:\n \"Run the a2h UI feature-verification phone agent (app_feature_verify.ts) to navigate an app and verify a feature path on a connected device. The multimodal model apiKey is resolved from the host and piped to the script over stdin \u2014 it is never placed in env or argv, so it is not visible to the spawned adb/hdc subprocesses. Use this instead of running `node app_feature_verify.ts` directly. The script runs its internal agent loop (screenshot \u2192 model \u2192 action) up to maxSteps and prints per-step thinking + actions; the full stdout is returned on success. One of task/prompt/feature is required; app and package are required.\",\n args: {\n type: \"object\",\n properties: {\n app: { type: \"string\", description: \"\u76EE\u6807 app \u663E\u793A\u540D\uFF08required\uFF09\" },\n package: { type: \"string\", description: \"\u76EE\u6807 app \u5305\u540D\uFF08required\uFF09\" },\n device: {\n type: \"string\",\n enum: [\"hdc\", \"adb\"],\n default: \"hdc\",\n description: \"\u8BBE\u5907\u9A71\u52A8\uFF1Ahdc(HarmonyOS) / adb(Android)\",\n },\n task: {\n type: \"string\",\n description: \"L1/L2/L3 \u4EFB\u52A1\u8DEF\u5F84\uFF0C\u5982 L1\u76F8\u518C\u8BE6\u60C5L2\u66F4\u591A\uFF08\u4E0E prompt/feature \u4E09\u9009\u4E00\uFF09\",\n },\n prompt: { type: \"string\", description: \"\u81EA\u7531 prompt\uFF08\u4E0E task/feature \u4E09\u9009\u4E00\uFF09\" },\n feature: { type: \"string\", description: \".md \u529F\u80FD\u6E05\u5355\u8DEF\u5F84\uFF08\u4E0E task/prompt \u4E09\u9009\u4E00\uFF09\" },\n maxSteps: { type: \"number\", default: 30, description: \"\u6700\u5927 agent \u6B65\u6570\" },\n appHints: { type: \"string\", description: \"\u5E94\u7528\u5BFC\u822A\u63D0\u793A\" },\n serial: { type: \"string\", description: \"\u8BBE\u5907\u5E8F\u5217\u53F7\" },\n limit: { type: \"number\", description: \"--feature \u6A21\u5F0F\u4E0B\u53D6\u524D N \u6761\" },\n offset: { type: \"number\", default: 1, description: \"--feature \u6A21\u5F0F\u4E0B\u504F\u79FB\" },\n quiet: { type: \"boolean\", default: false, description: \"\u6291\u5236\u63D0\u793A\u6027\u8F93\u51FA\" },\n },\n required: [\"app\", \"package\"],\n },\n execute: async (args: Record<string, unknown>, context: ToolContext) => {\n const app = String(args.app ?? \"\")\n const pkg = String(args.package ?? \"\")\n if (!app || !pkg) {\n throw new Error(\"a2h_app_feature_verify: `app` and `package` are required\")\n }\n const task = args.task != null ? String(args.task) : null\n const prompt = args.prompt != null ? String(args.prompt) : null\n const feature = args.feature != null ? String(args.feature) : null\n if (!task && !prompt && !feature) {\n throw new Error(\"a2h_app_feature_verify: one of `task` / `prompt` / `feature` is required\")\n }\n\n // Resolve host model params \u2014 the apiKey lives only in this process's\n // memory from here on.\n const model = await deps.resolveModelParams(context.worktree)\n if (!model || !model.apiKey) {\n throw new Error(\"a2h_app_feature_verify: host did not provide model params (apiKey missing)\")\n }\n\n // Assemble argv from provided args; append --model-stdin so the script\n // reads model config from stdin rather than env/argv.\n const argv: string[] = [\"--app\", app, \"--package\", pkg, \"--device\", String(args.device ?? \"hdc\")]\n if (task) argv.push(\"--task\", task)\n if (prompt) argv.push(\"--prompt\", prompt)\n if (feature) argv.push(\"--feature\", feature)\n if (args.maxSteps != null) argv.push(\"--max-steps\", String(args.maxSteps))\n if (args.appHints != null) argv.push(\"--app-hints\", String(args.appHints))\n if (args.serial != null) argv.push(\"--serial\", String(args.serial))\n if (args.limit != null) argv.push(\"--limit\", String(args.limit))\n if (args.offset != null) argv.push(\"--offset\", String(args.offset))\n if (args.quiet === true) argv.push(\"--quiet\")\n argv.push(\"--model-stdin\")\n\n // Sanitize env: strip every var that could carry a model apiKey so the\n // child (and its adb/hdc descendants) cannot read it from the environment.\n const env: NodeJS.ProcessEnv = { ...process.env }\n for (const k of SECRET_ENV) delete env[k]\n\n // Spawn via PATH `node` (same binary the shell used to run the .ts script\n // \u2014 guaranteed \u2265 22.18 for native TS). process.execPath might be an older\n // host node, so prefer PATH resolution to match prior working behavior.\n const child = spawn(\"node\", [SCRIPT_PATH, ...argv], {\n cwd: context.directory,\n env,\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n })\n\n // Pipe the model config to the child's stdin and close it \u2014 the key\n // traverses only the OS pipe, never env/argv/disk.\n child.stdin.end(\n JSON.stringify({ apiKey: model.apiKey, modelName: model.modelName, baseURL: model.baseURL }),\n )\n\n let stdout = \"\"\n let stderr = \"\"\n let spawnError = \"\"\n const stepRe = /\u6267\u884C\u52A8\u4F5C:\\s*(.*)/g\n child.stdout.on(\"data\", (chunk: Buffer) => {\n const text = chunk.toString(\"utf8\")\n stdout += text\n // Surface the latest step's action as the tool's running title.\n const matches = [...text.matchAll(stepRe)]\n if (matches.length) {\n const last = matches[matches.length - 1][1].trim()\n try {\n context.metadata({ title: `UI verify: ${last.slice(0, 80)}` })\n } catch {\n /* host may not implement metadata \u2014 non-essential */\n }\n }\n })\n child.stderr.on(\"data\", (chunk: Buffer) => {\n stderr += chunk.toString(\"utf8\")\n })\n\n // Honor abort: kill the child if the host cancels the tool call.\n const onAbort = () => {\n if (!child.killed) child.kill(\"SIGTERM\")\n }\n context.abort.addEventListener(\"abort\", onAbort)\n\n const code: number = await new Promise((resolve) => {\n child.on(\"close\", resolve)\n child.on(\"error\", (err: Error) => {\n spawnError = err.message\n resolve(-1)\n })\n })\n context.abort.removeEventListener(\"abort\", onAbort)\n\n if (code !== 0) {\n if (spawnError) {\n throw new Error(`a2h_app_feature_verify: failed to spawn node \u2014 ${spawnError}`)\n }\n const tail = stderr.slice(-2000)\n throw new Error(`a2h_app_feature_verify failed (exit ${code}): ${tail || \"(no stderr)\"}`)\n }\n return { ok: true, exitCode: code, output: cap(stdout) }\n },\n }\n}\n"],
5
- "mappings": "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",
3
+ "sources": ["../src/index.ts", "../src/paths.ts", "../src/discovery.ts", "../src/env.ts", "../src/tools/a2h-run-model-script.ts"],
4
+ "sourcesContent": ["import path from \"node:path\"\r\nimport type { A2HHostDeps, Plugin, PluginInput, PluginHooks } from \"./types\"\r\nimport { PKG_ROOT } from \"./paths\"\r\nimport { discoverA2HAgents } from \"./discovery\"\r\nimport { buildA2HEnvBase } from \"./env\"\r\nimport { makeA2hRunModelScriptTool } from \"./tools/a2h-run-model-script\"\r\n\r\n// Public re-exports \u2014 the package's external surface (dev eco code loads\r\n// dist/index.js at runtime and calls `createServer`; `A2HHostDeps` /\r\n// `PLUGIN_ID` are kept exported for any TS consumers).\r\nexport type { A2HHostDeps } from \"./types\"\r\nexport { PLUGIN_ID } from \"./paths\"\r\n\r\n/**\r\n * Create the A2H DevEco Code plugin.\r\n *\r\n * This is a self-contained plugin - skills/, agents/, and tools/\r\n * are bundled inside the package, NOT referenced from HomeTrans.\r\n * A2H-Plugin is the independently publishable \"plugin edition\" of HomeTrans.\r\n *\r\n * @param deps - host dependencies injected by DevEco Code's thin bridge.\r\n * The A2H Plugin defines what it needs (A2HHostDeps);\r\n * DevEco Code provides how to resolve it.\r\n * @returns A Plugin function ready to be registered as a built-in plugin.\r\n */\r\nexport function createServer(deps: A2HHostDeps): Plugin {\r\n return async (input: PluginInput): Promise<PluginHooks> => {\r\n // All A2H assets live inside this package (self-contained, no HomeTrans dep)\r\n const skillsRoot = path.join(PKG_ROOT, \"skills\")\r\n const agentsRoot = path.join(PKG_ROOT, \"agents\")\r\n\r\n // Auto-discover agents: scan top-level agents/*.md, parse `name:` from\r\n // frontmatter, strip frontmatter for prompt body. Adding/removing/renaming\r\n // an agent is just a file drop \u2014 no code change, no rebuild.\r\n const agentPrompts = discoverA2HAgents(agentsRoot)\r\n\r\n return {\r\n dispose: async () => {\r\n agentPrompts.clear()\r\n },\r\n\r\n // \u2500\u2500 config hook: register skills search paths and agents \u2500\u2500\r\n config: async (cfg: Record<string, unknown>) => {\r\n // --- Skills ---\r\n const skills = (cfg.skills as Record<string, unknown>) || {}\r\n cfg.skills = skills\r\n\r\n // Register the bundled skills/ directory as an additional search path.\r\n // All bundled skills are user-visible \u2014 there is no partial-visibility\r\n // (hidden) layer anymore, so we only register the search path here.\r\n const paths = (skills.paths as string[]) || []\r\n skills.paths = paths\r\n if (!paths.includes(skillsRoot)) {\r\n paths.push(skillsRoot)\r\n }\r\n\r\n // --- Agents ---\r\n const agent = (cfg.agent as Record<string, Record<string, unknown>>) || {}\r\n cfg.agent = agent\r\n\r\n for (const [name, prompt] of agentPrompts) {\r\n // Don't overwrite if the user already configured this agent\r\n if (!agent[name]) {\r\n agent[name] = {\r\n mode: \"subagent\",\r\n hidden: true,\r\n prompt,\r\n }\r\n }\r\n }\r\n\r\n },\r\n\r\n // \u2500\u2500 shell.env hook: inject DevEco Studio / SDK path vars only. The \u2500\u2500\r\n // multimodal model apiKey is deliberately NOT put in env (it would leak\r\n // to every adb/hdc descendant); the a2h_run_model_script tool pipes it to\r\n // the script over stdin instead. See src/env.ts.\r\n \"shell.env\": async (\r\n shellInput: { cwd: string; sessionID?: string; callID?: string },\r\n output: { env: Record<string, string> },\r\n ) => {\r\n const a2hEnv = await buildA2HEnvBase(deps)\r\n Object.assign(output.env, a2hEnv)\r\n },\r\n\r\n // \u2500\u2500 tool hook: run skill scripts that need the multimodal model without\r\n // leaking the apiKey into env/argv. The generic `a2h_run_model_script`\r\n // tool is driven by a registry of scripts (src/tools/a2h-run-model-\r\n // script.ts): the agent picks a registered `script` and passes its CLI\r\n // `commandArgs`; the tool resolves the apiKey from the host and pipes it to the\r\n // script over stdin. Adding a model script = one registry entry (no new\r\n // tool file, no index.ts change).\r\n //\r\n // The AutoTest engine (selftest / testcases / report / resolve-metadata)\r\n // is no longer vendored here \u2014 it lives in the HomeTrans package and is\r\n // invoked directly via `node` by agents/self-tester.md (see that file's\r\n // \"Resolving the AutoTest Tools Directory\" section). a2h therefore ships\r\n // only this one plugin tool and carries no autotest runtime dependency.\r\n tool: {\r\n a2h_run_model_script: makeA2hRunModelScriptTool(deps),\r\n },\r\n }\r\n }\r\n}\r\n", "import path from \"node:path\"\r\nimport { fileURLToPath } from \"node:url\"\r\n\r\n// Resolve package root (one directory above dist/ or src/). Computed once here\r\n// and imported everywhere else so there's a single source of truth. After\r\n// esbuild bundling, import.meta.url points at dist/index.js, so this still\r\n// resolves to the package root at runtime.\r\nconst __filename = fileURLToPath(import.meta.url)\r\nconst __dirname = path.dirname(__filename)\r\n\r\n/** Absolute path to the A2H-Plugin package root. */\r\nexport const PKG_ROOT = path.resolve(__dirname, \"..\")\r\n\r\n/** Public plugin id. */\r\nexport const PLUGIN_ID = \"android2harmony\"\r\n", "import path from \"node:path\"\r\nimport fs from \"node:fs\"\r\n\r\n/**\r\n * Strip YAML frontmatter (--- ... ---) from agent .md files.\r\n * Agent .md files have metadata in frontmatter; only the body\r\n * should be used as the LLM prompt.\r\n */\r\nexport function stripFrontmatter(content: string): string {\r\n const match = content.match(/^---\\s*\\n([\\s\\S]*?)\\n---\\s*\\n/)\r\n return match ? content.slice(match[0].length).trim() : content\r\n}\r\n\r\n/**\r\n * Minimal YAML frontmatter extractor for A2H-owned `.md` files (SKILL.md\r\n * and agent `.md`). A2H controls the frontmatter format, so we only need\r\n * the top-level `name` (a scalar). This avoids pulling a YAML dependency\r\n * into the plugin.\r\n *\r\n * Returns the parsed `name`, or null when the file has no usable frontmatter\r\n * (e.g. a README dropped into a directory that's also scanned for agents).\r\n */\r\nexport function parseFrontmatter(content: string): string | null {\r\n const match = content.match(/^---\\s*\\r?\\n([\\s\\S]*?)\\r?\\n---/)\r\n if (!match) return null\r\n const lines = match[1].split(/\\r?\\n/)\r\n\r\n for (const line of lines) {\r\n if (!line.trim() || line.trim().startsWith(\"#\")) continue\r\n if (line.length - line.trimStart().length !== 0) continue // skip indented (e.g. metadata block) lines\r\n\r\n const kv = line.trim().match(/^([a-zA-Z_][a-zA-Z0-9_-]*):\\s*(.*)$/)\r\n if (!kv) continue\r\n if (kv[1] === \"name\") {\r\n return kv[2].trim().replace(/^[\"']|[\"']$/g, \"\")\r\n }\r\n }\r\n\r\n return null\r\n}\r\n\r\n/**\r\n * Scan the bundled agents/ directory and return a `name \u2192 prompt body` map.\r\n *\r\n * Only top-level `.md` files are considered (subdirectories like `scripts/`\r\n * are skipped via `isFile()`). Files without a `name:` field in frontmatter\r\n * (e.g. a stray README) are silently skipped \u2014 the frontmatter name is the\r\n * source of truth, not the file basename. The body (frontmatter stripped)\r\n * becomes the LLM prompt.\r\n *\r\n * Adding a new agent is therefore a zero-code, zero-rebuild change: just\r\n * drop an `.md` with `name:` frontmatter into `agents/`.\r\n */\r\nexport function discoverA2HAgents(agentsRoot: string): Map<string, string> {\r\n const result = new Map<string, string>()\r\n let entries: fs.Dirent[]\r\n try {\r\n entries = fs.readdirSync(agentsRoot, { withFileTypes: true })\r\n } catch {\r\n return result\r\n }\r\n for (const entry of entries) {\r\n if (!entry.isFile() || !entry.name.endsWith(\".md\")) continue\r\n const file = path.join(agentsRoot, entry.name)\r\n let raw: string\r\n try {\r\n raw = fs.readFileSync(file, \"utf-8\")\r\n } catch {\r\n continue\r\n }\r\n const name = parseFrontmatter(raw)\r\n if (!name) continue // not a valid agent file (no `name:` frontmatter)\r\n result.set(name, stripFrontmatter(raw))\r\n }\r\n return result\r\n}\r\n", "import path from \"node:path\"\r\nimport type { A2HHostDeps } from \"./types\"\r\n\r\n/**\r\n * Build the A2H env var set from host-injected deps. Shared by the `shell.env`\r\n * hook (so every shell session sees these). Env vars are the single source of\r\n * truth in the plugin context for non-secret paths.\r\n *\r\n * Contains only DevEco/SDK path vars. The multimodal model apiKey is NOT\r\n * injected here anymore \u2014 putting it in `process.env` leaked it to every\r\n * descendant process (adb/hdc, sub-agents), to `/proc/<pid>/environ`, crash\r\n * dumps, and any `env`/`printenv` output. Instead the `a2h_run_model_script`\r\n * plugin tool resolves the params via `A2HHostDeps.resolveModelParams` and pipes\r\n * them to the script over stdin (`--model-stdin`), so the key stays in process\r\n * memory + the stdin pipe and never enters env/argv/disk.\r\n *\r\n * Nothing secret is present in this env set, but it is still good hygiene not\r\n * to dump it into logs verbatim.\r\n */\r\nexport async function buildA2HEnvBase(\r\n deps: A2HHostDeps,\r\n): Promise<Record<string, string>> {\r\n const env: Record<string, string> = {}\r\n\r\n const home = await deps.resolveDevEcoHome()\r\n if (home) {\r\n const sdk = deps.resolveSdkPath(home)\r\n env[\"DEVECO_HOME\"] = home\r\n env[\"DEVECO_SDK_HOME\"] = sdk\r\n env[\"OHOS_SDK_PATH\"] = path.join(sdk, \"default\", \"openharmony\", \"ets\")\r\n env[\"HMS_SDK_PATH\"] = path.join(sdk, \"default\", \"hms\", \"ets\")\r\n }\r\n\r\n return env\r\n}\r\n", "import { spawn } from \"node:child_process\"\r\nimport path from \"node:path\"\r\nimport { PKG_ROOT } from \"../paths\"\r\nimport type { A2HHostDeps, ToolContext, ToolDefinition } from \"../types\"\r\n\r\n/**\r\n * Generic \"run a skill script that needs the multimodal model\" tool.\r\n *\r\n * One tool, driven by a registry of scripts. The agent picks a `script` and\r\n * passes its params as an object (camelCase fields); the tool flattens them to\r\n * `--kebab-case` CLI argv and spawns the script. The model apiKey is resolved\r\n * from the host and piped to the script's stdin \u2014 the key traverses only the OS\r\n * pipe + process memory, never env/argv/disk, so it is invisible to the spawned\r\n * adb/hdc descendants. Adding a model script = one registry entry (no new tool\r\n * file, no index.ts change); the tool description auto-extends.\r\n */\r\n\r\ninterface ModelScriptEntry {\r\n /** `script` enum value the agent passes. */\r\n name: string\r\n /** Absolute path to the .js script (source; runs under node_modules without native type-stripping). */\r\n path: string\r\n /** Per-script params summary; documents the camelCase fields the agent passes. */\r\n argsHelp: string\r\n}\r\n\r\n/**\r\n * Registry of model-consuming skill scripts. This is the security allowlist\r\n * (the agent cannot pass an arbitrary path \u2014 only a registered `name`) and the\r\n * sole place to edit when adding a script. The tool description is built from\r\n * this so it auto-extends without touching tool code.\r\n */\r\nconst MODEL_SCRIPTS: ModelScriptEntry[] = [\r\n {\r\n name: \"app_feature_verify\",\r\n path: path.join(\r\n PKG_ROOT,\r\n \"skills\",\r\n \"hmos-incremental-ui-align\",\r\n \"scripts\",\r\n \"app_feature_verify.js\",\r\n ),\r\n argsHelp:\r\n \"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\" +\r\n \"| \u5B57\u6BB5 | \u7C7B\u578B | \u7EA6\u675F | \u8BF4\u660E |\\n\" +\r\n \"|---|---|---|---|\\n\" +\r\n \"| app | string | \u5FC5\u586B | \u76EE\u6807\u5E94\u7528\u663E\u793A\u540D |\\n\" +\r\n \"| package | string | \u5FC5\u586B | \u76EE\u6807\u5E94\u7528\u5305\u540D |\\n\" +\r\n \"| device | string | \u9ED8\u8BA4 hdc | \u8BBE\u5907\u9A71\u52A8,\u53D6\u503C hdc \u6216 adb |\\n\" +\r\n \"| prompt | string | \u4E09\u9009\u4E00 | \u81EA\u7531\u5BFC\u822A\u63CF\u8FF0 |\\n\" +\r\n \"| task | string | \u4E09\u9009\u4E00 | L1\u2026>L2\u2026>L3\u2026 \u5C42\u7EA7\u8DEF\u5F84 |\\n\" +\r\n \"| feature | string | \u4E09\u9009\u4E00 | features.md \u8DEF\u5F84 |\\n\" +\r\n \"| maxSteps | number | \u9ED8\u8BA4 30 | \u6700\u5927 agent \u6B65\u6570 |\\n\" +\r\n \"| serial | string | \u53EF\u9009 | \u8BBE\u5907\u5E8F\u5217\u53F7(\u591A\u8BBE\u5907\u65F6) |\\n\" +\r\n \"| appHints | string | \u53EF\u9009 | \u5E94\u7528\u5BFC\u822A\u63D0\u793A |\\n\" +\r\n \"| limit | number | \u53EF\u9009 | --feature \u6A21\u5F0F\u53D6\u524D N \u6761 |\\n\" +\r\n \"| offset | number | \u9ED8\u8BA4 1 | --feature \u6A21\u5F0F\u504F\u79FB |\\n\" +\r\n \"| quiet | boolean | \u53EF\u9009 | true \u65F6\u6291\u5236\u63D0\u793A\u6027\u8F93\u51FA |\\n\\n\" +\r\n \"\u7EA6\u675F:app \u4E0E package \u5FC5\u586B,\u4E14 prompt/task/feature \u4E09\u9009\u4E00\u3002\\n\" +\r\n '\u793A\u4F8B:commandArgs = {\"app\":\"\u56FE\u5E93\",\"package\":\"com.x.gallery\",\"device\":\"adb\",\"prompt\":\"\u8FDB\u5165\u8BBE\u7F6E\u9875\u9762\",\"maxSteps\":15}',\r\n },\r\n]\r\n\r\n/** Cap stdout returned to the host so a runaway loop can't overflow the chat. */\r\nfunction cap(s: string, max = 50000): string {\r\n if (s.length <= max) return s\r\n const half = Math.floor(max / 2)\r\n return s.slice(0, half) + `\\n\u2026[truncated ${s.length - max} chars]\u2026\\n` + s.slice(-half)\r\n}\r\n\r\n/**\r\n * Shared runner: resolve host model params, spawn the script with the model\r\n * config piped to stdin, collect stdout/stderr, honor abort, and return the\r\n * capped stdout (or throw on non-zero exit).\r\n *\r\n * The apiKey is resolved from the host and piped to the script over stdin\r\n * (`--model-stdin`); the agent never handles it.\r\n */\r\nasync function runModelScript(\r\n deps: A2HHostDeps,\r\n context: ToolContext,\r\n entry: ModelScriptEntry,\r\n argv: string[],\r\n): Promise<{ ok: true; exitCode: number; output: string }> {\r\n const model = await deps.resolveModelParams(context.worktree)\r\n if (!model || !model.apiKey) {\r\n throw new Error(\r\n `a2h_run_model_script (${entry.name}): host did not provide model params (apiKey missing)`,\r\n )\r\n }\r\n\r\n const finalArgv = [...argv, \"--model-stdin\"]\r\n\r\n // Spawn via PATH `node`. process.execPath might be an older/different host\r\n // node (e.g. a bundled runtime), so prefer PATH resolution to match prior\r\n // working behavior. The script is precompiled .js, so no native type-stripping\r\n // is involved and it runs fine even though the path sits under node_modules.\r\n const child = spawn(\"node\", [entry.path, ...finalArgv], {\r\n cwd: context.directory,\r\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\r\n })\r\n\r\n // Pipe the model config to the child's stdin and close it \u2014 the key traverses\r\n // only the OS pipe, never env/argv/disk.\r\n child.stdin.end(\r\n JSON.stringify({ apiKey: model.apiKey, modelName: model.modelName, baseURL: model.baseURL }),\r\n )\r\n\r\n let stdout = \"\"\r\n let stderr = \"\"\r\n let spawnError = \"\"\r\n child.stdout.on(\"data\", (chunk: Buffer) => {\r\n const text = chunk.toString(\"utf8\")\r\n stdout += text\r\n // Surface the last non-empty line as a running title so the host can show\r\n // progress. Generic (not script-specific) \u2014 the agent sees no secrets.\r\n const lines = text.split(/\\r?\\n/)\r\n for (let i = lines.length - 1; i >= 0; i--) {\r\n const line = lines[i].trim()\r\n if (line) {\r\n try {\r\n context.metadata({ title: line.slice(0, 80) })\r\n } catch {\r\n /* host may not implement metadata \u2014 non-essential */\r\n }\r\n break\r\n }\r\n }\r\n })\r\n child.stderr.on(\"data\", (chunk: Buffer) => {\r\n stderr += chunk.toString(\"utf8\")\r\n })\r\n\r\n // Honor abort: kill the child if the host cancels the tool call.\r\n const onAbort = () => {\r\n if (!child.killed) child.kill(\"SIGTERM\")\r\n }\r\n context.abort.addEventListener(\"abort\", onAbort)\r\n\r\n const code: number = await new Promise((resolve) => {\r\n child.on(\"close\", resolve)\r\n child.on(\"error\", (err: Error) => {\r\n spawnError = err.message\r\n resolve(-1)\r\n })\r\n })\r\n context.abort.removeEventListener(\"abort\", onAbort)\r\n\r\n if (code !== 0) {\r\n if (spawnError) {\r\n throw new Error(`a2h_run_model_script (${entry.name}): failed to spawn node \u2014 ${spawnError}`)\r\n }\r\n const tail = stderr.slice(-2000)\r\n throw new Error(\r\n `a2h_run_model_script (${entry.name}) failed (exit ${code}): ${tail || \"(no stderr)\"}`,\r\n )\r\n }\r\n return { ok: true, exitCode: code, output: cap(stdout) }\r\n}\r\n\r\n/** camelCase \u2192 --kebab-case flag (maxSteps \u2192 --max-steps). */\r\nconst kebab = (s: string): string => \"--\" + s.replace(/([a-z0-9])([A-Z])/g, \"$1-$2\").toLowerCase()\r\n\r\n/**\r\n * Flatten a params object into CLI argv: each entry \u2192 `--kebab-case` flag + its\r\n * value; booleans \u2192 flag-only when true (false/null/undefined skipped). Generic\r\n * over any script whose flags are kebab-cased, so it is not coupled to a schema.\r\n */\r\nfunction flattenParams(obj: Record<string, unknown>): string[] {\r\n const out: string[] = []\r\n for (const [k, v] of Object.entries(obj)) {\r\n if (v == null) continue\r\n if (typeof v === \"boolean\") {\r\n if (v) out.push(kebab(k))\r\n } else {\r\n out.push(kebab(k), String(v))\r\n }\r\n }\r\n return out\r\n}\r\n\r\nconst DESCRIPTION_PREFIX =\r\n \"\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\" +\r\n \"\u53EF\u7528\u811A\u672C\u53CA\u5176\u53C2\u6570:\\n\"\r\n\r\n/**\r\n * Build the `a2h_run_model_script` plugin tool.\r\n *\r\n * Generic over scripts: the agent selects a registered `script` and passes its\r\n * params as an object; the tool flattens them to CLI argv. The model apiKey is\r\n * resolved from the host and piped to the script over stdin \u2014 never env/argv/\r\n * disk, so invisible to adb/hdc descendants. Use this instead of running the\r\n * script via `node` directly (a direct run can only get the key from\r\n * env/argv/config, which leaks).\r\n */\r\nexport function makeA2hRunModelScriptTool(deps: A2HHostDeps): ToolDefinition {\r\n return {\r\n description:\r\n DESCRIPTION_PREFIX +\r\n MODEL_SCRIPTS.map((s) => `${s.name}: ${s.argsHelp}`).join(\"\\n\"),\r\n // `args` is a flat field-name\u2192field-schema MAP (not a full `{type:\"object\",\r\n // properties, required}` schema object). The host's `legacyJsonSchema` reads\r\n // this map and builds the LLM-facing schema itself: `{type:\"object\",\r\n // properties:{script, commandArgs}, required:[script, commandArgs]}` (it\r\n // marks every field required). Declaring the full-schema wrapper here would\r\n // make `legacyJsonSchema` mis-read `properties` as a literal field name \u2192 the\r\n // model receives a schema with a single `properties` field and wraps its\r\n // input under it \u2192 \"unknown script\". Keep it a field-map.\r\n args: {\r\n script: {\r\n type: \"string\",\r\n enum: MODEL_SCRIPTS.map((s) => s.name),\r\n description: \"\u8981\u8FD0\u884C\u7684\u6A21\u578B\u811A\u672C\u540D,\u89C1\u4E0A\u65B9\u5404\u6761\u76EE\",\r\n },\r\n commandArgs: {\r\n type: \"object\",\r\n description:\r\n '\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}',\r\n },\r\n },\r\n execute: async (args: Record<string, unknown>, context: ToolContext) => {\r\n const name = String(args.script ?? \"\")\r\n const entry = MODEL_SCRIPTS.find((s) => s.name === name)\r\n if (!entry) {\r\n throw new Error(\r\n `a2h_run_model_script: unknown script \"${name}\". Registered: ${MODEL_SCRIPTS.map((s) => s.name).join(\", \")}`,\r\n )\r\n }\r\n // `commandArgs` may arrive as an object, a JSON string of that object, or as\r\n // top-level fields (model-dependent) \u2014 collect them all, then flatten.\r\n const params: Record<string, unknown> = {}\r\n const a = args.commandArgs\r\n if (a && typeof a === \"object\") {\r\n Object.assign(params, a as Record<string, unknown>)\r\n } else if (typeof a === \"string\" && a.trim()) {\r\n try {\r\n const parsed: unknown = JSON.parse(a)\r\n if (parsed && typeof parsed === \"object\") {\r\n Object.assign(params, parsed as Record<string, unknown>)\r\n }\r\n } catch {\r\n /* not JSON \u2014 fall through to top-level fields */\r\n }\r\n }\r\n for (const [k, v] of Object.entries(args)) {\r\n if (k !== \"script\" && k !== \"commandArgs\") params[k] = v\r\n }\r\n return runModelScript(deps, context, entry, flattenParams(params))\r\n },\r\n }\r\n}\r\n"],
5
+ "mappings": "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",
6
6
  "names": ["path", "path", "path", "path", "path", "path"]
7
7
  }
package/package.json CHANGED
@@ -1,32 +1,36 @@
1
- {
2
- "name": "android2harmony",
3
- "version": "0.1.4",
4
- "description": "A2H (Android-to-HarmonyOS) Plugin for DevEco Code — standalone plugin containing A2H skills, agents, and tools for converting Android apps to HarmonyOS",
5
- "type": "module",
6
- "license": "MIT",
7
- "publishConfig": {
8
- "registry": "https://registry.npmjs.org/"
9
- },
10
- "main": "./dist/index.js",
11
- "exports": {
12
- ".": "./dist/index.js"
13
- },
14
- "files": [
15
- "dist",
16
- "skills",
17
- "agents"
18
- ],
19
- "scripts": {
20
- "build": "node scripts/build.js",
21
- "dev": "tsc --watch"
22
- },
23
- "engines": {
24
- "node": ">=22.18.0"
25
- },
26
- "dependencies": {},
27
- "devDependencies": {
28
- "@types/node": "^22.0.0",
29
- "esbuild": "^0.25.0",
30
- "typescript": "^5.4.5"
31
- }
32
- }
1
+ {
2
+ "name": "android2harmony",
3
+ "version": "0.1.5",
4
+ "description": "HomeTrans Plugin for DevEco Code — standalone plugin containing A2H skills, agents, and tools for converting Android apps to HarmonyOS",
5
+ "type": "module",
6
+ "license": "MIT",
7
+ "publishConfig": {
8
+ "registry": "https://registry.npmjs.org/"
9
+ },
10
+ "repository": {
11
+ "type": "git",
12
+ "url": "https://gitcode.com/SMAT/HomeTrans/tree/deveco-code"
13
+ },
14
+ "main": "./dist/index.js",
15
+ "exports": {
16
+ ".": "./dist/index.js"
17
+ },
18
+ "files": [
19
+ "dist",
20
+ "skills",
21
+ "agents"
22
+ ],
23
+ "scripts": {
24
+ "build": "node scripts/build.js",
25
+ "dev": "tsc --watch"
26
+ },
27
+ "engines": {
28
+ "node": ">=22.18.0"
29
+ },
30
+ "dependencies": {},
31
+ "devDependencies": {
32
+ "@types/node": "^22.0.0",
33
+ "esbuild": "^0.25.0",
34
+ "typescript": "^5.4.5"
35
+ }
36
+ }