@oniroproject/core 0.8.0 → 0.10.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
  }
@@ -522,7 +536,7 @@ function createInstallTempDir(opts) {
522
536
  } catch (err) {
523
537
  const fallback = os4.tmpdir();
524
538
  if (requested || isOutOfSpaceError(err) || path4.resolve(fallback) === path4.resolve(root)) throw err;
525
- const reason = err instanceof Error ? err.message.split("\n")[0] : String(err);
539
+ const reason = err instanceof Error ? err.message.split(/\r?\n/)[0] : String(err);
526
540
  (opts.logger ?? noopLogger).warn(`${reason} Falling back to '${fallback}'.`);
527
541
  return { dir: createTempWorkDir(fallback, opts.prefix), root: fallback };
528
542
  }
@@ -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,11 +2157,16 @@ 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
- const result = await hdcExec({ config: opts.config, args: ["install", hapPath], timeoutMs: 3e5 });
2164
+ const result = await hdcExec({
2165
+ config: opts.config,
2166
+ args: ["install", hapPath],
2167
+ deviceSerial: opts.deviceSerial,
2168
+ timeoutMs: 3e5
2169
+ });
2064
2170
  if (result.stdout.trim()) logger.info(result.stdout.trim());
2065
2171
  if (result.stderr.trim()) logger.warn(result.stderr.trim());
2066
2172
  ensureOk(result, `hdc install ${hapPath}`);
