@harmonyos-arkts/droid2hmos 0.0.9 → 0.0.10

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 (42) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +249 -249
  3. package/config/preflight-checklist.json +65 -65
  4. package/dist/cli.js +190 -26
  5. package/dist/cli.js.map +1 -1
  6. package/package.json +36 -36
  7. package/templates/ohos/AppScope/app.json5 +10 -10
  8. package/templates/ohos/AppScope/resources/base/element/string.json +8 -8
  9. package/templates/ohos/AppScope/resources/base/media/layered_image.json +6 -6
  10. package/templates/ohos/build-profile.json5 +41 -41
  11. package/templates/ohos/code-linter.json5 +31 -31
  12. package/templates/ohos/entry/build-profile.json5 +32 -32
  13. package/templates/ohos/entry/hvigorfile.ts +5 -5
  14. package/templates/ohos/entry/obfuscation-rules.txt +22 -22
  15. package/templates/ohos/entry/oh-package.json5 +10 -10
  16. package/templates/ohos/entry/src/main/ets/entryability/EntryAbility.ets +47 -47
  17. package/templates/ohos/entry/src/main/ets/entrybackupability/EntryBackupAbility.ets +15 -15
  18. package/templates/ohos/entry/src/main/ets/pages/Index.ets +22 -22
  19. package/templates/ohos/entry/src/main/module.json5 +49 -49
  20. package/templates/ohos/entry/src/main/resources/base/element/color.json +7 -7
  21. package/templates/ohos/entry/src/main/resources/base/element/float.json +8 -8
  22. package/templates/ohos/entry/src/main/resources/base/element/string.json +15 -15
  23. package/templates/ohos/entry/src/main/resources/base/media/layered_image.json +6 -6
  24. package/templates/ohos/entry/src/main/resources/base/profile/backup_config.json +2 -2
  25. package/templates/ohos/entry/src/main/resources/base/profile/main_pages.json +5 -5
  26. package/templates/ohos/entry/src/main/resources/dark/element/color.json +7 -7
  27. package/templates/ohos/entry/src/mock/mock-config.json5 +1 -1
  28. package/templates/ohos/entry/src/ohosTest/ets/test/Ability.test.ets +34 -34
  29. package/templates/ohos/entry/src/ohosTest/ets/test/List.test.ets +4 -4
  30. package/templates/ohos/entry/src/ohosTest/ets/testability/TestAbility.ets +48 -48
  31. package/templates/ohos/entry/src/ohosTest/ets/testability/pages/Index.ets +14 -14
  32. package/templates/ohos/entry/src/ohosTest/ets/testrunner/OpenHarmonyTestRunner.ets +41 -41
  33. package/templates/ohos/entry/src/ohosTest/module.json5 +25 -25
  34. package/templates/ohos/entry/src/ohosTest/resources/base/element/color.json +7 -7
  35. package/templates/ohos/entry/src/ohosTest/resources/base/element/string.json +12 -12
  36. package/templates/ohos/entry/src/ohosTest/resources/base/profile/test_pages.json +5 -5
  37. package/templates/ohos/entry/src/test/List.test.ets +4 -4
  38. package/templates/ohos/entry/src/test/LocalUnit.test.ets +32 -32
  39. package/templates/ohos/hvigor/hvigor-config.json5 +23 -23
  40. package/templates/ohos/hvigorfile.ts +5 -5
  41. package/templates/ohos/oh-package-lock.json5 +27 -27
  42. package/templates/ohos/oh-package.json5 +10 -10
