@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.js CHANGED
@@ -217,6 +217,14 @@ function getCmdToolsPath(config) {
217
217
  function getEmulatorDir(config) {
218
218
  return config.get("emulatorDir", defaultPaths.emulatorDir());
219
219
  }
220
+ function toLongPath(p, platform7 = os3.platform()) {
221
+ if (platform7 !== "win32") return p;
222
+ try {
223
+ return fs.realpathSync.native(p);
224
+ } catch {
225
+ return p;
226
+ }
227
+ }
220
228
  function pickExisting(candidates) {
221
229
  for (const c of candidates) {
222
230
  if (fs.existsSync(c)) return c;
@@ -252,12 +260,11 @@ function getHdcPath(config) {
252
260
  }
253
261
  return path2.join(base, "hdc");
254
262
  }
255
- function getHvigorwPath(config, projectDir) {
256
- const local = pickExisting([
257
- path2.join(projectDir, "hvigorw"),
258
- path2.join(projectDir, "hvigorw.bat"),
259
- path2.join(projectDir, "hvigorw.cmd")
260
- ]);
263
+ function getHvigorwPath(config, projectDir, platform7 = os3.platform()) {
264
+ const posixFirst = ["hvigorw", "hvigorw.bat", "hvigorw.cmd"];
265
+ const windowsFirst = ["hvigorw.bat", "hvigorw.cmd", "hvigorw"];
266
+ const names = platform7 === "win32" ? windowsFirst : posixFirst;
267
+ const local = pickExisting(names.map((n) => path2.join(projectDir, n)));
261
268
  if (fs.existsSync(local) && isProjectLocalHvigorwWorking(projectDir)) {
262
269
  return local;
263
270
  }
@@ -789,8 +796,82 @@ async function downloadAndInstallSdk(opts) {
789
796
  // src/cmdTools/install.ts
790
797
  import * as fs10 from "fs";
791
798
  import * as os6 from "os";
799
+ import * as path11 from "path";
800
+ import { spawnSync } from "child_process";
801
+
802
+ // src/hdc/spawnCompat.ts
792
803
  import * as path10 from "path";
793
- import { execFileSync } from "child_process";
804
+ import { spawn } from "child_process";
805
+ var WINDOWS_BATCH_RE = /\.(bat|cmd)$/i;
806
+ var CMD_META_RE = /([()[\]%!^"`<>&|;, *?])/g;
807
+ var CMD_SHIM_RE = /node_modules[\\/]\.bin[\\/][^\\/]+\.cmd$/i;
808
+ var UNSAFE_CMD_CHARS_RE = /["\r\n\0]/;
809
+ function needsCmdWrapper(command, platform7 = process.platform) {
810
+ return platform7 === "win32" && WINDOWS_BATCH_RE.test(command);
811
+ }
812
+ function escapeCmdArgument(arg, doubleEscapeMetaChars = false) {
813
+ let out = String(arg);
814
+ out = out.replace(/(\\*)"/g, '$1$1\\"');
815
+ out = out.replace(/(\\*)$/, "$1$1");
816
+ out = `"${out}"`;
817
+ out = out.replace(CMD_META_RE, "^$1");
818
+ if (doubleEscapeMetaChars) out = out.replace(CMD_META_RE, "^$1");
819
+ return out;
820
+ }
821
+ function escapeCmdCommand(command) {
822
+ return command.replace(CMD_META_RE, "^$1");
823
+ }
824
+ function buildCmdWrapper(command, args) {
825
+ assertCmdSafe(command, `command ${command}`);
826
+ args.forEach((a, i) => assertCmdSafe(a, `argument ${i} (${a})`));
827
+ const normalized = path10.normalize(command);
828
+ const double = CMD_SHIM_RE.test(normalized);
829
+ const line = [escapeCmdCommand(normalized), ...args.map((a) => escapeCmdArgument(a, double))].join(" ");
830
+ return {
831
+ command: process.env.ComSpec ?? process.env.COMSPEC ?? "cmd.exe",
832
+ args: ["/d", "/s", "/c", `"${line}"`]
833
+ };
834
+ }
835
+ function assertCmdSafe(value, label) {
836
+ if (UNSAFE_CMD_CHARS_RE.test(value)) {
837
+ throw new OniroError(
838
+ `Cannot run a Windows batch wrapper with a quote, newline or NUL in the ${label}: cmd.exe cannot pass it through unambiguously.`
839
+ );
840
+ }
841
+ }
842
+ function spawnCompat(command, args, options = {}) {
843
+ if (!needsCmdWrapper(command)) {
844
+ return spawn(command, [...args], { ...options, shell: false });
845
+ }
846
+ const wrapped = buildCmdWrapper(command, args);
847
+ return spawn(wrapped.command, wrapped.args, {
848
+ ...options,
849
+ shell: false,
850
+ // The line is already escaped; stop Node from re-quoting it.
851
+ windowsVerbatimArguments: true
852
+ });
853
+ }
854
+ function killProcessTree(child) {
855
+ if (process.platform === "win32" && typeof child.pid === "number") {
856
+ try {
857
+ const killer = spawn("taskkill", ["/pid", String(child.pid), "/T", "/F"], { stdio: "ignore" });
858
+ killer.on("error", () => {
859
+ try {
860
+ child.kill("SIGKILL");
861
+ } catch {
862
+ }
863
+ });
864
+ return;
865
+ } catch {
866
+ }
867
+ }
868
+ try {
869
+ child.kill("SIGKILL");
870
+ } catch {
871
+ }
872
+ }
873
+
874
+ // src/cmdTools/install.ts
794
875
  function getCmdToolsDownloadUrl(config, platform7 = os6.platform()) {
795
876
  if (platform7 === "linux") {
796
877
  return config.get("cmdToolsUrlLinux", defaultPaths.cmdToolsUrlLinux);
@@ -817,16 +898,16 @@ function getCmdToolsDownloadUrl(config, platform7 = os6.platform()) {
817
898
  }
818
899
  function findCmdToolsSourceDir(extractPath) {
819
900
  const known = [
820
- path10.join(extractPath, "command-line-tools"),
821
- path10.join(extractPath, "oh-command-line-tools"),
822
- path10.join(extractPath, "commandline-tools")
901
+ path11.join(extractPath, "command-line-tools"),
902
+ path11.join(extractPath, "oh-command-line-tools"),
903
+ path11.join(extractPath, "commandline-tools")
823
904
  ];
824
905
  for (const c of known) {
825
906
  if (fs10.existsSync(c)) return c;
826
907
  }
827
- const entries = fs10.readdirSync(extractPath, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => path10.join(extractPath, e.name));
908
+ const entries = fs10.readdirSync(extractPath, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => path11.join(extractPath, e.name));
828
909
  for (const dir of entries) {
829
- if (fs10.existsSync(path10.join(dir, "bin"))) return dir;
910
+ if (fs10.existsSync(path11.join(dir, "bin"))) return dir;
830
911
  }
831
912
  throw new OniroError("Could not locate command line tools folder in the extracted archive.");
832
913
  }
@@ -838,12 +919,12 @@ async function installCmdTools(opts) {
838
919
  const { dir: tmpDir, root: tmpRoot } = createInstallTempDir({
839
920
  config,
840
921
  override: opts.tmpDir,
841
- installRoot: path10.dirname(CMD_PATH),
922
+ installRoot: path11.dirname(CMD_PATH),
842
923
  prefix: "oniro-cmdtools-",
843
924
  logger
844
925
  });
845
- const zipPath = opts.localZipPath ?? path10.join(tmpDir, "oh-command-line-tools.zip");
846
- const extractPath = path10.join(tmpDir, "oh-command-line-tools");
926
+ const zipPath = opts.localZipPath ?? path11.join(tmpDir, "oh-command-line-tools.zip");
927
+ const extractPath = path11.join(tmpDir, "oh-command-line-tools");
847
928
  try {
848
929
  if (!opts.localZipPath) {
849
930
  const url = getCmdToolsDownloadUrl(config);
@@ -855,8 +936,8 @@ async function installCmdTools(opts) {
855
936
  fs10.mkdirSync(CMD_PATH, { recursive: true });
856
937
  const srcDir = findCmdToolsSourceDir(extractPath);
857
938
  for (const entry of fs10.readdirSync(srcDir)) {
858
- const src = path10.join(srcDir, entry);
859
- const dest = path10.join(CMD_PATH, entry);
939
+ const src = path11.join(srcDir, entry);
940
+ const dest = path11.join(CMD_PATH, entry);
860
941
  if (fs10.statSync(src).isDirectory()) {
861
942
  if (fs10.existsSync(dest)) fs10.rmSync(dest, { recursive: true, force: true });
862
943
  movePath(src, dest);
@@ -864,10 +945,10 @@ async function installCmdTools(opts) {
864
945
  fs10.copyFileSync(src, dest);
865
946
  }
866
947
  }
867
- const binDir = path10.join(CMD_PATH, "bin");
948
+ const binDir = path11.join(CMD_PATH, "bin");
868
949
  if (fs10.existsSync(binDir) && os6.platform() !== "win32") {
869
950
  for (const file of fs10.readdirSync(binDir)) {
870
- fs10.chmodSync(path10.join(binDir, file), 493);
951
+ fs10.chmodSync(path11.join(binDir, file), 493);
871
952
  }
872
953
  }
873
954
  progress?.report({ message: "Finalizing installation...", increment: 5 });
@@ -880,14 +961,14 @@ async function installCmdTools(opts) {
880
961
  }
881
962
  function getCmdToolsStatus(config) {
882
963
  const cmdPath = getCmdToolsPath(config);
883
- const binDir = path10.join(cmdPath, "bin");
964
+ const binDir = path11.join(cmdPath, "bin");
884
965
  const candidates = os6.platform() === "win32" ? ["ohpm.exe", "ohpm.cmd", "ohpm.bat", "ohpm"] : ["ohpm"];
885
- const bin = candidates.map((c) => path10.join(binDir, c)).find((p) => fs10.existsSync(p));
966
+ const bin = candidates.map((c) => path11.join(binDir, c)).find((p) => fs10.existsSync(p));
886
967
  if (!bin) {
887
968
  return { installed: false, status: "Not installed" };
888
969
  }
889
970
  try {
890
- const versionFile = path10.join(cmdPath, "version.txt");
971
+ const versionFile = path11.join(cmdPath, "version.txt");
891
972
  if (fs10.existsSync(versionFile)) {
892
973
  const content = fs10.readFileSync(versionFile, "utf8");
893
974
  for (const line of content.split(/\r?\n/)) {
@@ -898,7 +979,14 @@ function getCmdToolsStatus(config) {
898
979
  } catch {
899
980
  }
900
981
  try {
901
- const version = execFileSync(bin, ["-v"], { encoding: "utf8" }).trim();
982
+ const wrapped = needsCmdWrapper(bin);
983
+ const probe = wrapped ? buildCmdWrapper(bin, ["-v"]) : { command: bin, args: ["-v"] };
984
+ const res = spawnSync(probe.command, probe.args, {
985
+ encoding: "utf8",
986
+ windowsVerbatimArguments: wrapped
987
+ });
988
+ if (res.error || res.status !== 0) throw res.error ?? new Error(`exit ${String(res.status)}`);
989
+ const version = res.stdout.trim();
902
990
  return { installed: true, status: `Installed (${version})` };
903
991
  } catch {
904
992
  return { installed: true, status: "Installed (version unknown)" };
@@ -916,7 +1004,7 @@ function removeCmdTools(config) {
916
1004
 
917
1005
  // src/sign/encryptKey.ts
918
1006
  import * as fs11 from "fs";
919
- import * as path11 from "path";
1007
+ import * as path12 from "path";
920
1008
  import * as crypto2 from "crypto";
921
1009
  var DAEMON_ROOT_KEY_COMPONENT_LENGTH = 16;
922
1010
  var DAEMON_SALT_KEY_LENGTH = 16;
@@ -988,7 +1076,7 @@ function getRootKey(fdComponents, salt) {
988
1076
  function createAndStoreKey(dir, keyLength) {
989
1077
  const key = crypto2.randomBytes(keyLength);
990
1078
  const hash = crypto2.createHash("sha256").update(key).digest("hex");
991
- const filePath = path11.join(dir, hash);
1079
+ const filePath = path12.join(dir, hash);
992
1080
  fs11.writeFileSync(filePath, key);
993
1081
  fs11.chmodSync(filePath, KEY_FILE_PERMISSIONS);
994
1082
  return key;
@@ -997,23 +1085,23 @@ function createAndStoreEnKey(rootKey, ceDir) {
997
1085
  const workKey = crypto2.randomBytes(DAEMON_WORK_KEY_LENGTH);
998
1086
  const encrypted = encrypt(rootKey, workKey);
999
1087
  const hash = crypto2.createHash("sha256").update(encrypted).digest("hex");
1000
- const filePath = path11.join(ceDir, hash);
1088
+ const filePath = path12.join(ceDir, hash);
1001
1089
  fs11.writeFileSync(filePath, encrypted);
1002
1090
  fs11.chmodSync(filePath, KEY_FILE_PERMISSIONS);
1003
1091
  }
1004
1092
  function createMaterial(materialPath) {
1005
1093
  fs11.mkdirSync(materialPath, { recursive: true });
1006
1094
  fs11.chmodSync(materialPath, KEY_FILE_DIRECTORY_PERMISSIONS);
1007
- const fdDir = path11.join(materialPath, "fd");
1008
- const acDir = path11.join(materialPath, "ac");
1009
- const ceDir = path11.join(materialPath, "ce");
1095
+ const fdDir = path12.join(materialPath, "fd");
1096
+ const acDir = path12.join(materialPath, "ac");
1097
+ const ceDir = path12.join(materialPath, "ce");
1010
1098
  fs11.mkdirSync(fdDir, { recursive: true });
1011
1099
  fs11.mkdirSync(acDir, { recursive: true });
1012
1100
  fs11.mkdirSync(ceDir, { recursive: true });
1013
1101
  const fdSubDirs = ["0", "1", "2"];
1014
1102
  const fdComponents = [];
1015
1103
  for (const sub of fdSubDirs) {
1016
- const subDir = path11.join(fdDir, sub);
1104
+ const subDir = path12.join(fdDir, sub);
1017
1105
  fs11.mkdirSync(subDir, { recursive: true });
1018
1106
  const comp = createAndStoreKey(subDir, DAEMON_ROOT_KEY_COMPONENT_LENGTH);
1019
1107
  fdComponents.push(comp);
@@ -1030,17 +1118,17 @@ function readSingleFile(dir) {
1030
1118
  if (files.length !== 1) {
1031
1119
  throw new Error(`Signing material in ${dir} is illegal (expected exactly one file).`);
1032
1120
  }
1033
- return fs11.readFileSync(path11.join(dir, files[0]));
1121
+ return fs11.readFileSync(path12.join(dir, files[0]));
1034
1122
  }
1035
1123
  function getKey(materialPath) {
1036
1124
  if (!fs11.existsSync(materialPath) || !fs11.statSync(materialPath).isDirectory()) {
1037
1125
  throw new Error("Material directory does not exist.");
1038
1126
  }
1039
- const fdDir = path11.join(materialPath, "fd");
1040
- const fdComponents = ["0", "1", "2"].map((sub) => readSingleFile(path11.join(fdDir, sub)));
1041
- const salt = readSingleFile(path11.join(materialPath, "ac"));
1127
+ const fdDir = path12.join(materialPath, "fd");
1128
+ const fdComponents = ["0", "1", "2"].map((sub) => readSingleFile(path12.join(fdDir, sub)));
1129
+ const salt = readSingleFile(path12.join(materialPath, "ac"));
1042
1130
  const rootKey = getRootKey(fdComponents, salt);
1043
- const workMaterial = readSingleFile(path11.join(materialPath, "ce"));
1131
+ const workMaterial = readSingleFile(path12.join(materialPath, "ce"));
1044
1132
  return decrypt(rootKey, workMaterial);
1045
1133
  }
1046
1134
  function encryptPwd(password, materialPath) {
@@ -1056,8 +1144,8 @@ function decryptPwd(encryptedHex, materialPath) {
1056
1144
 
1057
1145
  // src/sign/generateSigningConfigs.ts
1058
1146
  import * as fs12 from "fs";
1059
- import * as path12 from "path";
1060
- import { execFileSync as execFileSync2 } from "child_process";
1147
+ import * as path13 from "path";
1148
+ import { execFileSync } from "child_process";
1061
1149
  import JSON52 from "json5";
1062
1150
 
1063
1151
  // src/sign/applicationCert.ts
@@ -1126,13 +1214,13 @@ var SIGNING_KIND_CONFIG = {
1126
1214
  };
1127
1215
  function copyFilesToProject(projectDir, paths, kind, logger, applicationCertPath) {
1128
1216
  logger.info("[sign] Copying signing material into project...");
1129
- const signaturesDir = path12.join(projectDir, "signatures");
1217
+ const signaturesDir = path13.join(projectDir, "signatures");
1130
1218
  fs12.mkdirSync(signaturesDir, { recursive: true });
1131
- fs12.copyFileSync(paths.keystore, path12.join(signaturesDir, "OpenHarmony.p12"));
1132
- fs12.copyFileSync(paths.profileCert, path12.join(signaturesDir, "OpenHarmonyProfileRelease.pem"));
1133
- fs12.copyFileSync(paths.unsignedProfileTemplate, path12.join(signaturesDir, "UnsgnedReleasedProfileTemplate.json"));
1219
+ fs12.copyFileSync(paths.keystore, path13.join(signaturesDir, "OpenHarmony.p12"));
1220
+ fs12.copyFileSync(paths.profileCert, path13.join(signaturesDir, "OpenHarmonyProfileRelease.pem"));
1221
+ fs12.copyFileSync(paths.unsignedProfileTemplate, path13.join(signaturesDir, "UnsgnedReleasedProfileTemplate.json"));
1134
1222
  if (kind === "application-release") {
1135
- const dest = path12.join(signaturesDir, "OpenHarmonyApplication.cer");
1223
+ const dest = path13.join(signaturesDir, "OpenHarmonyApplication.cer");
1136
1224
  if (applicationCertPath) {
1137
1225
  fs12.copyFileSync(applicationCertPath, dest);
1138
1226
  } else {
@@ -1155,8 +1243,8 @@ function extractLeafCertificate(chainPath) {
1155
1243
  function modifyProfileTemplate(projectDir, apl, appFeature, logger, options = {}) {
1156
1244
  const kind = options.kind ?? pickSigningKind(apl);
1157
1245
  logger.info(`[sign] Modifying profile template (apl=${apl}, app-feature=${appFeature}, kind=${kind})...`);
1158
- const appJsonPath = path12.join(projectDir, "AppScope/app.json5");
1159
- const profileTemplatePath = path12.join(projectDir, "signatures/UnsgnedReleasedProfileTemplate.json");
1246
+ const appJsonPath = path13.join(projectDir, "AppScope/app.json5");
1247
+ const profileTemplatePath = path13.join(projectDir, "signatures/UnsgnedReleasedProfileTemplate.json");
1160
1248
  if (!fs12.existsSync(appJsonPath)) {
1161
1249
  throw new OniroError(`${appJsonPath} does not exist.`);
1162
1250
  }
@@ -1181,7 +1269,7 @@ function modifyProfileTemplate(projectDir, apl, appFeature, logger, options = {}
1181
1269
  profileTemplate["bundle-info"]["bundle-name"] = appJson.app.bundleName;
1182
1270
  profileTemplate["bundle-info"]["apl"] = apl;
1183
1271
  profileTemplate["bundle-info"]["app-feature"] = appFeature;
1184
- const distCertChainPath = path12.join(
1272
+ const distCertChainPath = path13.join(
1185
1273
  projectDir,
1186
1274
  "signatures",
1187
1275
  SIGNING_KIND_CONFIG[kind].certBasename
@@ -1195,9 +1283,9 @@ function modifyProfileTemplate(projectDir, apl, appFeature, logger, options = {}
1195
1283
  }
1196
1284
  function generateP7bFile(projectDir, paths, logger, passwords) {
1197
1285
  logger.info("[sign] Generating P7b profile via hap-sign-tool...");
1198
- const signaturesDir = path12.join(projectDir, "signatures");
1199
- const profileTemplatePath = path12.join(signaturesDir, "UnsgnedReleasedProfileTemplate.json");
1200
- const outputProfilePath = path12.join(signaturesDir, "app1-profile.p7b");
1286
+ const signaturesDir = path13.join(projectDir, "signatures");
1287
+ const profileTemplatePath = path13.join(signaturesDir, "UnsgnedReleasedProfileTemplate.json");
1288
+ const outputProfilePath = path13.join(signaturesDir, "app1-profile.p7b");
1201
1289
  const args = [
1202
1290
  "-jar",
1203
1291
  paths.signTool,
@@ -1222,7 +1310,7 @@ function generateP7bFile(projectDir, paths, logger, passwords) {
1222
1310
  passwords.store
1223
1311
  ];
1224
1312
  try {
1225
- execFileSync2("java", args, { stdio: "inherit" });
1313
+ execFileSync("java", args, { stdio: "inherit" });
1226
1314
  } catch (e) {
1227
1315
  throw new OniroError(
1228
1316
  `hap-sign-tool failed. Ensure a JDK (with keytool/java) is on PATH. Underlying error: ${e.message}`,
@@ -1231,7 +1319,7 @@ function generateP7bFile(projectDir, paths, logger, passwords) {
1231
1319
  }
1232
1320
  }
1233
1321
  function detectSigningConfigNames(projectDir) {
1234
- const buildProfilePath = path12.join(projectDir, "build-profile.json5");
1322
+ const buildProfilePath = path13.join(projectDir, "build-profile.json5");
1235
1323
  if (!fs12.existsSync(buildProfilePath)) return ["default"];
1236
1324
  let parsed;
1237
1325
  try {
@@ -1252,8 +1340,8 @@ function detectSigningConfigNames(projectDir) {
1252
1340
  }
1253
1341
  function updateBuildProfile(projectDir, kind, logger, passwords) {
1254
1342
  logger.info("[sign] Writing signing configs into build-profile.json5...");
1255
- const materialDir = path12.join(projectDir, "signatures", "material");
1256
- const buildProfilePath = path12.join(projectDir, "build-profile.json5");
1343
+ const materialDir = path13.join(projectDir, "signatures", "material");
1344
+ const buildProfilePath = path13.join(projectDir, "build-profile.json5");
1257
1345
  const encryptedStorePassword = encryptPwd(passwords.store, materialDir);
1258
1346
  const encryptedKeyPassword = encryptPwd(passwords.key, materialDir);
1259
1347
  let buildProfile = { app: {} };
@@ -1284,7 +1372,7 @@ function updateBuildProfile(projectDir, kind, logger, passwords) {
1284
1372
  }
1285
1373
  function prepareMaterialDirectory(projectDir, logger) {
1286
1374
  logger.info("[sign] Generating fresh signing material...");
1287
- const materialDir = path12.join(projectDir, "signatures", "material");
1375
+ const materialDir = path13.join(projectDir, "signatures", "material");
1288
1376
  if (fs12.existsSync(materialDir)) {
1289
1377
  fs12.rmSync(materialDir, { recursive: true, force: true });
1290
1378
  }
@@ -1309,15 +1397,15 @@ function generateSigningConfigs(options) {
1309
1397
  "Could not detect project SDK version (compileSdkVersion missing in build-profile.json5)."
1310
1398
  );
1311
1399
  }
1312
- const sdkPath = path12.join(sdkHome, String(sdkVersion));
1400
+ const sdkPath = path13.join(sdkHome, String(sdkVersion));
1313
1401
  if (!fs12.existsSync(sdkPath)) {
1314
1402
  throw new OniroError(`SDK path does not exist: ${sdkPath}`);
1315
1403
  }
1316
1404
  const paths = {
1317
- signTool: path12.join(sdkPath, "toolchains/lib/hap-sign-tool.jar"),
1318
- keystore: path12.join(sdkPath, "toolchains/lib/OpenHarmony.p12"),
1319
- profileCert: path12.join(sdkPath, "toolchains/lib/OpenHarmonyProfileRelease.pem"),
1320
- unsignedProfileTemplate: path12.join(sdkPath, "toolchains/lib/UnsgnedReleasedProfileTemplate.json")
1405
+ signTool: path13.join(sdkPath, "toolchains/lib/hap-sign-tool.jar"),
1406
+ keystore: path13.join(sdkPath, "toolchains/lib/OpenHarmony.p12"),
1407
+ profileCert: path13.join(sdkPath, "toolchains/lib/OpenHarmonyProfileRelease.pem"),
1408
+ unsignedProfileTemplate: path13.join(sdkPath, "toolchains/lib/UnsgnedReleasedProfileTemplate.json")
1321
1409
  };
1322
1410
  const kind = pickSigningKind(apl);
1323
1411
  const passwords = options.passwords ?? DEFAULT_PASSWORDS;
@@ -1332,7 +1420,7 @@ function generateSigningConfigs(options) {
1332
1420
 
1333
1421
  // src/emulator/install.ts
1334
1422
  import * as fs13 from "fs";
1335
- import * as path13 from "path";
1423
+ import * as path14 from "path";
1336
1424
  async function installEmulator(opts) {
1337
1425
  const { config, progress, abortSignal } = opts;
1338
1426
  const logger = opts.logger ?? noopLogger;
@@ -1342,18 +1430,18 @@ async function installEmulator(opts) {
1342
1430
  const { dir: tmpDir, root: tmpRoot } = createInstallTempDir({
1343
1431
  config,
1344
1432
  override: opts.tmpDir,
1345
- installRoot: path13.dirname(emulatorDir),
1433
+ installRoot: path14.dirname(emulatorDir),
1346
1434
  prefix: "oniro-emulator-",
1347
1435
  logger
1348
1436
  });
1349
- const tmpZip = path13.join(tmpDir, "oniro_emulator.zip");
1437
+ const tmpZip = path14.join(tmpDir, "oniro_emulator.zip");
1350
1438
  try {
1351
1439
  fs13.mkdirSync(emulatorDir, { recursive: true });
1352
1440
  progress?.report({ message: "Downloading emulator...", increment: 0 });
1353
1441
  await downloadFile({ url, dest: tmpZip, progress, abortSignal, start: 0, range: 50, what, tmpRoot });
1354
1442
  progress?.report({ message: "Extracting emulator...", increment: 0 });
1355
1443
  await extractZipWithProgress({ zipPath: tmpZip, dest: emulatorDir, progress, start: 50, range: 45, logger });
1356
- const runSh = path13.join(emulatorDir, "images", "run.sh");
1444
+ const runSh = path14.join(emulatorDir, "images", "run.sh");
1357
1445
  if (fs13.existsSync(runSh)) {
1358
1446
  fs13.chmodSync(runSh, 493);
1359
1447
  }
@@ -1365,8 +1453,8 @@ async function installEmulator(opts) {
1365
1453
  }
1366
1454
  }
1367
1455
  function isEmulatorInstalled(config) {
1368
- const imagesDir = path13.join(getEmulatorDir(config), "images");
1369
- return fs13.existsSync(path13.join(imagesDir, "run.sh")) || fs13.existsSync(path13.join(imagesDir, "run.bat"));
1456
+ const imagesDir = path14.join(getEmulatorDir(config), "images");
1457
+ return fs13.existsSync(path14.join(imagesDir, "run.sh")) || fs13.existsSync(path14.join(imagesDir, "run.bat"));
1370
1458
  }
1371
1459
  function removeEmulator(config) {
1372
1460
  const emulatorDir = getEmulatorDir(config);
@@ -1376,13 +1464,16 @@ function removeEmulator(config) {
1376
1464
  }
1377
1465
 
1378
1466
  // src/emulator/lifecycle.ts
1379
- import * as fs14 from "fs";
1467
+ import * as fs15 from "fs";
1380
1468
  import * as os7 from "os";
1381
- import * as path14 from "path";
1469
+ import * as path15 from "path";
1382
1470
  import { spawn as spawn2 } from "child_process";
1383
1471
 
1384
1472
  // src/hdc/exec.ts
1385
- import { spawn } from "child_process";
1473
+ import * as fs14 from "fs";
1474
+ function errorMessage(err) {
1475
+ return err instanceof Error ? err.message : String(err);
1476
+ }
1386
1477
  function runProcess(opts) {
1387
1478
  const { command, args, cwd, env, onOutput } = opts;
1388
1479
  const timeoutMs = opts.timeoutMs ?? 6e4;
@@ -1392,7 +1483,17 @@ function runProcess(opts) {
1392
1483
  reject(new CancelledError(`Command aborted before start: ${command}`));
1393
1484
  return;
1394
1485
  }
1395
- const child = spawn(command, [...args], { cwd, env, shell: false });
1486
+ if (needsCmdWrapper(command) && !fs14.existsSync(command)) {
1487
+ reject(new OniroError(`Failed to spawn ${command}: ENOENT (no such file)`));
1488
+ return;
1489
+ }
1490
+ let child;
1491
+ try {
1492
+ child = spawnCompat(command, args, { cwd, env });
1493
+ } catch (err) {
1494
+ reject(new OniroError(`Failed to spawn ${command}: ${errorMessage(err)}`, err));
1495
+ return;
1496
+ }
1396
1497
  let stdout = "";
1397
1498
  let stderr = "";
1398
1499
  let settled = false;
@@ -1405,26 +1506,20 @@ function runProcess(opts) {
1405
1506
  if (settled) return;
1406
1507
  settled = true;
1407
1508
  cleanup();
1408
- try {
1409
- child.kill("SIGKILL");
1410
- } catch {
1411
- }
1509
+ killProcessTree(child);
1412
1510
  reject(new CancelledError(`Command aborted: ${command} ${args.join(" ")}`));
1413
1511
  };
1414
1512
  const timer = setTimeout(() => {
1415
1513
  timedOut = true;
1416
- try {
1417
- child.kill("SIGKILL");
1418
- } catch {
1419
- }
1514
+ killProcessTree(child);
1420
1515
  }, timeoutMs);
1421
1516
  abortSignal?.addEventListener("abort", onAbort, { once: true });
1422
- child.stdout.on("data", (d) => {
1517
+ child.stdout?.on("data", (d) => {
1423
1518
  const s = d.toString();
1424
1519
  stdout += s;
1425
1520
  onOutput?.(s, "stdout");
1426
1521
  });
1427
- child.stderr.on("data", (d) => {
1522
+ child.stderr?.on("data", (d) => {
1428
1523
  const s = d.toString();
1429
1524
  stderr += s;
1430
1525
  onOutput?.(s, "stderr");
@@ -1496,7 +1591,7 @@ function ensureRemoteOk(result, command) {
1496
1591
  }
1497
1592
 
1498
1593
  // src/emulator/lifecycle.ts
1499
- var PID_FILE = path14.join(os7.tmpdir(), "oniro_emulator.pid");
1594
+ var PID_FILE = path15.join(os7.tmpdir(), "oniro_emulator.pid");
1500
1595
  async function attemptHdcConnection(config, address = "127.0.0.1:55555", logger = noopLogger) {
1501
1596
  try {
1502
1597
  await hdcExec({ config, args: ["start", "-r"], logger });
@@ -1509,24 +1604,24 @@ async function attemptHdcConnection(config, address = "127.0.0.1:55555", logger
1509
1604
  }
1510
1605
  }
1511
1606
  function resolveLauncher(imagesPath, headless, connect) {
1512
- const runSh = path14.join(imagesPath, "run.sh");
1513
- const runBat = path14.join(imagesPath, "run.bat");
1607
+ const runSh = path15.join(imagesPath, "run.sh");
1608
+ const runBat = path15.join(imagesPath, "run.bat");
1514
1609
  const extra = [];
1515
1610
  if (headless) extra.push("--headless");
1516
1611
  if (connect) extra.push("--connect", connect);
1517
1612
  if (os7.platform() === "win32") {
1518
- if (fs14.existsSync(runBat)) {
1613
+ if (fs15.existsSync(runBat)) {
1519
1614
  return { command: "cmd.exe", args: ["/c", runBat, ...extra], cwd: imagesPath };
1520
1615
  }
1521
- if (fs14.existsSync(runSh)) {
1616
+ if (fs15.existsSync(runSh)) {
1522
1617
  return { command: "bash", args: [runSh, ...extra], cwd: imagesPath };
1523
1618
  }
1524
1619
  throw new OniroError(`No emulator launcher (run.bat or run.sh) found in ${imagesPath}.`);
1525
1620
  }
1526
- if (fs14.existsSync(runSh)) {
1621
+ if (fs15.existsSync(runSh)) {
1527
1622
  return { command: "bash", args: [runSh, ...extra], cwd: imagesPath };
1528
1623
  }
1529
- if (fs14.existsSync(runBat)) {
1624
+ if (fs15.existsSync(runBat)) {
1530
1625
  return { command: "bash", args: [runBat, ...extra], cwd: imagesPath };
1531
1626
  }
1532
1627
  throw new OniroError(`No emulator launcher (run.sh or run.bat) found in ${imagesPath}.`);
@@ -1534,9 +1629,9 @@ function resolveLauncher(imagesPath, headless, connect) {
1534
1629
  async function startEmulator(opts) {
1535
1630
  const { config } = opts;
1536
1631
  const logger = opts.logger ?? noopLogger;
1537
- if (os7.platform() !== "win32" && fs14.existsSync(PID_FILE)) {
1632
+ if (os7.platform() !== "win32" && fs15.existsSync(PID_FILE)) {
1538
1633
  try {
1539
- const pid = Number(fs14.readFileSync(PID_FILE, "utf8").trim());
1634
+ const pid = Number(fs15.readFileSync(PID_FILE, "utf8").trim());
1540
1635
  if (Number.isFinite(pid) && pid > 0) {
1541
1636
  try {
1542
1637
  process.kill(pid, 0);
@@ -1552,13 +1647,13 @@ async function startEmulator(opts) {
1552
1647
  logger.warn(`Could not read PID file: ${err.message}`);
1553
1648
  }
1554
1649
  }
1555
- const imagesPath = path14.join(getEmulatorDir(config), "images");
1556
- if (!fs14.existsSync(imagesPath)) {
1650
+ const imagesPath = path15.join(getEmulatorDir(config), "images");
1651
+ if (!fs15.existsSync(imagesPath)) {
1557
1652
  throw new OniroError(`Emulator images directory not found at ${imagesPath}. Run \`oniro-app emulator install\` first.`);
1558
1653
  }
1559
1654
  const launcher = resolveLauncher(imagesPath, opts.headless === true, opts.connect);
1560
1655
  const logPath = opts.logFile ?? (os7.platform() === "win32" ? "NUL" : "/dev/null");
1561
- const logFd = fs14.openSync(logPath, "a");
1656
+ const logFd = fs15.openSync(logPath, "a");
1562
1657
  let child;
1563
1658
  try {
1564
1659
  child = spawn2(launcher.command, [...launcher.args], {
@@ -1568,12 +1663,12 @@ async function startEmulator(opts) {
1568
1663
  windowsHide: true
1569
1664
  });
1570
1665
  } finally {
1571
- fs14.closeSync(logFd);
1666
+ fs15.closeSync(logFd);
1572
1667
  }
1573
1668
  if (!child.pid) {
1574
1669
  throw new OniroError(`Failed to spawn launcher (${launcher.command} ${launcher.args.join(" ")}).`);
1575
1670
  }
1576
- fs14.writeFileSync(PID_FILE, String(child.pid));
1671
+ fs15.writeFileSync(PID_FILE, String(child.pid));
1577
1672
  child.unref();
1578
1673
  logger.info(`Emulator launched (PID ${child.pid}). Logs: ${logPath}`);
1579
1674
  const earlyExit = new Promise((resolve4) => {
@@ -1620,24 +1715,24 @@ ${tail}` : ""}`);
1620
1715
  }
1621
1716
  function readTail(filePath, bytes) {
1622
1717
  try {
1623
- const stat = fs14.statSync(filePath);
1718
+ const stat = fs15.statSync(filePath);
1624
1719
  const start = Math.max(0, stat.size - bytes);
1625
- const fd = fs14.openSync(filePath, "r");
1720
+ const fd = fs15.openSync(filePath, "r");
1626
1721
  try {
1627
1722
  const buf = Buffer.alloc(stat.size - start);
1628
- fs14.readSync(fd, buf, 0, buf.length, start);
1723
+ fs15.readSync(fd, buf, 0, buf.length, start);
1629
1724
  return buf.toString("utf8");
1630
1725
  } finally {
1631
- fs14.closeSync(fd);
1726
+ fs15.closeSync(fd);
1632
1727
  }
1633
1728
  } catch {
1634
1729
  return "";
1635
1730
  }
1636
1731
  }
1637
1732
  async function stopEmulator(logger = noopLogger) {
1638
- if (fs14.existsSync(PID_FILE)) {
1733
+ if (fs15.existsSync(PID_FILE)) {
1639
1734
  try {
1640
- const pid = Number(fs14.readFileSync(PID_FILE, "utf8").trim());
1735
+ const pid = Number(fs15.readFileSync(PID_FILE, "utf8").trim());
1641
1736
  if (Number.isFinite(pid) && pid > 0) {
1642
1737
  try {
1643
1738
  process.kill(pid, "SIGTERM");
@@ -1656,7 +1751,7 @@ async function stopEmulator(logger = noopLogger) {
1656
1751
  } catch (err) {
1657
1752
  logger.warn(`[emulator] could not run ${killCmd}: ${err.message}`);
1658
1753
  }
1659
- fs14.rm(PID_FILE, { force: true }, (err) => {
1754
+ fs15.rm(PID_FILE, { force: true }, (err) => {
1660
1755
  if (err) logger.warn(`Could not remove PID file: ${err.message}`);
1661
1756
  });
1662
1757
  }
@@ -1823,21 +1918,20 @@ async function waitForCondition(opts) {
1823
1918
  }
1824
1919
 
1825
1920
  // src/hdc/app.ts
1826
- import * as fs16 from "fs";
1827
- import * as path16 from "path";
1828
- import { spawn as spawn3 } from "child_process";
1921
+ import * as fs17 from "fs";
1922
+ import * as path17 from "path";
1829
1923
 
1830
1924
  // src/hdc/project.ts
1831
- import * as fs15 from "fs";
1832
- import * as path15 from "path";
1925
+ import * as fs16 from "fs";
1926
+ import * as path16 from "path";
1833
1927
  import JSON53 from "json5";
1834
1928
  function readJson5(filePath) {
1835
- const content = fs15.readFileSync(filePath, "utf-8");
1929
+ const content = fs16.readFileSync(filePath, "utf-8");
1836
1930
  return JSON53.parse(content);
1837
1931
  }
1838
1932
  function getBundleName(projectDir) {
1839
- const appJsonPath = path15.join(projectDir, "AppScope", "app.json5");
1840
- if (!fs15.existsSync(appJsonPath)) {
1933
+ const appJsonPath = path16.join(projectDir, "AppScope", "app.json5");
1934
+ if (!fs16.existsSync(appJsonPath)) {
1841
1935
  throw new OniroError(`Could not find app.json5 at ${appJsonPath}`);
1842
1936
  }
1843
1937
  const appJson = readJson5(appJsonPath);
@@ -1847,8 +1941,8 @@ function getBundleName(projectDir) {
1847
1941
  return appJson.app.bundleName;
1848
1942
  }
1849
1943
  function getMainAbility(projectDir, moduleName = "entry", abilityName) {
1850
- const moduleJsonPath = path15.join(projectDir, moduleName, "src", "main", "module.json5");
1851
- if (!fs15.existsSync(moduleJsonPath)) {
1944
+ const moduleJsonPath = path16.join(projectDir, moduleName, "src", "main", "module.json5");
1945
+ if (!fs16.existsSync(moduleJsonPath)) {
1852
1946
  throw new OniroError(`Could not find module.json5 at ${moduleJsonPath}`);
1853
1947
  }
1854
1948
  const moduleJson = readJson5(moduleJsonPath);
@@ -1871,8 +1965,8 @@ function getMainAbility(projectDir, moduleName = "entry", abilityName) {
1871
1965
  async function installApp(opts) {
1872
1966
  const logger = scopedLogger(opts.logger ?? noopLogger, "hdc");
1873
1967
  const relativeHapPath = opts.hapPath ?? opts.config.get("hapPath", defaultPaths.hapPath);
1874
- const hapPath = path16.isAbsolute(relativeHapPath) ? relativeHapPath : path16.join(opts.projectDir, relativeHapPath);
1875
- if (!fs16.existsSync(hapPath)) {
1968
+ const hapPath = path17.isAbsolute(relativeHapPath) ? relativeHapPath : path17.join(opts.projectDir, relativeHapPath);
1969
+ if (!fs17.existsSync(hapPath)) {
1876
1970
  throw new OniroError(`HAP file not found at: ${hapPath}. Build and sign the app first.`);
1877
1971
  }
1878
1972
  const result = await hdcExec({ config: opts.config, args: ["install", hapPath], timeoutMs: 3e5 });
@@ -1957,10 +2051,16 @@ function listRunningProcesses(config, options = {}) {
1957
2051
  const timeoutMs = options.timeoutMs ?? 1e3;
1958
2052
  const hdc = getHdcPath(config);
1959
2053
  return new Promise((resolve4, reject) => {
1960
- const proc = spawn3(hdc, ["track-jpid"]);
2054
+ let proc;
2055
+ try {
2056
+ proc = spawnCompat(hdc, ["track-jpid"]);
2057
+ } catch (err) {
2058
+ reject(new OniroError(`Failed to spawn ${hdc}: ${err instanceof Error ? err.message : String(err)}`, err));
2059
+ return;
2060
+ }
1961
2061
  const processes = [];
1962
2062
  let resolved = false;
1963
- proc.stdout.on("data", (data) => {
2063
+ proc.stdout?.on("data", (data) => {
1964
2064
  for (const line of data.toString().split("\n").filter(Boolean)) {
1965
2065
  const match = line.match(/^(\d+)\s+(.+)$/);
1966
2066
  if (!match) continue;
@@ -1969,13 +2069,13 @@ function listRunningProcesses(config, options = {}) {
1969
2069
  processes.push({ pid, name });
1970
2070
  if (options.targetProcessName && name === options.targetProcessName && !resolved) {
1971
2071
  resolved = true;
1972
- proc.kill();
2072
+ killProcessTree(proc);
1973
2073
  resolve4(pid);
1974
2074
  return;
1975
2075
  }
1976
2076
  }
1977
2077
  });
1978
- proc.stderr.on("data", (data) => {
2078
+ proc.stderr?.on("data", (data) => {
1979
2079
  logger.warn(`[hdc track-jpid] ${data.toString()}`);
1980
2080
  });
1981
2081
  proc.on("error", (err) => {
@@ -1987,7 +2087,7 @@ function listRunningProcesses(config, options = {}) {
1987
2087
  const timeout = setTimeout(() => {
1988
2088
  if (resolved) return;
1989
2089
  resolved = true;
1990
- proc.kill();
2090
+ killProcessTree(proc);
1991
2091
  if (options.targetProcessName) {
1992
2092
  reject(new OniroError(`Could not find process for bundle: ${options.targetProcessName}`));
1993
2093
  } else {
@@ -2145,12 +2245,12 @@ async function reboot(opts) {
2145
2245
  }
2146
2246
 
2147
2247
  // src/hdc/screen.ts
2148
- import * as fs17 from "fs";
2248
+ import * as fs18 from "fs";
2149
2249
  import * as os8 from "os";
2150
- import * as path17 from "path";
2250
+ import * as path18 from "path";
2151
2251
  import { randomBytes as randomBytes2 } from "crypto";
2152
2252
  function tmpName(ext) {
2153
- return path17.join(os8.tmpdir(), `oniro_screen_${randomBytes2(6).toString("hex")}.${ext}`);
2253
+ return path18.join(os8.tmpdir(), `oniro_screen_${randomBytes2(6).toString("hex")}.${ext}`);
2154
2254
  }
2155
2255
  async function takeScreenshot(opts) {
2156
2256
  const remote = "/data/local/tmp/oniro_screenshot.jpeg";
@@ -2160,11 +2260,11 @@ async function takeScreenshot(opts) {
2160
2260
  const local = tmpName("jpeg");
2161
2261
  try {
2162
2262
  await recvFile({ config: opts.config, remote, local, deviceSerial: opts.deviceSerial, timeoutMs });
2163
- const pixels = await fs17.promises.readFile(local);
2263
+ const pixels = await fs18.promises.readFile(local);
2164
2264
  const size = await getDisplaySize(opts.config, { deviceSerial: opts.deviceSerial }) ?? { width: 0, height: 0 };
2165
2265
  return { pixels, width: size.width, height: size.height };
2166
2266
  } finally {
2167
- fs17.promises.unlink(local).catch(() => {
2267
+ fs18.promises.unlink(local).catch(() => {
2168
2268
  });
2169
2269
  void shell({ config: opts.config, command: `rm -f ${remote}`, deviceSerial: opts.deviceSerial }).catch(() => {
2170
2270
  });
@@ -2183,9 +2283,9 @@ async function captureBurst(opts) {
2183
2283
  const local = tmpName(`burst_${i}.jpeg`);
2184
2284
  try {
2185
2285
  await recvFile({ config: opts.config, remote: `${remoteDir}/f${i}.jpeg`, local, deviceSerial: opts.deviceSerial });
2186
- frames.push(await fs17.promises.readFile(local));
2286
+ frames.push(await fs18.promises.readFile(local));
2187
2287
  } finally {
2188
- fs17.promises.unlink(local).catch(() => {
2288
+ fs18.promises.unlink(local).catch(() => {
2189
2289
  });
2190
2290
  }
2191
2291
  }
@@ -2311,9 +2411,9 @@ async function sendRawTouch(opts) {
2311
2411
  }
2312
2412
 
2313
2413
  // src/hdc/dump.ts
2314
- import * as fs18 from "fs";
2414
+ import * as fs19 from "fs";
2315
2415
  import * as os9 from "os";
2316
- import * as path18 from "path";
2416
+ import * as path19 from "path";
2317
2417
  import { randomBytes as randomBytes3 } from "crypto";
2318
2418
  function parseBounds(s) {
2319
2419
  const m = /^\[(-?\d+),(-?\d+)\]\[(-?\d+),(-?\d+)\]$/.exec(s ?? "");
@@ -2398,10 +2498,10 @@ async function dumpLayout(opts) {
2398
2498
  if (!m) throw new OniroError(`Could not parse dumpLayout output:
2399
2499
  ${dump.stdout}`);
2400
2500
  const remote = m[1];
2401
- const local = path18.join(os9.tmpdir(), `oniro_layout_${randomBytes3(6).toString("hex")}.json`);
2501
+ const local = path19.join(os9.tmpdir(), `oniro_layout_${randomBytes3(6).toString("hex")}.json`);
2402
2502
  try {
2403
2503
  await recvFile({ config: opts.config, remote, local, deviceSerial: opts.deviceSerial, timeoutMs });
2404
- const parsed = JSON.parse(await fs18.promises.readFile(local, "utf8"));
2504
+ const parsed = JSON.parse(await fs19.promises.readFile(local, "utf8"));
2405
2505
  const display = await getDisplaySize(opts.config, { deviceSerial: opts.deviceSerial });
2406
2506
  if (opts.prune === false) {
2407
2507
  return { display, tree: parsed };
@@ -2410,7 +2510,7 @@ ${dump.stdout}`);
2410
2510
  const tree = Array.isArray(prunedRaw) ? { children: prunedRaw } : prunedRaw ?? { children: [] };
2411
2511
  return { display, tree };
2412
2512
  } finally {
2413
- fs18.promises.unlink(local).catch(() => {
2513
+ fs19.promises.unlink(local).catch(() => {
2414
2514
  });
2415
2515
  void shell({ config: opts.config, command: `rm -f ${remote}`, deviceSerial: opts.deviceSerial }).catch(() => {
2416
2516
  });
@@ -2418,14 +2518,21 @@ ${dump.stdout}`);
2418
2518
  }
2419
2519
 
2420
2520
  // src/hdc/hilog.ts
2421
- import { spawn as spawn4 } from "child_process";
2422
2521
  function setHilogLevel(opts) {
2423
2522
  const logger = opts.logger ?? noopLogger;
2424
2523
  const hdc = getHdcPath(opts.config);
2425
2524
  return new Promise((resolve4, reject) => {
2426
- const child = spawn4(hdc, ["shell", "hilog", "-b", opts.level]);
2525
+ let child;
2526
+ try {
2527
+ child = spawnCompat(hdc, ["shell", "hilog", "-b", opts.level]);
2528
+ } catch (err) {
2529
+ const msg = err instanceof Error ? err.message : String(err);
2530
+ logger.error(`[hilog] failed to spawn hdc: ${msg}`);
2531
+ reject(err instanceof Error ? err : new Error(msg));
2532
+ return;
2533
+ }
2427
2534
  let stderr = "";
2428
- child.stderr.on("data", (chunk) => {
2535
+ child.stderr?.on("data", (chunk) => {
2429
2536
  stderr += chunk.toString();
2430
2537
  });
2431
2538
  child.once("error", (err) => {
@@ -2454,7 +2561,7 @@ function streamHilog(opts) {
2454
2561
  if (opts.domain !== void 0 && `${opts.domain}` !== "") {
2455
2562
  args.push("-D", `${opts.domain}`);
2456
2563
  }
2457
- return spawn4(hdc, args);
2564
+ return spawnCompat(hdc, args);
2458
2565
  }
2459
2566
  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+(.*)$/;
2460
2567
  function parseHilogLine(line) {
@@ -2500,10 +2607,7 @@ function waitForLog(opts) {
2500
2607
  if (reconnectTimer) clearTimeout(reconnectTimer);
2501
2608
  opts.abortSignal?.removeEventListener("abort", onAbort);
2502
2609
  if (child) {
2503
- try {
2504
- child.kill("SIGKILL");
2505
- } catch {
2506
- }
2610
+ killProcessTree(child);
2507
2611
  child = null;
2508
2612
  }
2509
2613
  };
@@ -2519,6 +2623,7 @@ function waitForLog(opts) {
2519
2623
  cleanup();
2520
2624
  reject(err);
2521
2625
  };
2626
+ const failWith = (err) => fail(err instanceof Error ? err : new Error(String(err)));
2522
2627
  const onAbort = () => fail(new CancelledError("waitForLog cancelled."));
2523
2628
  const timer = setTimeout(
2524
2629
  () => fail(new OniroError(`No log line matched ${String(opts.pattern)} within ${opts.timeoutMs}ms.`)),
@@ -2548,13 +2653,13 @@ function waitForLog(opts) {
2548
2653
  if (settled || child !== stream) return;
2549
2654
  child = null;
2550
2655
  if (Date.now() < deadline) {
2551
- reconnectTimer = setTimeout(() => void spawnStream(), 1e3);
2656
+ reconnectTimer = setTimeout(() => void spawnStream().catch(failWith), 1e3);
2552
2657
  }
2553
2658
  };
2554
2659
  stream.once("close", onEnd);
2555
2660
  stream.once("error", onEnd);
2556
2661
  };
2557
- void spawnStream();
2662
+ void spawnStream().catch(failWith);
2558
2663
  });
2559
2664
  }
2560
2665
  function watchLog(opts) {
@@ -2567,10 +2672,7 @@ function watchLog(opts) {
2567
2672
  clearTimeout(timer);
2568
2673
  opts.abortSignal?.removeEventListener("abort", onAbort);
2569
2674
  if (child) {
2570
- try {
2571
- child.kill("SIGKILL");
2572
- } catch {
2573
- }
2675
+ killProcessTree(child);
2574
2676
  child = null;
2575
2677
  }
2576
2678
  };
@@ -2603,7 +2705,12 @@ function watchLog(opts) {
2603
2705
  if (entry && opts.pattern.test(entryText(entry))) matches.push(entry);
2604
2706
  }
2605
2707
  });
2606
- })();
2708
+ })().catch((err) => {
2709
+ if (settled) return;
2710
+ settled = true;
2711
+ cleanup();
2712
+ reject(err instanceof Error ? err : new Error(String(err)));
2713
+ });
2607
2714
  });
2608
2715
  }
2609
2716
  async function dumpLog(opts) {
@@ -2630,9 +2737,8 @@ async function dumpLog(opts) {
2630
2737
  }
2631
2738
 
2632
2739
  // src/build/runHvigorw.ts
2633
- import * as fs19 from "fs";
2634
- import * as path19 from "path";
2635
- import { spawn as spawn5 } from "child_process";
2740
+ import * as fs20 from "fs";
2741
+ import * as path20 from "path";
2636
2742
  function buildHvigorwArgs(opts) {
2637
2743
  const args = [opts.task ?? "assembleHap", "--mode", "module"];
2638
2744
  args.push("-p", `product=${opts.product ?? "default"}`);
@@ -2644,19 +2750,23 @@ function buildHvigorwArgs(opts) {
2644
2750
  return args;
2645
2751
  }
2646
2752
  function runHvigorw(opts) {
2647
- const { config, projectDir } = opts;
2753
+ const { config } = opts;
2754
+ const projectDir = toLongPath(opts.projectDir);
2648
2755
  const logger = opts.logger ?? noopLogger;
2649
- if (!fs19.existsSync(path19.join(projectDir, "build-profile.json5"))) {
2756
+ if (!fs20.existsSync(path20.join(projectDir, "build-profile.json5"))) {
2650
2757
  throw new OniroError(`Not an OpenHarmony project: ${projectDir} (build-profile.json5 not found).`);
2651
2758
  }
2652
- if (!fs19.existsSync(getCmdToolsPath(config))) {
2759
+ if (!fs20.existsSync(getCmdToolsPath(config))) {
2653
2760
  throw new CmdToolsNotInstalledError(getCmdToolsPath(config));
2654
2761
  }
2655
2762
  const hvigorw = getHvigorwPath(config, projectDir);
2763
+ if (!fs20.existsSync(hvigorw)) {
2764
+ throw new OniroError(`hvigorw wrapper not found at ${hvigorw}.`);
2765
+ }
2656
2766
  const args = buildHvigorwArgs(opts);
2657
2767
  if (process.platform !== "win32") {
2658
2768
  try {
2659
- fs19.chmodSync(hvigorw, 493);
2769
+ fs20.chmodSync(hvigorw, 493);
2660
2770
  } catch {
2661
2771
  }
2662
2772
  }
@@ -2670,26 +2780,29 @@ function runHvigorw(opts) {
2670
2780
  return;
2671
2781
  }
2672
2782
  logger.info(`[build] ${hvigorw} ${args.join(" ")}`);
2673
- const child = spawn5(hvigorw, args, { cwd: projectDir, env, shell: false });
2783
+ let child;
2784
+ try {
2785
+ child = spawnCompat(hvigorw, args, { cwd: projectDir, env });
2786
+ } catch (err) {
2787
+ reject(new OniroError(`Failed to start hvigorw: ${err instanceof Error ? err.message : String(err)}`, err));
2788
+ return;
2789
+ }
2674
2790
  let settled = false;
2675
2791
  const cleanup = () => opts.abortSignal?.removeEventListener("abort", onAbort);
2676
2792
  const onAbort = () => {
2677
2793
  if (settled) return;
2678
2794
  settled = true;
2679
2795
  cleanup();
2680
- try {
2681
- child.kill("SIGKILL");
2682
- } catch {
2683
- }
2796
+ killProcessTree(child);
2684
2797
  reject(new CancelledError("hvigorw build cancelled."));
2685
2798
  };
2686
2799
  opts.abortSignal?.addEventListener("abort", onAbort, { once: true });
2687
- child.stdout.on("data", (chunk) => {
2800
+ child.stdout?.on("data", (chunk) => {
2688
2801
  const text = chunk.toString();
2689
2802
  opts.onOutput?.(text, "stdout");
2690
2803
  logger.info(text.trimEnd());
2691
2804
  });
2692
- child.stderr.on("data", (chunk) => {
2805
+ child.stderr?.on("data", (chunk) => {
2693
2806
  const text = chunk.toString();
2694
2807
  opts.onOutput?.(text, "stderr");
2695
2808
  logger.warn(text.trimEnd());
@@ -2712,8 +2825,8 @@ function runHvigorw(opts) {
2712
2825
  }
2713
2826
 
2714
2827
  // src/build/discoverHaps.ts
2715
- import * as fs20 from "fs";
2716
- import * as path20 from "path";
2828
+ import * as fs21 from "fs";
2829
+ import * as path21 from "path";
2717
2830
  function moduleOfHap(hapFileName) {
2718
2831
  const dash = hapFileName.indexOf("-");
2719
2832
  return dash > 0 ? hapFileName.slice(0, dash) : hapFileName.replace(/\.hap$/i, "");
@@ -2724,9 +2837,9 @@ async function discoverHaps(opts) {
2724
2837
  const result = {};
2725
2838
  const walk = async (dir, depth) => {
2726
2839
  if (depth > maxDepth) return;
2727
- const entries = await fs20.promises.readdir(dir, { withFileTypes: true }).catch(() => []);
2840
+ const entries = await fs21.promises.readdir(dir, { withFileTypes: true }).catch(() => []);
2728
2841
  for (const e of entries) {
2729
- const full = path20.join(dir, e.name);
2842
+ const full = path21.join(dir, e.name);
2730
2843
  if (e.isDirectory()) {
2731
2844
  if (e.name === "node_modules" || e.name === "oh_modules" || e.name === ".git") continue;
2732
2845
  await walk(full, depth + 1);
@@ -2743,8 +2856,8 @@ async function discoverHaps(opts) {
2743
2856
  }
2744
2857
 
2745
2858
  // src/build/ohpm.ts
2746
- import * as fs21 from "fs";
2747
- import * as path21 from "path";
2859
+ import * as fs22 from "fs";
2860
+ import * as path22 from "path";
2748
2861
  function runOhpm(opts) {
2749
2862
  return runProcess({
2750
2863
  command: getOhpmPath(opts.config),
@@ -2757,7 +2870,7 @@ function runOhpm(opts) {
2757
2870
  });
2758
2871
  }
2759
2872
  async function ensureOhModules(opts) {
2760
- if (fs21.existsSync(path21.join(opts.projectDir, "oh_modules"))) {
2873
+ if (fs22.existsSync(path22.join(opts.projectDir, "oh_modules"))) {
2761
2874
  return { installed: false };
2762
2875
  }
2763
2876
  const res = await runOhpm({
@@ -2809,10 +2922,11 @@ async function buildHap(opts) {
2809
2922
  const logger = scopedLogger(opts.logger ?? noopLogger, "build");
2810
2923
  const warnings = [];
2811
2924
  const start = Date.now();
2925
+ const projectDir = toLongPath(opts.projectDir);
2812
2926
  if (opts.autoInstallDeps !== false) {
2813
2927
  const r = await ensureOhModules({
2814
2928
  config: opts.config,
2815
- projectDir: opts.projectDir,
2929
+ projectDir,
2816
2930
  abortSignal: opts.abortSignal,
2817
2931
  onOutput: opts.onOutput,
2818
2932
  logger: opts.logger
@@ -2821,7 +2935,7 @@ async function buildHap(opts) {
2821
2935
  }
2822
2936
  const { exitCode } = await runHvigorw({
2823
2937
  config: opts.config,
2824
- projectDir: opts.projectDir,
2938
+ projectDir,
2825
2939
  product: opts.product,
2826
2940
  module: opts.module,
2827
2941
  buildMode: opts.mode,
@@ -2831,7 +2945,7 @@ async function buildHap(opts) {
2831
2945
  onOutput: opts.onOutput,
2832
2946
  logger: opts.logger
2833
2947
  });
2834
- const discoveredHaps = await discoverHaps({ projectDir: opts.projectDir });
2948
+ const discoveredHaps = await discoverHaps({ projectDir });
2835
2949
  const anySigned = Object.values(discoveredHaps).some((m) => m.signed.length > 0);
2836
2950
  if (!anySigned) {
2837
2951
  warnings.push(
@@ -2852,21 +2966,21 @@ function isValidBundleName(bundleName) {
2852
2966
  }
2853
2967
 
2854
2968
  // src/project/templates.ts
2855
- import * as fs22 from "fs";
2856
- import * as path22 from "path";
2969
+ import * as fs23 from "fs";
2970
+ import * as path23 from "path";
2857
2971
  function toHumanTemplateName(folderName) {
2858
2972
  return folderName.replace(/([a-z])([A-Z])/g, "$1 $2");
2859
2973
  }
2860
2974
  function listTemplates(templateRoot) {
2861
- if (!fs22.existsSync(templateRoot)) return [];
2862
- const entries = fs22.readdirSync(templateRoot, { withFileTypes: true });
2975
+ if (!fs23.existsSync(templateRoot)) return [];
2976
+ const entries = fs23.readdirSync(templateRoot, { withFileTypes: true });
2863
2977
  return entries.filter((e) => e.isDirectory()).map((e) => {
2864
- const dir = path22.join(templateRoot, e.name);
2865
- const metaPath = path22.join(dir, "template.json");
2978
+ const dir = path23.join(templateRoot, e.name);
2979
+ const metaPath = path23.join(dir, "template.json");
2866
2980
  let meta = {};
2867
- if (fs22.existsSync(metaPath)) {
2981
+ if (fs23.existsSync(metaPath)) {
2868
2982
  try {
2869
- meta = JSON.parse(fs22.readFileSync(metaPath, "utf8"));
2983
+ meta = JSON.parse(fs23.readFileSync(metaPath, "utf8"));
2870
2984
  } catch {
2871
2985
  }
2872
2986
  }
@@ -2881,57 +2995,57 @@ function listTemplates(templateRoot) {
2881
2995
  function validateTemplateLayout(templateDir, defaultModuleName) {
2882
2996
  const required = [
2883
2997
  "build-profile.json5",
2884
- path22.join("AppScope", "app.json5"),
2885
- path22.join(defaultModuleName, "src", "main", "module.json5"),
2886
- path22.join(defaultModuleName, "oh-package.json5"),
2998
+ path23.join("AppScope", "app.json5"),
2999
+ path23.join(defaultModuleName, "src", "main", "module.json5"),
3000
+ path23.join(defaultModuleName, "oh-package.json5"),
2887
3001
  "hvigorfile.ts"
2888
3002
  ];
2889
- return required.filter((rel) => !fs22.existsSync(path22.join(templateDir, rel)));
3003
+ return required.filter((rel) => !fs23.existsSync(path23.join(templateDir, rel)));
2890
3004
  }
2891
3005
 
2892
3006
  // src/project/createScaffold.ts
2893
- import * as fs24 from "fs";
2894
- import * as path23 from "path";
3007
+ import * as fs25 from "fs";
3008
+ import * as path24 from "path";
2895
3009
 
2896
3010
  // src/project/jsonHelpers.ts
2897
- import * as fs23 from "fs";
3011
+ import * as fs24 from "fs";
2898
3012
  import JSON54 from "json5";
2899
3013
  function readJson5File(filePath) {
2900
- return JSON54.parse(fs23.readFileSync(filePath, "utf8"));
3014
+ return JSON54.parse(fs24.readFileSync(filePath, "utf8"));
2901
3015
  }
2902
3016
  function writeJson5File(filePath, value) {
2903
- fs23.writeFileSync(filePath, JSON.stringify(value, null, 2) + "\n", "utf8");
3017
+ fs24.writeFileSync(filePath, JSON.stringify(value, null, 2) + "\n", "utf8");
2904
3018
  }
2905
3019
  function readJsonFile(filePath) {
2906
- return JSON.parse(fs23.readFileSync(filePath, "utf8"));
3020
+ return JSON.parse(fs24.readFileSync(filePath, "utf8"));
2907
3021
  }
2908
3022
  function writeJsonFile(filePath, value) {
2909
- fs23.writeFileSync(filePath, JSON.stringify(value, null, 2) + "\n", "utf8");
3023
+ fs24.writeFileSync(filePath, JSON.stringify(value, null, 2) + "\n", "utf8");
2910
3024
  }
2911
3025
 
2912
3026
  // src/project/createScaffold.ts
2913
3027
  var IGNORED_DIRS = /* @__PURE__ */ new Set(["oh_modules", "node_modules", "build", ".hvigor"]);
2914
3028
  async function pathExists(p) {
2915
3029
  try {
2916
- await fs24.promises.access(p);
3030
+ await fs25.promises.access(p);
2917
3031
  return true;
2918
3032
  } catch {
2919
3033
  return false;
2920
3034
  }
2921
3035
  }
2922
3036
  async function copyDirRecursive(srcDir, destDir) {
2923
- await fs24.promises.mkdir(destDir, { recursive: true });
2924
- const entries = await fs24.promises.readdir(srcDir, { withFileTypes: true });
3037
+ await fs25.promises.mkdir(destDir, { recursive: true });
3038
+ const entries = await fs25.promises.readdir(srcDir, { withFileTypes: true });
2925
3039
  for (const entry of entries) {
2926
- const src = path23.join(srcDir, entry.name);
2927
- const dest = path23.join(destDir, entry.name);
3040
+ const src = path24.join(srcDir, entry.name);
3041
+ const dest = path24.join(destDir, entry.name);
2928
3042
  if (entry.isDirectory()) {
2929
3043
  await copyDirRecursive(src, dest);
2930
3044
  } else if (entry.isSymbolicLink()) {
2931
3045
  continue;
2932
3046
  } else if (entry.isFile()) {
2933
3047
  if (entry.name === "template.json") continue;
2934
- await fs24.promises.copyFile(src, dest);
3048
+ await fs25.promises.copyFile(src, dest);
2935
3049
  }
2936
3050
  }
2937
3051
  }
@@ -2939,9 +3053,9 @@ async function normalizeJson5ToJson(projectDir, logger) {
2939
3053
  const stack = [projectDir];
2940
3054
  while (stack.length > 0) {
2941
3055
  const currentDir = stack.pop();
2942
- const entries = await fs24.promises.readdir(currentDir, { withFileTypes: true });
3056
+ const entries = await fs25.promises.readdir(currentDir, { withFileTypes: true });
2943
3057
  for (const entry of entries) {
2944
- const fullPath = path23.join(currentDir, entry.name);
3058
+ const fullPath = path24.join(currentDir, entry.name);
2945
3059
  if (entry.isDirectory()) {
2946
3060
  if (IGNORED_DIRS.has(entry.name)) continue;
2947
3061
  stack.push(fullPath);
@@ -2963,23 +3077,23 @@ function toJavaPropertiesPath(p) {
2963
3077
  function createOrUpdateLocalProperties(projectDir, sdkDir) {
2964
3078
  const content = `sdk.dir=${toJavaPropertiesPath(sdkDir)}
2965
3079
  `;
2966
- fs24.writeFileSync(path23.join(projectDir, "local.properties"), content, "utf8");
3080
+ fs25.writeFileSync(path24.join(projectDir, "local.properties"), content, "utf8");
2967
3081
  }
2968
3082
  function renameIfExists(fromPath, toPath) {
2969
- if (fs24.existsSync(fromPath) && fromPath !== toPath) {
2970
- fs24.renameSync(fromPath, toPath);
3083
+ if (fs25.existsSync(fromPath) && fromPath !== toPath) {
3084
+ fs25.renameSync(fromPath, toPath);
2971
3085
  }
2972
3086
  }
2973
3087
  function updateTemplateConfigs(projectDir, args, sdkBaseDir) {
2974
- const appJsonPath = path23.join(projectDir, "AppScope", "app.json5");
2975
- if (fs24.existsSync(appJsonPath)) {
3088
+ const appJsonPath = path24.join(projectDir, "AppScope", "app.json5");
3089
+ if (fs25.existsSync(appJsonPath)) {
2976
3090
  const appJson = readJson5File(appJsonPath);
2977
3091
  appJson.app = appJson.app ?? {};
2978
3092
  appJson.app.bundleName = args.bundleName;
2979
3093
  writeJson5File(appJsonPath, appJson);
2980
3094
  }
2981
- const stringsPath = path23.join(projectDir, "AppScope", "resources", "base", "element", "string.json");
2982
- if (fs24.existsSync(stringsPath)) {
3095
+ const stringsPath = path24.join(projectDir, "AppScope", "resources", "base", "element", "string.json");
3096
+ if (fs25.existsSync(stringsPath)) {
2983
3097
  const strings = readJsonFile(stringsPath);
2984
3098
  if (Array.isArray(strings.string)) {
2985
3099
  const appName = strings.string.find((s) => s.name === "app_name");
@@ -2987,8 +3101,8 @@ function updateTemplateConfigs(projectDir, args, sdkBaseDir) {
2987
3101
  }
2988
3102
  writeJsonFile(stringsPath, strings);
2989
3103
  }
2990
- const buildProfilePath = path23.join(projectDir, "build-profile.json5");
2991
- if (fs24.existsSync(buildProfilePath)) {
3104
+ const buildProfilePath = path24.join(projectDir, "build-profile.json5");
3105
+ if (fs25.existsSync(buildProfilePath)) {
2992
3106
  const buildProfile = readJson5File(buildProfilePath);
2993
3107
  buildProfile.app = buildProfile.app ?? {};
2994
3108
  if (Array.isArray(buildProfile.app.products) && buildProfile.app.products.length > 0) {
@@ -3001,23 +3115,23 @@ function updateTemplateConfigs(projectDir, args, sdkBaseDir) {
3001
3115
  }
3002
3116
  writeJson5File(buildProfilePath, buildProfile);
3003
3117
  }
3004
- const moduleJsonPath = path23.join(projectDir, args.moduleName, "src", "main", "module.json5");
3005
- if (fs24.existsSync(moduleJsonPath)) {
3118
+ const moduleJsonPath = path24.join(projectDir, args.moduleName, "src", "main", "module.json5");
3119
+ if (fs25.existsSync(moduleJsonPath)) {
3006
3120
  const moduleJson = readJson5File(moduleJsonPath);
3007
3121
  moduleJson.module = moduleJson.module ?? {};
3008
3122
  moduleJson.module.name = args.moduleName;
3009
3123
  writeJson5File(moduleJsonPath, moduleJson);
3010
3124
  }
3011
- const moduleOhPackagePath = path23.join(projectDir, args.moduleName, "oh-package.json5");
3012
- if (fs24.existsSync(moduleOhPackagePath)) {
3125
+ const moduleOhPackagePath = path24.join(projectDir, args.moduleName, "oh-package.json5");
3126
+ if (fs25.existsSync(moduleOhPackagePath)) {
3013
3127
  const pkg = readJson5File(moduleOhPackagePath);
3014
3128
  pkg.name = args.moduleName;
3015
3129
  writeJson5File(moduleOhPackagePath, pkg);
3016
3130
  }
3017
- const vscodeDir = path23.join(projectDir, ".vscode");
3018
- fs24.mkdirSync(vscodeDir, { recursive: true });
3131
+ const vscodeDir = path24.join(projectDir, ".vscode");
3132
+ fs25.mkdirSync(vscodeDir, { recursive: true });
3019
3133
  const hapPath = `${args.moduleName}/build/default/outputs/default/${args.moduleName}-default-signed.hap`;
3020
- writeJsonFile(path23.join(vscodeDir, "settings.json"), {
3134
+ writeJsonFile(path24.join(vscodeDir, "settings.json"), {
3021
3135
  "oniro.hapPath": hapPath,
3022
3136
  "files.associations": { "*.json5": "jsonc" }
3023
3137
  });
@@ -3034,7 +3148,7 @@ async function createScaffold(opts) {
3034
3148
  if (!opts.location || !await pathExists(opts.location)) {
3035
3149
  throw new OniroError(`Location does not exist: ${opts.location}`);
3036
3150
  }
3037
- const templateDir = path23.join(opts.templateRoot, opts.templateId);
3151
+ const templateDir = path24.join(opts.templateRoot, opts.templateId);
3038
3152
  if (!await pathExists(templateDir)) {
3039
3153
  throw new OniroError(`Template not found: ${templateDir}`);
3040
3154
  }
@@ -3047,17 +3161,17 @@ async function createScaffold(opts) {
3047
3161
  throw new OniroError(`Template '${opts.templateId}' is missing required files:
3048
3162
  - ${missing.join("\n- ")}`);
3049
3163
  }
3050
- const projectDir = path23.join(opts.location, opts.projectName);
3164
+ const projectDir = path24.join(opts.location, opts.projectName);
3051
3165
  if (await pathExists(projectDir)) {
3052
3166
  if (!opts.overwrite) {
3053
3167
  throw new OniroError(`Destination already exists: ${projectDir}. Pass overwrite: true to replace it.`);
3054
3168
  }
3055
- await fs24.promises.rm(projectDir, { recursive: true, force: true });
3169
+ await fs25.promises.rm(projectDir, { recursive: true, force: true });
3056
3170
  }
3057
3171
  logger.info(`[create] Scaffolding ${opts.templateId} at ${projectDir}`);
3058
3172
  await copyDirRecursive(templateDir, projectDir);
3059
3173
  if (moduleName !== defaultModuleName) {
3060
- renameIfExists(path23.join(projectDir, defaultModuleName), path23.join(projectDir, moduleName));
3174
+ renameIfExists(path24.join(projectDir, defaultModuleName), path24.join(projectDir, moduleName));
3061
3175
  }
3062
3176
  updateTemplateConfigs(
3063
3177
  projectDir,
@@ -3074,11 +3188,11 @@ async function createScaffold(opts) {
3074
3188
  }
3075
3189
 
3076
3190
  // src/project/listModules.ts
3077
- import * as fs25 from "fs";
3078
- import * as path24 from "path";
3191
+ import * as fs26 from "fs";
3192
+ import * as path25 from "path";
3079
3193
  function listModules(opts) {
3080
- const buildProfile = path24.join(opts.projectDir, "build-profile.json5");
3081
- if (!fs25.existsSync(buildProfile)) {
3194
+ const buildProfile = path25.join(opts.projectDir, "build-profile.json5");
3195
+ if (!fs26.existsSync(buildProfile)) {
3082
3196
  throw new OniroError(`build-profile.json5 not found at ${buildProfile}`);
3083
3197
  }
3084
3198
  const parsed = readJson5File(buildProfile);
@@ -3090,12 +3204,12 @@ function listModules(opts) {
3090
3204
  }
3091
3205
 
3092
3206
  // src/project/abilities.ts
3093
- import * as fs26 from "fs";
3094
- import * as path25 from "path";
3207
+ import * as fs27 from "fs";
3208
+ import * as path26 from "path";
3095
3209
  function listAbilities(opts) {
3096
3210
  const moduleName = opts.moduleName ?? "entry";
3097
- const moduleJsonPath = path25.join(opts.projectDir, moduleName, "src", "main", "module.json5");
3098
- if (!fs26.existsSync(moduleJsonPath)) {
3211
+ const moduleJsonPath = path26.join(opts.projectDir, moduleName, "src", "main", "module.json5");
3212
+ if (!fs27.existsSync(moduleJsonPath)) {
3099
3213
  throw new OniroError(`Could not find module.json5 at ${moduleJsonPath}`);
3100
3214
  }
3101
3215
  const parsed = readJson5File(moduleJsonPath);
@@ -3103,12 +3217,12 @@ function listAbilities(opts) {
3103
3217
  }
3104
3218
 
3105
3219
  // src/project/prepareSigning.ts
3106
- import * as fs27 from "fs";
3107
- import * as path26 from "path";
3220
+ import * as fs28 from "fs";
3221
+ import * as path27 from "path";
3108
3222
  function hasSigningArtifacts(dir) {
3109
- if (!fs27.existsSync(path26.join(dir, "signatures"))) return false;
3110
- const buildProfile = path26.join(dir, "build-profile.json5");
3111
- if (!fs27.existsSync(buildProfile)) return false;
3223
+ if (!fs28.existsSync(path27.join(dir, "signatures"))) return false;
3224
+ const buildProfile = path27.join(dir, "build-profile.json5");
3225
+ if (!fs28.existsSync(buildProfile)) return false;
3112
3226
  try {
3113
3227
  const parsed = readJson5File(buildProfile);
3114
3228
  return Array.isArray(parsed.app?.signingConfigs) && parsed.app.signingConfigs.length > 0;
@@ -3198,8 +3312,8 @@ async function diffHapAssets(opts) {
3198
3312
  }
3199
3313
 
3200
3314
  // src/install/apply.ts
3201
- import * as fs28 from "fs";
3202
- import * as path27 from "path";
3315
+ import * as fs29 from "fs";
3316
+ import * as path28 from "path";
3203
3317
  function decideInstallMethod(s) {
3204
3318
  if (!s.signInfoInconsistent) return "replace";
3205
3319
  if (s.isSystemBundle && !s.allowUninstall) return "refuse";
@@ -3220,7 +3334,7 @@ async function pidOf(config, bundle, deviceSerial) {
3220
3334
  async function resolveHapPath(opts) {
3221
3335
  let hapPath = opts.hapPath;
3222
3336
  if (hapPath) {
3223
- if (!path27.isAbsolute(hapPath) && opts.projectDir) hapPath = path27.join(opts.projectDir, hapPath);
3337
+ if (!path28.isAbsolute(hapPath) && opts.projectDir) hapPath = path28.join(opts.projectDir, hapPath);
3224
3338
  } else {
3225
3339
  if (!opts.projectDir) throw new OniroError("applyChanges requires hapPath or projectDir.");
3226
3340
  const haps = await discoverHaps({ projectDir: opts.projectDir });
@@ -3239,7 +3353,7 @@ async function resolveHapPath(opts) {
3239
3353
  hapPath = withSigned[0][1].signed[0];
3240
3354
  }
3241
3355
  }
3242
- if (!fs28.existsSync(hapPath)) throw new OniroError(`HAP not found at ${hapPath}.`);
3356
+ if (!fs29.existsSync(hapPath)) throw new OniroError(`HAP not found at ${hapPath}.`);
3243
3357
  return hapPath;
3244
3358
  }
3245
3359
  async function applyChanges(opts) {
@@ -3249,7 +3363,7 @@ async function applyChanges(opts) {
3249
3363
  const allowUninstall = opts.allowUninstall ?? false;
3250
3364
  const hapPath = await resolveHapPath(opts);
3251
3365
  let assetsChanged = false;
3252
- if (opts.installedHapPath && fs28.existsSync(opts.installedHapPath)) {
3366
+ if (opts.installedHapPath && fs29.existsSync(opts.installedHapPath)) {
3253
3367
  try {
3254
3368
  const diff = await diffHapAssets({ installedHap: opts.installedHapPath, newHap: hapPath });
3255
3369
  assetsChanged = diff.addedAssetPaths.length > 0 || diff.removedAssetPaths.length > 0;
@@ -3291,7 +3405,7 @@ async function applyChanges(opts) {
3291
3405
  }
3292
3406
 
3293
3407
  // src/index.ts
3294
- var VERSION = "0.8.0";
3408
+ var VERSION = "0.9.0";
3295
3409
  export {
3296
3410
  ALL_SDKS,
3297
3411
  APL_VALUES,
@@ -3310,6 +3424,7 @@ export {
3310
3424
  VERSION,
3311
3425
  applyChanges,
3312
3426
  attemptHdcConnection,
3427
+ buildCmdWrapper,
3313
3428
  buildGestureCommands,
3314
3429
  buildGestureUitestCommand,
3315
3430
  buildHap,
@@ -3346,6 +3461,8 @@ export {
3346
3461
  ensureOhModules,
3347
3462
  ensureOk,
3348
3463
  ensureRemoteOk,
3464
+ escapeCmdArgument,
3465
+ escapeCmdCommand,
3349
3466
  extractTarball,
3350
3467
  extractZipWithProgress,
3351
3468
  findAppProcessId,
@@ -3384,12 +3501,14 @@ export {
3384
3501
  isRamBackedPath,
3385
3502
  isValidBundleName,
3386
3503
  isValidProjectName,
3504
+ killProcessTree,
3387
3505
  launchApp,
3388
3506
  listAbilities,
3389
3507
  listDevices,
3390
3508
  listModules,
3391
3509
  listRunningProcesses,
3392
3510
  listTemplates,
3511
+ needsCmdWrapper,
3393
3512
  nonInteractivePrompter,
3394
3513
  noopLogger,
3395
3514
  noopProgress,
@@ -3426,11 +3545,13 @@ export {
3426
3545
  setHilogLevel,
3427
3546
  shell,
3428
3547
  shellChecked,
3548
+ spawnCompat,
3429
3549
  startEmulator,
3430
3550
  staticConfig,
3431
3551
  stopEmulator,
3432
3552
  streamHilog,
3433
3553
  takeScreenshot,
3554
+ toLongPath,
3434
3555
  toSpaceError,
3435
3556
  uninstallApp,
3436
3557
  validateTemplateLayout,