@@ -2142,11 +2248,17 @@ function listRunningProcesses(config, options = {}) {
2142
2248
  const timeoutMs = options.timeoutMs ?? 1e3;
2143
2249
  const hdc = getHdcPath(config);
2144
2250
  return new Promise((resolve4, reject) => {
2145
- const proc = (0, import_node_child_process5.spawn)(hdc, ["track-jpid"]);
2251
+ let proc;
2252
+ try {
2253
+ proc = spawnCompat(hdc, ["track-jpid"]);
2254
+ } catch (err) {
2255
+ reject(new OniroError(`Failed to spawn ${hdc}: ${err instanceof Error ? err.message : String(err)}`, err));
2256
+ return;
2257
+ }
2146
2258
  const processes = [];
2147
2259
  let resolved = false;
2148
- proc.stdout.on("data", (data) => {
2149
- for (const line of data.toString().split("\n").filter(Boolean)) {
2260
+ proc.stdout?.on("data", (data) => {
2261
+ for (const line of data.toString().split(/\r?\n/).filter(Boolean)) {
2150
2262
  const match = line.match(/^(\d+)\s+(.+)$/);
2151
2263
  if (!match) continue;
2152
2264
  const pid = match[1];
@@ -2154,13 +2266,13 @@ function listRunningProcesses(config, options = {}) {
2154
2266
  processes.push({ pid, name });
2155
2267
  if (options.targetProcessName && name === options.targetProcessName && !resolved) {
2156
2268
  resolved = true;
2157
- proc.kill();
2269
+ killProcessTree(proc);
2158
2270
  resolve4(pid);
2159
2271
  return;
2160
2272
  }
2161
2273
  }
2162
2274
  });
2163
- proc.stderr.on("data", (data) => {
2275
+ proc.stderr?.on("data", (data) => {
2164
2276
  logger.warn(`[hdc track-jpid] ${data.toString()}`);
2165
2277
  });
2166
2278
  proc.on("error", (err) => {
@@ -2172,7 +2284,7 @@ function listRunningProcesses(config, options = {}) {
2172
2284
  const timeout = setTimeout(() => {
2173
2285
  if (resolved) return;
2174
2286
  resolved = true;
2175
- proc.kill();
2287
+ killProcessTree(proc);
2176
2288
  if (options.targetProcessName) {
2177
2289
  reject(new OniroError(`Could not find process for bundle: ${options.targetProcessName}`));
2178
2290
  } else {
@@ -2330,12 +2442,12 @@ async function reboot(opts) {
2330
2442
  }
2331
2443
 
2332
2444
  // src/hdc/screen.ts
2333
- var fs17 = __toESM(require("fs"), 1);
2445
+ var fs18 = __toESM(require("fs"), 1);
2334
2446
  var os8 = __toESM(require("os"), 1);
2335
- var path17 = __toESM(require("path"), 1);
2447
+ var path18 = __toESM(require("path"), 1);
2336
2448
  var import_node_crypto = require("crypto");
2337
2449
  function tmpName(ext) {
2338
- return path17.join(os8.tmpdir(), `oniro_screen_${(0, import_node_crypto.randomBytes)(6).toString("hex")}.${ext}`);
2450
+ return path18.join(os8.tmpdir(), `oniro_screen_${(0, import_node_crypto.randomBytes)(6).toString("hex")}.${ext}`);
2339
2451
  }
2340
2452
  async function takeScreenshot(opts) {
2341
2453
  const remote = "/data/local/tmp/oniro_screenshot.jpeg";
@@ -2345,11 +2457,11 @@ async function takeScreenshot(opts) {
2345
2457
  const local = tmpName("jpeg");
2346
2458
  try {
2347
2459
  await recvFile({ config: opts.config, remote, local, deviceSerial: opts.deviceSerial, timeoutMs });
2348
- const pixels = await fs17.promises.readFile(local);
2460
+ const pixels = await fs18.promises.readFile(local);
2349
2461
  const size = await getDisplaySize(opts.config, { deviceSerial: opts.deviceSerial }) ?? { width: 0, height: 0 };
2350
2462
  return { pixels, width: size.width, height: size.height };
2351
2463
  } finally {
2352
- fs17.promises.unlink(local).catch(() => {
2464
+ fs18.promises.unlink(local).catch(() => {
2353
2465
  });
2354
2466
  void shell({ config: opts.config, command: `rm -f ${remote}`, deviceSerial: opts.deviceSerial }).catch(() => {
2355
2467
  });
@@ -2368,9 +2480,9 @@ async function captureBurst(opts) {
2368
2480
  const local = tmpName(`burst_${i}.jpeg`);
2369
2481
  try {
2370
2482
  await recvFile({ config: opts.config, remote: `${remoteDir}/f${i}.jpeg`, local, deviceSerial: opts.deviceSerial });
2371
- frames.push(await fs17.promises.readFile(local));
2483
+ frames.push(await fs18.promises.readFile(local));
2372
2484
  } finally {
2373
- fs17.promises.unlink(local).catch(() => {
2485
+ fs18.promises.unlink(local).catch(() => {
2374
2486
  });
2375
2487
  }
2376
2488
  }
@@ -2496,9 +2608,9 @@ async function sendRawTouch(opts) {
2496
2608
  }
2497
2609
 
2498
2610
  // src/hdc/dump.ts
2499
- var fs18 = __toESM(require("fs"), 1);
2611
+ var fs19 = __toESM(require("fs"), 1);
2500
2612
  var os9 = __toESM(require("os"), 1);
2501
- var path18 = __toESM(require("path"), 1);
2613
+ var path19 = __toESM(require("path"), 1);
2502
2614
  var import_node_crypto2 = require("crypto");
2503
2615
  function parseBounds(s) {
2504
2616
  const m = /^\[(-?\d+),(-?\d+)\]\[(-?\d+),(-?\d+)\]$/.exec(s ?? "");
@@ -2583,10 +2695,10 @@ async function dumpLayout(opts) {
2583
2695
  if (!m) throw new OniroError(`Could not parse dumpLayout output:
2584
2696
  ${dump.stdout}`);
2585
2697
  const remote = m[1];
2586
- const local = path18.join(os9.tmpdir(), `oniro_layout_${(0, import_node_crypto2.randomBytes)(6).toString("hex")}.json`);
2698
+ const local = path19.join(os9.tmpdir(), `oniro_layout_${(0, import_node_crypto2.randomBytes)(6).toString("hex")}.json`);
2587
2699
  try {
2588
2700
  await recvFile({ config: opts.config, remote, local, deviceSerial: opts.deviceSerial, timeoutMs });
2589
- const parsed = JSON.parse(await fs18.promises.readFile(local, "utf8"));
2701
+ const parsed = JSON.parse(await fs19.promises.readFile(local, "utf8"));
2590
2702
  const display = await getDisplaySize(opts.config, { deviceSerial: opts.deviceSerial });
2591
2703
  if (opts.prune === false) {
2592
2704
  return { display, tree: parsed };
@@ -2595,7 +2707,7 @@ ${dump.stdout}`);
2595
2707
  const tree = Array.isArray(prunedRaw) ? { children: prunedRaw } : prunedRaw ?? { children: [] };
2596
2708
  return { display, tree };
2597
2709
  } finally {
2598
- fs18.promises.unlink(local).catch(() => {
2710
+ fs19.promises.unlink(local).catch(() => {
2599
2711
  });
2600
2712
  void shell({ config: opts.config, command: `rm -f ${remote}`, deviceSerial: opts.deviceSerial }).catch(() => {
2601
2713
  });
@@ -2603,14 +2715,21 @@ ${dump.stdout}`);
2603
2715
  }
2604
2716
 
2605
2717
  // src/hdc/hilog.ts
2606
- var import_node_child_process6 = require("child_process");
2607
2718
  function setHilogLevel(opts) {
2608
2719
  const logger = opts.logger ?? noopLogger;
2609
2720
  const hdc = getHdcPath(opts.config);
2610
2721
  return new Promise((resolve4, reject) => {
2611
- const child = (0, import_node_child_process6.spawn)(hdc, ["shell", "hilog", "-b", opts.level]);
2722
+ let child;
2723
+ try {
2724
+ child = spawnCompat(hdc, ["shell", "hilog", "-b", opts.level]);
2725
+ } catch (err) {
2726
+ const msg = err instanceof Error ? err.message : String(err);
2727
+ logger.error(`[hilog] failed to spawn hdc: ${msg}`);
2728
+ reject(err instanceof Error ? err : new Error(msg));
2729
+ return;
2730
+ }
2612
2731
  let stderr = "";
2613
- child.stderr.on("data", (chunk) => {
2732
+ child.stderr?.on("data", (chunk) => {
2614
2733
  stderr += chunk.toString();
2615
2734
  });
2616
2735
  child.once("error", (err) => {
@@ -2639,7 +2758,7 @@ function streamHilog(opts) {
2639
2758
  if (opts.domain !== void 0 && `${opts.domain}` !== "") {
2640
2759
  args.push("-D", `${opts.domain}`);
2641
2760
  }
2642
- return (0, import_node_child_process6.spawn)(hdc, args);
2761
+ return spawnCompat(hdc, args);
2643
2762
  }
2644
2763
  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
2764
  function parseHilogLine(line) {
@@ -2685,10 +2804,7 @@ function waitForLog(opts) {
2685
2804
  if (reconnectTimer) clearTimeout(reconnectTimer);
2686
2805
  opts.abortSignal?.removeEventListener("abort", onAbort);
2687
2806
  if (child) {
2688
- try {
2689
- child.kill("SIGKILL");
2690
- } catch {
2691
- }
2807
+ killProcessTree(child);
2692
2808
  child = null;
2693
2809
  }
2694
2810
  };
@@ -2704,6 +2820,7 @@ function waitForLog(opts) {
2704
2820
  cleanup();
2705
2821
  reject(err);
2706
2822
  };
2823
+ const failWith = (err) => fail(err instanceof Error ? err : new Error(String(err)));
2707
2824
  const onAbort = () => fail(new CancelledError("waitForLog cancelled."));
2708
2825
  const timer = setTimeout(
2709
2826
  () => fail(new OniroError(`No log line matched ${String(opts.pattern)} within ${opts.timeoutMs}ms.`)),
@@ -2718,10 +2835,9 @@ function waitForLog(opts) {
2718
2835
  child = stream;
2719
2836
  stream.stdout.on("data", (d) => {
2720
2837
  buf += d.toString();
2721
- let idx;
2722
- while ((idx = buf.indexOf("\n")) >= 0) {
2723
- const line = buf.slice(0, idx);
2724
- buf = buf.slice(idx + 1);
2838
+ const lines = buf.split(/\r?\n/);
2839
+ buf = lines.pop() ?? "";
2840
+ for (const line of lines) {
2725
2841
  const entry = parseHilogLine(line);
2726
2842
  if (entry && opts.pattern.test(entryText(entry))) {
2727
2843
  finish(entry);
@@ -2733,13 +2849,13 @@ function waitForLog(opts) {
2733
2849
  if (settled || child !== stream) return;
2734
2850
  child = null;
2735
2851
  if (Date.now() < deadline) {
2736
- reconnectTimer = setTimeout(() => void spawnStream(), 1e3);
2852
+ reconnectTimer = setTimeout(() => void spawnStream().catch(failWith), 1e3);
2737
2853
  }
2738
2854
  };
2739
2855
  stream.once("close", onEnd);
2740
2856
  stream.once("error", onEnd);
2741
2857
  };
2742
- void spawnStream();
2858
+ void spawnStream().catch(failWith);
2743
2859
  });
2744
2860
  }
2745
2861
  function watchLog(opts) {
@@ -2752,10 +2868,7 @@ function watchLog(opts) {
2752
2868
  clearTimeout(timer);
2753
2869
  opts.abortSignal?.removeEventListener("abort", onAbort);
2754
2870
  if (child) {
2755
- try {
2756
- child.kill("SIGKILL");
2757
- } catch {
2758
- }
2871
+ killProcessTree(child);
2759
2872
  child = null;
2760
2873
  }
2761
2874
  };
@@ -2780,15 +2893,19 @@ function watchLog(opts) {
2780
2893
  child = stream;
2781
2894
  stream.stdout.on("data", (d) => {
2782
2895
  buf += d.toString();
2783
- let idx;
2784
- while ((idx = buf.indexOf("\n")) >= 0) {
2785
- const line = buf.slice(0, idx);
2786
- buf = buf.slice(idx + 1);
2896
+ const lines = buf.split(/\r?\n/);
2897
+ buf = lines.pop() ?? "";
2898
+ for (const line of lines) {
2787
2899
  const entry = parseHilogLine(line);
2788
2900
  if (entry && opts.pattern.test(entryText(entry))) matches.push(entry);
2789
2901
  }
2790
2902
  });
2791
- })();
2903
+ })().catch((err) => {
2904
+ if (settled) return;
2905
+ settled = true;
2906
+ cleanup();
2907
+ reject(err instanceof Error ? err : new Error(String(err)));
2908
+ });
2792
2909
  });
2793
2910
  }
2794
2911
  async function dumpLog(opts) {
@@ -2811,13 +2928,12 @@ async function dumpLog(opts) {
2811
2928
  timeoutMs: opts.timeoutMs ?? 3e4,
2812
2929
  logger: opts.logger
2813
2930
  });
2814
- return res.stdout.split("\n").map((line) => parseHilogLine(line)).filter((e) => e !== null);
2931
+ return res.stdout.split(/\r?\n/).map((line) => parseHilogLine(line)).filter((e) => e !== null);
2815
2932
  }
2816
2933
 
2817
2934
  // 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");
2935
+ var fs20 = __toESM(require("fs"), 1);
2936
+ var path20 = __toESM(require("path"), 1);
2821
2937
  function buildHvigorwArgs(opts) {
2822
2938
  const args = [opts.task ?? "assembleHap", "--mode", "module"];
2823
2939
  args.push("-p", `product=${opts.product ?? "default"}`);
@@ -2829,19 +2945,23 @@ function buildHvigorwArgs(opts) {
2829
2945
  return args;
2830
2946
  }
2831
2947
  function runHvigorw(opts) {
2832
- const { config, projectDir } = opts;
2948
+ const { config } = opts;
2949
+ const projectDir = toLongPath(opts.projectDir);
2833
2950
  const logger = opts.logger ?? noopLogger;
2834
- if (!fs19.existsSync(path19.join(projectDir, "build-profile.json5"))) {
2951
+ if (!fs20.existsSync(path20.join(projectDir, "build-profile.json5"))) {
2835
2952
  throw new OniroError(`Not an OpenHarmony project: ${projectDir} (build-profile.json5 not found).`);
2836
2953
  }
2837
- if (!fs19.existsSync(getCmdToolsPath(config))) {
2954
+ if (!fs20.existsSync(getCmdToolsPath(config))) {
2838
2955
  throw new CmdToolsNotInstalledError(getCmdToolsPath(config));
2839
2956
  }
2840
2957
  const hvigorw = getHvigorwPath(config, projectDir);
2958
+ if (!fs20.existsSync(hvigorw)) {
2959
+ throw new OniroError(`hvigorw wrapper not found at ${hvigorw}.`);
2960
+ }
2841
2961
  const args = buildHvigorwArgs(opts);
2842
2962
  if (process.platform !== "win32") {
2843
2963
  try {
2844
- fs19.chmodSync(hvigorw, 493);
2964
+ fs20.chmodSync(hvigorw, 493);
2845
2965
  } catch {
2846
2966
  }
2847
2967
  }
@@ -2855,26 +2975,29 @@ function runHvigorw(opts) {
2855
2975
  return;
2856
2976
  }
2857
2977
  logger.info(`[build] ${hvigorw} ${args.join(" ")}`);
2858
- const child = (0, import_node_child_process7.spawn)(hvigorw, args, { cwd: projectDir, env, shell: false });
2978
+ let child;
2979
+ try {
2980
+ child = spawnCompat(hvigorw, args, { cwd: projectDir, env });
2981
+ } catch (err) {
2982
+ reject(new OniroError(`Failed to start hvigorw: ${err instanceof Error ? err.message : String(err)}`, err));
2983
+ return;
2984
+ }
2859
2985
  let settled = false;
2860
2986
  const cleanup = () => opts.abortSignal?.removeEventListener("abort", onAbort);
2861
2987
  const onAbort = () => {
2862
2988
  if (settled) return;
2863
2989
  settled = true;
2864
2990
  cleanup();
2865
- try {
2866
- child.kill("SIGKILL");
2867
- } catch {
2868
- }
2991
+ killProcessTree(child);
2869
2992
  reject(new CancelledError("hvigorw build cancelled."));
2870
2993
  };
2871
2994
  opts.abortSignal?.addEventListener("abort", onAbort, { once: true });
2872
- child.stdout.on("data", (chunk) => {
2995
+ child.stdout?.on("data", (chunk) => {
2873
2996
  const text = chunk.toString();
2874
2997
  opts.onOutput?.(text, "stdout");
2875
2998
  logger.info(text.trimEnd());
2876
2999
  });
2877
- child.stderr.on("data", (chunk) => {
3000
+ child.stderr?.on("data", (chunk) => {
2878
3001
  const text = chunk.toString();
2879
3002
  opts.onOutput?.(text, "stderr");
2880
3003
  logger.warn(text.trimEnd());
@@ -2897,8 +3020,8 @@ function runHvigorw(opts) {
2897
3020
  }
2898
3021
 
2899
3022
  // src/build/discoverHaps.ts
2900
- var fs20 = __toESM(require("fs"), 1);
2901
- var path20 = __toESM(require("path"), 1);
3023
+ var fs21 = __toESM(require("fs"), 1);
3024
+ var path21 = __toESM(require("path"), 1);
2902
3025
  function moduleOfHap(hapFileName) {
2903
3026
  const dash = hapFileName.indexOf("-");
2904
3027
  return dash > 0 ? hapFileName.slice(0, dash) : hapFileName.replace(/\.hap$/i, "");
@@ -2909,9 +3032,9 @@ async function discoverHaps(opts) {
2909
3032
  const result = {};
2910
3033
  const walk = async (dir, depth) => {
2911
3034
  if (depth > maxDepth) return;
2912
- const entries = await fs20.promises.readdir(dir, { withFileTypes: true }).catch(() => []);
3035
+ const entries = await fs21.promises.readdir(dir, { withFileTypes: true }).catch(() => []);
2913
3036
  for (const e of entries) {
2914
- const full = path20.join(dir, e.name);
3037
+ const full = path21.join(dir, e.name);
2915
3038
  if (e.isDirectory()) {
2916
3039
  if (e.name === "node_modules" || e.name === "oh_modules" || e.name === ".git") continue;
2917
3040
  await walk(full, depth + 1);
@@ -2928,8 +3051,8 @@ async function discoverHaps(opts) {
2928
3051
  }
2929
3052
 
2930
3053
  // src/build/ohpm.ts
2931
- var fs21 = __toESM(require("fs"), 1);
2932
- var path21 = __toESM(require("path"), 1);
3054
+ var fs22 = __toESM(require("fs"), 1);
3055
+ var path22 = __toESM(require("path"), 1);
2933
3056
  function runOhpm(opts) {
2934
3057
  return runProcess({
2935
3058
  command: getOhpmPath(opts.config),
@@ -2942,7 +3065,7 @@ function runOhpm(opts) {
2942
3065
  });
2943
3066
  }
2944
3067
  async function ensureOhModules(opts) {
2945
- if (fs21.existsSync(path21.join(opts.projectDir, "oh_modules"))) {
3068
+ if (fs22.existsSync(path22.join(opts.projectDir, "oh_modules"))) {
2946
3069
  return { installed: false };
2947
3070
  }
2948
3071
  const res = await runOhpm({
@@ -2994,10 +3117,11 @@ async function buildHap(opts) {
2994
3117
  const logger = scopedLogger(opts.logger ?? noopLogger, "build");
2995
3118
  const warnings = [];
2996
3119
  const start = Date.now();
3120
+ const projectDir = toLongPath(opts.projectDir);
2997
3121
  if (opts.autoInstallDeps !== false) {
2998
3122
  const r = await ensureOhModules({
2999
3123
  config: opts.config,
3000
- projectDir: opts.projectDir,
3124
+ projectDir,
3001
3125
  abortSignal: opts.abortSignal,
3002
3126
  onOutput: opts.onOutput,
3003
3127
  logger: opts.logger
@@ -3006,7 +3130,7 @@ async function buildHap(opts) {
3006
3130
  }
3007
3131
  const { exitCode } = await runHvigorw({
3008
3132
  config: opts.config,
3009
- projectDir: opts.projectDir,
3133
+ projectDir,
3010
3134
  product: opts.product,
3011
3135
  module: opts.module,
3012
3136
  buildMode: opts.mode,
@@ -3016,7 +3140,7 @@ async function buildHap(opts) {
3016
3140
  onOutput: opts.onOutput,
3017
3141
  logger: opts.logger
3018
3142
  });
3019
- const discoveredHaps = await discoverHaps({ projectDir: opts.projectDir });
3143
+ const discoveredHaps = await discoverHaps({ projectDir });
3020
3144
  const anySigned = Object.values(discoveredHaps).some((m) => m.signed.length > 0);
3021
3145
  if (!anySigned) {
3022
3146
  warnings.push(
@@ -3037,21 +3161,21 @@ function isValidBundleName(bundleName) {
3037
3161
  }
3038
3162
 
3039
3163
  // src/project/templates.ts
3040
- var fs22 = __toESM(require("fs"), 1);
3041
- var path22 = __toESM(require("path"), 1);
3164
+ var fs23 = __toESM(require("fs"), 1);
3165
+ var path23 = __toESM(require("path"), 1);
3042
3166
  function toHumanTemplateName(folderName) {
3043
3167
  return folderName.replace(/([a-z])([A-Z])/g, "$1 $2");
3044
3168
  }
3045
3169
  function listTemplates(templateRoot) {
3046
- if (!fs22.existsSync(templateRoot)) return [];
3047
- const entries = fs22.readdirSync(templateRoot, { withFileTypes: true });
3170
+ if (!fs23.existsSync(templateRoot)) return [];
3171
+ const entries = fs23.readdirSync(templateRoot, { withFileTypes: true });
3048
3172
  return entries.filter((e) => e.isDirectory()).map((e) => {
3049
- const dir = path22.join(templateRoot, e.name);
3050
- const metaPath = path22.join(dir, "template.json");
3173
+ const dir = path23.join(templateRoot, e.name);
3174
+ const metaPath = path23.join(dir, "template.json");
3051
3175
  let meta = {};
3052
- if (fs22.existsSync(metaPath)) {
3176
+ if (fs23.existsSync(metaPath)) {
3053
3177
  try {
3054
- meta = JSON.parse(fs22.readFileSync(metaPath, "utf8"));
3178
+ meta = JSON.parse(fs23.readFileSync(metaPath, "utf8"));
3055
3179
  } catch {
3056
3180
  }
3057
3181
  }
@@ -3066,57 +3190,57 @@ function listTemplates(templateRoot) {
3066
3190
  function validateTemplateLayout(templateDir, defaultModuleName) {
3067
3191
  const required = [
3068
3192
  "build-profile.json5",
3069
- path22.join("AppScope", "app.json5"),
3070
- path22.join(defaultModuleName, "src", "main", "module.json5"),
3071
- path22.join(defaultModuleName, "oh-package.json5"),
3193
+ path23.join("AppScope", "app.json5"),
3194
+ path23.join(defaultModuleName, "src", "main", "module.json5"),
3195
+ path23.join(defaultModuleName, "oh-package.json5"),
3072
3196
  "hvigorfile.ts"
3073
3197
  ];
3074
- return required.filter((rel) => !fs22.existsSync(path22.join(templateDir, rel)));
3198
+ return required.filter((rel) => !fs23.existsSync(path23.join(templateDir, rel)));
3075
3199
  }
3076
3200
 
3077
3201
  // src/project/createScaffold.ts
3078
- var fs24 = __toESM(require("fs"), 1);
3079
- var path23 = __toESM(require("path"), 1);
3202
+ var fs25 = __toESM(require("fs"), 1);
3203
+ var path24 = __toESM(require("path"), 1);
3080
3204
 
3081
3205
  // src/project/jsonHelpers.ts
3082
- var fs23 = __toESM(require("fs"), 1);
3206
+ var fs24 = __toESM(require("fs"), 1);
3083
3207
  var import_json54 = __toESM(require("json5"), 1);
3084
3208
  function readJson5File(filePath) {
3085
- return import_json54.default.parse(fs23.readFileSync(filePath, "utf8"));
3209
+ return import_json54.default.parse(fs24.readFileSync(filePath, "utf8"));
3086
3210
  }
3087
3211
  function writeJson5File(filePath, value) {
3088
- fs23.writeFileSync(filePath, JSON.stringify(value, null, 2) + "\n", "utf8");
3212
+ fs24.writeFileSync(filePath, JSON.stringify(value, null, 2) + "\n", "utf8");
3089
3213
  }
3090
3214
  function readJsonFile(filePath) {
3091
- return JSON.parse(fs23.readFileSync(filePath, "utf8"));
3215
+ return JSON.parse(fs24.readFileSync(filePath, "utf8"));
3092
3216
  }
3093
3217
  function writeJsonFile(filePath, value) {
3094
- fs23.writeFileSync(filePath, JSON.stringify(value, null, 2) + "\n", "utf8");
3218
+ fs24.writeFileSync(filePath, JSON.stringify(value, null, 2) + "\n", "utf8");
3095
3219
  }
3096
3220
 
3097
3221
  // src/project/createScaffold.ts
3098
3222
  var IGNORED_DIRS = /* @__PURE__ */ new Set(["oh_modules", "node_modules", "build", ".hvigor"]);
3099
3223
  async function pathExists(p) {
3100
3224
  try {
3101
- await fs24.promises.access(p);
3225
+ await fs25.promises.access(p);
3102
3226
  return true;
3103
3227
  } catch {
3104
3228
  return false;
3105
3229
  }
3106
3230
  }
3107
3231
  async function copyDirRecursive(srcDir, destDir) {
3108
- await fs24.promises.mkdir(destDir, { recursive: true });
3109
- const entries = await fs24.promises.readdir(srcDir, { withFileTypes: true });
3232
+ await fs25.promises.mkdir(destDir, { recursive: true });
3233
+ const entries = await fs25.promises.readdir(srcDir, { withFileTypes: true });
3110
3234
  for (const entry of entries) {
3111
- const src = path23.join(srcDir, entry.name);
3112
- const dest = path23.join(destDir, entry.name);
3235
+ const src = path24.join(srcDir, entry.name);
3236
+ const dest = path24.join(destDir, entry.name);
3113
3237
  if (entry.isDirectory()) {
3114
3238
  await copyDirRecursive(src, dest);
3115
3239
  } else if (entry.isSymbolicLink()) {
3116
3240
  continue;
3117
3241
  } else if (entry.isFile()) {
3118
3242
  if (entry.name === "template.json") continue;
3119
- await fs24.promises.copyFile(src, dest);
3243
+ await fs25.promises.copyFile(src, dest);
3120
3244
  }
3121
3245
  }
3122
3246
  }
@@ -3124,9 +3248,9 @@ async function normalizeJson5ToJson(projectDir, logger) {
3124
3248
  const stack = [projectDir];
3125
3249
  while (stack.length > 0) {
3126
3250
  const currentDir = stack.pop();
3127
- const entries = await fs24.promises.readdir(currentDir, { withFileTypes: true });
3251
+ const entries = await fs25.promises.readdir(currentDir, { withFileTypes: true });
3128
3252
  for (const entry of entries) {
3129
- const fullPath = path23.join(currentDir, entry.name);
3253
+ const fullPath = path24.join(currentDir, entry.name);
3130
3254
  if (entry.isDirectory()) {
3131
3255
  if (IGNORED_DIRS.has(entry.name)) continue;
3132
3256
  stack.push(fullPath);
@@ -3148,23 +3272,23 @@ function toJavaPropertiesPath(p) {
3148
3272
  function createOrUpdateLocalProperties(projectDir, sdkDir) {
3149
3273
  const content = `sdk.dir=${toJavaPropertiesPath(sdkDir)}
3150
3274
  `;
3151
- fs24.writeFileSync(path23.join(projectDir, "local.properties"), content, "utf8");
3275
+ fs25.writeFileSync(path24.join(projectDir, "local.properties"), content, "utf8");
3152
3276
  }
3153
3277
  function renameIfExists(fromPath, toPath) {
3154
- if (fs24.existsSync(fromPath) && fromPath !== toPath) {
3155
- fs24.renameSync(fromPath, toPath);
3278
+ if (fs25.existsSync(fromPath) && fromPath !== toPath) {
3279
+ fs25.renameSync(fromPath, toPath);
3156
3280
  }
3157
3281
  }
3158
3282
  function updateTemplateConfigs(projectDir, args, sdkBaseDir) {
3159
- const appJsonPath = path23.join(projectDir, "AppScope", "app.json5");
3160
- if (fs24.existsSync(appJsonPath)) {
3283
+ const appJsonPath = path24.join(projectDir, "AppScope", "app.json5");
3284
+ if (fs25.existsSync(appJsonPath)) {
3161
3285
  const appJson = readJson5File(appJsonPath);
3162
3286
  appJson.app = appJson.app ?? {};
3163
3287
  appJson.app.bundleName = args.bundleName;
3164
3288
  writeJson5File(appJsonPath, appJson);
3165
3289
  }
3166
- const stringsPath = path23.join(projectDir, "AppScope", "resources", "base", "element", "string.json");
3167
- if (fs24.existsSync(stringsPath)) {
3290
+ const stringsPath = path24.join(projectDir, "AppScope", "resources", "base", "element", "string.json");
3291
+ if (fs25.existsSync(stringsPath)) {
3168
3292
  const strings = readJsonFile(stringsPath);
3169
3293
  if (Array.isArray(strings.string)) {
3170
3294
  const appName = strings.string.find((s) => s.name === "app_name");
@@ -3172,8 +3296,8 @@ function updateTemplateConfigs(projectDir, args, sdkBaseDir) {
3172
3296
  }
3173
3297
  writeJsonFile(stringsPath, strings);
3174
3298
  }
3175
- const buildProfilePath = path23.join(projectDir, "build-profile.json5");
3176
- if (fs24.existsSync(buildProfilePath)) {
3299
+ const buildProfilePath = path24.join(projectDir, "build-profile.json5");
3300
+ if (fs25.existsSync(buildProfilePath)) {
3177
3301
  const buildProfile = readJson5File(buildProfilePath);
3178
3302
  buildProfile.app = buildProfile.app ?? {};
3179
3303
  if (Array.isArray(buildProfile.app.products) && buildProfile.app.products.length > 0) {
@@ -3186,23 +3310,23 @@ function updateTemplateConfigs(projectDir, args, sdkBaseDir) {
3186
3310
  }
3187
3311
  writeJson5File(buildProfilePath, buildProfile);
3188
3312
  }
3189
- const moduleJsonPath = path23.join(projectDir, args.moduleName, "src", "main", "module.json5");
3190
- if (fs24.existsSync(moduleJsonPath)) {
3313
+ const moduleJsonPath = path24.join(projectDir, args.moduleName, "src", "main", "module.json5");
3314
+ if (fs25.existsSync(moduleJsonPath)) {
3191
3315
  const moduleJson = readJson5File(moduleJsonPath);
3192
3316
  moduleJson.module = moduleJson.module ?? {};
3193
3317
  moduleJson.module.name = args.moduleName;
3194
3318
  writeJson5File(moduleJsonPath, moduleJson);
3195
3319
  }
3196
- const moduleOhPackagePath = path23.join(projectDir, args.moduleName, "oh-package.json5");
3197
- if (fs24.existsSync(moduleOhPackagePath)) {
3320
+ const moduleOhPackagePath = path24.join(projectDir, args.moduleName, "oh-package.json5");
3321
+ if (fs25.existsSync(moduleOhPackagePath)) {
3198
3322
  const pkg = readJson5File(moduleOhPackagePath);
3199
3323
  pkg.name = args.moduleName;
3200
3324
  writeJson5File(moduleOhPackagePath, pkg);
3201
3325
  }
3202
- const vscodeDir = path23.join(projectDir, ".vscode");
3203
- fs24.mkdirSync(vscodeDir, { recursive: true });
3326
+ const vscodeDir = path24.join(projectDir, ".vscode");
3327
+ fs25.mkdirSync(vscodeDir, { recursive: true });
3204
3328
  const hapPath = `${args.moduleName}/build/default/outputs/default/${args.moduleName}-default-signed.hap`;
3205
- writeJsonFile(path23.join(vscodeDir, "settings.json"), {
3329
+ writeJsonFile(path24.join(vscodeDir, "settings.json"), {
3206
3330
  "oniro.hapPath": hapPath,
3207
3331
  "files.associations": { "*.json5": "jsonc" }
3208
3332
  });
@@ -3219,7 +3343,7 @@ async function createScaffold(opts) {
3219
3343
  if (!opts.location || !await pathExists(opts.location)) {
3220
3344
  throw new OniroError(`Location does not exist: ${opts.location}`);
3221
3345
  }
3222
- const templateDir = path23.join(opts.templateRoot, opts.templateId);
3346
+ const templateDir = path24.join(opts.templateRoot, opts.templateId);
3223
3347
  if (!await pathExists(templateDir)) {
3224
3348
  throw new OniroError(`Template not found: ${templateDir}`);
3225
3349
  }
@@ -3232,17 +3356,17 @@ async function createScaffold(opts) {
3232
3356
  throw new OniroError(`Template '${opts.templateId}' is missing required files:
3233
3357
  - ${missing.join("\n- ")}`);
3234
3358
  }
3235
- const projectDir = path23.join(opts.location, opts.projectName);
3359
+ const projectDir = path24.join(opts.location, opts.projectName);
3236
3360
  if (await pathExists(projectDir)) {
3237
3361
  if (!opts.overwrite) {
3238
3362
  throw new OniroError(`Destination already exists: ${projectDir}. Pass overwrite: true to replace it.`);
3239
3363
  }
3240
- await fs24.promises.rm(projectDir, { recursive: true, force: true });
3364
+ await fs25.promises.rm(projectDir, { recursive: true, force: true });
3241
3365
  }
3242
3366
  logger.info(`[create] Scaffolding ${opts.templateId} at ${projectDir}`);
3243
3367
  await copyDirRecursive(templateDir, projectDir);
3244
3368
  if (moduleName !== defaultModuleName) {
3245
- renameIfExists(path23.join(projectDir, defaultModuleName), path23.join(projectDir, moduleName));
3369
+ renameIfExists(path24.join(projectDir, defaultModuleName), path24.join(projectDir, moduleName));
3246
3370
  }
3247
3371
  updateTemplateConfigs(
3248
3372
  projectDir,
@@ -3259,11 +3383,11 @@ async function createScaffold(opts) {
3259
3383
  }
3260
3384
 
3261
3385
  // src/project/listModules.ts
3262
- var fs25 = __toESM(require("fs"), 1);
3263
- var path24 = __toESM(require("path"), 1);
3386
+ var fs26 = __toESM(require("fs"), 1);
3387
+ var path25 = __toESM(require("path"), 1);
3264
3388
  function listModules(opts) {
3265
- const buildProfile = path24.join(opts.projectDir, "build-profile.json5");
3266
- if (!fs25.existsSync(buildProfile)) {
3389
+ const buildProfile = path25.join(opts.projectDir, "build-profile.json5");
3390
+ if (!fs26.existsSync(buildProfile)) {
3267
3391
  throw new OniroError(`build-profile.json5 not found at ${buildProfile}`);
3268
3392
  }
3269
3393
  const parsed = readJson5File(buildProfile);
@@ -3275,12 +3399,12 @@ function listModules(opts) {
3275
3399
  }
3276
3400
 
3277
3401
  // src/project/abilities.ts
3278
- var fs26 = __toESM(require("fs"), 1);
3279
- var path25 = __toESM(require("path"), 1);
3402
+ var fs27 = __toESM(require("fs"), 1);
3403
+ var path26 = __toESM(require("path"), 1);
3280
3404
  function listAbilities(opts) {
3281
3405
  const moduleName = opts.moduleName ?? "entry";
3282
- const moduleJsonPath = path25.join(opts.projectDir, moduleName, "src", "main", "module.json5");
3283
- if (!fs26.existsSync(moduleJsonPath)) {
3406
+ const moduleJsonPath = path26.join(opts.projectDir, moduleName, "src", "main", "module.json5");
3407
+ if (!fs27.existsSync(moduleJsonPath)) {
3284
3408
  throw new OniroError(`Could not find module.json5 at ${moduleJsonPath}`);
3285
3409
  }
3286
3410
  const parsed = readJson5File(moduleJsonPath);
@@ -3288,12 +3412,12 @@ function listAbilities(opts) {
3288
3412
  }
3289
3413
 
3290
3414
  // src/project/prepareSigning.ts
3291
- var fs27 = __toESM(require("fs"), 1);
3292
- var path26 = __toESM(require("path"), 1);
3415
+ var fs28 = __toESM(require("fs"), 1);
3416
+ var path27 = __toESM(require("path"), 1);
3293
3417
  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;
3418
+ if (!fs28.existsSync(path27.join(dir, "signatures"))) return false;
3419
+ const buildProfile = path27.join(dir, "build-profile.json5");
3420
+ if (!fs28.existsSync(buildProfile)) return false;
3297
3421
  try {
3298
3422
  const parsed = readJson5File(buildProfile);
3299
3423
  return Array.isArray(parsed.app?.signingConfigs) && parsed.app.signingConfigs.length > 0;
@@ -3383,8 +3507,8 @@ async function diffHapAssets(opts) {
3383
3507
  }
3384
3508
 
3385
3509
  // src/install/apply.ts
3386
- var fs28 = __toESM(require("fs"), 1);
3387
- var path27 = __toESM(require("path"), 1);
3510
+ var fs29 = __toESM(require("fs"), 1);
3511
+ var path28 = __toESM(require("path"), 1);
3388
3512
  function decideInstallMethod(s) {
3389
3513
  if (!s.signInfoInconsistent) return "replace";
3390
3514
  if (s.isSystemBundle && !s.allowUninstall) return "refuse";
@@ -3405,7 +3529,7 @@ async function pidOf(config, bundle, deviceSerial) {
3405
3529
  async function resolveHapPath(opts) {
3406
3530
  let hapPath = opts.hapPath;
3407
3531
  if (hapPath) {
3408
- if (!path27.isAbsolute(hapPath) && opts.projectDir) hapPath = path27.join(opts.projectDir, hapPath);
3532
+ if (!path28.isAbsolute(hapPath) && opts.projectDir) hapPath = path28.join(opts.projectDir, hapPath);
3409
3533
  } else {
3410
3534
  if (!opts.projectDir) throw new OniroError("applyChanges requires hapPath or projectDir.");
3411
3535
  const haps = await discoverHaps({ projectDir: opts.projectDir });
@@ -3424,7 +3548,7 @@ async function resolveHapPath(opts) {
3424
3548
  hapPath = withSigned[0][1].signed[0];
3425
3549
  }
3426
3550
  }
3427
- if (!fs28.existsSync(hapPath)) throw new OniroError(`HAP not found at ${hapPath}.`);
3551
+ if (!fs29.existsSync(hapPath)) throw new OniroError(`HAP not found at ${hapPath}.`);
3428
3552
  return hapPath;
3429
3553
  }
3430
3554
  async function applyChanges(opts) {
@@ -3434,7 +3558,7 @@ async function applyChanges(opts) {
3434
3558
  const allowUninstall = opts.allowUninstall ?? false;
3435
3559
  const hapPath = await resolveHapPath(opts);
3436
3560
  let assetsChanged = false;
3437
- if (opts.installedHapPath && fs28.existsSync(opts.installedHapPath)) {
3561
+ if (opts.installedHapPath && fs29.existsSync(opts.installedHapPath)) {
3438
3562
  try {
3439
3563
  const diff = await diffHapAssets({ installedHap: opts.installedHapPath, newHap: hapPath });
3440
3564
  assetsChanged = diff.addedAssetPaths.length > 0 || diff.removedAssetPaths.length > 0;
@@ -3476,7 +3600,7 @@ async function applyChanges(opts) {
3476
3600
  }
3477
3601
 
3478
3602
  // src/index.ts
3479
- var VERSION = "0.8.0";
3603
+ var VERSION = "0.10.0";
3480
3604
  // Annotate the CommonJS export names for ESM import in node:
3481
3605
  0 && (module.exports = {
3482
3606
  ALL_SDKS,
@@ -3496,6 +3620,7 @@ var VERSION = "0.8.0";
3496
3620
  VERSION,
3497
3621
  applyChanges,
3498
3622
  attemptHdcConnection,
3623
+ buildCmdWrapper,
3499
3624
  buildGestureCommands,
3500
3625
  buildGestureUitestCommand,
3501
3626
  buildHap,
@@ -3532,6 +3657,8 @@ var VERSION = "0.8.0";
3532
3657
  ensureOhModules,
3533
3658
  ensureOk,
3534
3659
  ensureRemoteOk,
3660
+ escapeCmdArgument,
3661
+ escapeCmdCommand,
3535
3662
  extractTarball,
3536
3663
  extractZipWithProgress,
3537
3664
  findAppProcessId,
@@ -3570,12 +3697,14 @@ var VERSION = "0.8.0";
3570
3697
  isRamBackedPath,
3571
3698
  isValidBundleName,
3572
3699
  isValidProjectName,
3700
+ killProcessTree,
3573
3701
  launchApp,
3574
3702
  listAbilities,
3575
3703
  listDevices,
3576
3704
  listModules,
3577
3705
  listRunningProcesses,
3578
3706
  listTemplates,
3707
+ needsCmdWrapper,
3579
3708
  nonInteractivePrompter,
3580
3709
  noopLogger,
3581
3710
  noopProgress,
@@ -3612,11 +3741,13 @@ var VERSION = "0.8.0";
3612
3741
  setHilogLevel,
3613
3742
  shell,
3614
3743
  shellChecked,
3744
+ spawnCompat,
3615
3745
  startEmulator,
3616
3746
  staticConfig,
3617
3747
  stopEmulator,
3618
3748
  streamHilog,
3619
3749
  takeScreenshot,
3750
+ toLongPath,
3620
3751
  toSpaceError,
3621
3752
  uninstallApp,
3622
3753
  validateTemplateLayout,