@@ -1,65 +1,65 @@
1
- {
2
- "version": 1,
3
- "checks": {
4
- "android-ui-stack": {
5
- "name": "Android UI 技术栈",
6
- "required": true,
7
- "enabled": true
8
- },
9
- "git": {
10
- "name": "Git 仓库和工作区",
11
- "required": true,
12
- "enabled": true
13
- },
14
- "toolchain": {
15
- "name": "主机综合工具链",
16
- "required": true,
17
- "enabled": true
18
- },
19
- "devecocli-auth": {
20
- "name": "DevEco CLI 登录状态",
21
- "required": true,
22
- "enabled": true
23
- },
24
- "appgraph": {
25
- "name": "AppGraph 兼容性和健康状态",
26
- "required": true,
27
- "enabled": true
28
- },
29
- "config": {
30
- "name": ".migration/config.json",
31
- "required": true,
32
- "enabled": true
33
- },
34
- "android-build": {
35
- "name": "Android 基线构建",
36
- "required": false,
37
- "enabled": false
38
- },
39
- "android-emulator": {
40
- "name": "Android 模拟器",
41
- "required": false,
42
- "enabled": true
43
- },
44
- "harmony-project": {
45
- "name": "HarmonyOS 工程存在或初始化",
46
- "required": true,
47
- "enabled": true
48
- },
49
- "harmony-sdk": {
50
- "name": "HarmonyOS SDK 声明根目录",
51
- "required": false,
52
- "enabled": true
53
- },
54
- "harmony-build": {
55
- "name": "HarmonyOS 应用 HAP 构建",
56
- "required": true,
57
- "enabled": true
58
- },
59
- "harmony-device": {
60
- "name": "HarmonyOS 设备和 Shell",
61
- "required": true,
62
- "enabled": true
63
- }
64
- }
65
- }
1
+ {
2
+ "version": 1,
3
+ "checks": {
4
+ "android-ui-stack": {
5
+ "name": "Android UI 技术栈",
6
+ "required": true,
7
+ "enabled": true
8
+ },
9
+ "git": {
10
+ "name": "Git 仓库和工作区",
11
+ "required": true,
12
+ "enabled": true
13
+ },
14
+ "toolchain": {
15
+ "name": "主机综合工具链",
16
+ "required": true,
17
+ "enabled": true
18
+ },
19
+ "devecocli-auth": {
20
+ "name": "DevEco CLI 登录状态",
21
+ "required": true,
22
+ "enabled": true
23
+ },
24
+ "appgraph": {
25
+ "name": "AppGraph 兼容性和健康状态",
26
+ "required": true,
27
+ "enabled": true
28
+ },
29
+ "config": {
30
+ "name": ".migration/config.json",
31
+ "required": true,
32
+ "enabled": true
33
+ },
34
+ "android-build": {
35
+ "name": "Android 基线构建",
36
+ "required": false,
37
+ "enabled": false
38
+ },
39
+ "android-emulator": {
40
+ "name": "Android 模拟器",
41
+ "required": false,
42
+ "enabled": true
43
+ },
44
+ "harmony-project": {
45
+ "name": "HarmonyOS 工程存在或初始化",
46
+ "required": true,
47
+ "enabled": true
48
+ },
49
+ "harmony-sdk": {
50
+ "name": "HarmonyOS SDK 声明根目录",
51
+ "required": false,
52
+ "enabled": true
53
+ },
54
+ "harmony-build": {
55
+ "name": "HarmonyOS 应用 HAP 构建",
56
+ "required": true,
57
+ "enabled": true
58
+ },
59
+ "harmony-device": {
60
+ "name": "HarmonyOS 设备和 Shell",
61
+ "required": true,
62
+ "enabled": true
63
+ }
64
+ }
65
+ }
package/dist/cli.js CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  // src/cli.ts
4
4
  import path36 from "path";
5
- import { existsSync as existsSync9, readFileSync as readFileSync7, realpathSync } from "fs";
5
+ import { existsSync as existsSync9, readFileSync as readFileSync8, realpathSync, readdirSync as readdirSync5 } from "fs";
6
6
  import { pathToFileURL } from "url";
7
7
 
8
8
  // src/commands/lib/spawn-command.ts
