@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.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
  }
@@ -337,7 +344,7 @@ function createInstallTempDir(opts) {
337
344
  } catch (err) {
338
345
  const fallback = os4.tmpdir();
339
346
  if (requested || isOutOfSpaceError(err) || path4.resolve(fallback) === path4.resolve(root)) throw err;
340
- const reason = err instanceof Error ? err.message.split("\n")[0] : String(err);
347
+ const reason = err instanceof Error ? err.message.split(/\r?\n/)[0] : String(err);
341
348
  (opts.logger ?? noopLogger).warn(`${reason} Falling back to '${fallback}'.`);
342
349
  return { dir: createTempWorkDir(fallback, opts.prefix), root: fallback };
343
350
  }
@@ -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,11 +1965,16 @@ 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
- const result = await hdcExec({ config: opts.config, args: ["install", hapPath], timeoutMs: 3e5 });
1972
+ const result = await hdcExec({
1973
+ config: opts.config,
1974
+ args: ["install", hapPath],
1975
+ deviceSerial: opts.deviceSerial,
1976
+ timeoutMs: 3e5
1977
+ });
1879
1978
  if (result.stdout.trim()) logger.info(result.stdout.trim());
1880
1979
  if (result.stderr.trim()) logger.warn(result.stderr.trim());
1881
1980
  ensureOk(result, `hdc install ${hapPath}`);
