@oniroproject/core 0.8.0 → 0.9.0

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/index.cjs CHANGED
@@ -47,6 +47,7 @@ __export(index_exports, {
47
47
  VERSION: () => VERSION,
48
48
  applyChanges: () => applyChanges,
49
49
  attemptHdcConnection: () => attemptHdcConnection,
50
+ buildCmdWrapper: () => buildCmdWrapper,
50
51
  buildGestureCommands: () => buildGestureCommands,
51
52
  buildGestureUitestCommand: () => buildGestureUitestCommand,
52
53
  buildHap: () => buildHap,
@@ -83,6 +84,8 @@ __export(index_exports, {
83
84
  ensureOhModules: () => ensureOhModules,
84
85
  ensureOk: () => ensureOk,
85
86
  ensureRemoteOk: () => ensureRemoteOk,
87
+ escapeCmdArgument: () => escapeCmdArgument,
88
+ escapeCmdCommand: () => escapeCmdCommand,
86
89
  extractTarball: () => extractTarball,
87
90
  extractZipWithProgress: () => extractZipWithProgress,
88
91
  findAppProcessId: () => findAppProcessId,
@@ -121,12 +124,14 @@ __export(index_exports, {
121
124
  isRamBackedPath: () => isRamBackedPath,
122
125
  isValidBundleName: () => isValidBundleName,
123
126
  isValidProjectName: () => isValidProjectName,
127
+ killProcessTree: () => killProcessTree,
124
128
  launchApp: () => launchApp,
125
129
  listAbilities: () => listAbilities,
126
130
  listDevices: () => listDevices,
127
131
  listModules: () => listModules,
128
132
  listRunningProcesses: () => listRunningProcesses,
129
133
  listTemplates: () => listTemplates,
134
+ needsCmdWrapper: () => needsCmdWrapper,
130
135
  nonInteractivePrompter: () => nonInteractivePrompter,
131
136
  noopLogger: () => noopLogger,
132
137
  noopProgress: () => noopProgress,
@@ -163,11 +168,13 @@ __export(index_exports, {
163
168
  setHilogLevel: () => setHilogLevel,
164
169
  shell: () => shell,
165
170
  shellChecked: () => shellChecked,
171
+ spawnCompat: () => spawnCompat,
166
172
  startEmulator: () => startEmulator,
167
173
  staticConfig: () => staticConfig,
168
174
  stopEmulator: () => stopEmulator,
169
175
  streamHilog: () => streamHilog,
170
176
  takeScreenshot: () => takeScreenshot,
177
+ toLongPath: () => toLongPath,
171
178
  toSpaceError: () => toSpaceError,
172
179
  uninstallApp: () => uninstallApp,
173
180
  validateTemplateLayout: () => validateTemplateLayout,
@@ -402,6 +409,14 @@ function getCmdToolsPath(config) {
402
409
  function getEmulatorDir(config) {
403
410
  return config.get("emulatorDir", defaultPaths.emulatorDir());
404
411
  }
412
+ function toLongPath(p, platform7 = os3.platform()) {
413
+ if (platform7 !== "win32") return p;
414
+ try {
415
+ return fs.realpathSync.native(p);
416
+ } catch {
417
+ return p;
418
+ }
419
+ }
405
420
  function pickExisting(candidates) {
406
421
  for (const c of candidates) {
407
422
  if (fs.existsSync(c)) return c;
@@ -437,12 +452,11 @@ function getHdcPath(config) {
437
452
  }
438
453
  return path2.join(base, "hdc");
439
454
  }
440
- function getHvigorwPath(config, projectDir) {
441
- const local = pickExisting([
442
- path2.join(projectDir, "hvigorw"),
443
- path2.join(projectDir, "hvigorw.bat"),
444
- path2.join(projectDir, "hvigorw.cmd")
445
- ]);
455
+ function getHvigorwPath(config, projectDir, platform7 = os3.platform()) {
456
+ const posixFirst = ["hvigorw", "hvigorw.bat", "hvigorw.cmd"];
457
+ const windowsFirst = ["hvigorw.bat", "hvigorw.cmd", "hvigorw"];
458
+ const names = platform7 === "win32" ? windowsFirst : posixFirst;
459
+ const local = pickExisting(names.map((n) => path2.join(projectDir, n)));
446
460
  if (fs.existsSync(local) && isProjectLocalHvigorwWorking(projectDir)) {
447
461
  return local;
448
462
  }
@@ -974,8 +988,82 @@ async function downloadAndInstallSdk(opts) {
974
988
  // src/cmdTools/install.ts
975
989
  var fs10 = __toESM(require("fs"), 1);
976
990
  var os6 = __toESM(require("os"), 1);
991
+ var path11 = __toESM(require("path"), 1);
992
+ var import_node_child_process2 = require("child_process");
993
+
994
+ // src/hdc/spawnCompat.ts
977
995
  var path10 = __toESM(require("path"), 1);
978
996
  var import_node_child_process = require("child_process");
997
+ var WINDOWS_BATCH_RE = /\.(bat|cmd)$/i;
998
+ var CMD_META_RE = /([()[\]%!^"`<>&|;, *?])/g;
999
+ var CMD_SHIM_RE = /node_modules[\\/]\.bin[\\/][^\\/]+\.cmd$/i;
1000
+ var UNSAFE_CMD_CHARS_RE = /["\r\n\0]/;
1001
+ function needsCmdWrapper(command, platform7 = process.platform) {
1002
+ return platform7 === "win32" && WINDOWS_BATCH_RE.test(command);
1003
+ }
1004
+ function escapeCmdArgument(arg, doubleEscapeMetaChars = false) {
1005
+ let out = String(arg);
1006
+ out = out.replace(/(\\*)"/g, '$1$1\\"');
1007
+ out = out.replace(/(\\*)$/, "$1$1");
1008
+ out = `"${out}"`;
1009
+ out = out.replace(CMD_META_RE, "^$1");
1010
+ if (doubleEscapeMetaChars) out = out.replace(CMD_META_RE, "^$1");
1011
+ return out;
1012
+ }
1013
+ function escapeCmdCommand(command) {
1014
+ return command.replace(CMD_META_RE, "^$1");
1015
+ }
1016
+ function buildCmdWrapper(command, args) {
1017
+ assertCmdSafe(command, `command ${command}`);
1018
+ args.forEach((a, i) => assertCmdSafe(a, `argument ${i} (${a})`));
1019
+ const normalized = path10.normalize(command);
1020
+ const double = CMD_SHIM_RE.test(normalized);
1021
+ const line = [escapeCmdCommand(normalized), ...args.map((a) => escapeCmdArgument(a, double))].join(" ");
1022
+ return {
1023
+ command: process.env.ComSpec ?? process.env.COMSPEC ?? "cmd.exe",
1024
+ args: ["/d", "/s", "/c", `"${line}"`]
1025
+ };
1026
+ }
1027
+ function assertCmdSafe(value, label) {
1028
+ if (UNSAFE_CMD_CHARS_RE.test(value)) {
1029
+ throw new OniroError(
1030
+ `Cannot run a Windows batch wrapper with a quote, newline or NUL in the ${label}: cmd.exe cannot pass it through unambiguously.`
1031
+ );
1032
+ }
1033
+ }
1034
+ function spawnCompat(command, args, options = {}) {
1035
+ if (!needsCmdWrapper(command)) {
1036
+ return (0, import_node_child_process.spawn)(command, [...args], { ...options, shell: false });
1037
+ }
1038
+ const wrapped = buildCmdWrapper(command, args);
1039
+ return (0, import_node_child_process.spawn)(wrapped.command, wrapped.args, {
1040
+ ...options,
1041
+ shell: false,
1042
+ // The line is already escaped; stop Node from re-quoting it.
1043
+ windowsVerbatimArguments: true
1044
+ });
1045
+ }
1046
+ function killProcessTree(child) {
1047
+ if (process.platform === "win32" && typeof child.pid === "number") {
1048
+ try {
1049
+ const killer = (0, import_node_child_process.spawn)("taskkill", ["/pid", String(child.pid), "/T", "/F"], { stdio: "ignore" });
1050
+ killer.on("error", () => {
1051
+ try {
1052
+ child.kill("SIGKILL");
1053
+ } catch {
1054
+ }
1055
+ });
1056
+ return;
1057
+ } catch {
1058
+ }
1059
+ }
1060
+ try {
1061
+ child.kill("SIGKILL");
1062
+ } catch {
1063
+ }
1064
+ }
1065
+
1066
+ // src/cmdTools/install.ts
979
1067
  function getCmdToolsDownloadUrl(config, platform7 = os6.platform()) {
980
1068
  if (platform7 === "linux") {
981
1069
  return config.get("cmdToolsUrlLinux", defaultPaths.cmdToolsUrlLinux);
@@ -1002,16 +1090,16 @@ function getCmdToolsDownloadUrl(config, platform7 = os6.platform()) {
1002
1090
  }
1003
1091
  function findCmdToolsSourceDir(extractPath) {
1004
1092
  const known = [
1005
- path10.join(extractPath, "command-line-tools"),
1006
- path10.join(extractPath, "oh-command-line-tools"),
1007
- path10.join(extractPath, "commandline-tools")
1093
+ path11.join(extractPath, "command-line-tools"),
1094
+ path11.join(extractPath, "oh-command-line-tools"),
1095
+ path11.join(extractPath, "commandline-tools")
1008
1096
  ];
1009
1097
  for (const c of known) {
1010
1098
  if (fs10.existsSync(c)) return c;
1011
1099
  }
1012
- const entries = fs10.readdirSync(extractPath, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => path10.join(extractPath, e.name));
1100
+ const entries = fs10.readdirSync(extractPath, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => path11.join(extractPath, e.name));
1013
1101
  for (const dir of entries) {
1014
- if (fs10.existsSync(path10.join(dir, "bin"))) return dir;
1102
+ if (fs10.existsSync(path11.join(dir, "bin"))) return dir;
1015
1103
  }
1016
1104
  throw new OniroError("Could not locate command line tools folder in the extracted archive.");
1017
1105
  }
@@ -1023,12 +1111,12 @@ async function installCmdTools(opts) {
1023
1111
  const { dir: tmpDir, root: tmpRoot } = createInstallTempDir({
1024
1112
  config,
1025
1113
  override: opts.tmpDir,
1026
- installRoot: path10.dirname(CMD_PATH),
1114
+ installRoot: path11.dirname(CMD_PATH),
1027
1115
  prefix: "oniro-cmdtools-",
1028
1116
  logger
1029
1117
  });
1030
- const zipPath = opts.localZipPath ?? path10.join(tmpDir, "oh-command-line-tools.zip");
1031
- const extractPath = path10.join(tmpDir, "oh-command-line-tools");
1118
+ const zipPath = opts.localZipPath ?? path11.join(tmpDir, "oh-command-line-tools.zip");
1119
+ const extractPath = path11.join(tmpDir, "oh-command-line-tools");
1032
1120
  try {
1033
1121
  if (!opts.localZipPath) {
1034
1122
  const url = getCmdToolsDownloadUrl(config);
@@ -1040,8 +1128,8 @@ async function installCmdTools(opts) {
1040
1128
  fs10.mkdirSync(CMD_PATH, { recursive: true });
1041
1129
  const srcDir = findCmdToolsSourceDir(extractPath);
1042
1130
  for (const entry of fs10.readdirSync(srcDir)) {
1043
- const src = path10.join(srcDir, entry);
1044
- const dest = path10.join(CMD_PATH, entry);
1131
+ const src = path11.join(srcDir, entry);
1132
+ const dest = path11.join(CMD_PATH, entry);
1045
1133
  if (fs10.statSync(src).isDirectory()) {
1046
1134
  if (fs10.existsSync(dest)) fs10.rmSync(dest, { recursive: true, force: true });
1047
1135
  movePath(src, dest);
@@ -1049,10 +1137,10 @@ async function installCmdTools(opts) {
1049
1137
  fs10.copyFileSync(src, dest);
1050
1138
  }
1051
1139
  }
1052
- const binDir = path10.join(CMD_PATH, "bin");
1140
+ const binDir = path11.join(CMD_PATH, "bin");
1053
1141
  if (fs10.existsSync(binDir) && os6.platform() !== "win32") {
1054
1142
  for (const file of fs10.readdirSync(binDir)) {
1055
- fs10.chmodSync(path10.join(binDir, file), 493);
1143
+ fs10.chmodSync(path11.join(binDir, file), 493);
1056
1144
  }
1057
1145
  }
1058
1146
  progress?.report({ message: "Finalizing installation...", increment: 5 });
@@ -1065,14 +1153,14 @@ async function installCmdTools(opts) {
1065
1153
  }
1066
1154
  function getCmdToolsStatus(config) {
1067
1155
  const cmdPath = getCmdToolsPath(config);
1068
- const binDir = path10.join(cmdPath, "bin");
1156
+ const binDir = path11.join(cmdPath, "bin");
1069
1157
  const candidates = os6.platform() === "win32" ? ["ohpm.exe", "ohpm.cmd", "ohpm.bat", "ohpm"] : ["ohpm"];
1070
- const bin = candidates.map((c) => path10.join(binDir, c)).find((p) => fs10.existsSync(p));
1158
+ const bin = candidates.map((c) => path11.join(binDir, c)).find((p) => fs10.existsSync(p));
1071
1159
  if (!bin) {
1072
1160
  return { installed: false, status: "Not installed" };
1073
1161
  }
1074
1162
  try {
1075
- const versionFile = path10.join(cmdPath, "version.txt");
1163
+ const versionFile = path11.join(cmdPath, "version.txt");
1076
1164
  if (fs10.existsSync(versionFile)) {
1077
1165
  const content = fs10.readFileSync(versionFile, "utf8");
1078
1166
  for (const line of content.split(/\r?\n/)) {
@@ -1083,7 +1171,14 @@ function getCmdToolsStatus(config) {
1083
1171
  } catch {
1084
1172
  }
1085
1173
  try {
1086
- const version = (0, import_node_child_process.execFileSync)(bin, ["-v"], { encoding: "utf8" }).trim();
1174
+ const wrapped = needsCmdWrapper(bin);
1175
+ const probe = wrapped ? buildCmdWrapper(bin, ["-v"]) : { command: bin, args: ["-v"] };
1176
+ const res = (0, import_node_child_process2.spawnSync)(probe.command, probe.args, {
1177
+ encoding: "utf8",
1178
+ windowsVerbatimArguments: wrapped
1179
+ });
1180
+ if (res.error || res.status !== 0) throw res.error ?? new Error(`exit ${String(res.status)}`);
1181
+ const version = res.stdout.trim();
1087
1182
  return { installed: true, status: `Installed (${version})` };
1088
1183
  } catch {
1089
1184
  return { installed: true, status: "Installed (version unknown)" };
@@ -1101,7 +1196,7 @@ function removeCmdTools(config) {
1101
1196
 
1102
1197
  // src/sign/encryptKey.ts
1103
1198
  var fs11 = __toESM(require("fs"), 1);
1104
- var path11 = __toESM(require("path"), 1);
1199
+ var path12 = __toESM(require("path"), 1);
1105
1200
  var crypto2 = __toESM(require("crypto"), 1);
1106
1201
  var DAEMON_ROOT_KEY_COMPONENT_LENGTH = 16;
1107
1202
  var DAEMON_SALT_KEY_LENGTH = 16;
@@ -1173,7 +1268,7 @@ function getRootKey(fdComponents, salt) {
1173
1268
  function createAndStoreKey(dir, keyLength) {
1174
1269
  const key = crypto2.randomBytes(keyLength);
1175
1270
  const hash = crypto2.createHash("sha256").update(key).digest("hex");
1176
- const filePath = path11.join(dir, hash);
1271
+ const filePath = path12.join(dir, hash);
1177
1272
  fs11.writeFileSync(filePath, key);
1178
1273
  fs11.chmodSync(filePath, KEY_FILE_PERMISSIONS);
1179
1274
  return key;
@@ -1182,23 +1277,23 @@ function createAndStoreEnKey(rootKey, ceDir) {
1182
1277
  const workKey = crypto2.randomBytes(DAEMON_WORK_KEY_LENGTH);
1183
1278
  const encrypted = encrypt(rootKey, workKey);
1184
1279
  const hash = crypto2.createHash("sha256").update(encrypted).digest("hex");
1185
- const filePath = path11.join(ceDir, hash);
1280
+ const filePath = path12.join(ceDir, hash);
1186
1281
  fs11.writeFileSync(filePath, encrypted);
1187
1282
  fs11.chmodSync(filePath, KEY_FILE_PERMISSIONS);
1188
1283
  }
1189
1284
  function createMaterial(materialPath) {
1190
1285
  fs11.mkdirSync(materialPath, { recursive: true });
1191
1286
  fs11.chmodSync(materialPath, KEY_FILE_DIRECTORY_PERMISSIONS);
1192
- const fdDir = path11.join(materialPath, "fd");
1193
- const acDir = path11.join(materialPath, "ac");
1194
- const ceDir = path11.join(materialPath, "ce");
1287
+ const fdDir = path12.join(materialPath, "fd");
1288
+ const acDir = path12.join(materialPath, "ac");
1289
+ const ceDir = path12.join(materialPath, "ce");
1195
1290
  fs11.mkdirSync(fdDir, { recursive: true });
1196
1291
  fs11.mkdirSync(acDir, { recursive: true });
1197
1292
  fs11.mkdirSync(ceDir, { recursive: true });
1198
1293
  const fdSubDirs = ["0", "1", "2"];
1199
1294
  const fdComponents = [];
1200
1295
  for (const sub of fdSubDirs) {
1201
- const subDir = path11.join(fdDir, sub);
1296
+ const subDir = path12.join(fdDir, sub);
1202
1297
  fs11.mkdirSync(subDir, { recursive: true });
1203
1298
  const comp = createAndStoreKey(subDir, DAEMON_ROOT_KEY_COMPONENT_LENGTH);
1204
1299
  fdComponents.push(comp);
@@ -1215,17 +1310,17 @@ function readSingleFile(dir) {
1215
1310
  if (files.length !== 1) {
1216
1311
  throw new Error(`Signing material in ${dir} is illegal (expected exactly one file).`);
1217
1312
  }
1218
- return fs11.readFileSync(path11.join(dir, files[0]));
1313
+ return fs11.readFileSync(path12.join(dir, files[0]));
1219
1314
  }
1220
1315
  function getKey(materialPath) {
1221
1316
  if (!fs11.existsSync(materialPath) || !fs11.statSync(materialPath).isDirectory()) {
1222
1317
  throw new Error("Material directory does not exist.");
1223
1318
  }
1224
- const fdDir = path11.join(materialPath, "fd");
1225
- const fdComponents = ["0", "1", "2"].map((sub) => readSingleFile(path11.join(fdDir, sub)));
1226
- const salt = readSingleFile(path11.join(materialPath, "ac"));
1319
+ const fdDir = path12.join(materialPath, "fd");
1320
+ const fdComponents = ["0", "1", "2"].map((sub) => readSingleFile(path12.join(fdDir, sub)));
1321
+ const salt = readSingleFile(path12.join(materialPath, "ac"));
1227
1322
  const rootKey = getRootKey(fdComponents, salt);
1228
- const workMaterial = readSingleFile(path11.join(materialPath, "ce"));
1323
+ const workMaterial = readSingleFile(path12.join(materialPath, "ce"));
1229
1324
  return decrypt(rootKey, workMaterial);
1230
1325
  }
1231
1326
  function encryptPwd(password, materialPath) {
@@ -1241,8 +1336,8 @@ function decryptPwd(encryptedHex, materialPath) {
1241
1336
 
1242
1337
  // src/sign/generateSigningConfigs.ts
1243
1338
  var fs12 = __toESM(require("fs"), 1);
1244
- var path12 = __toESM(require("path"), 1);
1245
- var import_node_child_process2 = require("child_process");
1339
+ var path13 = __toESM(require("path"), 1);
1340
+ var import_node_child_process3 = require("child_process");
1246
1341
  var import_json52 = __toESM(require("json5"), 1);
1247
1342
 
1248
1343
  // src/sign/applicationCert.ts
@@ -1311,13 +1406,13 @@ var SIGNING_KIND_CONFIG = {
1311
1406
  };
1312
1407
  function copyFilesToProject(projectDir, paths, kind, logger, applicationCertPath) {
1313
1408
  logger.info("[sign] Copying signing material into project...");
1314
- const signaturesDir = path12.join(projectDir, "signatures");
1409
+ const signaturesDir = path13.join(projectDir, "signatures");
1315
1410
  fs12.mkdirSync(signaturesDir, { recursive: true });
1316
- fs12.copyFileSync(paths.keystore, path12.join(signaturesDir, "OpenHarmony.p12"));
1317
- fs12.copyFileSync(paths.profileCert, path12.join(signaturesDir, "OpenHarmonyProfileRelease.pem"));
1318
- fs12.copyFileSync(paths.unsignedProfileTemplate, path12.join(signaturesDir, "UnsgnedReleasedProfileTemplate.json"));
1411
+ fs12.copyFileSync(paths.keystore, path13.join(signaturesDir, "OpenHarmony.p12"));
1412
+ fs12.copyFileSync(paths.profileCert, path13.join(signaturesDir, "OpenHarmonyProfileRelease.pem"));
1413
+ fs12.copyFileSync(paths.unsignedProfileTemplate, path13.join(signaturesDir, "UnsgnedReleasedProfileTemplate.json"));
1319
1414
  if (kind === "application-release") {
1320
- const dest = path12.join(signaturesDir, "OpenHarmonyApplication.cer");
1415
+ const dest = path13.join(signaturesDir, "OpenHarmonyApplication.cer");
1321
1416
  if (applicationCertPath) {
1322
1417
  fs12.copyFileSync(applicationCertPath, dest);
1323
1418
  } else {
@@ -1340,8 +1435,8 @@ function extractLeafCertificate(chainPath) {
1340
1435
  function modifyProfileTemplate(projectDir, apl, appFeature, logger, options = {}) {
1341
1436
  const kind = options.kind ?? pickSigningKind(apl);
1342
1437
  logger.info(`[sign] Modifying profile template (apl=${apl}, app-feature=${appFeature}, kind=${kind})...`);
1343
- const appJsonPath = path12.join(projectDir, "AppScope/app.json5");
1344
- const profileTemplatePath = path12.join(projectDir, "signatures/UnsgnedReleasedProfileTemplate.json");
1438
+ const appJsonPath = path13.join(projectDir, "AppScope/app.json5");
1439
+ const profileTemplatePath = path13.join(projectDir, "signatures/UnsgnedReleasedProfileTemplate.json");
1345
1440
  if (!fs12.existsSync(appJsonPath)) {
1346
1441
  throw new OniroError(`${appJsonPath} does not exist.`);
1347
1442
  }
@@ -1366,7 +1461,7 @@ function modifyProfileTemplate(projectDir, apl, appFeature, logger, options = {}
1366
1461
  profileTemplate["bundle-info"]["bundle-name"] = appJson.app.bundleName;
1367
1462
  profileTemplate["bundle-info"]["apl"] = apl;
1368
1463
  profileTemplate["bundle-info"]["app-feature"] = appFeature;
1369
- const distCertChainPath = path12.join(
1464
+ const distCertChainPath = path13.join(
1370
1465
  projectDir,
1371
1466
  "signatures",
1372
1467
  SIGNING_KIND_CONFIG[kind].certBasename
@@ -1380,9 +1475,9 @@ function modifyProfileTemplate(projectDir, apl, appFeature, logger, options = {}
1380
1475
  }
1381
1476
  function generateP7bFile(projectDir, paths, logger, passwords) {
1382
1477
  logger.info("[sign] Generating P7b profile via hap-sign-tool...");
1383
- const signaturesDir = path12.join(projectDir, "signatures");
1384
- const profileTemplatePath = path12.join(signaturesDir, "UnsgnedReleasedProfileTemplate.json");
1385
- const outputProfilePath = path12.join(signaturesDir, "app1-profile.p7b");
1478
+ const signaturesDir = path13.join(projectDir, "signatures");
1479
+ const profileTemplatePath = path13.join(signaturesDir, "UnsgnedReleasedProfileTemplate.json");
1480
+ const outputProfilePath = path13.join(signaturesDir, "app1-profile.p7b");
1386
1481
  const args = [
1387
1482
  "-jar",
1388
1483
  paths.signTool,
@@ -1407,7 +1502,7 @@ function generateP7bFile(projectDir, paths, logger, passwords) {
1407
1502
  passwords.store
1408
1503
  ];
1409
1504
  try {
1410
- (0, import_node_child_process2.execFileSync)("java", args, { stdio: "inherit" });
1505
+ (0, import_node_child_process3.execFileSync)("java", args, { stdio: "inherit" });
1411
1506
  } catch (e) {
1412
1507
  throw new OniroError(
1413
1508
  `hap-sign-tool failed. Ensure a JDK (with keytool/java) is on PATH. Underlying error: ${e.message}`,
@@ -1416,7 +1511,7 @@ function generateP7bFile(projectDir, paths, logger, passwords) {
1416
1511
  }
1417
1512
  }
1418
1513
  function detectSigningConfigNames(projectDir) {
1419
- const buildProfilePath = path12.join(projectDir, "build-profile.json5");
1514
+ const buildProfilePath = path13.join(projectDir, "build-profile.json5");
1420
1515
  if (!fs12.existsSync(buildProfilePath)) return ["default"];
1421
1516
  let parsed;
1422
1517
  try {
@@ -1437,8 +1532,8 @@ function detectSigningConfigNames(projectDir) {
1437
1532
  }
1438
1533
  function updateBuildProfile(projectDir, kind, logger, passwords) {
1439
1534
  logger.info("[sign] Writing signing configs into build-profile.json5...");
1440
- const materialDir = path12.join(projectDir, "signatures", "material");
1441
- const buildProfilePath = path12.join(projectDir, "build-profile.json5");
1535
+ const materialDir = path13.join(projectDir, "signatures", "material");
1536
+ const buildProfilePath = path13.join(projectDir, "build-profile.json5");
1442
1537
  const encryptedStorePassword = encryptPwd(passwords.store, materialDir);
1443
1538
  const encryptedKeyPassword = encryptPwd(passwords.key, materialDir);
1444
1539
  let buildProfile = { app: {} };
@@ -1469,7 +1564,7 @@ function updateBuildProfile(projectDir, kind, logger, passwords) {
1469
1564
  }
1470
1565
  function prepareMaterialDirectory(projectDir, logger) {
1471
1566
  logger.info("[sign] Generating fresh signing material...");
1472
- const materialDir = path12.join(projectDir, "signatures", "material");
1567
+ const materialDir = path13.join(projectDir, "signatures", "material");
1473
1568
  if (fs12.existsSync(materialDir)) {
1474
1569
  fs12.rmSync(materialDir, { recursive: true, force: true });
1475
1570
  }
@@ -1494,15 +1589,15 @@ function generateSigningConfigs(options) {
1494
1589
  "Could not detect project SDK version (compileSdkVersion missing in build-profile.json5)."
1495
1590
  );
1496
1591
  }
1497
- const sdkPath = path12.join(sdkHome, String(sdkVersion));
1592
+ const sdkPath = path13.join(sdkHome, String(sdkVersion));
1498
1593
  if (!fs12.existsSync(sdkPath)) {
1499
1594
  throw new OniroError(`SDK path does not exist: ${sdkPath}`);
1500
1595
  }
1501
1596
  const paths = {
1502
- signTool: path12.join(sdkPath, "toolchains/lib/hap-sign-tool.jar"),
1503
- keystore: path12.join(sdkPath, "toolchains/lib/OpenHarmony.p12"),
1504
- profileCert: path12.join(sdkPath, "toolchains/lib/OpenHarmonyProfileRelease.pem"),
1505
- unsignedProfileTemplate: path12.join(sdkPath, "toolchains/lib/UnsgnedReleasedProfileTemplate.json")
1597
+ signTool: path13.join(sdkPath, "toolchains/lib/hap-sign-tool.jar"),
1598
+ keystore: path13.join(sdkPath, "toolchains/lib/OpenHarmony.p12"),
1599
+ profileCert: path13.join(sdkPath, "toolchains/lib/OpenHarmonyProfileRelease.pem"),
1600
+ unsignedProfileTemplate: path13.join(sdkPath, "toolchains/lib/UnsgnedReleasedProfileTemplate.json")
1506
1601
  };
1507
1602
  const kind = pickSigningKind(apl);
1508
1603
  const passwords = options.passwords ?? DEFAULT_PASSWORDS;
@@ -1517,7 +1612,7 @@ function generateSigningConfigs(options) {
1517
1612
 
1518
1613
  // src/emulator/install.ts
1519
1614
  var fs13 = __toESM(require("fs"), 1);
1520
- var path13 = __toESM(require("path"), 1);
1615
+ var path14 = __toESM(require("path"), 1);
1521
1616
  async function installEmulator(opts) {
1522
1617
  const { config, progress, abortSignal } = opts;
1523
1618
  const logger = opts.logger ?? noopLogger;
@@ -1527,18 +1622,18 @@ async function installEmulator(opts) {
1527
1622
  const { dir: tmpDir, root: tmpRoot } = createInstallTempDir({
1528
1623
  config,
1529
1624
  override: opts.tmpDir,
1530
- installRoot: path13.dirname(emulatorDir),
1625
+ installRoot: path14.dirname(emulatorDir),
1531
1626
  prefix: "oniro-emulator-",
1532
1627
  logger
1533
1628
  });
1534
- const tmpZip = path13.join(tmpDir, "oniro_emulator.zip");
1629
+ const tmpZip = path14.join(tmpDir, "oniro_emulator.zip");
1535
1630
  try {
1536
1631
  fs13.mkdirSync(emulatorDir, { recursive: true });
1537
1632
  progress?.report({ message: "Downloading emulator...", increment: 0 });
1538
1633
  await downloadFile({ url, dest: tmpZip, progress, abortSignal, start: 0, range: 50, what, tmpRoot });
1539
1634
  progress?.report({ message: "Extracting emulator...", increment: 0 });
1540
1635
  await extractZipWithProgress({ zipPath: tmpZip, dest: emulatorDir, progress, start: 50, range: 45, logger });
1541
- const runSh = path13.join(emulatorDir, "images", "run.sh");
1636
+ const runSh = path14.join(emulatorDir, "images", "run.sh");
1542
1637
  if (fs13.existsSync(runSh)) {
1543
1638
  fs13.chmodSync(runSh, 493);
1544
1639
  }
@@ -1550,8 +1645,8 @@ async function installEmulator(opts) {
1550
1645
  }
1551
1646
  }
1552
1647
  function isEmulatorInstalled(config) {
1553
- const imagesDir = path13.join(getEmulatorDir(config), "images");
1554
- return fs13.existsSync(path13.join(imagesDir, "run.sh")) || fs13.existsSync(path13.join(imagesDir, "run.bat"));
1648
+ const imagesDir = path14.join(getEmulatorDir(config), "images");
1649
+ return fs13.existsSync(path14.join(imagesDir, "run.sh")) || fs13.existsSync(path14.join(imagesDir, "run.bat"));
1555
1650
  }
1556
1651
  function removeEmulator(config) {
1557
1652
  const emulatorDir = getEmulatorDir(config);
@@ -1561,13 +1656,16 @@ function removeEmulator(config) {
1561
1656
  }
1562
1657
 
1563
1658
  // src/emulator/lifecycle.ts
1564
- var fs14 = __toESM(require("fs"), 1);
1659
+ var fs15 = __toESM(require("fs"), 1);
1565
1660
  var os7 = __toESM(require("os"), 1);
1566
- var path14 = __toESM(require("path"), 1);
1661
+ var path15 = __toESM(require("path"), 1);
1567
1662
  var import_node_child_process4 = require("child_process");
1568
1663
 
1569
1664
  // src/hdc/exec.ts
1570
- var import_node_child_process3 = require("child_process");
1665
+ var fs14 = __toESM(require("fs"), 1);
1666
+ function errorMessage(err) {
1667
+ return err instanceof Error ? err.message : String(err);
1668
+ }
1571
1669
  function runProcess(opts) {
1572
1670
  const { command, args, cwd, env, onOutput } = opts;
1573
1671
  const timeoutMs = opts.timeoutMs ?? 6e4;
@@ -1577,7 +1675,17 @@ function runProcess(opts) {
1577
1675
  reject(new CancelledError(`Command aborted before start: ${command}`));
1578
1676
  return;
1579
1677
  }
1580
- const child = (0, import_node_child_process3.spawn)(command, [...args], { cwd, env, shell: false });
1678
+ if (needsCmdWrapper(command) && !fs14.existsSync(command)) {
1679
+ reject(new OniroError(`Failed to spawn ${command}: ENOENT (no such file)`));
1680
+ return;
1681
+ }
1682
+ let child;
1683
+ try {
1684
+ child = spawnCompat(command, args, { cwd, env });
1685
+ } catch (err) {
1686
+ reject(new OniroError(`Failed to spawn ${command}: ${errorMessage(err)}`, err));
1687
+ return;
1688
+ }
1581
1689
  let stdout = "";
1582
1690
  let stderr = "";
1583
1691
  let settled = false;
@@ -1590,26 +1698,20 @@ function runProcess(opts) {
1590
1698
  if (settled) return;
1591
1699
  settled = true;
1592
1700
  cleanup();
1593
- try {
1594
- child.kill("SIGKILL");
1595
- } catch {
1596
- }
1701
+ killProcessTree(child);
1597
1702
  reject(new CancelledError(`Command aborted: ${command} ${args.join(" ")}`));
1598
1703
  };
1599
1704
  const timer = setTimeout(() => {
1600
1705
  timedOut = true;
1601
- try {
1602
- child.kill("SIGKILL");
1603
- } catch {
1604
- }
1706
+ killProcessTree(child);
1605
1707
  }, timeoutMs);
1606
1708
  abortSignal?.addEventListener("abort", onAbort, { once: true });
1607
- child.stdout.on("data", (d) => {
1709
+ child.stdout?.on("data", (d) => {
1608
1710
  const s = d.toString();
1609
1711
  stdout += s;
1610
1712
  onOutput?.(s, "stdout");
1611
1713
  });
1612
- child.stderr.on("data", (d) => {
1714
+ child.stderr?.on("data", (d) => {
1613
1715
  const s = d.toString();
1614
1716
  stderr += s;
1615
1717
  onOutput?.(s, "stderr");
@@ -1681,7 +1783,7 @@ function ensureRemoteOk(result, command) {
1681
1783
  }
1682
1784
 
1683
1785
  // src/emulator/lifecycle.ts
1684
- var PID_FILE = path14.join(os7.tmpdir(), "oniro_emulator.pid");
1786
+ var PID_FILE = path15.join(os7.tmpdir(), "oniro_emulator.pid");
1685
1787
  async function attemptHdcConnection(config, address = "127.0.0.1:55555", logger = noopLogger) {
1686
1788
  try {
1687
1789
  await hdcExec({ config, args: ["start", "-r"], logger });
@@ -1694,24 +1796,24 @@ async function attemptHdcConnection(config, address = "127.0.0.1:55555", logger
1694
1796
  }
1695
1797
  }
1696
1798
  function resolveLauncher(imagesPath, headless, connect) {
1697
- const runSh = path14.join(imagesPath, "run.sh");
1698
- const runBat = path14.join(imagesPath, "run.bat");
1799
+ const runSh = path15.join(imagesPath, "run.sh");
1800
+ const runBat = path15.join(imagesPath, "run.bat");
1699
1801
  const extra = [];
1700
1802
  if (headless) extra.push("--headless");
1701
1803
  if (connect) extra.push("--connect", connect);
1702
1804
  if (os7.platform() === "win32") {
1703
- if (fs14.existsSync(runBat)) {
1805
+ if (fs15.existsSync(runBat)) {
1704
1806
  return { command: "cmd.exe", args: ["/c", runBat, ...extra], cwd: imagesPath };
1705
1807
  }
1706
- if (fs14.existsSync(runSh)) {
1808
+ if (fs15.existsSync(runSh)) {
1707
1809
  return { command: "bash", args: [runSh, ...extra], cwd: imagesPath };
1708
1810
  }
1709
1811
  throw new OniroError(`No emulator launcher (run.bat or run.sh) found in ${imagesPath}.`);
1710
1812
  }
1711
- if (fs14.existsSync(runSh)) {
1813
+ if (fs15.existsSync(runSh)) {
1712
1814
  return { command: "bash", args: [runSh, ...extra], cwd: imagesPath };
1713
1815
  }
1714
- if (fs14.existsSync(runBat)) {
1816
+ if (fs15.existsSync(runBat)) {
1715
1817
  return { command: "bash", args: [runBat, ...extra], cwd: imagesPath };
1716
1818
  }
1717
1819
  throw new OniroError(`No emulator launcher (run.sh or run.bat) found in ${imagesPath}.`);
@@ -1719,9 +1821,9 @@ function resolveLauncher(imagesPath, headless, connect) {
1719
1821
  async function startEmulator(opts) {
1720
1822
  const { config } = opts;
1721
1823
  const logger = opts.logger ?? noopLogger;
1722
- if (os7.platform() !== "win32" && fs14.existsSync(PID_FILE)) {
1824
+ if (os7.platform() !== "win32" && fs15.existsSync(PID_FILE)) {
1723
1825
  try {
1724
- const pid = Number(fs14.readFileSync(PID_FILE, "utf8").trim());
1826
+ const pid = Number(fs15.readFileSync(PID_FILE, "utf8").trim());
1725
1827
  if (Number.isFinite(pid) && pid > 0) {
1726
1828
  try {
1727
1829
  process.kill(pid, 0);
@@ -1737,13 +1839,13 @@ async function startEmulator(opts) {
1737
1839
  logger.warn(`Could not read PID file: ${err.message}`);
1738
1840
  }
1739
1841
  }
1740
- const imagesPath = path14.join(getEmulatorDir(config), "images");
1741
- if (!fs14.existsSync(imagesPath)) {
1842
+ const imagesPath = path15.join(getEmulatorDir(config), "images");
1843
+ if (!fs15.existsSync(imagesPath)) {
1742
1844
  throw new OniroError(`Emulator images directory not found at ${imagesPath}. Run \`oniro-app emulator install\` first.`);
1743
1845
  }
1744
1846
  const launcher = resolveLauncher(imagesPath, opts.headless === true, opts.connect);
1745
1847
  const logPath = opts.logFile ?? (os7.platform() === "win32" ? "NUL" : "/dev/null");
1746
- const logFd = fs14.openSync(logPath, "a");
1848
+ const logFd = fs15.openSync(logPath, "a");
1747
1849
  let child;
1748
1850
  try {
1749
1851
  child = (0, import_node_child_process4.spawn)(launcher.command, [...launcher.args], {
@@ -1753,12 +1855,12 @@ async function startEmulator(opts) {
1753
1855
  windowsHide: true
1754
1856
  });
1755
1857
  } finally {
1756
- fs14.closeSync(logFd);
1858
+ fs15.closeSync(logFd);
1757
1859
  }
1758
1860
  if (!child.pid) {
1759
1861
  throw new OniroError(`Failed to spawn launcher (${launcher.command} ${launcher.args.join(" ")}).`);
1760
1862
  }
1761
- fs14.writeFileSync(PID_FILE, String(child.pid));
1863
+ fs15.writeFileSync(PID_FILE, String(child.pid));
1762
1864
  child.unref();
1763
1865
  logger.info(`Emulator launched (PID ${child.pid}). Logs: ${logPath}`);
1764
1866
  const earlyExit = new Promise((resolve4) => {
@@ -1805,24 +1907,24 @@ ${tail}` : ""}`);
1805
1907
  }
1806
1908
  function readTail(filePath, bytes) {
1807
1909
  try {
1808
- const stat = fs14.statSync(filePath);
1910
+ const stat = fs15.statSync(filePath);
1809
1911
  const start = Math.max(0, stat.size - bytes);
1810
- const fd = fs14.openSync(filePath, "r");
1912
+ const fd = fs15.openSync(filePath, "r");
1811
1913
  try {
1812
1914
  const buf = Buffer.alloc(stat.size - start);
1813
- fs14.readSync(fd, buf, 0, buf.length, start);
1915
+ fs15.readSync(fd, buf, 0, buf.length, start);
1814
1916
  return buf.toString("utf8");
1815
1917
  } finally {
1816
- fs14.closeSync(fd);
1918
+ fs15.closeSync(fd);
1817
1919
  }
1818
1920
  } catch {
1819
1921
  return "";
1820
1922
  }
1821
1923
  }
1822
1924
  async function stopEmulator(logger = noopLogger) {
1823
- if (fs14.existsSync(PID_FILE)) {
1925
+ if (fs15.existsSync(PID_FILE)) {
1824
1926
  try {
1825
- const pid = Number(fs14.readFileSync(PID_FILE, "utf8").trim());
1927
+ const pid = Number(fs15.readFileSync(PID_FILE, "utf8").trim());
1826
1928
  if (Number.isFinite(pid) && pid > 0) {
1827
1929
  try {
1828
1930
  process.kill(pid, "SIGTERM");
@@ -1841,7 +1943,7 @@ async function stopEmulator(logger = noopLogger) {
1841
1943
  } catch (err) {
1842
1944
  logger.warn(`[emulator] could not run ${killCmd}: ${err.message}`);
1843
1945
  }
1844
- fs14.rm(PID_FILE, { force: true }, (err) => {
1946
+ fs15.rm(PID_FILE, { force: true }, (err) => {
1845
1947
  if (err) logger.warn(`Could not remove PID file: ${err.message}`);
1846
1948
  });
1847
1949
  }
@@ -2008,21 +2110,20 @@ async function waitForCondition(opts) {
2008
2110
  }
2009
2111
 
2010
2112
  // src/hdc/app.ts
2011
- var fs16 = __toESM(require("fs"), 1);
2012
- var path16 = __toESM(require("path"), 1);
2013
- var import_node_child_process5 = require("child_process");
2113
+ var fs17 = __toESM(require("fs"), 1);
2114
+ var path17 = __toESM(require("path"), 1);
2014
2115
 
2015
2116
  // src/hdc/project.ts
2016
- var fs15 = __toESM(require("fs"), 1);
2017
- var path15 = __toESM(require("path"), 1);
2117
+ var fs16 = __toESM(require("fs"), 1);
2118
+ var path16 = __toESM(require("path"), 1);
2018
2119
  var import_json53 = __toESM(require("json5"), 1);
2019
2120
  function readJson5(filePath) {
2020
- const content = fs15.readFileSync(filePath, "utf-8");
2121
+ const content = fs16.readFileSync(filePath, "utf-8");
2021
2122
  return import_json53.default.parse(content);
2022
2123
  }
2023
2124
  function getBundleName(projectDir) {
2024
- const appJsonPath = path15.join(projectDir, "AppScope", "app.json5");
2025
- if (!fs15.existsSync(appJsonPath)) {
2125
+ const appJsonPath = path16.join(projectDir, "AppScope", "app.json5");
2126
+ if (!fs16.existsSync(appJsonPath)) {
2026
2127
  throw new OniroError(`Could not find app.json5 at ${appJsonPath}`);
2027
2128
  }
2028
2129
  const appJson = readJson5(appJsonPath);
@@ -2032,8 +2133,8 @@ function getBundleName(projectDir) {
2032
2133
  return appJson.app.bundleName;
2033
2134
  }
2034
2135
  function getMainAbility(projectDir, moduleName = "entry", abilityName) {
2035
- const moduleJsonPath = path15.join(projectDir, moduleName, "src", "main", "module.json5");
2036
- if (!fs15.existsSync(moduleJsonPath)) {
2136
+ const moduleJsonPath = path16.join(projectDir, moduleName, "src", "main", "module.json5");
2137
+ if (!fs16.existsSync(moduleJsonPath)) {
2037
2138
  throw new OniroError(`Could not find module.json5 at ${moduleJsonPath}`);
2038
2139
  }
2039
2140
  const moduleJson = readJson5(moduleJsonPath);
@@ -2056,8 +2157,8 @@ function getMainAbility(projectDir, moduleName = "entry", abilityName) {
2056
2157
  async function installApp(opts) {
2057
2158
  const logger = scopedLogger(opts.logger ?? noopLogger, "hdc");
2058
2159
  const relativeHapPath = opts.hapPath ?? opts.config.get("hapPath", defaultPaths.hapPath);
2059
- const hapPath = path16.isAbsolute(relativeHapPath) ? relativeHapPath : path16.join(opts.projectDir, relativeHapPath);
2060
- if (!fs16.existsSync(hapPath)) {
2160
+ const hapPath = path17.isAbsolute(relativeHapPath) ? relativeHapPath : path17.join(opts.projectDir, relativeHapPath);
2161
+ if (!fs17.existsSync(hapPath)) {
2061
2162
  throw new OniroError(`HAP file not found at: ${hapPath}. Build and sign the app first.`);
2062
2163
  }
2063
2164
  const result = await hdcExec({ config: opts.config, args: ["install", hapPath], timeoutMs: 3e5 });
@@ -2142,10 +2243,16 @@ function listRunningProcesses(config, options = {}) {
2142
2243
  const timeoutMs = options.timeoutMs ?? 1e3;
2143
2244
  const hdc = getHdcPath(config);
2144
2245
  return new Promise((resolve4, reject) => {
2145
- const proc = (0, import_node_child_process5.spawn)(hdc, ["track-jpid"]);
2246
+ let proc;
2247
+ try {
2248
+ proc = spawnCompat(hdc, ["track-jpid"]);
2249
+ } catch (err) {
2250
+ reject(new OniroError(`Failed to spawn ${hdc}: ${err instanceof Error ? err.message : String(err)}`, err));
2251
+ return;
2252
+ }
2146
2253
  const processes = [];
2147
2254
  let resolved = false;
2148
- proc.stdout.on("data", (data) => {
2255
+ proc.stdout?.on("data", (data) => {
2149
2256
  for (const line of data.toString().split("\n").filter(Boolean)) {
2150
2257
  const match = line.match(/^(\d+)\s+(.+)$/);
2151
2258
  if (!match) continue;
@@ -2154,13 +2261,13 @@ function listRunningProcesses(config, options = {}) {
2154
2261
  processes.push({ pid, name });
2155
2262
  if (options.targetProcessName && name === options.targetProcessName && !resolved) {
2156
2263
  resolved = true;
2157
- proc.kill();
2264
+ killProcessTree(proc);
2158
2265
  resolve4(pid);
2159
2266
  return;
2160
2267
  }
2161
2268
  }
2162
2269
  });
2163
- proc.stderr.on("data", (data) => {
2270
+ proc.stderr?.on("data", (data) => {
2164
2271
  logger.warn(`[hdc track-jpid] ${data.toString()}`);
2165
2272
  });
2166
2273
  proc.on("error", (err) => {
@@ -2172,7 +2279,7 @@ function listRunningProcesses(config, options = {}) {
2172
2279
  const timeout = setTimeout(() => {
2173
2280
  if (resolved) return;
2174
2281
  resolved = true;
2175
- proc.kill();
2282
+ killProcessTree(proc);
2176
2283
  if (options.targetProcessName) {
2177
2284
  reject(new OniroError(`Could not find process for bundle: ${options.targetProcessName}`));
2178
2285
  } else {
@@ -2330,12 +2437,12 @@ async function reboot(opts) {
2330
2437
  }
2331
2438
 
2332
2439
  // src/hdc/screen.ts
2333
- var fs17 = __toESM(require("fs"), 1);
2440
+ var fs18 = __toESM(require("fs"), 1);
2334
2441
  var os8 = __toESM(require("os"), 1);
2335
- var path17 = __toESM(require("path"), 1);
2442
+ var path18 = __toESM(require("path"), 1);
2336
2443
  var import_node_crypto = require("crypto");
2337
2444
  function tmpName(ext) {
2338
- return path17.join(os8.tmpdir(), `oniro_screen_${(0, import_node_crypto.randomBytes)(6).toString("hex")}.${ext}`);
2445
+ return path18.join(os8.tmpdir(), `oniro_screen_${(0, import_node_crypto.randomBytes)(6).toString("hex")}.${ext}`);
2339
2446
  }
2340
2447
  async function takeScreenshot(opts) {
2341
2448
  const remote = "/data/local/tmp/oniro_screenshot.jpeg";
@@ -2345,11 +2452,11 @@ async function takeScreenshot(opts) {
2345
2452
  const local = tmpName("jpeg");
2346
2453
  try {
2347
2454
  await recvFile({ config: opts.config, remote, local, deviceSerial: opts.deviceSerial, timeoutMs });
2348
- const pixels = await fs17.promises.readFile(local);
2455
+ const pixels = await fs18.promises.readFile(local);
2349
2456
  const size = await getDisplaySize(opts.config, { deviceSerial: opts.deviceSerial }) ?? { width: 0, height: 0 };
2350
2457
  return { pixels, width: size.width, height: size.height };
2351
2458
  } finally {
2352
- fs17.promises.unlink(local).catch(() => {
2459
+ fs18.promises.unlink(local).catch(() => {
2353
2460
  });
2354
2461
  void shell({ config: opts.config, command: `rm -f ${remote}`, deviceSerial: opts.deviceSerial }).catch(() => {
2355
2462
  });
@@ -2368,9 +2475,9 @@ async function captureBurst(opts) {
2368
2475
  const local = tmpName(`burst_${i}.jpeg`);
2369
2476
  try {
2370
2477
  await recvFile({ config: opts.config, remote: `${remoteDir}/f${i}.jpeg`, local, deviceSerial: opts.deviceSerial });
2371
- frames.push(await fs17.promises.readFile(local));
2478
+ frames.push(await fs18.promises.readFile(local));
2372
2479
  } finally {
2373
- fs17.promises.unlink(local).catch(() => {
2480
+ fs18.promises.unlink(local).catch(() => {
2374
2481
  });
2375
2482
  }
2376
2483
  }
@@ -2496,9 +2603,9 @@ async function sendRawTouch(opts) {
2496
2603
  }
2497
2604
 
2498
2605
  // src/hdc/dump.ts
2499
- var fs18 = __toESM(require("fs"), 1);
2606
+ var fs19 = __toESM(require("fs"), 1);
2500
2607
  var os9 = __toESM(require("os"), 1);
2501
- var path18 = __toESM(require("path"), 1);
2608
+ var path19 = __toESM(require("path"), 1);
2502
2609
  var import_node_crypto2 = require("crypto");
2503
2610
  function parseBounds(s) {
2504
2611
  const m = /^\[(-?\d+),(-?\d+)\]\[(-?\d+),(-?\d+)\]$/.exec(s ?? "");
@@ -2583,10 +2690,10 @@ async function dumpLayout(opts) {
2583
2690
  if (!m) throw new OniroError(`Could not parse dumpLayout output:
2584
2691
  ${dump.stdout}`);
2585
2692
  const remote = m[1];
2586
- const local = path18.join(os9.tmpdir(), `oniro_layout_${(0, import_node_crypto2.randomBytes)(6).toString("hex")}.json`);
2693
+ const local = path19.join(os9.tmpdir(), `oniro_layout_${(0, import_node_crypto2.randomBytes)(6).toString("hex")}.json`);
2587
2694
  try {
2588
2695
  await recvFile({ config: opts.config, remote, local, deviceSerial: opts.deviceSerial, timeoutMs });
2589
- const parsed = JSON.parse(await fs18.promises.readFile(local, "utf8"));
2696
+ const parsed = JSON.parse(await fs19.promises.readFile(local, "utf8"));
2590
2697
  const display = await getDisplaySize(opts.config, { deviceSerial: opts.deviceSerial });
2591
2698
  if (opts.prune === false) {
2592
2699
  return { display, tree: parsed };
@@ -2595,7 +2702,7 @@ ${dump.stdout}`);
2595
2702
  const tree = Array.isArray(prunedRaw) ? { children: prunedRaw } : prunedRaw ?? { children: [] };
2596
2703
  return { display, tree };
2597
2704
  } finally {
2598
- fs18.promises.unlink(local).catch(() => {
2705
+ fs19.promises.unlink(local).catch(() => {
2599
2706
  });
2600
2707
  void shell({ config: opts.config, command: `rm -f ${remote}`, deviceSerial: opts.deviceSerial }).catch(() => {
2601
2708
  });
@@ -2603,14 +2710,21 @@ ${dump.stdout}`);
2603
2710
  }
2604
2711
 
2605
2712
  // src/hdc/hilog.ts
2606
- var import_node_child_process6 = require("child_process");
2607
2713
  function setHilogLevel(opts) {
2608
2714
  const logger = opts.logger ?? noopLogger;
2609
2715
  const hdc = getHdcPath(opts.config);
2610
2716
  return new Promise((resolve4, reject) => {
2611
- const child = (0, import_node_child_process6.spawn)(hdc, ["shell", "hilog", "-b", opts.level]);
2717
+ let child;
2718
+ try {
2719
+ child = spawnCompat(hdc, ["shell", "hilog", "-b", opts.level]);
2720
+ } catch (err) {
2721
+ const msg = err instanceof Error ? err.message : String(err);
2722
+ logger.error(`[hilog] failed to spawn hdc: ${msg}`);
2723
+ reject(err instanceof Error ? err : new Error(msg));
2724
+ return;
2725
+ }
2612
2726
  let stderr = "";
2613
- child.stderr.on("data", (chunk) => {
2727
+ child.stderr?.on("data", (chunk) => {
2614
2728
  stderr += chunk.toString();
2615
2729
  });
2616
2730
  child.once("error", (err) => {
@@ -2639,7 +2753,7 @@ function streamHilog(opts) {
2639
2753
  if (opts.domain !== void 0 && `${opts.domain}` !== "") {
2640
2754
  args.push("-D", `${opts.domain}`);
2641
2755
  }
2642
- return (0, import_node_child_process6.spawn)(hdc, args);
2756
+ return spawnCompat(hdc, args);
2643
2757
  }
2644
2758
  var HILOG_LINE_RE = /^(\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d{3})\s+(\d+)\s+(\d+)\s+([DIWEF])\s+(\S+):\s+(.*)$/;
2645
2759
  function parseHilogLine(line) {
@@ -2685,10 +2799,7 @@ function waitForLog(opts) {
2685
2799
  if (reconnectTimer) clearTimeout(reconnectTimer);
2686
2800
  opts.abortSignal?.removeEventListener("abort", onAbort);
2687
2801
  if (child) {
2688
- try {
2689
- child.kill("SIGKILL");
2690
- } catch {
2691
- }
2802
+ killProcessTree(child);
2692
2803
  child = null;
2693
2804
  }
2694
2805
  };
@@ -2704,6 +2815,7 @@ function waitForLog(opts) {
2704
2815
  cleanup();
2705
2816
  reject(err);
2706
2817
  };
2818
+ const failWith = (err) => fail(err instanceof Error ? err : new Error(String(err)));
2707
2819
  const onAbort = () => fail(new CancelledError("waitForLog cancelled."));
2708
2820
  const timer = setTimeout(
2709
2821
  () => fail(new OniroError(`No log line matched ${String(opts.pattern)} within ${opts.timeoutMs}ms.`)),
@@ -2733,13 +2845,13 @@ function waitForLog(opts) {
2733
2845
  if (settled || child !== stream) return;
2734
2846
  child = null;
2735
2847
  if (Date.now() < deadline) {
2736
- reconnectTimer = setTimeout(() => void spawnStream(), 1e3);
2848
+ reconnectTimer = setTimeout(() => void spawnStream().catch(failWith), 1e3);
2737
2849
  }
2738
2850
  };
2739
2851
  stream.once("close", onEnd);
2740
2852
  stream.once("error", onEnd);
2741
2853
  };
2742
- void spawnStream();
2854
+ void spawnStream().catch(failWith);
2743
2855
  });
2744
2856
  }
2745
2857
  function watchLog(opts) {
@@ -2752,10 +2864,7 @@ function watchLog(opts) {
2752
2864
  clearTimeout(timer);
2753
2865
  opts.abortSignal?.removeEventListener("abort", onAbort);
2754
2866
  if (child) {
2755
- try {
2756
- child.kill("SIGKILL");
2757
- } catch {
2758
- }
2867
+ killProcessTree(child);
2759
2868
  child = null;
2760
2869
  }
2761
2870
  };
@@ -2788,7 +2897,12 @@ function watchLog(opts) {
2788
2897
  if (entry && opts.pattern.test(entryText(entry))) matches.push(entry);
2789
2898
  }
2790
2899
  });
2791
- })();
2900
+ })().catch((err) => {
2901
+ if (settled) return;
2902
+ settled = true;
2903
+ cleanup();
2904
+ reject(err instanceof Error ? err : new Error(String(err)));
2905
+ });
2792
2906
  });
2793
2907
  }
2794
2908
  async function dumpLog(opts) {
@@ -2815,9 +2929,8 @@ async function dumpLog(opts) {
2815
2929
  }
2816
2930
 
2817
2931
  // src/build/runHvigorw.ts
2818
- var fs19 = __toESM(require("fs"), 1);
2819
- var path19 = __toESM(require("path"), 1);
2820
- var import_node_child_process7 = require("child_process");
2932
+ var fs20 = __toESM(require("fs"), 1);
2933
+ var path20 = __toESM(require("path"), 1);
2821
2934
  function buildHvigorwArgs(opts) {
2822
2935
  const args = [opts.task ?? "assembleHap", "--mode", "module"];
2823
2936
  args.push("-p", `product=${opts.product ?? "default"}`);
@@ -2829,19 +2942,23 @@ function buildHvigorwArgs(opts) {
2829
2942
  return args;
2830
2943
  }
2831
2944
  function runHvigorw(opts) {
2832
- const { config, projectDir } = opts;
2945
+ const { config } = opts;
2946
+ const projectDir = toLongPath(opts.projectDir);
2833
2947
  const logger = opts.logger ?? noopLogger;
2834
- if (!fs19.existsSync(path19.join(projectDir, "build-profile.json5"))) {
2948
+ if (!fs20.existsSync(path20.join(projectDir, "build-profile.json5"))) {
2835
2949
  throw new OniroError(`Not an OpenHarmony project: ${projectDir} (build-profile.json5 not found).`);
2836
2950
  }
2837
- if (!fs19.existsSync(getCmdToolsPath(config))) {
2951
+ if (!fs20.existsSync(getCmdToolsPath(config))) {
2838
2952
  throw new CmdToolsNotInstalledError(getCmdToolsPath(config));
2839
2953
  }
2840
2954
  const hvigorw = getHvigorwPath(config, projectDir);
2955
+ if (!fs20.existsSync(hvigorw)) {
2956
+ throw new OniroError(`hvigorw wrapper not found at ${hvigorw}.`);
2957
+ }
2841
2958
  const args = buildHvigorwArgs(opts);
2842
2959
  if (process.platform !== "win32") {
2843
2960
  try {
2844
- fs19.chmodSync(hvigorw, 493);
2961
+ fs20.chmodSync(hvigorw, 493);
2845
2962
  } catch {
2846
2963
  }
2847
2964
  }
@@ -2855,26 +2972,29 @@ function runHvigorw(opts) {
2855
2972
  return;
2856
2973
  }
2857
2974
  logger.info(`[build] ${hvigorw} ${args.join(" ")}`);
2858
- const child = (0, import_node_child_process7.spawn)(hvigorw, args, { cwd: projectDir, env, shell: false });
2975
+ let child;
2976
+ try {
2977
+ child = spawnCompat(hvigorw, args, { cwd: projectDir, env });
2978
+ } catch (err) {
2979
+ reject(new OniroError(`Failed to start hvigorw: ${err instanceof Error ? err.message : String(err)}`, err));
2980
+ return;
2981
+ }
2859
2982
  let settled = false;
2860
2983
  const cleanup = () => opts.abortSignal?.removeEventListener("abort", onAbort);
2861
2984
  const onAbort = () => {
2862
2985
  if (settled) return;
2863
2986
  settled = true;
2864
2987
  cleanup();
2865
- try {
2866
- child.kill("SIGKILL");
2867
- } catch {
2868
- }
2988
+ killProcessTree(child);
2869
2989
  reject(new CancelledError("hvigorw build cancelled."));
2870
2990
  };
2871
2991
  opts.abortSignal?.addEventListener("abort", onAbort, { once: true });
2872
- child.stdout.on("data", (chunk) => {
2992
+ child.stdout?.on("data", (chunk) => {
2873
2993
  const text = chunk.toString();
2874
2994
  opts.onOutput?.(text, "stdout");
2875
2995
  logger.info(text.trimEnd());
2876
2996
  });
2877
- child.stderr.on("data", (chunk) => {
2997
+ child.stderr?.on("data", (chunk) => {
2878
2998
  const text = chunk.toString();
2879
2999
  opts.onOutput?.(text, "stderr");
2880
3000
  logger.warn(text.trimEnd());
@@ -2897,8 +3017,8 @@ function runHvigorw(opts) {
2897
3017
  }
2898
3018
 
2899
3019
  // src/build/discoverHaps.ts
2900
- var fs20 = __toESM(require("fs"), 1);
2901
- var path20 = __toESM(require("path"), 1);
3020
+ var fs21 = __toESM(require("fs"), 1);
3021
+ var path21 = __toESM(require("path"), 1);
2902
3022
  function moduleOfHap(hapFileName) {
2903
3023
  const dash = hapFileName.indexOf("-");
2904
3024
  return dash > 0 ? hapFileName.slice(0, dash) : hapFileName.replace(/\.hap$/i, "");
@@ -2909,9 +3029,9 @@ async function discoverHaps(opts) {
2909
3029
  const result = {};
2910
3030
  const walk = async (dir, depth) => {
2911
3031
  if (depth > maxDepth) return;
2912
- const entries = await fs20.promises.readdir(dir, { withFileTypes: true }).catch(() => []);
3032
+ const entries = await fs21.promises.readdir(dir, { withFileTypes: true }).catch(() => []);
2913
3033
  for (const e of entries) {
2914
- const full = path20.join(dir, e.name);
3034
+ const full = path21.join(dir, e.name);
2915
3035
  if (e.isDirectory()) {
2916
3036
  if (e.name === "node_modules" || e.name === "oh_modules" || e.name === ".git") continue;
2917
3037
  await walk(full, depth + 1);
@@ -2928,8 +3048,8 @@ async function discoverHaps(opts) {
2928
3048
  }
2929
3049
 
2930
3050
  // src/build/ohpm.ts
2931
- var fs21 = __toESM(require("fs"), 1);
2932
- var path21 = __toESM(require("path"), 1);
3051
+ var fs22 = __toESM(require("fs"), 1);
3052
+ var path22 = __toESM(require("path"), 1);
2933
3053
  function runOhpm(opts) {
2934
3054
  return runProcess({
2935
3055
  command: getOhpmPath(opts.config),
@@ -2942,7 +3062,7 @@ function runOhpm(opts) {
2942
3062
  });
2943
3063
  }
2944
3064
  async function ensureOhModules(opts) {
2945
- if (fs21.existsSync(path21.join(opts.projectDir, "oh_modules"))) {
3065
+ if (fs22.existsSync(path22.join(opts.projectDir, "oh_modules"))) {
2946
3066
  return { installed: false };
2947
3067
  }
2948
3068
  const res = await runOhpm({
@@ -2994,10 +3114,11 @@ async function buildHap(opts) {
2994
3114
  const logger = scopedLogger(opts.logger ?? noopLogger, "build");
2995
3115
  const warnings = [];
2996
3116
  const start = Date.now();
3117
+ const projectDir = toLongPath(opts.projectDir);
2997
3118
  if (opts.autoInstallDeps !== false) {
2998
3119
  const r = await ensureOhModules({
2999
3120
  config: opts.config,
3000
- projectDir: opts.projectDir,
3121
+ projectDir,
3001
3122
  abortSignal: opts.abortSignal,
3002
3123
  onOutput: opts.onOutput,
3003
3124
  logger: opts.logger
@@ -3006,7 +3127,7 @@ async function buildHap(opts) {
3006
3127
  }
3007
3128
  const { exitCode } = await runHvigorw({
3008
3129
  config: opts.config,
3009
- projectDir: opts.projectDir,
3130
+ projectDir,
3010
3131
  product: opts.product,
3011
3132
  module: opts.module,
3012
3133
  buildMode: opts.mode,
@@ -3016,7 +3137,7 @@ async function buildHap(opts) {
3016
3137
  onOutput: opts.onOutput,
3017
3138
  logger: opts.logger
3018
3139
  });
3019
- const discoveredHaps = await discoverHaps({ projectDir: opts.projectDir });
3140
+ const discoveredHaps = await discoverHaps({ projectDir });
3020
3141
  const anySigned = Object.values(discoveredHaps).some((m) => m.signed.length > 0);
3021
3142
  if (!anySigned) {
3022
3143
  warnings.push(
@@ -3037,21 +3158,21 @@ function isValidBundleName(bundleName) {
3037
3158
  }
3038
3159
 
3039
3160
  // src/project/templates.ts
3040
- var fs22 = __toESM(require("fs"), 1);
3041
- var path22 = __toESM(require("path"), 1);
3161
+ var fs23 = __toESM(require("fs"), 1);
3162
+ var path23 = __toESM(require("path"), 1);
3042
3163
  function toHumanTemplateName(folderName) {
3043
3164
  return folderName.replace(/([a-z])([A-Z])/g, "$1 $2");
3044
3165
  }
3045
3166
  function listTemplates(templateRoot) {
3046
- if (!fs22.existsSync(templateRoot)) return [];
3047
- const entries = fs22.readdirSync(templateRoot, { withFileTypes: true });
3167
+ if (!fs23.existsSync(templateRoot)) return [];
3168
+ const entries = fs23.readdirSync(templateRoot, { withFileTypes: true });
3048
3169
  return entries.filter((e) => e.isDirectory()).map((e) => {
3049
- const dir = path22.join(templateRoot, e.name);
3050
- const metaPath = path22.join(dir, "template.json");
3170
+ const dir = path23.join(templateRoot, e.name);
3171
+ const metaPath = path23.join(dir, "template.json");
3051
3172
  let meta = {};
3052
- if (fs22.existsSync(metaPath)) {
3173
+ if (fs23.existsSync(metaPath)) {
3053
3174
  try {
3054
- meta = JSON.parse(fs22.readFileSync(metaPath, "utf8"));
3175
+ meta = JSON.parse(fs23.readFileSync(metaPath, "utf8"));
3055
3176
  } catch {
3056
3177
  }
3057
3178
  }
@@ -3066,57 +3187,57 @@ function listTemplates(templateRoot) {
3066
3187
  function validateTemplateLayout(templateDir, defaultModuleName) {
3067
3188
  const required = [
3068
3189
  "build-profile.json5",
3069
- path22.join("AppScope", "app.json5"),
3070
- path22.join(defaultModuleName, "src", "main", "module.json5"),
3071
- path22.join(defaultModuleName, "oh-package.json5"),
3190
+ path23.join("AppScope", "app.json5"),
3191
+ path23.join(defaultModuleName, "src", "main", "module.json5"),
3192
+ path23.join(defaultModuleName, "oh-package.json5"),
3072
3193
  "hvigorfile.ts"
3073
3194
  ];
3074
- return required.filter((rel) => !fs22.existsSync(path22.join(templateDir, rel)));
3195
+ return required.filter((rel) => !fs23.existsSync(path23.join(templateDir, rel)));
3075
3196
  }
3076
3197
 
3077
3198
  // src/project/createScaffold.ts
3078
- var fs24 = __toESM(require("fs"), 1);
3079
- var path23 = __toESM(require("path"), 1);
3199
+ var fs25 = __toESM(require("fs"), 1);
3200
+ var path24 = __toESM(require("path"), 1);
3080
3201
 
3081
3202
  // src/project/jsonHelpers.ts
3082
- var fs23 = __toESM(require("fs"), 1);
3203
+ var fs24 = __toESM(require("fs"), 1);
3083
3204
  var import_json54 = __toESM(require("json5"), 1);
3084
3205
  function readJson5File(filePath) {
3085
- return import_json54.default.parse(fs23.readFileSync(filePath, "utf8"));
3206
+ return import_json54.default.parse(fs24.readFileSync(filePath, "utf8"));
3086
3207
  }
3087
3208
  function writeJson5File(filePath, value) {
3088
- fs23.writeFileSync(filePath, JSON.stringify(value, null, 2) + "\n", "utf8");
3209
+ fs24.writeFileSync(filePath, JSON.stringify(value, null, 2) + "\n", "utf8");
3089
3210
  }
3090
3211
  function readJsonFile(filePath) {
3091
- return JSON.parse(fs23.readFileSync(filePath, "utf8"));
3212
+ return JSON.parse(fs24.readFileSync(filePath, "utf8"));
3092
3213
  }
3093
3214
  function writeJsonFile(filePath, value) {
3094
- fs23.writeFileSync(filePath, JSON.stringify(value, null, 2) + "\n", "utf8");
3215
+ fs24.writeFileSync(filePath, JSON.stringify(value, null, 2) + "\n", "utf8");
3095
3216
  }
3096
3217
 
3097
3218
  // src/project/createScaffold.ts
3098
3219
  var IGNORED_DIRS = /* @__PURE__ */ new Set(["oh_modules", "node_modules", "build", ".hvigor"]);
3099
3220
  async function pathExists(p) {
3100
3221
  try {
3101
- await fs24.promises.access(p);
3222
+ await fs25.promises.access(p);
3102
3223
  return true;
3103
3224
  } catch {
3104
3225
  return false;
3105
3226
  }
3106
3227
  }
3107
3228
  async function copyDirRecursive(srcDir, destDir) {
3108
- await fs24.promises.mkdir(destDir, { recursive: true });
3109
- const entries = await fs24.promises.readdir(srcDir, { withFileTypes: true });
3229
+ await fs25.promises.mkdir(destDir, { recursive: true });
3230
+ const entries = await fs25.promises.readdir(srcDir, { withFileTypes: true });
3110
3231
  for (const entry of entries) {
3111
- const src = path23.join(srcDir, entry.name);
3112
- const dest = path23.join(destDir, entry.name);
3232
+ const src = path24.join(srcDir, entry.name);
3233
+ const dest = path24.join(destDir, entry.name);
3113
3234
  if (entry.isDirectory()) {
3114
3235
  await copyDirRecursive(src, dest);
3115
3236
  } else if (entry.isSymbolicLink()) {
3116
3237
  continue;
3117
3238
  } else if (entry.isFile()) {
3118
3239
  if (entry.name === "template.json") continue;
3119
- await fs24.promises.copyFile(src, dest);
3240
+ await fs25.promises.copyFile(src, dest);
3120
3241
  }
3121
3242
  }
3122
3243
  }
@@ -3124,9 +3245,9 @@ async function normalizeJson5ToJson(projectDir, logger) {
3124
3245
  const stack = [projectDir];
3125
3246
  while (stack.length > 0) {
3126
3247
  const currentDir = stack.pop();
3127
- const entries = await fs24.promises.readdir(currentDir, { withFileTypes: true });
3248
+ const entries = await fs25.promises.readdir(currentDir, { withFileTypes: true });
3128
3249
  for (const entry of entries) {
3129
- const fullPath = path23.join(currentDir, entry.name);
3250
+ const fullPath = path24.join(currentDir, entry.name);
3130
3251
  if (entry.isDirectory()) {
3131
3252
  if (IGNORED_DIRS.has(entry.name)) continue;
3132
3253
  stack.push(fullPath);
@@ -3148,23 +3269,23 @@ function toJavaPropertiesPath(p) {
3148
3269
  function createOrUpdateLocalProperties(projectDir, sdkDir) {
3149
3270
  const content = `sdk.dir=${toJavaPropertiesPath(sdkDir)}
3150
3271
  `;
3151
- fs24.writeFileSync(path23.join(projectDir, "local.properties"), content, "utf8");
3272
+ fs25.writeFileSync(path24.join(projectDir, "local.properties"), content, "utf8");
3152
3273
  }
3153
3274
  function renameIfExists(fromPath, toPath) {
3154
- if (fs24.existsSync(fromPath) && fromPath !== toPath) {
3155
- fs24.renameSync(fromPath, toPath);
3275
+ if (fs25.existsSync(fromPath) && fromPath !== toPath) {
3276
+ fs25.renameSync(fromPath, toPath);
3156
3277
  }
3157
3278
  }
3158
3279
  function updateTemplateConfigs(projectDir, args, sdkBaseDir) {
3159
- const appJsonPath = path23.join(projectDir, "AppScope", "app.json5");
3160
- if (fs24.existsSync(appJsonPath)) {
3280
+ const appJsonPath = path24.join(projectDir, "AppScope", "app.json5");
3281
+ if (fs25.existsSync(appJsonPath)) {
3161
3282
  const appJson = readJson5File(appJsonPath);
3162
3283
  appJson.app = appJson.app ?? {};
3163
3284
  appJson.app.bundleName = args.bundleName;
3164
3285
  writeJson5File(appJsonPath, appJson);
3165
3286
  }
3166
- const stringsPath = path23.join(projectDir, "AppScope", "resources", "base", "element", "string.json");
3167
- if (fs24.existsSync(stringsPath)) {
3287
+ const stringsPath = path24.join(projectDir, "AppScope", "resources", "base", "element", "string.json");
3288
+ if (fs25.existsSync(stringsPath)) {
3168
3289
  const strings = readJsonFile(stringsPath);
3169
3290
  if (Array.isArray(strings.string)) {
3170
3291
  const appName = strings.string.find((s) => s.name === "app_name");
@@ -3172,8 +3293,8 @@ function updateTemplateConfigs(projectDir, args, sdkBaseDir) {
3172
3293
  }
3173
3294
  writeJsonFile(stringsPath, strings);
3174
3295
  }
3175
- const buildProfilePath = path23.join(projectDir, "build-profile.json5");
3176
- if (fs24.existsSync(buildProfilePath)) {
3296
+ const buildProfilePath = path24.join(projectDir, "build-profile.json5");
3297
+ if (fs25.existsSync(buildProfilePath)) {
3177
3298
  const buildProfile = readJson5File(buildProfilePath);
3178
3299
  buildProfile.app = buildProfile.app ?? {};
3179
3300
  if (Array.isArray(buildProfile.app.products) && buildProfile.app.products.length > 0) {
@@ -3186,23 +3307,23 @@ function updateTemplateConfigs(projectDir, args, sdkBaseDir) {
3186
3307
  }
3187
3308
  writeJson5File(buildProfilePath, buildProfile);
3188
3309
  }
3189
- const moduleJsonPath = path23.join(projectDir, args.moduleName, "src", "main", "module.json5");
3190
- if (fs24.existsSync(moduleJsonPath)) {
3310
+ const moduleJsonPath = path24.join(projectDir, args.moduleName, "src", "main", "module.json5");
3311
+ if (fs25.existsSync(moduleJsonPath)) {
3191
3312
  const moduleJson = readJson5File(moduleJsonPath);
3192
3313
  moduleJson.module = moduleJson.module ?? {};
3193
3314
  moduleJson.module.name = args.moduleName;
3194
3315
  writeJson5File(moduleJsonPath, moduleJson);
3195
3316
  }
3196
- const moduleOhPackagePath = path23.join(projectDir, args.moduleName, "oh-package.json5");
3197
- if (fs24.existsSync(moduleOhPackagePath)) {
3317
+ const moduleOhPackagePath = path24.join(projectDir, args.moduleName, "oh-package.json5");
3318
+ if (fs25.existsSync(moduleOhPackagePath)) {
3198
3319
  const pkg = readJson5File(moduleOhPackagePath);
3199
3320
  pkg.name = args.moduleName;
3200
3321
  writeJson5File(moduleOhPackagePath, pkg);
3201
3322
  }
3202
- const vscodeDir = path23.join(projectDir, ".vscode");
3203
- fs24.mkdirSync(vscodeDir, { recursive: true });
3323
+ const vscodeDir = path24.join(projectDir, ".vscode");
3324
+ fs25.mkdirSync(vscodeDir, { recursive: true });
3204
3325
  const hapPath = `${args.moduleName}/build/default/outputs/default/${args.moduleName}-default-signed.hap`;
3205
- writeJsonFile(path23.join(vscodeDir, "settings.json"), {
3326
+ writeJsonFile(path24.join(vscodeDir, "settings.json"), {
3206
3327
  "oniro.hapPath": hapPath,
3207
3328
  "files.associations": { "*.json5": "jsonc" }
3208
3329
  });
@@ -3219,7 +3340,7 @@ async function createScaffold(opts) {
3219
3340
  if (!opts.location || !await pathExists(opts.location)) {
3220
3341
  throw new OniroError(`Location does not exist: ${opts.location}`);
3221
3342
  }
3222
- const templateDir = path23.join(opts.templateRoot, opts.templateId);
3343
+ const templateDir = path24.join(opts.templateRoot, opts.templateId);
3223
3344
  if (!await pathExists(templateDir)) {
3224
3345
  throw new OniroError(`Template not found: ${templateDir}`);
3225
3346
  }
@@ -3232,17 +3353,17 @@ async function createScaffold(opts) {
3232
3353
  throw new OniroError(`Template '${opts.templateId}' is missing required files:
3233
3354
  - ${missing.join("\n- ")}`);
3234
3355
  }
3235
- const projectDir = path23.join(opts.location, opts.projectName);
3356
+ const projectDir = path24.join(opts.location, opts.projectName);
3236
3357
  if (await pathExists(projectDir)) {
3237
3358
  if (!opts.overwrite) {
3238
3359
  throw new OniroError(`Destination already exists: ${projectDir}. Pass overwrite: true to replace it.`);
3239
3360
  }
3240
- await fs24.promises.rm(projectDir, { recursive: true, force: true });
3361
+ await fs25.promises.rm(projectDir, { recursive: true, force: true });
3241
3362
  }
3242
3363
  logger.info(`[create] Scaffolding ${opts.templateId} at ${projectDir}`);
3243
3364
  await copyDirRecursive(templateDir, projectDir);
3244
3365
  if (moduleName !== defaultModuleName) {
3245
- renameIfExists(path23.join(projectDir, defaultModuleName), path23.join(projectDir, moduleName));
3366
+ renameIfExists(path24.join(projectDir, defaultModuleName), path24.join(projectDir, moduleName));
3246
3367
  }
3247
3368
  updateTemplateConfigs(
3248
3369
  projectDir,
@@ -3259,11 +3380,11 @@ async function createScaffold(opts) {
3259
3380
  }
3260
3381
 
3261
3382
  // src/project/listModules.ts
3262
- var fs25 = __toESM(require("fs"), 1);
3263
- var path24 = __toESM(require("path"), 1);
3383
+ var fs26 = __toESM(require("fs"), 1);
3384
+ var path25 = __toESM(require("path"), 1);
3264
3385
  function listModules(opts) {
3265
- const buildProfile = path24.join(opts.projectDir, "build-profile.json5");
3266
- if (!fs25.existsSync(buildProfile)) {
3386
+ const buildProfile = path25.join(opts.projectDir, "build-profile.json5");
3387
+ if (!fs26.existsSync(buildProfile)) {
3267
3388
  throw new OniroError(`build-profile.json5 not found at ${buildProfile}`);
3268
3389
  }
3269
3390
  const parsed = readJson5File(buildProfile);
@@ -3275,12 +3396,12 @@ function listModules(opts) {
3275
3396
  }
3276
3397
 
3277
3398
  // src/project/abilities.ts
3278
- var fs26 = __toESM(require("fs"), 1);
3279
- var path25 = __toESM(require("path"), 1);
3399
+ var fs27 = __toESM(require("fs"), 1);
3400
+ var path26 = __toESM(require("path"), 1);
3280
3401
  function listAbilities(opts) {
3281
3402
  const moduleName = opts.moduleName ?? "entry";
3282
- const moduleJsonPath = path25.join(opts.projectDir, moduleName, "src", "main", "module.json5");
3283
- if (!fs26.existsSync(moduleJsonPath)) {
3403
+ const moduleJsonPath = path26.join(opts.projectDir, moduleName, "src", "main", "module.json5");
3404
+ if (!fs27.existsSync(moduleJsonPath)) {
3284
3405
  throw new OniroError(`Could not find module.json5 at ${moduleJsonPath}`);
3285
3406
  }
3286
3407
  const parsed = readJson5File(moduleJsonPath);
@@ -3288,12 +3409,12 @@ function listAbilities(opts) {
3288
3409
  }
3289
3410
 
3290
3411
  // src/project/prepareSigning.ts
3291
- var fs27 = __toESM(require("fs"), 1);
3292
- var path26 = __toESM(require("path"), 1);
3412
+ var fs28 = __toESM(require("fs"), 1);
3413
+ var path27 = __toESM(require("path"), 1);
3293
3414
  function hasSigningArtifacts(dir) {
3294
- if (!fs27.existsSync(path26.join(dir, "signatures"))) return false;
3295
- const buildProfile = path26.join(dir, "build-profile.json5");
3296
- if (!fs27.existsSync(buildProfile)) return false;
3415
+ if (!fs28.existsSync(path27.join(dir, "signatures"))) return false;
3416
+ const buildProfile = path27.join(dir, "build-profile.json5");
3417
+ if (!fs28.existsSync(buildProfile)) return false;
3297
3418
  try {
3298
3419
  const parsed = readJson5File(buildProfile);
3299
3420
  return Array.isArray(parsed.app?.signingConfigs) && parsed.app.signingConfigs.length > 0;
@@ -3383,8 +3504,8 @@ async function diffHapAssets(opts) {
3383
3504
  }
3384
3505
 
3385
3506
  // src/install/apply.ts
3386
- var fs28 = __toESM(require("fs"), 1);
3387
- var path27 = __toESM(require("path"), 1);
3507
+ var fs29 = __toESM(require("fs"), 1);
3508
+ var path28 = __toESM(require("path"), 1);
3388
3509
  function decideInstallMethod(s) {
3389
3510
  if (!s.signInfoInconsistent) return "replace";
3390
3511
  if (s.isSystemBundle && !s.allowUninstall) return "refuse";
@@ -3405,7 +3526,7 @@ async function pidOf(config, bundle, deviceSerial) {
3405
3526
  async function resolveHapPath(opts) {
3406
3527
  let hapPath = opts.hapPath;
3407
3528
  if (hapPath) {
3408
- if (!path27.isAbsolute(hapPath) && opts.projectDir) hapPath = path27.join(opts.projectDir, hapPath);
3529
+ if (!path28.isAbsolute(hapPath) && opts.projectDir) hapPath = path28.join(opts.projectDir, hapPath);
3409
3530
  } else {
3410
3531
  if (!opts.projectDir) throw new OniroError("applyChanges requires hapPath or projectDir.");
3411
3532
  const haps = await discoverHaps({ projectDir: opts.projectDir });
@@ -3424,7 +3545,7 @@ async function resolveHapPath(opts) {
3424
3545
  hapPath = withSigned[0][1].signed[0];
3425
3546
  }
3426
3547
  }
3427
- if (!fs28.existsSync(hapPath)) throw new OniroError(`HAP not found at ${hapPath}.`);
3548
+ if (!fs29.existsSync(hapPath)) throw new OniroError(`HAP not found at ${hapPath}.`);
3428
3549
  return hapPath;
3429
3550
  }
3430
3551
  async function applyChanges(opts) {
@@ -3434,7 +3555,7 @@ async function applyChanges(opts) {
3434
3555
  const allowUninstall = opts.allowUninstall ?? false;
3435
3556
  const hapPath = await resolveHapPath(opts);
3436
3557
  let assetsChanged = false;
3437
- if (opts.installedHapPath && fs28.existsSync(opts.installedHapPath)) {
3558
+ if (opts.installedHapPath && fs29.existsSync(opts.installedHapPath)) {
3438
3559
  try {
3439
3560
  const diff = await diffHapAssets({ installedHap: opts.installedHapPath, newHap: hapPath });
3440
3561
  assetsChanged = diff.addedAssetPaths.length > 0 || diff.removedAssetPaths.length > 0;
@@ -3476,7 +3597,7 @@ async function applyChanges(opts) {
3476
3597
  }
3477
3598
 
3478
3599
  // src/index.ts
3479
- var VERSION = "0.8.0";
3600
+ var VERSION = "0.9.0";
3480
3601
  // Annotate the CommonJS export names for ESM import in node:
3481
3602
  0 && (module.exports = {
3482
3603
  ALL_SDKS,
@@ -3496,6 +3617,7 @@ var VERSION = "0.8.0";
3496
3617
  VERSION,
3497
3618
  applyChanges,
3498
3619
  attemptHdcConnection,
3620
+ buildCmdWrapper,
3499
3621
  buildGestureCommands,
3500
3622
  buildGestureUitestCommand,
3501
3623
  buildHap,
@@ -3532,6 +3654,8 @@ var VERSION = "0.8.0";
3532
3654
  ensureOhModules,
3533
3655
  ensureOk,
3534
3656
  ensureRemoteOk,
3657
+ escapeCmdArgument,
3658
+ escapeCmdCommand,
3535
3659
  extractTarball,
3536
3660
  extractZipWithProgress,
3537
3661
  findAppProcessId,
@@ -3570,12 +3694,14 @@ var VERSION = "0.8.0";
3570
3694
  isRamBackedPath,
3571
3695
  isValidBundleName,
3572
3696
  isValidProjectName,
3697
+ killProcessTree,
3573
3698
  launchApp,
3574
3699
  listAbilities,
3575
3700
  listDevices,
3576
3701
  listModules,
3577
3702
  listRunningProcesses,
3578
3703
  listTemplates,
3704
+ needsCmdWrapper,
3579
3705
  nonInteractivePrompter,
3580
3706
  noopLogger,
3581
3707
  noopProgress,
@@ -3612,11 +3738,13 @@ var VERSION = "0.8.0";
3612
3738
  setHilogLevel,
3613
3739
  shell,
3614
3740
  shellChecked,
3741
+ spawnCompat,
3615
3742
  startEmulator,
3616
3743
  staticConfig,
3617
3744
  stopEmulator,
3618
3745
  streamHilog,
3619
3746
  takeScreenshot,
3747
+ toLongPath,
3620
3748
  toSpaceError,
3621
3749
  uninstallApp,
3622
3750
  validateTemplateLayout,