@@ -3132,7 +3132,7 @@ function runHarmonyTest({ projectRoot, harmonyProject, timeoutMs, testClass = nu
3132
3132
  }
3133
3133
 
3134
3134
  // src/commands/python-hypium.ts
3135
- import { existsSync as existsSync5, statSync as statSync3 } from "fs";
3135
+ import { existsSync as existsSync5, readFileSync as readFileSync4, readdirSync as readdirSync3, statSync as statSync3 } from "fs";
3136
3136
  import path16 from "path";
3137
3137
  var safeTestName = /^[A-Za-z0-9_./-]+$/;
3138
3138
  function executableWithArgs(executable, args) {
@@ -3222,8 +3222,51 @@ function runHarmonyPythonHypiumTest({
3222
3222
  projectRoot,
3223
3223
  timeoutMs
3224
3224
  });
3225
+ if (execute.status !== "passed") {
3226
+ return {
3227
+ status: execute.status,
3228
+ key: "harmonyPythonTest",
3229
+ runner: "python",
3230
+ stage: "execute",
3231
+ target,
3232
+ appArtifact: appBuild.artifact,
3233
+ hypiumProject: testProjectRoot,
3234
+ testLists,
3235
+ reportPath: reports,
3236
+ signature,
3237
+ installApp,
3238
+ execute,
3239
+ summary: `Python Hypium \u6267\u884C\u5931\u8D25\uFF0C\u8BF7\u786E\u8BA4\u5DF2\u4E3A ${python} \u5B89\u88C5 hypium\uFF08python -m pip install hypium -U\uFF09`
3240
+ };
3241
+ }
3242
+ const stdoutText = (() => {
3243
+ try {
3244
+ return existsSync5(execute.stdout) ? readFileSync4(execute.stdout, "utf8") : "";
3245
+ } catch {
3246
+ return "";
3247
+ }
3248
+ })();
3249
+ const structuredHypiumResult = pythonHypiumResult({ stdoutText, reportsDir: reports });
3250
+ if (!structuredHypiumResult.valid) {
3251
+ return {
3252
+ status: "failed",
3253
+ key: "harmonyPythonTest",
3254
+ runner: "python",
3255
+ stage: "parse-summary",
3256
+ target,
3257
+ appArtifact: appBuild.artifact,
3258
+ hypiumProject: testProjectRoot,
3259
+ testLists,
3260
+ reportPath: reports,
3261
+ signature,
3262
+ installApp,
3263
+ execute,
3264
+ structuredHypiumResult,
3265
+ summary: `Hypium \u7ED3\u6784\u5316\u7ED3\u679C\u4E0D\u901A\u8FC7: ${structuredHypiumResult.summary}`
3266
+ };
3267
+ }
3225
3268
  return {
3226
- status: execute.status,
3269
+ status: "passed",
3227
3270
  key: "harmonyPythonTest",
3228
3271
  runner: "python",
3229
3272
  stage: "execute",
@@ -3235,9 +3278,123 @@ function runHarmonyPythonHypiumTest({
3235
3278
  signature,
3236
3279
  installApp,
3237
3280
  execute,
3238
- summary: execute.status === "passed" ? "HarmonyOS \u5E94\u7528\u6784\u5EFA\u3001\u5B89\u88C5\u548C Python Hypium \u6D4B\u8BD5\u6267\u884C\u6210\u529F" : `Python Hypium \u6267\u884C\u5931\u8D25\uFF0C\u8BF7\u786E\u8BA4\u5DF2\u4E3A ${python} \u5B89\u88C5 hypium\uFF08python -m pip install hypium -U\uFF09`
3281
+ structuredHypiumResult,
3282
+ tests: structuredHypiumResult,
3283
+ summary: `HarmonyOS \u5E94\u7528\u6784\u5EFA\u3001\u5B89\u88C5\u548C Python Hypium \u6D4B\u8BD5\u6267\u884C\u6210\u529F\uFF08${structuredHypiumResult.passed}/${structuredHypiumResult.total}\uFF09`
3239
3284
  };
3240
3285
  }
3286
+ function junitAttribute(tag, name) {
3287
+ const match = new RegExp(`\\b${name}="(-?\\d+)"`).exec(tag);
3288
+ return match ? Number(match[1]) : 0;
3289
+ }
3290
+ function aggregateJunitXmlResults(xmlTexts) {
3291
+ const suiteTags = [];
3292
+ let wrapperTag = "";
3293
+ const executedCases = [];
3294
+ for (const xml of xmlTexts) {
3295
+ const text = String(xml ?? "");
3296
+ wrapperTag = wrapperTag || (/<testsuites\b[^>]*>/.exec(text)?.[0] ?? "");
3297
+ for (const match of text.matchAll(/<testsuite\b[^>]*>/g)) suiteTags.push(match[0]);
3298
+ for (const match of text.matchAll(/<testcase\b[^>]*>/g)) {
3299
+ const classname = /\bclassname="([^"]*)"/.exec(match[0])?.[1];
3300
+ const name = /\bname="([^"]*)"/.exec(match[0])?.[1];
3301
+ if (name) executedCases.push(`${classname ?? "suite"}#${name}`);
3302
+ }
3303
+ }
3304
+ if (!wrapperTag && !suiteTags.length) return null;
3305
+ const sources = wrapperTag ? [wrapperTag] : suiteTags;
3306
+ const sum = (attribute) => sources.reduce((total2, tag) => total2 + junitAttribute(tag, attribute), 0);
3307
+ const total = sum("tests");
3308
+ const failed = sum("failures");
3309
+ const errors = sum("errors");
3310
+ const ignored = sum("ignored") + sum("skipped");
3311
+ const disabled = sum("disabled");
3312
+ const unavailable = sum("unavailable");
3313
+ const passed = Math.max(total - failed - errors - ignored - disabled - unavailable, 0);
3314
+ const problems = [];
3315
+ if (total <= 0) problems.push("zero tests");
3316
+ if (failed) problems.push(`failed ${failed}`);
3317
+ if (errors) problems.push(`errors ${errors}`);
3318
+ if (ignored) problems.push(`ignored ${ignored}`);
3319
+ if (disabled) problems.push(`disabled ${disabled}`);
3320
+ if (unavailable) problems.push(`unavailable ${unavailable}`);
3321
+ return {
3322
+ valid: problems.length === 0,
3323
+ total,
3324
+ passed,
3325
+ failed,
3326
+ errors,
3327
+ ignored,
3328
+ disabled,
3329
+ summary: problems.length ? `Hypium rejected: ${problems.join(", ")}` : `Hypium ${passed}/${total} passed`,
3330
+ executedCases
3331
+ };
3332
+ }
3333
+ function pythonHypiumResult({ stdoutText, reportsDir }) {
3334
+ const empty2 = (summary) => ({
3335
+ valid: false,
3336
+ total: 0,
3337
+ passed: 0,
3338
+ failed: 0,
3339
+ errors: 0,
3340
+ ignored: 0,
3341
+ disabled: 0,
3342
+ executedCases: [],
3343
+ summary
3344
+ });
3345
+ const stdoutMatch = /Tests run:\s*(\d+)\s*,\s*Failure:\s*(\d+)\s*,\s*Error:\s*(\d+)\s*,\s*(?:Pass(?:ed)?:\s*(\d+)\s*,\s*)?Ignore:\s*(\d+)/.exec(String(stdoutText ?? ""));
3346
+ if (stdoutMatch) {
3347
+ const total = Number(stdoutMatch[1]);
3348
+ const failed = Number(stdoutMatch[2]);
3349
+ const errors = Number(stdoutMatch[3]);
3350
+ const reportedPass = stdoutMatch[4] !== void 0 ? Number(stdoutMatch[4]) : null;
3351
+ const ignored = Number(stdoutMatch[5]);
3352
+ const passed = reportedPass ?? Math.max(total - failed - errors - ignored, 0);
3353
+ const problems = [];
3354
+ if (total <= 0) problems.push("zero tests");
3355
+ if (failed) problems.push(`failed ${failed}`);
3356
+ if (errors) problems.push(`errors ${errors}`);
3357
+ if (ignored) problems.push(`ignored ${ignored}`);
3358
+ return {
3359
+ valid: problems.length === 0,
3360
+ total,
3361
+ passed,
3362
+ failed,
3363
+ errors,
3364
+ ignored,
3365
+ disabled: 0,
3366
+ executedCases: [],
3367
+ summary: problems.length ? `Hypium rejected: ${problems.join(", ")}` : `Hypium ${passed}/${total} passed`
3368
+ };
3369
+ }
3370
+ const xmlTexts = [];
3371
+ const aggregatePath = path16.join(reportsDir, "summary_report.xml");
3372
+ if (existsSync5(aggregatePath)) {
3373
+ try {
3374
+ xmlTexts.push(readFileSync4(aggregatePath, "utf8"));
3375
+ } catch {
3376
+ }
3377
+ }
3378
+ if (existsSync5(reportsDir)) {
3379
+ try {
3380
+ const stamped = readdirSync3(reportsDir).filter((entry) => /^\d+$/.test(entry)).sort().reverse();
3381
+ for (const stamp of stamped) {
3382
+ const summaryPath = path16.join(reportsDir, stamp, "result", "summary.xml");
3383
+ if (existsSync5(summaryPath)) {
3384
+ try {
3385
+ xmlTexts.push(readFileSync4(summaryPath, "utf8"));
3386
+ } catch {
3387
+ }
3388
+ break;
3389
+ }
3390
+ }
3391
+ } catch {
3392
+ }
3393
+ }
3394
+ const aggregated = aggregateJunitXmlResults(xmlTexts);
3395
+ if (aggregated) return aggregated;
3396
+ return empty2(`Hypium did not produce a machine-readable test summary (stdout or ${reportsDir})`);
3397
+ }
3241
3398
 