@@ -1957,11 +2056,17 @@ function listRunningProcesses(config, options = {}) {
1957
2056
  const timeoutMs = options.timeoutMs ?? 1e3;
1958
2057
  const hdc = getHdcPath(config);
1959
2058
  return new Promise((resolve4, reject) => {
1960
- const proc = spawn3(hdc, ["track-jpid"]);
2059
+ let proc;
2060
+ try {
2061
+ proc = spawnCompat(hdc, ["track-jpid"]);
2062
+ } catch (err) {
2063
+ reject(new OniroError(`Failed to spawn ${hdc}: ${err instanceof Error ? err.message : String(err)}`, err));
2064
+ return;
2065
+ }
1961
2066
  const processes = [];
1962
2067
  let resolved = false;
1963
- proc.stdout.on("data", (data) => {
1964
- for (const line of data.toString().split("\n").filter(Boolean)) {
2068
+ proc.stdout?.on("data", (data) => {
2069
+ for (const line of data.toString().split(/\r?\n/).filter(Boolean)) {
1965
2070
  const match = line.match(/^(\d+)\s+(.+)$/);
1966
2071
  if (!match) continue;
1967
2072
  const pid = match[1];
@@ -1969,13 +2074,13 @@ function listRunningProcesses(config, options = {}) {
1969
2074
  processes.push({ pid, name });
1970
2075
  if (options.targetProcessName && name === options.targetProcessName && !resolved) {
1971
2076
  resolved = true;
1972
- proc.kill();
2077
+ killProcessTree(proc);
1973
2078
  resolve4(pid);
1974
2079
  return;
1975
2080
  }
1976
2081
  }
1977
2082
  });
1978
- proc.stderr.on("data", (data) => {
2083
+ proc.stderr?.on("data", (data) => {
1979
2084
  logger.warn(`[hdc track-jpid] ${data.toString()}`);
1980
2085
  });
1981
2086
  proc.on("error", (err) => {
@@ -1987,7 +2092,7 @@ function listRunningProcesses(config, options = {}) {
1987
2092
  const timeout = setTimeout(() => {
1988
2093
  if (resolved) return;
1989
2094
  resolved = true;
1990
- proc.kill();
2095
+ killProcessTree(proc);
1991
2096
  if (options.targetProcessName) {
1992
2097
  reject(new OniroError(`Could not find process for bundle: ${options.targetProcessName}`));
1993
2098
  } else {
@@ -2145,12 +2250,12 @@ async function reboot(opts) {
2145
2250
  }
2146
2251
 
2147
2252
  // src/hdc/screen.ts
2148
- import * as fs17 from "fs";
2253
+ import * as fs18 from "fs";
2149
2254
  import * as os8 from "os";
2150
- import * as path17 from "path";
2255
+ import * as path18 from "path";
2151
2256
  import { randomBytes as randomBytes2 } from "crypto";
2152
2257
  function tmpName(ext) {
2153
- return path17.join(os8.tmpdir(), `oniro_screen_${randomBytes2(6).toString("hex")}.${ext}`);
2258
+ return path18.join(os8.tmpdir(), `oniro_screen_${randomBytes2(6).toString("hex")}.${ext}`);
2154
2259
  }
2155
2260
  async function takeScreenshot(opts) {
2156
2261
  const remote = "/data/local/tmp/oniro_screenshot.jpeg";
@@ -2160,11 +2265,11 @@ async function takeScreenshot(opts) {
2160
2265
  const local = tmpName("jpeg");
2161
2266
  try {
2162
2267
  await recvFile({ config: opts.config, remote, local, deviceSerial: opts.deviceSerial, timeoutMs });
2163
- const pixels = await fs17.promises.readFile(local);
2268
+ const pixels = await fs18.promises.readFile(local);
2164
2269
  const size = await getDisplaySize(opts.config, { deviceSerial: opts.deviceSerial }) ?? { width: 0, height: 0 };
2165
2270
  return { pixels, width: size.width, height: size.height };
2166
2271
  } finally {
2167
- fs17.promises.unlink(local).catch(() => {
2272
+ fs18.promises.unlink(local).catch(() => {
2168
2273
  });
2169
2274
  void shell({ config: opts.config, command: `rm -f ${remote}`, deviceSerial: opts.deviceSerial }).catch(() => {
2170
2275
  });
@@ -2183,9 +2288,9 @@ async function captureBurst(opts) {
2183
2288
  const local = tmpName(`burst_${i}.jpeg`);
2184
2289
  try {
2185
2290
  await recvFile({ config: opts.config, remote: `${remoteDir}/f${i}.jpeg`, local, deviceSerial: opts.deviceSerial });
2186
- frames.push(await fs17.promises.readFile(local));
2291
+ frames.push(await fs18.promises.readFile(local));
2187
2292
  } finally {
2188
- fs17.promises.unlink(local).catch(() => {
2293
+ fs18.promises.unlink(local).catch(() => {
2189
2294
  });
2190
2295
  }
2191
2296
  }
@@ -2311,9 +2416,9 @@ async function sendRawTouch(opts) {
2311
2416
  }
2312
2417
 
2313
2418
  // src/hdc/dump.ts
2314
- import * as fs18 from "fs";
2419
+ import * as fs19 from "fs";
2315
2420
  import * as os9 from "os";
2316
- import * as path18 from "path";
2421
+ import * as path19 from "path";
2317
2422
  import { randomBytes as randomBytes3 } from "crypto";
2318
2423
  function parseBounds(s) {
2319
2424
  const m = /^\[(-?\d+),(-?\d+)\]\[(-?\d+),(-?\d+)\]$/.exec(s ?? "");
@@ -2398,10 +2503,10 @@ async function dumpLayout(opts) {
2398
2503
  if (!m) throw new OniroError(`Could not parse dumpLayout output:
2399
2504
  ${dump.stdout}`);
2400
2505
  const remote = m[1];
2401
- const local = path18.join(os9.tmpdir(), `oniro_layout_${randomBytes3(6).toString("hex")}.json`);
2506
+ const local = path19.join(os9.tmpdir(), `oniro_layout_${randomBytes3(6).toString("hex")}.json`);
2402
2507
  try {
2403
2508
  await recvFile({ config: opts.config, remote, local, deviceSerial: opts.deviceSerial, timeoutMs });
2404
- const parsed = JSON.parse(await fs18.promises.readFile(local, "utf8"));
2509
+ const parsed = JSON.parse(await fs19.promises.readFile(local, "utf8"));
2405
2510
  const display = await getDisplaySize(opts.config, { deviceSerial: opts.deviceSerial });
2406
2511
  if (opts.prune === false) {
2407
2512
  return { display, tree: parsed };
@@ -2410,7 +2515,7 @@ ${dump.stdout}`);
2410
2515
  const tree = Array.isArray(prunedRaw) ? { children: prunedRaw } : prunedRaw ?? { children: [] };
2411
2516
  return { display, tree };
2412
2517
  } finally {
2413
- fs18.promises.unlink(local).catch(() => {
2518
+ fs19.promises.unlink(local).catch(() => {
2414
2519
  });
2415
2520
  void shell({ config: opts.config, command: `rm -f ${remote}`, deviceSerial: opts.deviceSerial }).catch(() => {
2416
2521
  });
@@ -2418,14 +2523,21 @@ ${dump.stdout}`);
2418
2523
  }
2419
2524
 
2420
2525
  // src/hdc/hilog.ts
2421
- import { spawn as spawn4 } from "child_process";
2422
2526
  function setHilogLevel(opts) {
2423
2527
  const logger = opts.logger ?? noopLogger;
2424
2528
  const hdc = getHdcPath(opts.config);
2425
2529
  return new Promise((resolve4, reject) => {
2426
- const child = spawn4(hdc, ["shell", "hilog", "-b", opts.level]);
2530
+ let child;
2531
+ try {
2532
+ child = spawnCompat(hdc, ["shell", "hilog", "-b", opts.level]);
2533
+ } catch (err) {
2534
+ const msg = err instanceof Error ? err.message : String(err);
2535
+ logger.error(`[hilog] failed to spawn hdc: ${msg}`);
2536
+ reject(err instanceof Error ? err : new Error(msg));
2537
+ return;
2538
+ }
2427
2539
  let stderr = "";
2428
- child.stderr.on("data", (chunk) => {
2540
+ child.stderr?.on("data", (chunk) => {
2429
2541
  stderr += chunk.toString();
2430
2542
  });
2431
2543
  child.once("error", (err) => {
@@ -2454,7 +2566,7 @@ function streamHilog(opts) {
2454
2566
  if (opts.domain !== void 0 && `${opts.domain}` !== "") {
2455
2567
  args.push("-D", `${opts.domain}`);
2456
2568
  }
2457
- return spawn4(hdc, args);
2569
+ return spawnCompat(hdc, args);
2458
2570
  }
2459
2571
  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
2572
  function parseHilogLine(line) {
@@ -2500,10 +2612,7 @@ function waitForLog(opts) {
2500
2612
  if (reconnectTimer) clearTimeout(reconnectTimer);
2501
2613
  opts.abortSignal?.removeEventListener("abort", onAbort);
2502
2614
  if (child) {
2503
- try {
2504
- child.kill("SIGKILL");
2505
- } catch {
2506
- }
2615
+ killProcessTree(child);
2507
2616
  child = null;
2508
2617
  }
2509
2618
  };
@@ -2519,6 +2628,7 @@ function waitForLog(opts) {
2519
2628
  cleanup();
2520
2629
  reject(err);
2521
2630
  };
2631
+ const failWith = (err) => fail(err instanceof Error ? err : new Error(String(err)));
2522
2632
  const onAbort = () => fail(new CancelledError("waitForLog cancelled."));
2523
2633
  const timer = setTimeout(
2524
2634
  () => fail(new OniroError(`No log line matched ${String(opts.pattern)} within ${opts.timeoutMs}ms.`)),
@@ -2533,10 +2643,9 @@ function waitForLog(opts) {
2533
2643
  child = stream;
2534
2644
  stream.stdout.on("data", (d) => {
2535
2645
  buf += d.toString();
2536
- let idx;
2537
- while ((idx = buf.indexOf("\n")) >= 0) {
2538
- const line = buf.slice(0, idx);
2539
- buf = buf.slice(idx + 1);
2646
+ const lines = buf.split(/\r?\n/);
2647
+ buf = lines.pop() ?? "";
2648
+ for (const line of lines) {
2540
2649
  const entry = parseHilogLine(line);
2541
2650
  if (entry && opts.pattern.test(entryText(entry))) {
2542
2651
  finish(entry);
@@ -2548,13 +2657,13 @@ function waitForLog(opts) {
2548
2657
  if (settled || child !== stream) return;
2549
2658
  child = null;
2550
2659
  if (Date.now() < deadline) {
2551
- reconnectTimer = setTimeout(() => void spawnStream(), 1e3);
2660
+ reconnectTimer = setTimeout(() => void spawnStream().catch(failWith), 1e3);
2552
2661
  }
2553
2662
  };
2554
2663
  stream.once("close", onEnd);
2555
2664
  stream.once("error", onEnd);
2556
2665
  };
2557
- void spawnStream();
2666
+ void spawnStream().catch(failWith);
2558
2667
  });
2559
2668
  }
2560
2669
  function watchLog(opts) {
@@ -2567,10 +2676,7 @@ function watchLog(opts) {
2567
2676
  clearTimeout(timer);
2568
2677
  opts.abortSignal?.removeEventListener("abort", onAbort);
2569
2678
  if (child) {
2570
- try {
2571
- child.kill("SIGKILL");
2572
- } catch {
2573
- }
2679
+ killProcessTree(child);
2574
2680
  child = null;
2575
2681
  }
2576
2682
  };
@@ -2595,15 +2701,19 @@ function watchLog(opts) {
2595
2701
  child = stream;
2596
2702
  stream.stdout.on("data", (d) => {
2597
2703
  buf += d.toString();
2598
- let idx;
2599
- while ((idx = buf.indexOf("\n")) >= 0) {
2600
- const line = buf.slice(0, idx);
2601
- buf = buf.slice(idx + 1);
2704
+ const lines = buf.split(/\r?\n/);
2705
+ buf = lines.pop() ?? "";
2706
+ for (const line of lines) {
2602
2707
  const entry = parseHilogLine(line);
2603
2708
  if (entry && opts.pattern.test(entryText(entry))) matches.push(entry);
2604
2709
  }
2605
2710
  });
2606
- })();
2711
+ })().catch((err) => {
2712
+ if (settled) return;
2713
+ settled = true;
2714
+ cleanup();
2715
+ reject(err instanceof Error ? err : new Error(String(err)));
2716
+ });
2607
2717
  });
2608
2718
  }
2609
2719
  async function dumpLog(opts) {
@@ -2626,13 +2736,12 @@ async function dumpLog(opts) {
2626
2736
  timeoutMs: opts.timeoutMs ?? 3e4,
2627
2737
  logger: opts.logger
2628
2738
  });
2629
- return res.stdout.split("\n").map((line) => parseHilogLine(line)).filter((e) => e !== null);
2739
+ return res.stdout.split(/\r?\n/).map((line) => parseHilogLine(line)).filter((e) => e !== null);
2630
2740
  }
2631
2741
 
2632
2742
  // src/build/runHvigorw.ts
2633
- import * as fs19 from "fs";
2634
- import * as path19 from "path";
2635
- import { spawn as spawn5 } from "child_process";
2743
+ import * as fs20 from "fs";
2744
+ import * as path20 from "path";
2636
2745
  function buildHvigorwArgs(opts) {
2637
2746
  const args = [opts.task ?? "assembleHap", "--mode", "module"];
2638
2747
  args.push("-p", `product=${opts.product ?? "default"}`);
@@ -2644,19 +2753,23 @@ function buildHvigorwArgs(opts) {
2644
2753
  return args;
2645
2754
  }
2646
2755
  function runHvigorw(opts) {
2647
- const { config, projectDir } = opts;
2756
+ const { config } = opts;
2757
+ const projectDir = toLongPath(opts.projectDir);
2648
2758
  const logger = opts.logger ?? noopLogger;
2649
- if (!fs19.existsSync(path19.join(projectDir, "build-profile.json5"))) {
2759
+ if (!fs20.existsSync(path20.join(projectDir, "build-profile.json5"))) {
2650
2760
  throw new OniroError(`Not an OpenHarmony project: ${projectDir} (build-profile.json5 not found).`);
2651
2761
  }
2652
- if (!fs19.existsSync(getCmdToolsPath(config))) {
2762
+ if (!fs20.existsSync(getCmdToolsPath(config))) {
2653
2763
  throw new CmdToolsNotInstalledError(getCmdToolsPath(config));
2654
2764
  }
2655
2765
  const hvigorw = getHvigorwPath(config, projectDir);
2766
+ if (!fs20.existsSync(hvigorw)) {
2767
+ throw new OniroError(`hvigorw wrapper not found at ${hvigorw}.`);
2768
+ }
2656
2769
  const args = buildHvigorwArgs(opts);
2657
2770
  if (process.platform !== "win32") {
2658
2771
  try {
2659
- fs19.chmodSync(hvigorw, 493);
2772
+ fs20.chmodSync(hvigorw, 493);
2660
2773
  } catch {
2661
2774
  }
2662
2775
  }
@@ -2670,26 +2783,29 @@ function runHvigorw(opts) {
2670
2783
  return;
2671
2784
  }
2672
2785
  logger.info(`[build] ${hvigorw} ${args.join(" ")}`);
2673
- const child = spawn5(hvigorw, args, { cwd: projectDir, env, shell: false });
2786
+ let child;
2787
+ try {
2788
+ child = spawnCompat(hvigorw, args, { cwd: projectDir, env });
2789
+ } catch (err) {
2790
+ reject(new OniroError(`Failed to start hvigorw: ${err instanceof Error ? err.message : String(err)}`, err));
2791
+ return;
2792
+ }
2674
2793
  let settled = false;
2675
2794
  const cleanup = () => opts.abortSignal?.removeEventListener("abort", onAbort);
2676
2795
  const onAbort = () => {
2677
2796
  if (settled) return;
2678
2797
  settled = true;
2679
2798
  cleanup();
2680
- try {
2681
- child.kill("SIGKILL");
2682
- } catch {
2683
- }
2799
+ killProcessTree(child);
2684
2800
  reject(new CancelledError("hvigorw build cancelled."));
2685
2801
  };
2686
2802
  opts.abortSignal?.addEventListener("abort", onAbort, { once: true });
2687
- child.stdout.on("data", (chunk) => {
2803
+ child.stdout?.on("data", (chunk) => {
2688
2804
  const text = chunk.toString();
2689
2805
  opts.onOutput?.(text, "stdout");
2690
2806
  logger.info(text.trimEnd());
2691
2807
  });
2692
- child.stderr.on("data", (chunk) => {
2808
+ child.stderr?.on("data", (chunk) => {
2693
2809
  const text = chunk.toString();
2694
2810
  opts.onOutput?.(text, "stderr");
2695
2811
  logger.warn(text.trimEnd());
@@ -2712,8 +2828,8 @@ function runHvigorw(opts) {
2712
2828
  }
2713
2829
 
2714
2830
  // src/build/discoverHaps.ts
2715
- import * as fs20 from "fs";
2716
- import * as path20 from "path";
2831
+ import * as fs21 from "fs";
2832
+ import * as path21 from "path";
2717
2833
  function moduleOfHap(hapFileName) {
2718
2834
  const dash = hapFileName.indexOf("-");
2719
2835
  return dash > 0 ? hapFileName.slice(0, dash) : hapFileName.replace(/\.hap$/i, "");
@@ -2724,9 +2840,9 @@ async function discoverHaps(opts) {
2724
2840
  const result = {};
2725
2841
  const walk = async (dir, depth) => {
2726
2842
  if (depth > maxDepth) return;
2727
- const entries = await fs20.promises.readdir(dir, { withFileTypes: true }).catch(() => []);
2843
+ const entries = await fs21.promises.readdir(dir, { withFileTypes: true }).catch(() => []);
2728
2844
  for (const e of entries) {
2729
- const full = path20.join(dir, e.name);
2845
+ const full = path21.join(dir, e.name);
2730
2846
  if (e.isDirectory()) {
2731
2847
  if (e.name === "node_modules" || e.name === "oh_modules" || e.name === ".git") continue;
2732
2848
  await walk(full, depth + 1);
@@ -2743,8 +2859,8 @@ async function discoverHaps(opts) {
2743
2859
  }
2744
2860
 
2745
2861
  // src/build/ohpm.ts
2746
- import * as fs21 from "fs";
2747
- import * as path21 from "path";
2862
+ import * as fs22 from "fs";
2863
+ import * as path22 from "path";
2748
2864
  function runOhpm(opts) {
2749
2865
  return runProcess({
2750
2866
  command: getOhpmPath(opts.config),
@@ -2757,7 +2873,7 @@ function runOhpm(opts) {
2757
2873
  });
2758
2874
  }
2759
2875
  async function ensureOhModules(opts) {
2760
- if (fs21.existsSync(path21.join(opts.projectDir, "oh_modules"))) {
2876
+ if (fs22.existsSync(path22.join(opts.projectDir, "oh_modules"))) {
2761
2877
  return { installed: false };
2762
2878
  }
2763
2879
  const res = await runOhpm({
@@ -2809,10 +2925,11 @@ async function buildHap(opts) {
2809
2925
  const logger = scopedLogger(opts.logger ?? noopLogger, "build");
2810
2926
  const warnings = [];
2811
2927
  const start = Date.now();
2928
+ const projectDir = toLongPath(opts.projectDir);
2812
2929
  if (opts.autoInstallDeps !== false) {
2813
2930
  const r = await ensureOhModules({
2814
2931
  config: opts.config,
2815
- projectDir: opts.projectDir,
2932
+ projectDir,
2816
2933
  abortSignal: opts.abortSignal,
2817
2934
  onOutput: opts.onOutput,
2818
2935
  logger: opts.logger
@@ -2821,7 +2938,7 @@ async function buildHap(opts) {
2821
2938
  }
2822
2939
  const { exitCode } = await runHvigorw({
2823
2940
  config: opts.config,
2824
- projectDir: opts.projectDir,
2941
+ projectDir,
2825
2942
  product: opts.product,
2826
2943
  module: opts.module,
2827
2944
  buildMode: opts.mode,
@@ -2831,7 +2948,7 @@ async function buildHap(opts) {
2831
2948
  onOutput: opts.onOutput,
2832
2949
  logger: opts.logger
2833
2950
  });
2834
- const discoveredHaps = await discoverHaps({ projectDir: opts.projectDir });
2951
+ const discoveredHaps = await discoverHaps({ projectDir });
2835
2952
  const anySigned = Object.values(discoveredHaps).some((m) => m.signed.length > 0);
2836
2953
  if (!anySigned) {
2837
2954
  warnings.push(
@@ -2852,21 +2969,21 @@ function isValidBundleName(bundleName) {
2852
2969
  }
2853
2970
 
2854
2971
  // src/project/templates.ts
2855
- import * as fs22 from "fs";
2856
- import * as path22 from "path";
2972
+ import * as fs23 from "fs";
2973
+ import * as path23 from "path";
2857
2974
  function toHumanTemplateName(folderName) {
2858
2975
  return folderName.replace(/([a-z])([A-Z])/g, "$1 $2");
2859
2976
  }
2860
2977
  function listTemplates(templateRoot) {
2861
- if (!fs22.existsSync(templateRoot)) return [];
2862
- const entries = fs22.readdirSync(templateRoot, { withFileTypes: true });
2978
+ if (!fs23.existsSync(templateRoot)) return [];
2979
+ const entries = fs23.readdirSync(templateRoot, { withFileTypes: true });
2863
2980
  return entries.filter((e) => e.isDirectory()).map((e) => {
2864
- const dir = path22.join(templateRoot, e.name);
2865
- const metaPath = path22.join(dir, "template.json");
2981
+ const dir = path23.join(templateRoot, e.name);
2982
+ const metaPath = path23.join(dir, "template.json");
2866
2983
  let meta = {};
2867
- if (fs22.existsSync(metaPath)) {
2984
+ if (fs23.existsSync(metaPath)) {
2868
2985
  try {
2869
- meta = JSON.parse(fs22.readFileSync(metaPath, "utf8"));
2986
+ meta = JSON.parse(fs23.readFileSync(metaPath, "utf8"));
2870
2987
  } catch {
2871
2988
  }
2872
2989
  }
@@ -2881,57 +2998,57 @@ function listTemplates(templateRoot) {
2881
2998
  function validateTemplateLayout(templateDir, defaultModuleName) {
2882
2999
  const required = [
2883
3000
  "build-profile.json5",
2884
- path22.join("AppScope", "app.json5"),
2885
- path22.join(defaultModuleName, "src", "main", "module.json5"),
2886
- path22.join(defaultModuleName, "oh-package.json5"),
3001
+ path23.join("AppScope", "app.json5"),
3002
+ path23.join(defaultModuleName, "src", "main", "module.json5"),
3003
+ path23.join(defaultModuleName, "oh-package.json5"),
2887
3004
  "hvigorfile.ts"
2888
3005
  ];
2889
- return required.filter((rel) => !fs22.existsSync(path22.join(templateDir, rel)));
3006
+ return required.filter((rel) => !fs23.existsSync(path23.join(templateDir, rel)));
2890
3007
  }
2891
3008
 
2892
3009
  // src/project/createScaffold.ts
2893
- import * as fs24 from "fs";
2894
- import * as path23 from "path";
3010
+ import * as fs25 from "fs";
3011
+ import * as path24 from "path";
2895
3012
 
2896
3013
  // src/project/jsonHelpers.ts
2897
- import * as fs23 from "fs";
3014
+ import * as fs24 from "fs";
2898
3015
  import JSON54 from "json5";
2899
3016
  function readJson5File(filePath) {
2900
- return JSON54.parse(fs23.readFileSync(filePath, "utf8"));
3017
+ return JSON54.parse(fs24.readFileSync(filePath, "utf8"));
2901
3018
  }
2902
3019
  function writeJson5File(filePath, value) {
2903
- fs23.writeFileSync(filePath, JSON.stringify(value, null, 2) + "\n", "utf8");
3020
+ fs24.writeFileSync(filePath, JSON.stringify(value, null, 2) + "\n", "utf8");
2904
3021
  }
2905
3022
  function readJsonFile(filePath) {
2906
- return JSON.parse(fs23.readFileSync(filePath, "utf8"));
3023
+ return JSON.parse(fs24.readFileSync(filePath, "utf8"));
2907
3024
  }
2908
3025
  function writeJsonFile(filePath, value) {
2909
- fs23.writeFileSync(filePath, JSON.stringify(value, null, 2) + "\n", "utf8");
3026
+ fs24.writeFileSync(filePath, JSON.stringify(value, null, 2) + "\n", "utf8");
2910
3027
  }
2911
3028
 
2912
3029
  // src/project/createScaffold.ts
2913
3030
  var IGNORED_DIRS = /* @__PURE__ */ new Set(["oh_modules", "node_modules", "build", ".hvigor"]);
2914
3031
  async function pathExists(p) {
2915
3032
  try {
2916
- await fs24.promises.access(p);
3033
+ await fs25.promises.access(p);
2917
3034
  return true;
2918
3035
  } catch {
2919
3036
  return false;
2920
3037
  }
2921
3038
  }
2922
3039
  async function copyDirRecursive(srcDir, destDir) {
2923
- await fs24.promises.mkdir(destDir, { recursive: true });
2924
- const entries = await fs24.promises.readdir(srcDir, { withFileTypes: true });
3040
+ await fs25.promises.mkdir(destDir, { recursive: true });
3041
+ const entries = await fs25.promises.readdir(srcDir, { withFileTypes: true });
2925
3042
  for (const entry of entries) {
2926
- const src = path23.join(srcDir, entry.name);
2927
- const dest = path23.join(destDir, entry.name);
3043
+ const src = path24.join(srcDir, entry.name);
3044
+ const dest = path24.join(destDir, entry.name);
2928
3045
  if (entry.isDirectory()) {
2929
3046
  await copyDirRecursive(src, dest);
2930
3047
  } else if (entry.isSymbolicLink()) {
2931
3048
  continue;
2932
3049
  } else if (entry.isFile()) {
2933
3050
  if (entry.name === "template.json") continue;
2934
- await fs24.promises.copyFile(src, dest);
3051
+ await fs25.promises.copyFile(src, dest);
2935
3052
  }
2936
3053
  }
2937
3054
  }
@@ -2939,9 +3056,9 @@ async function normalizeJson5ToJson(projectDir, logger) {
2939
3056
  const stack = [projectDir];
2940
3057
  while (stack.length > 0) {
2941
3058
  const currentDir = stack.pop();
2942
- const entries = await fs24.promises.readdir(currentDir, { withFileTypes: true });
3059
+ const entries = await fs25.promises.readdir(currentDir, { withFileTypes: true });
2943
3060
  for (const entry of entries) {
2944
- const fullPath = path23.join(currentDir, entry.name);
3061
+ const fullPath = path24.join(currentDir, entry.name);
2945
3062
  if (entry.isDirectory()) {
2946
3063
  if (IGNORED_DIRS.has(entry.name)) continue;
2947
3064
  stack.push(fullPath);
@@ -2963,23 +3080,23 @@ function toJavaPropertiesPath(p) {
2963
3080
  function createOrUpdateLocalProperties(projectDir, sdkDir) {
2964
3081
  const content = `sdk.dir=${toJavaPropertiesPath(sdkDir)}
2965
3082
  `;
2966
- fs24.writeFileSync(path23.join(projectDir, "local.properties"), content, "utf8");
3083
+ fs25.writeFileSync(path24.join(projectDir, "local.properties"), content, "utf8");
2967
3084
  }
2968
3085
  function renameIfExists(fromPath, toPath) {
2969
- if (fs24.existsSync(fromPath) && fromPath !== toPath) {
2970
- fs24.renameSync(fromPath, toPath);
3086
+ if (fs25.existsSync(fromPath) && fromPath !== toPath) {
3087
+ fs25.renameSync(fromPath, toPath);
2971
3088
  }
2972
3089
  }
2973
3090
  function updateTemplateConfigs(projectDir, args, sdkBaseDir) {
2974
- const appJsonPath = path23.join(projectDir, "AppScope", "app.json5");
2975
- if (fs24.existsSync(appJsonPath)) {
3091
+ const appJsonPath = path24.join(projectDir, "AppScope", "app.json5");
3092
+ if (fs25.existsSync(appJsonPath)) {
2976
3093
  const appJson = readJson5File(appJsonPath);
2977
3094
  appJson.app = appJson.app ?? {};
2978
3095
  appJson.app.bundleName = args.bundleName;
2979
3096
  writeJson5File(appJsonPath, appJson);
2980
3097
  }
2981
- const stringsPath = path23.join(projectDir, "AppScope", "resources", "base", "element", "string.json");
2982
- if (fs24.existsSync(stringsPath)) {
3098
+ const stringsPath = path24.join(projectDir, "AppScope", "resources", "base", "element", "string.json");
3099
+ if (fs25.existsSync(stringsPath)) {
2983
3100
  const strings = readJsonFile(stringsPath);
2984
3101
  if (Array.isArray(strings.string)) {
2985
3102
  const appName = strings.string.find((s) => s.name === "app_name");
@@ -2987,8 +3104,8 @@ function updateTemplateConfigs(projectDir, args, sdkBaseDir) {
2987
3104
  }
2988
3105
  writeJsonFile(stringsPath, strings);
2989
3106
  }
2990
- const buildProfilePath = path23.join(projectDir, "build-profile.json5");
2991
- if (fs24.existsSync(buildProfilePath)) {
3107
+ const buildProfilePath = path24.join(projectDir, "build-profile.json5");
3108
+ if (fs25.existsSync(buildProfilePath)) {
2992
3109
  const buildProfile = readJson5File(buildProfilePath);
2993
3110
  buildProfile.app = buildProfile.app ?? {};
2994
3111
  if (Array.isArray(buildProfile.app.products) && buildProfile.app.products.length > 0) {
@@ -3001,23 +3118,23 @@ function updateTemplateConfigs(projectDir, args, sdkBaseDir) {
3001
3118
  }
3002
3119
  writeJson5File(buildProfilePath, buildProfile);
3003
3120
  }
3004
- const moduleJsonPath = path23.join(projectDir, args.moduleName, "src", "main", "module.json5");
3005
- if (fs24.existsSync(moduleJsonPath)) {
3121
+ const moduleJsonPath = path24.join(projectDir, args.moduleName, "src", "main", "module.json5");
3122
+ if (fs25.existsSync(moduleJsonPath)) {
3006
3123
  const moduleJson = readJson5File(moduleJsonPath);
3007
3124
  moduleJson.module = moduleJson.module ?? {};
3008
3125
  moduleJson.module.name = args.moduleName;
3009
3126
  writeJson5File(moduleJsonPath, moduleJson);
3010
3127
  }
3011
- const moduleOhPackagePath = path23.join(projectDir, args.moduleName, "oh-package.json5");
3012
- if (fs24.existsSync(moduleOhPackagePath)) {
3128
+ const moduleOhPackagePath = path24.join(projectDir, args.moduleName, "oh-package.json5");
3129
+ if (fs25.existsSync(moduleOhPackagePath)) {
3013
3130
  const pkg = readJson5File(moduleOhPackagePath);
3014
3131
  pkg.name = args.moduleName;
3015
3132
  writeJson5File(moduleOhPackagePath, pkg);
3016
3133
  }
3017
- const vscodeDir = path23.join(projectDir, ".vscode");
3018
- fs24.mkdirSync(vscodeDir, { recursive: true });
3134
+ const vscodeDir = path24.join(projectDir, ".vscode");
3135
+ fs25.mkdirSync(vscodeDir, { recursive: true });
3019
3136
  const hapPath = `${args.moduleName}/build/default/outputs/default/${args.moduleName}-default-signed.hap`;
3020
- writeJsonFile(path23.join(vscodeDir, "settings.json"), {
3137
+ writeJsonFile(path24.join(vscodeDir, "settings.json"), {
3021
3138
  "oniro.hapPath": hapPath,
3022
3139
  "files.associations": { "*.json5": "jsonc" }
3023
3140
  });
@@ -3034,7 +3151,7 @@ async function createScaffold(opts) {
3034
3151
  if (!opts.location || !await pathExists(opts.location)) {
3035
3152
  throw new OniroError(`Location does not exist: ${opts.location}`);
3036
3153
  }
3037
- const templateDir = path23.join(opts.templateRoot, opts.templateId);
3154
+ const templateDir = path24.join(opts.templateRoot, opts.templateId);
3038
3155
  if (!await pathExists(templateDir)) {
3039
3156
  throw new OniroError(`Template not found: ${templateDir}`);
3040
3157
  }
@@ -3047,17 +3164,17 @@ async function createScaffold(opts) {
3047
3164
  throw new OniroError(`Template '${opts.templateId}' is missing required files:
3048
3165
  - ${missing.join("\n- ")}`);
3049
3166
  }
3050
- const projectDir = path23.join(opts.location, opts.projectName);
3167
+ const projectDir = path24.join(opts.location, opts.projectName);
3051
3168
  if (await pathExists(projectDir)) {
3052
3169
  if (!opts.overwrite) {
3053
3170
  throw new OniroError(`Destination already exists: ${projectDir}. Pass overwrite: true to replace it.`);
3054
3171
  }
3055
- await fs24.promises.rm(projectDir, { recursive: true, force: true });
3172
+ await fs25.promises.rm(projectDir, { recursive: true, force: true });
3056
3173
  }
3057
3174
  logger.info(`[create] Scaffolding ${opts.templateId} at ${projectDir}`);
3058
3175
  await copyDirRecursive(templateDir, projectDir);
3059
3176
  if (moduleName !== defaultModuleName) {
3060
- renameIfExists(path23.join(projectDir, defaultModuleName), path23.join(projectDir, moduleName));
3177
+ renameIfExists(path24.join(projectDir, defaultModuleName), path24.join(projectDir, moduleName));
3061
3178
  }
3062
3179
  updateTemplateConfigs(
3063
3180
  projectDir,
@@ -3074,11 +3191,11 @@ async function createScaffold(opts) {
3074
3191
  }
3075
3192
 
3076
3193
  // src/project/listModules.ts
3077
- import * as fs25 from "fs";
3078
- import * as path24 from "path";
3194
+ import * as fs26 from "fs";
3195
+ import * as path25 from "path";
3079
3196
  function listModules(opts) {
3080
- const buildProfile = path24.join(opts.projectDir, "build-profile.json5");
3081
- if (!fs25.existsSync(buildProfile)) {
3197
+ const buildProfile = path25.join(opts.projectDir, "build-profile.json5");
3198
+ if (!fs26.existsSync(buildProfile)) {
3082
3199
  throw new OniroError(`build-profile.json5 not found at ${buildProfile}`);
3083
3200
  }
3084
3201
  const parsed = readJson5File(buildProfile);
@@ -3090,12 +3207,12 @@ function listModules(opts) {
3090
3207
  }
3091
3208
 
3092
3209
  // src/project/abilities.ts
3093
- import * as fs26 from "fs";
3094
- import * as path25 from "path";
3210
+ import * as fs27 from "fs";
3211
+ import * as path26 from "path";
3095
3212
  function listAbilities(opts) {
3096
3213
  const moduleName = opts.moduleName ?? "entry";
3097
- const moduleJsonPath = path25.join(opts.projectDir, moduleName, "src", "main", "module.json5");
3098
- if (!fs26.existsSync(moduleJsonPath)) {
3214
+ const moduleJsonPath = path26.join(opts.projectDir, moduleName, "src", "main", "module.json5");
3215
+ if (!fs27.existsSync(moduleJsonPath)) {
3099
3216
  throw new OniroError(`Could not find module.json5 at ${moduleJsonPath}`);
3100
3217
  }
3101
3218
  const parsed = readJson5File(moduleJsonPath);
@@ -3103,12 +3220,12 @@ function listAbilities(opts) {
3103
3220
  }
3104
3221
 
3105
3222
  // src/project/prepareSigning.ts
3106
- import * as fs27 from "fs";
3107
- import * as path26 from "path";
3223
+ import * as fs28 from "fs";
3224
+ import * as path27 from "path";
3108
3225
  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;
3226
+ if (!fs28.existsSync(path27.join(dir, "signatures"))) return false;
3227
+ const buildProfile = path27.join(dir, "build-profile.json5");
3228
+ if (!fs28.existsSync(buildProfile)) return false;
3112
3229
  try {
3113
3230
  const parsed = readJson5File(buildProfile);
3114
3231
  return Array.isArray(parsed.app?.signingConfigs) && parsed.app.signingConfigs.length > 0;
@@ -3198,8 +3315,8 @@ async function diffHapAssets(opts) {
3198
3315
  }
3199
3316
 
3200
3317
  // src/install/apply.ts
3201
- import * as fs28 from "fs";
3202
- import * as path27 from "path";
3318
+ import * as fs29 from "fs";
3319
+ import * as path28 from "path";
3203
3320
  function decideInstallMethod(s) {
3204
3321
  if (!s.signInfoInconsistent) return "replace";
3205
3322
  if (s.isSystemBundle && !s.allowUninstall) return "refuse";
@@ -3220,7 +3337,7 @@ async function pidOf(config, bundle, deviceSerial) {
3220
3337
  async function resolveHapPath(opts) {
3221
3338
  let hapPath = opts.hapPath;
3222
3339
  if (hapPath) {
3223
- if (!path27.isAbsolute(hapPath) && opts.projectDir) hapPath = path27.join(opts.projectDir, hapPath);
3340
+ if (!path28.isAbsolute(hapPath) && opts.projectDir) hapPath = path28.join(opts.projectDir, hapPath);
3224
3341
  } else {
3225
3342
  if (!opts.projectDir) throw new OniroError("applyChanges requires hapPath or projectDir.");
3226
3343
  const haps = await discoverHaps({ projectDir: opts.projectDir });
@@ -3239,7 +3356,7 @@ async function resolveHapPath(opts) {
3239
3356
  hapPath = withSigned[0][1].signed[0];
3240
3357
  }
3241
3358
  }
3242
- if (!fs28.existsSync(hapPath)) throw new OniroError(`HAP not found at ${hapPath}.`);
3359
+ if (!fs29.existsSync(hapPath)) throw new OniroError(`HAP not found at ${hapPath}.`);
3243
3360
  return hapPath;
3244
3361
  }
3245
3362
  async function applyChanges(opts) {
@@ -3249,7 +3366,7 @@ async function applyChanges(opts) {
3249
3366
  const allowUninstall = opts.allowUninstall ?? false;
3250
3367
  const hapPath = await resolveHapPath(opts);
3251
3368
  let assetsChanged = false;
3252
- if (opts.installedHapPath && fs28.existsSync(opts.installedHapPath)) {
3369
+ if (opts.installedHapPath && fs29.existsSync(opts.installedHapPath)) {
3253
3370
  try {
3254
3371
  const diff = await diffHapAssets({ installedHap: opts.installedHapPath, newHap: hapPath });
3255
3372
  assetsChanged = diff.addedAssetPaths.length > 0 || diff.removedAssetPaths.length > 0;
@@ -3291,7 +3408,7 @@ async function applyChanges(opts) {
3291
3408
  }
3292
3409
 
3293
3410
  // src/index.ts
3294
- var VERSION = "0.8.0";
3411
+ var VERSION = "0.10.0";
3295
3412
  export {
3296
3413
  ALL_SDKS,
3297
3414
  APL_VALUES,
@@ -3310,6 +3427,7 @@ export {
3310
3427
  VERSION,
3311
3428
  applyChanges,
3312
3429
  attemptHdcConnection,
3430
+ buildCmdWrapper,
3313
3431
  buildGestureCommands,
3314
3432
  buildGestureUitestCommand,
3315
3433
  buildHap,
@@ -3346,6 +3464,8 @@ export {
3346
3464
  ensureOhModules,
3347
3465
  ensureOk,
3348
3466
  ensureRemoteOk,
3467
+ escapeCmdArgument,
3468
+ escapeCmdCommand,
3349
3469
  extractTarball,
3350
3470
  extractZipWithProgress,
3351
3471
  findAppProcessId,
@@ -3384,12 +3504,14 @@ export {
3384
3504
  isRamBackedPath,
3385
3505
  isValidBundleName,
3386
3506
  isValidProjectName,
3507
+ killProcessTree,
3387
3508
  launchApp,
3388
3509
  listAbilities,
3389
3510
  listDevices,
3390
3511
  listModules,
3391
3512
  listRunningProcesses,
3392
3513
  listTemplates,
3514
+ needsCmdWrapper,
3393
3515
  nonInteractivePrompter,
3394
3516
  noopLogger,
3395
3517
  noopProgress,
@@ -3426,11 +3548,13 @@ export {
3426
3548
  setHilogLevel,
3427
3549
  shell,
3428
3550
  shellChecked,
3551
+ spawnCompat,
3429
3552
  startEmulator,
3430
3553
  staticConfig,
3431
3554
  stopEmulator,
3432
3555
  streamHilog,
3433
3556
  takeScreenshot,
3557
+ toLongPath,
3434
3558
  toSpaceError,
3435
3559
  uninstallApp,
3436
3560
  validateTemplateLayout,