@harmonyos-arkts/droid2hmos 0.0.8 → 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.
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
@@ -2395,6 +2395,14 @@ import { existsSync as existsSync3, readdirSync, statSync } from "fs";
2395
2395
  import path13 from "path";
2396
2396
  var DEVECO_CLI_PACKAGE = "@deveco/deveco-cli@latest";
2397
2397
  var DEVECO_CLI_INSTALL_COMMAND = `npm install -g ${DEVECO_CLI_PACKAGE}`;
2398
+ var NODE_INSPECTOR_OPTION = /(?:^|\s)--inspect(?:-brk|-port|-wait)?(?:=(?:"[^"]*"|'[^']*'|\S+))?(?=\s|$)/gu;
2399
+ function devEcoCliEnvironment(environment = process.env) {
2400
+ const result = { ...environment, DEVECO_CLI_SKIP_VERSION_CHECK: "1" };
2401
+ const nodeOptions = String(environment.NODE_OPTIONS ?? "").replace(NODE_INSPECTOR_OPTION, "").trim();
2402
+ if (nodeOptions) result.NODE_OPTIONS = nodeOptions;
2403
+ else delete result.NODE_OPTIONS;
2404
+ return result;
2405
+ }
2398
2406
  function executableName() {
2399
2407
  return process.env.DEVECO_CLI_BINARY || "devecocli";
2400
2408
  }
@@ -2402,14 +2410,14 @@ function devEcoCliSpawn(executable, args) {
2402
2410
  const scriptWrapper = /\.m?js$/i.test(executable);
2403
2411
  return scriptWrapper ? [process.execPath, executable, ...args] : [executable, ...args];
2404
2412
  }
2405
- function runDevEcoCli(args, { executable = executableName(), runner = spawnCommandSync, timeoutMs = 15e3 } = {}, cwd) {
2413
+ function runDevEcoCli(args, { executable = executableName(), runner = spawnCommandSync, timeoutMs = 15e3, environment = process.env } = {}, cwd) {
2406
2414
  const [command2, ...commandArgs] = devEcoCliSpawn(executable, args);
2407
2415
  const result = runner(command2, commandArgs, {
2408
2416
  cwd,
2409
2417
  encoding: "utf8",
2410
2418
  timeout: timeoutMs,
2411
2419
  windowsHide: true,
2412
- env: { ...process.env, DEVECO_CLI_SKIP_VERSION_CHECK: "1" }
2420
+ env: devEcoCliEnvironment(environment)
2413
2421
  });
2414
2422
  return {
2415
2423
  executable,
@@ -2421,13 +2429,13 @@ function runDevEcoCli(args, { executable = executableName(), runner = spawnComma
2421
2429
  errorCode: result.error && "code" in result.error ? result.error.code : null
2422
2430
  };
2423
2431
  }
2424
- async function runDevEcoCliAsync(args, { executable = executableName(), timeoutMs = 15e3 } = {}, cwd) {
2432
+ async function runDevEcoCliAsync(args, { executable = executableName(), timeoutMs = 15e3, environment = process.env } = {}, cwd) {
2425
2433
  const [command2, ...commandArgs] = devEcoCliSpawn(executable, args);
2426
2434
  const child = spawnCommand(command2, commandArgs, {
2427
2435
  cwd,
2428
2436
  windowsHide: true,
2429
2437
  detached: process.platform !== "win32",
2430
- env: { ...process.env, DEVECO_CLI_SKIP_VERSION_CHECK: "1" },
2438
+ env: devEcoCliEnvironment(environment),
2431
2439
  stdio: ["ignore", "pipe", "pipe"]
2432
2440
  });
2433
2441
  let stdout = "";
@@ -2508,7 +2516,7 @@ function checkDevEcoCliAuth(cli = {}) {
2508
2516
  const result = runDevEcoCli(["auth", "status"], cli);
2509
2517
  const output = [result.stdout, result.stderr].map((value) => value.trim()).filter(Boolean).join("\n");
2510
2518
  const loggedOut = /(?:^|\n)Not logged in(?:\n|$)/i.test(output);
2511
- const currentUser = output.match(/(?:^|\n)Current user:\s*(.+?)(?:\n|$)/i)?.[1]?.trim();
2519
+ const currentUser = output.match(/Current user:\s*([^\r\n]+)/i)?.[1]?.trim();
2512
2520
  if (result.status !== "passed" || loggedOut || !currentUser) {
2513
2521
  return {
2514
2522
  status: "failed",
@@ -3124,7 +3132,7 @@ function runHarmonyTest({ projectRoot, harmonyProject, timeoutMs, testClass = nu
3124
3132
  }
3125
3133
 
3126
3134
  // src/commands/python-hypium.ts
3127
- 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";
3128
3136
  import path16 from "path";
3129
3137
  var safeTestName = /^[A-Za-z0-9_./-]+$/;
3130
3138
  function executableWithArgs(executable, args) {
@@ -3214,8 +3222,51 @@ function runHarmonyPythonHypiumTest({
3214
3222
  projectRoot,
3215
3223
  timeoutMs
3216
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
+ }
3217
3268
  return {
3218
- status: execute.status,
3269
+ status: "passed",
3219
3270
  key: "harmonyPythonTest",
3220
3271
  runner: "python",
3221
3272
  stage: "execute",
@@ -3227,9 +3278,123 @@ function runHarmonyPythonHypiumTest({
3227
3278
  signature,
3228
3279
  installApp,
3229
3280
  execute,
3230
- 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`
3231
3284
  };
3232
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
+ }
3233
3398
 
3234
3399
  // src/commands/detect-harmony-sdk.ts
3235
3400
  import fs14 from "fs";
@@ -3348,12 +3513,12 @@ function parseProperties(text) {
3348
3513
  }
3349
3514
 
3350
3515
  // src/commands/upgrade.ts
3351
- import { readFileSync as readFileSync4 } from "fs";
3516
+ import { readFileSync as readFileSync5 } from "fs";
3352
3517
  import path18 from "path";
3353
3518
  function readPackageMetadata(packageRoot) {
3354
3519
  if (!packageRoot) throw new Error("Cannot locate droid2hmos package metadata");
3355
3520
  const packagePath = path18.join(packageRoot, "package.json");
3356
- const metadata = JSON.parse(readFileSync4(packagePath, "utf8"));
3521
+ const metadata = JSON.parse(readFileSync5(packagePath, "utf8"));
3357
3522
  if (!metadata.name || !metadata.version) throw new Error(`Invalid package metadata: ${packagePath}`);
3358
3523
  return metadata;
3359
3524
  }
@@ -4074,8 +4239,8 @@ async function runPreflight(options) {
4074
4239
  }
4075
4240
 
4076
4241
  // src/commands/deveco-auth-login.ts
4077
- import { spawn as spawn3, spawnSync as spawnSync6 } from "child_process";
4078
- import { closeSync as closeSync3, existsSync as existsSync6, mkdirSync as mkdirSync3, openSync as openSync3, readFileSync as readFileSync5, unlinkSync as unlinkSync2, writeFileSync as writeFileSync2 } from "fs";
4242
+ import { spawnSync as spawnSync6 } from "child_process";
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";
4079
4244
  import path24 from "path";
4080
4245
  import { setTimeout as delay2 } from "timers/promises";
4081
4246
  var MANAGED_LOGIN_SERVICE_NAME = "devecocli-auth";
@@ -4107,7 +4272,7 @@ function isAlive2(pid) {
4107
4272
  }
4108
4273
  function readPidFile(pidFile) {
4109
4274
  try {
4110
- const pid = Number.parseInt(readFileSync5(pidFile, "utf8").trim(), 10);
4275
+ const pid = Number.parseInt(readFileSync6(pidFile, "utf8").trim(), 10);
4111
4276
  return Number.isInteger(pid) ? pid : null;
4112
4277
  } catch {
4113
4278
  return null;
@@ -4115,7 +4280,7 @@ function readPidFile(pidFile) {
4115
4280
  }
4116
4281
  function tailFile(file, maxChars) {
4117
4282
  try {
4118
- const content = readFileSync5(file, "utf8").trim();
4283
+ const content = readFileSync6(file, "utf8").trim();
4119
4284
  return content.length > maxChars ? `\u2026${content.slice(-maxChars)}` : content;
4120
4285
  } catch {
4121
4286
  return "";
@@ -4206,13 +4371,13 @@ async function killOrphanDevEcoCliLogins(options) {
4206
4371
  return { killedPids, managedPid: managedPid ?? null };
4207
4372
  }
4208
4373
  function runDetachedDevEcoAuthHost(requestPath) {
4209
- const request = JSON.parse(readFileSync5(requestPath, "utf8"));
4374
+ const request = JSON.parse(readFileSync6(requestPath, "utf8"));
4210
4375
  mkdirSync3(path24.dirname(request.stdout), { recursive: true });
4211
4376
  mkdirSync3(path24.dirname(request.stderr), { recursive: true });
4212
4377
  const stdout = openSync3(request.stdout, "a");
4213
4378
  const stderr = openSync3(request.stderr, "a");
4214
4379
  try {
4215
- const child = spawn3(request.command[0], request.command.slice(1), {
4380
+ const child = spawnCommand(request.command[0], request.command.slice(1), {
4216
4381
  cwd: request.cwd,
4217
4382
  detached: true,
4218
4383
  env: { ...process.env, ...request.env ?? {} },
@@ -4220,6 +4385,13 @@ function runDetachedDevEcoAuthHost(requestPath) {
4220
4385
  windowsHide: true
4221
4386
  });
4222
4387
  const stdin = child.stdin;
4388
+ if (!stdin) {
4389
+ try {
4390
+ child.kill();
4391
+ } catch {
4392
+ }
4393
+ throw new Error("DevEco CLI \u767B\u5F55\u8FDB\u7A0B\u7684 stdin \u4E0D\u53EF\u7528");
4394
+ }
4223
4395
  stdin.on("error", () => {
4224
4396
  });
4225
4397
  stdin.write("\n");
@@ -4257,7 +4429,7 @@ function runDetachedDevEcoAuthHost(requestPath) {
4257
4429
  function findLoginUrl(state) {
4258
4430
  for (const file of [state.stdoutLog, state.stderrLog]) {
4259
4431
  try {
4260
- const match = readFileSync5(file, "utf8").match(LOGIN_URL_PATTERN);
4432
+ const match = readFileSync6(file, "utf8").match(LOGIN_URL_PATTERN);
4261
4433
  if (match) return match[0];
4262
4434
  } catch {
4263
4435
  }
@@ -4310,7 +4482,7 @@ async function managedDevEcoCliLogin(options) {
4310
4482
  stdout: state.stdoutLog,
4311
4483
  stderr: state.stderrLog,
4312
4484
  command: devEcoCliSpawn(executable, ["auth", "login"]),
4313
- env: { DEVECO_CLI_SKIP_VERSION_CHECK: "1" }
4485
+ env: devEcoCliEnvironment()
4314
4486
  }, null, 2), "utf8");
4315
4487
  launchHostOutsideRunner(requestPath, options.hostCommand);
4316
4488
  progress(`\u5DF2\u6301\u4E45\u62C9\u8D77\u767B\u5F55\u8FDB\u7A0B\uFF08\u56DE\u8C03\u670D\u52A1\u5668\u968F\u8FDB\u7A0B\u5B58\u6D3B\uFF09\uFF0C\u65E5\u5FD7: ${state.stdoutLog}`);
@@ -4344,10 +4516,10 @@ async function managedDevEcoCliLogin(options) {
4344
4516
  };
4345
4517
  }
4346
4518
  if (pid2 === null && existsSync6(state.exitFile)) {
4347
- 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"}`);
4348
4520
  }
4349
4521
  if (pid2 !== null && !isAlive2(pid2) && !existsSync6(state.pidFile)) {
4350
- 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"}`);
4351
4523
  }
4352
4524
  const remainingSeconds = Math.round((deadline - Date.now()) / 1e3);
4353
4525
  if (lastCountdownSeconds - remainingSeconds >= 30) {
@@ -4776,13 +4948,13 @@ Select a scenario with query parameter \`?scenario=empty\` or header \`x-droid2h
4776
4948
  }
4777
4949
 
4778
4950
  // src/commands/logs.ts
4779
- 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";
4780
4952
  import path28 from "path";
4781
4953
  function loadRecords(projectRoot) {
4782
4954
  const directory = path28.join(migrationDirectory(projectRoot), "logs", "daily");
4783
4955
  if (!existsSync7(directory)) return [];
4784
- return readdirSync3(directory).filter((file) => file.endsWith(".jsonl")).sort().flatMap(
4785
- (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))
4786
4958
  );
4787
4959
  }
4788
4960
  function queryCommandLogs(project, action, value) {
@@ -8296,7 +8468,7 @@ Usage:
8296
8468
  droid2hmos config <init|validate> [project] [--harmony-project <path>]
8297
8469
  droid2hmos build harmony [project] [--timeout-ms <ms>]
8298
8470
  droid2hmos test harmony [project] [--runner arkts] (--class <class> [--case <case>] | --all) [--target <serial>] [--case-timeout-ms <ms>] [--ref <report-ref>...] [--timeout-ms <ms>]
8299
- 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>]
8300
8472
  droid2hmos report [project] [--require-complete]
8301
8473
  droid2hmos report <init|validate|render> [project] [--require-complete]
8302
8474
  droid2hmos report update [project] --ref <ref> [--status <status>] [--harmony-file <path>...] [--android-source <path>...] [--evidence <path>...] [--difference <text>|--clear-difference]
@@ -8479,7 +8651,7 @@ async function runCli(argv, runtime) {
8479
8651
  const guardResult = guard("analyze:harmony", project);
8480
8652
  const configPath = path36.join(migrationDirectory(projectRoot), "config.json");
8481
8653
  if (!existsSync9(configPath)) throw new Error(`Migration config not found: ${configPath}`);
8482
- const config = JSON.parse(readFileSync7(configPath, "utf8"));
8654
+ const config = JSON.parse(readFileSync8(configPath, "utf8"));
8483
8655
  const configuredHarmony = config.harmonyProject ?? defaultHarmonyProjectPath(projectRoot);
8484
8656
  const harmonyProject = path36.isAbsolute(configuredHarmony) ? configuredHarmony : path36.resolve(projectRoot, configuredHarmony);
8485
8657
  const forwarded = ["build", harmonyProject, "--platform", "harmony"];
@@ -8640,7 +8812,7 @@ async function runCli(argv, runtime) {
8640
8812
  const configPath = path36.join(migrationDirectory(projectRoot), "config.json");
8641
8813
  if (!existsSync9(configPath)) throw new Error(`Migration config not found: ${configPath}`);
8642
8814
  const guardResult = guard("build:harmony", projectRoot);
8643
- const config = JSON.parse(readFileSync7(configPath, "utf8"));
8815
+ const config = JSON.parse(readFileSync8(configPath, "utf8"));
8644
8816
  const configuredHarmony = config.harmonyProject ?? defaultHarmonyProjectPath(projectRoot);
8645
8817
  const harmonyProject = path36.isAbsolute(configuredHarmony) ? configuredHarmony : path36.resolve(projectRoot, configuredHarmony);
8646
8818
  let result;
@@ -8675,17 +8847,24 @@ async function runCli(argv, runtime) {
8675
8847
  if (!Number.isFinite(caseTimeoutMs) || caseTimeoutMs <= 0) throw new CliError("--case-timeout-ms must be a positive number");
8676
8848
  const configPath = path36.join(migrationDirectory(projectRoot), "config.json");
8677
8849
  if (!existsSync9(configPath)) throw new Error(`Migration config not found: ${configPath}`);
8678
- const config = JSON.parse(readFileSync7(configPath, "utf8"));
8850
+ const config = JSON.parse(readFileSync8(configPath, "utf8"));
8679
8851
  const configuredHarmony = config.harmonyProject ?? defaultHarmonyProjectPath(projectRoot);
8680
8852
  const harmonyProject = path36.isAbsolute(configuredHarmony) ? configuredHarmony : path36.resolve(projectRoot, configuredHarmony);
8681
8853
  let result;
8682
8854
  try {
8683
8855
  if (runner === "python") {
8684
- 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");
8685
8857
  const hypiumProject = optionValue(args, "--hypium-project");
8686
- const testLists = optionValues(args, "--test-list").flatMap((value) => value.split(";")).map((value) => value.trim()).filter(Boolean);
8687
8858
  if (!hypiumProject) throw new CliError("Python Hypium requires --hypium-project");
8688
- 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
+ }
8689
8868
  result = runHarmonyPythonHypiumTest({
8690
8869
  projectRoot,
8691
8870
  harmonyProject,
@@ -8790,7 +8969,7 @@ async function runCli(argv, runtime) {
8790
8969
  const projectRoot = path36.resolve(project);
8791
8970
  const configPath = path36.join(migrationDirectory(projectRoot), "config.json");
8792
8971
  if (!existsSync9(configPath)) throw new Error(`Migration config not found: ${configPath}`);
8793
- const config = JSON.parse(readFileSync7(configPath, "utf8"));
8972
+ const config = JSON.parse(readFileSync8(configPath, "utf8"));
8794
8973
  const configuredHarmony = config.harmonyProject ?? defaultHarmonyProjectPath(projectRoot);
8795
8974
  const harmonyProject = path36.isAbsolute(configuredHarmony) ? configuredHarmony : path36.resolve(projectRoot, configuredHarmony);
8796
8975
  const timeoutMs = Number(optionValue(args, "--timeout-ms") ?? 5 * 60 * 1e3);
@@ -8838,7 +9017,7 @@ async function runCli(argv, runtime) {
8838
9017
  let execution;
8839
9018
  if (before.status !== "passed") execution = { status: "failed", skipped: true, summary: "Baseline evidence capture failed; interaction was not executed" };
8840
9019
  else {
8841
- 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"));
8842
9021
  const harmonyProject = path36.isAbsolute(config.harmonyProject) ? config.harmonyProject : path36.resolve(projectRoot, config.harmonyProject);
8843
9022
  const testClass = optionValue(args, "--class") ?? null;
8844
9023
  const testCase = optionValue(args, "--case") ?? null;
@@ -8977,7 +9156,7 @@ var invokedHref = (() => {
8977
9156
  if (invokedHref && invokedHref === entryHref) {
8978
9157
  const argv = process.argv.slice(2);
8979
9158
  const runtime = resolveRuntimePaths(import.meta.url);
8980
- 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";
8981
9160
  const excluded = /* @__PURE__ */ new Set(["help", "-h", "--help", "status", "internal", "dev", "logs"]);
8982
9161
  const readOnlyTaskQuery = argv[0] === "task" && ["status", "next", "show"].includes(argv[1] ?? "");
8983
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) => {