3242
3399
  // src/commands/detect-harmony-sdk.ts
3243
3400
  import fs14 from "fs";
@@ -3356,12 +3513,12 @@ function parseProperties(text) {
3356
3513
  }
3357
3514
 
3358
3515
  // src/commands/upgrade.ts
3359
- import { readFileSync as readFileSync4 } from "fs";
3516
+ import { readFileSync as readFileSync5 } from "fs";
3360
3517
  import path18 from "path";
3361
3518
  function readPackageMetadata(packageRoot) {
3362
3519
  if (!packageRoot) throw new Error("Cannot locate droid2hmos package metadata");
3363
3520
  const packagePath = path18.join(packageRoot, "package.json");
3364
- const metadata = JSON.parse(readFileSync4(packagePath, "utf8"));
3521
+ const metadata = JSON.parse(readFileSync5(packagePath, "utf8"));
3365
3522
  if (!metadata.name || !metadata.version) throw new Error(`Invalid package metadata: ${packagePath}`);
3366
3523
  return metadata;
3367
3524
  }
@@ -4083,7 +4240,7 @@ async function runPreflight(options) {
4083
4240
 
4084
4241
  // src/commands/deveco-auth-login.ts
4085
4242
  import { spawnSync as spawnSync6 } from "child_process";
4086
- import { closeSync as closeSync3, existsSync as existsSync6, mkdirSync as mkdirSync3, openSync as openSync3, readFileSync as readFileSync5, unlinkSync as unlinkSync2, writeFileSync as writeFileSync2 } from "fs";
4243
+ import { closeSync as closeSync3, existsSync as existsSync6, mkdirSync as mkdirSync3, openSync as openSync3, readFileSync as readFileSync6, unlinkSync as unlinkSync2, writeFileSync as writeFileSync2 } from "fs";
4087
4244
  import path24 from "path";
4088
4245
  import { setTimeout as delay2 } from "timers/promises";
4089
4246
  var MANAGED_LOGIN_SERVICE_NAME = "devecocli-auth";
@@ -4115,7 +4272,7 @@ function isAlive2(pid) {
4115
4272
  }
4116
4273
  function readPidFile(pidFile) {
4117
4274
  try {
4118
- const pid = Number.parseInt(readFileSync5(pidFile, "utf8").trim(), 10);
4275
+ const pid = Number.parseInt(readFileSync6(pidFile, "utf8").trim(), 10);
4119
4276
  return Number.isInteger(pid) ? pid : null;
4120
4277
  } catch {
4121
4278
  return null;
@@ -4123,7 +4280,7 @@ function readPidFile(pidFile) {
4123
4280
  }
4124
4281
  function tailFile(file, maxChars) {
4125
4282
  try {
4126
- const content = readFileSync5(file, "utf8").trim();
4283
+ const content = readFileSync6(file, "utf8").trim();
4127
4284
  return content.length > maxChars ? `\u2026${content.slice(-maxChars)}` : content;
4128
4285
  } catch {
4129
4286
  return "";
@@ -4214,7 +4371,7 @@ async function killOrphanDevEcoCliLogins(options) {
4214
4371
  return { killedPids, managedPid: managedPid ?? null };
4215
4372
  }
4216
4373
  function runDetachedDevEcoAuthHost(requestPath) {
4217
- const request = JSON.parse(readFileSync5(requestPath, "utf8"));
4374
+ const request = JSON.parse(readFileSync6(requestPath, "utf8"));
4218
4375
  mkdirSync3(path24.dirname(request.stdout), { recursive: true });
4219
4376
  mkdirSync3(path24.dirname(request.stderr), { recursive: true });
4220
4377
  const stdout = openSync3(request.stdout, "a");
@@ -4272,7 +4429,7 @@ function runDetachedDevEcoAuthHost(requestPath) {
4272
4429
  function findLoginUrl(state) {
4273
4430
  for (const file of [state.stdoutLog, state.stderrLog]) {
4274
4431
  try {
4275
- const match = readFileSync5(file, "utf8").match(LOGIN_URL_PATTERN);
4432
+ const match = readFileSync6(file, "utf8").match(LOGIN_URL_PATTERN);
4276
4433
  if (match) return match[0];
4277
4434
  } catch {
4278
4435
  }
@@ -4359,10 +4516,10 @@ async function managedDevEcoCliLogin(options) {
4359
4516
  };
4360
4517
  }
4361
4518
  if (pid2 === null && existsSync6(state.exitFile)) {
4362
- return failure(`\u767B\u5F55\u8FDB\u7A0B\u63D0\u524D\u9000\u51FA\uFF08exit ${readFileSync5(state.exitFile, "utf8").trim()}\uFF09: ${tailFile(state.stderrLog, 800) || tailFile(state.stdoutLog, 800) || "\u65E0\u8F93\u51FA"}`);
4519
+ return failure(`\u767B\u5F55\u8FDB\u7A0B\u63D0\u524D\u9000\u51FA\uFF08exit ${readFileSync6(state.exitFile, "utf8").trim()}\uFF09: ${tailFile(state.stderrLog, 800) || tailFile(state.stdoutLog, 800) || "\u65E0\u8F93\u51FA"}`);
4363
4520
  }
4364
4521
  if (pid2 !== null && !isAlive2(pid2) && !existsSync6(state.pidFile)) {
4365
- return failure(`\u767B\u5F55\u8FDB\u7A0B\u63D0\u524D\u9000\u51FA\uFF08exit ${existsSync6(state.exitFile) ? readFileSync5(state.exitFile, "utf8").trim() : "unknown"}\uFF09: ${tailFile(state.stderrLog, 800) || tailFile(state.stdoutLog, 800) || "\u65E0\u8F93\u51FA"}`);
4522
+ return failure(`\u767B\u5F55\u8FDB\u7A0B\u63D0\u524D\u9000\u51FA\uFF08exit ${existsSync6(state.exitFile) ? readFileSync6(state.exitFile, "utf8").trim() : "unknown"}\uFF09: ${tailFile(state.stderrLog, 800) || tailFile(state.stdoutLog, 800) || "\u65E0\u8F93\u51FA"}`);
4366
4523
  }
4367
4524
  const remainingSeconds = Math.round((deadline - Date.now()) / 1e3);
4368
4525
  if (lastCountdownSeconds - remainingSeconds >= 30) {
@@ -4791,13 +4948,13 @@ Select a scenario with query parameter \`?scenario=empty\` or header \`x-droid2h
4791
4948
  }
4792
4949
 
4793
4950
  // src/commands/logs.ts
4794
- import { existsSync as existsSync7, readFileSync as readFileSync6, readdirSync as readdirSync3 } from "fs";
4951
+ import { existsSync as existsSync7, readFileSync as readFileSync7, readdirSync as readdirSync4 } from "fs";
4795
4952
  import path28 from "path";
4796
4953
  function loadRecords(projectRoot) {
4797
4954
  const directory = path28.join(migrationDirectory(projectRoot), "logs", "daily");
4798
4955
  if (!existsSync7(directory)) return [];
4799
- return readdirSync3(directory).filter((file) => file.endsWith(".jsonl")).sort().flatMap(
4800
- (file) => readFileSync6(path28.join(directory, file), "utf8").split(/\r?\n/).filter(Boolean).map((line) => JSON.parse(line))
4956
+ return readdirSync4(directory).filter((file) => file.endsWith(".jsonl")).sort().flatMap(
4957
+ (file) => readFileSync7(path28.join(directory, file), "utf8").split(/\r?\n/).filter(Boolean).map((line) => JSON.parse(line))
4801
4958
  );
4802
4959
  }
4803
4960
  function queryCommandLogs(project, action, value) {
@@ -8311,7 +8468,7 @@ Usage:
8311
8468
  droid2hmos config <init|validate> [project] [--harmony-project <path>]
8312
8469
  droid2hmos build harmony [project] [--timeout-ms <ms>]
8313
8470
  droid2hmos test harmony [project] [--runner arkts] (--class <class> [--case <case>] | --all) [--target <serial>] [--case-timeout-ms <ms>] [--ref <report-ref>...] [--timeout-ms <ms>]
8314
- droid2hmos test harmony [project] --runner python --hypium-project <path> --test-list <name>... [--target <serial>] [--python <path>] [--report-path <path>] [--ref <report-ref>...] [--timeout-ms <ms>]
8471
+ droid2hmos test harmony [project] --runner python --hypium-project <path> (--test-list <name>...|--all) [--target <serial>] [--python <path>] [--report-path <path>] [--ref <report-ref>...] [--timeout-ms <ms>]
8315
8472
  droid2hmos report [project] [--require-complete]
8316
8473
  droid2hmos report <init|validate|render> [project] [--require-complete]
8317
8474
  droid2hmos report update [project] --ref <ref> [--status <status>] [--harmony-file <path>...] [--android-source <path>...] [--evidence <path>...] [--difference <text>|--clear-difference]
@@ -8494,7 +8651,7 @@ async function runCli(argv, runtime) {
8494
8651
  const guardResult = guard("analyze:harmony", project);
8495
8652
  const configPath = path36.join(migrationDirectory(projectRoot), "config.json");
8496
8653
  if (!existsSync9(configPath)) throw new Error(`Migration config not found: ${configPath}`);
8497
- const config = JSON.parse(readFileSync7(configPath, "utf8"));
8654
+ const config = JSON.parse(readFileSync8(configPath, "utf8"));
8498
8655
  const configuredHarmony = config.harmonyProject ?? defaultHarmonyProjectPath(projectRoot);
8499
8656
  const harmonyProject = path36.isAbsolute(configuredHarmony) ? configuredHarmony : path36.resolve(projectRoot, configuredHarmony);
8500
8657
  const forwarded = ["build", harmonyProject, "--platform", "harmony"];
@@ -8655,7 +8812,7 @@ async function runCli(argv, runtime) {
8655
8812
  const configPath = path36.join(migrationDirectory(projectRoot), "config.json");
8656
8813
  if (!existsSync9(configPath)) throw new Error(`Migration config not found: ${configPath}`);
8657
8814
  const guardResult = guard("build:harmony", projectRoot);
8658
- const config = JSON.parse(readFileSync7(configPath, "utf8"));
8815
+ const config = JSON.parse(readFileSync8(configPath, "utf8"));
8659
8816
  const configuredHarmony = config.harmonyProject ?? defaultHarmonyProjectPath(projectRoot);
8660
8817
  const harmonyProject = path36.isAbsolute(configuredHarmony) ? configuredHarmony : path36.resolve(projectRoot, configuredHarmony);
8661
8818
  let result;
@@ -8690,17 +8847,24 @@ async function runCli(argv, runtime) {
8690
8847
  if (!Number.isFinite(caseTimeoutMs) || caseTimeoutMs <= 0) throw new CliError("--case-timeout-ms must be a positive number");
8691
8848
  const configPath = path36.join(migrationDirectory(projectRoot), "config.json");
8692
8849
  if (!existsSync9(configPath)) throw new Error(`Migration config not found: ${configPath}`);
8693
- const config = JSON.parse(readFileSync7(configPath, "utf8"));
8850
+ const config = JSON.parse(readFileSync8(configPath, "utf8"));
8694
8851
  const configuredHarmony = config.harmonyProject ?? defaultHarmonyProjectPath(projectRoot);
8695
8852
  const harmonyProject = path36.isAbsolute(configuredHarmony) ? configuredHarmony : path36.resolve(projectRoot, configuredHarmony);
8696
8853
  let result;
8697
8854
  try {
8698
8855
  if (runner === "python") {
8699
- if (testClass || testCase || fullRun) throw new CliError("Python Hypium uses --test-list instead of --class, --case, or --all");
8856
+ if (testClass || testCase) throw new CliError("Python Hypium uses --test-list instead of --class or --case");
8700
8857
  const hypiumProject = optionValue(args, "--hypium-project");
8701
- const testLists = optionValues(args, "--test-list").flatMap((value) => value.split(";")).map((value) => value.trim()).filter(Boolean);
8702
8858
  if (!hypiumProject) throw new CliError("Python Hypium requires --hypium-project");
8703
- if (!testLists.length) throw new CliError("Python Hypium requires at least one --test-list");
8859
+ let testLists = optionValues(args, "--test-list").flatMap((value) => value.split(";")).map((value) => value.trim()).filter(Boolean);
8860
+ if (fullRun) {
8861
+ const projectDir = path36.isAbsolute(hypiumProject) ? path36.resolve(hypiumProject) : path36.resolve(projectRoot, hypiumProject);
8862
+ const testcaseDir = path36.join(projectDir, "testcases");
8863
+ testLists = existsSync9(testcaseDir) ? readdirSync5(testcaseDir).filter((f) => f.endsWith(".json")).map((f) => f.replace(/\.json$/i, "")).sort() : [];
8864
+ if (!testLists.length) throw new CliError(`Python Hypium \u5168\u91CF\u56DE\u5F52\u672A\u53D1\u73B0\u7528\u4F8B\u914D\u7F6E\uFF08${testcaseDir}/*.json\uFF09`);
8865
+ } else if (!testLists.length) {
8866
+ throw new CliError("Python Hypium requires at least one --test-list");
8867
+ }
8704
8868
  result = runHarmonyPythonHypiumTest({
8705
8869
  projectRoot,
8706
8870
  harmonyProject,
@@ -8805,7 +8969,7 @@ async function runCli(argv, runtime) {
8805
8969
  const projectRoot = path36.resolve(project);
8806
8970
  const configPath = path36.join(migrationDirectory(projectRoot), "config.json");
8807
8971
  if (!existsSync9(configPath)) throw new Error(`Migration config not found: ${configPath}`);
8808
- const config = JSON.parse(readFileSync7(configPath, "utf8"));
8972
+ const config = JSON.parse(readFileSync8(configPath, "utf8"));
8809
8973
  const configuredHarmony = config.harmonyProject ?? defaultHarmonyProjectPath(projectRoot);
8810
8974
  const harmonyProject = path36.isAbsolute(configuredHarmony) ? configuredHarmony : path36.resolve(projectRoot, configuredHarmony);
8811
8975
  const timeoutMs = Number(optionValue(args, "--timeout-ms") ?? 5 * 60 * 1e3);
@@ -8853,7 +9017,7 @@ async function runCli(argv, runtime) {
8853
9017
  let execution;
8854
9018
  if (before.status !== "passed") execution = { status: "failed", skipped: true, summary: "Baseline evidence capture failed; interaction was not executed" };
8855
9019
  else {
8856
- const config = JSON.parse(readFileSync7(path36.join(migrationDirectory(projectRoot), "config.json"), "utf8"));
9020
+ const config = JSON.parse(readFileSync8(path36.join(migrationDirectory(projectRoot), "config.json"), "utf8"));
8857
9021
  const harmonyProject = path36.isAbsolute(config.harmonyProject) ? config.harmonyProject : path36.resolve(projectRoot, config.harmonyProject);
8858
9022
  const testClass = optionValue(args, "--class") ?? null;
8859
9023
  const testCase = optionValue(args, "--case") ?? null;
@@ -8992,7 +9156,7 @@ var invokedHref = (() => {
8992
9156
  if (invokedHref && invokedHref === entryHref) {
8993
9157
  const argv = process.argv.slice(2);
8994
9158
  const runtime = resolveRuntimePaths(import.meta.url);
8995
- const version = runtime.packageRoot && existsSync9(path36.join(runtime.packageRoot, "package.json")) ? JSON.parse(readFileSync7(path36.join(runtime.packageRoot, "package.json"), "utf8")).version ?? "unknown" : "unknown";
9159
+ const version = runtime.packageRoot && existsSync9(path36.join(runtime.packageRoot, "package.json")) ? JSON.parse(readFileSync8(path36.join(runtime.packageRoot, "package.json"), "utf8")).version ?? "unknown" : "unknown";
8996
9160
  const excluded = /* @__PURE__ */ new Set(["help", "-h", "--help", "status", "internal", "dev", "logs"]);
8997
9161
  const readOnlyTaskQuery = argv[0] === "task" && ["status", "next", "show"].includes(argv[1] ?? "");
8998
9162
  Promise.resolve(excluded.has(argv[0] ?? "help") || readOnlyTaskQuery || process.env.DROID2HMOS_DISABLE_LOGGING === "1" ? runCli(argv, runtime) : withCommandLog(argv, version, () => runCli(argv, runtime))).then((exitCode) => {