@oniroproject/core 0.15.1 → 0.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -45,6 +45,7 @@ __export(index_exports, {
45
45
  OHOS_DAILY_BUILDS_URL: () => OHOS_DAILY_BUILDS_URL,
46
46
  OHOS_URL_BASE: () => OHOS_URL_BASE,
47
47
  OniroError: () => OniroError,
48
+ SIGNED_HAP_CHUNK_SIZE: () => SIGNED_HAP_CHUNK_SIZE,
48
49
  SIGNING_ERRORS: () => SIGNING_ERRORS,
49
50
  SdkNotInstalledError: () => SdkNotInstalledError,
50
51
  TMP_DIR_HINT: () => TMP_DIR_HINT,
@@ -80,6 +81,7 @@ __export(index_exports, {
80
81
  dedupEntries: () => dedupEntries,
81
82
  defaultContext: () => defaultContext,
82
83
  defaultPaths: () => defaultPaths,
84
+ defaultSignedHapPath: () => defaultSignedHapPath,
83
85
  describeLayoutNode: () => describeLayoutNode,
84
86
  detectAaStartFailure: () => detectAaStartFailure,
85
87
  detectCmdToolsFlavour: () => detectCmdToolsFlavour,
@@ -146,6 +148,7 @@ __export(index_exports, {
146
148
  getSupportedSdksForUi: () => getSupportedSdksForUi,
147
149
  harmonyOsAutoSign: () => harmonyOsAutoSign,
148
150
  harmonyOsSdkLabelForApi: () => harmonyOsSdkLabelForApi,
151
+ harmonyOsSignHap: () => harmonyOsSignHap,
149
152
  hdcClientFailure: () => hdcClientFailure,
150
153
  hdcExec: () => hdcExec,
151
154
  hdcTargetArgs: () => hdcTargetArgs,
@@ -199,6 +202,7 @@ __export(index_exports, {
199
202
  prepareSigning: () => prepareSigning,
200
203
  pruneLayout: () => pruneLayout,
201
204
  readHapBundleName: () => readHapBundleName,
205
+ readHapSigningInfo: () => readHapSigningInfo,
202
206
  readJson5File: () => readJson5File,
203
207
  readJsonFile: () => readJsonFile,
204
208
  readProvisionProfile: () => readProvisionProfile,
@@ -226,6 +230,7 @@ __export(index_exports, {
226
230
  setHilogLevel: () => setHilogLevel,
227
231
  shell: () => shell,
228
232
  shellChecked: () => shellChecked,
233
+ signOpenHarmonyHap: () => signOpenHarmonyHap,
229
234
  spawnCompat: () => spawnCompat,
230
235
  startEmulator: () => startEmulator,
231
236
  staticConfig: () => staticConfig,
@@ -245,6 +250,7 @@ __export(index_exports, {
245
250
  waitForTarget: () => waitForTarget,
246
251
  watchLog: () => watchLog,
247
252
  waypointsToRawEvents: () => waypointsToRawEvents,
253
+ withHapZip: () => withHapZip,
248
254
  withoutWindow: () => withoutWindow,
249
255
  writeJson5File: () => writeJson5File,
250
256
  writeJsonFile: () => writeJsonFile
@@ -834,14 +840,14 @@ var pipelineAsync = (0, import_node_util.promisify)(import_node_stream.pipeline)
834
840
  async function downloadFile(opts) {
835
841
  const { url, dest, progress, abortSignal } = opts;
836
842
  const proto = url.startsWith("https") ? https : http;
837
- return new Promise((resolve7, reject) => {
843
+ return new Promise((resolve9, reject) => {
838
844
  const s = Math.max(0, Math.min(100, opts.start ?? 0));
839
845
  const r = Math.max(0, Math.min(100 - s, opts.range ?? 100));
840
846
  let settled = false;
841
847
  const done = (err) => {
842
848
  if (settled) return;
843
849
  settled = true;
844
- err ? reject(err) : resolve7();
850
+ err ? reject(err) : resolve9();
845
851
  };
846
852
  if (abortSignal?.aborted) {
847
853
  done(new CancelledError("Download cancelled before start."));
@@ -1019,15 +1025,26 @@ async function extractZipWithProgress(opts) {
1019
1025
  async function extractTarball(tarPath, dest, strip = 0, filter) {
1020
1026
  await tar.x({ file: tarPath, cwd: dest, strip, ...filter ? { filter: (p) => filter(p) } : {} });
1021
1027
  }
1022
- async function readZipEntry(zipPath, match) {
1023
- const zip = new import_node_stream_zip.default.async({ file: zipPath });
1028
+ var SIGNED_HAP_CHUNK_SIZE = 128 * 1024;
1029
+ async function withHapZip(zipPath, use) {
1030
+ const zip = new import_node_stream_zip.default({ file: zipPath, chunkSize: SIGNED_HAP_CHUNK_SIZE, storeEntries: true });
1031
+ const close = () => new Promise((resolve9) => zip.close(() => resolve9()));
1024
1032
  try {
1025
- const entry = Object.values(await zip.entries()).find((e) => !e.isDirectory && match(e.name));
1026
- return entry ? await zip.entryData(entry) : void 0;
1033
+ await new Promise((resolve9, reject) => {
1034
+ zip.once("ready", () => resolve9());
1035
+ zip.once("error", (err) => reject(err));
1036
+ });
1037
+ return await use({ entries: () => zip.entries(), entryData: (name) => zip.entryDataSync(name) });
1027
1038
  } finally {
1028
- await zip.close();
1039
+ await close();
1029
1040
  }
1030
1041
  }
1042
+ function readZipEntry(zipPath, match) {
1043
+ return withHapZip(zipPath, (zip) => {
1044
+ const entry = Object.values(zip.entries()).find((e) => !e.isDirectory && match(e.name));
1045
+ return entry ? zip.entryData(entry.name) : void 0;
1046
+ });
1047
+ }
1031
1048
 
1032
1049
  // src/sdk/list.ts
1033
1050
  var fs7 = __toESM(require("fs"), 1);
@@ -1678,9 +1695,16 @@ var SIGNING_KIND_CONFIG = {
1678
1695
  certBasename: "OpenHarmonyApplication.cer"
1679
1696
  }
1680
1697
  };
1681
- function copyFilesToProject(projectDir, paths, kind, logger, applicationCertPath) {
1682
- logger.info("[sign] Copying signing material into project...");
1683
- const signaturesDir = path14.join(projectDir, "signatures");
1698
+ function sdkSigningFiles(sdkPath) {
1699
+ return {
1700
+ signTool: path14.join(sdkPath, "toolchains/lib/hap-sign-tool.jar"),
1701
+ keystore: path14.join(sdkPath, "toolchains/lib/OpenHarmony.p12"),
1702
+ profileCert: path14.join(sdkPath, "toolchains/lib/OpenHarmonyProfileRelease.pem"),
1703
+ unsignedProfileTemplate: path14.join(sdkPath, "toolchains/lib/UnsgnedReleasedProfileTemplate.json")
1704
+ };
1705
+ }
1706
+ function copySigningFiles(signaturesDir, paths, kind, logger, applicationCertPath) {
1707
+ logger.info(`[sign] Copying signing material into ${signaturesDir}...`);
1684
1708
  fs13.mkdirSync(signaturesDir, { recursive: true });
1685
1709
  fs13.copyFileSync(paths.keystore, path14.join(signaturesDir, "OpenHarmony.p12"));
1686
1710
  fs13.copyFileSync(paths.profileCert, path14.join(signaturesDir, "OpenHarmonyProfileRelease.pem"));
@@ -1707,10 +1731,7 @@ function extractLeafCertificate(chainPath) {
1707
1731
  return parts[2].trim() + "\n-----END CERTIFICATE-----\n";
1708
1732
  }
1709
1733
  function modifyProfileTemplate(projectDir, apl, appFeature, logger, options = {}) {
1710
- const kind = options.kind ?? pickSigningKind(apl);
1711
- logger.info(`[sign] Modifying profile template (apl=${apl}, app-feature=${appFeature}, kind=${kind})...`);
1712
1734
  const appJsonPath = path14.join(projectDir, "AppScope/app.json5");
1713
- const profileTemplatePath = path14.join(projectDir, "signatures/UnsgnedReleasedProfileTemplate.json");
1714
1735
  if (!fs13.existsSync(appJsonPath)) {
1715
1736
  throw new OniroError(`${appJsonPath} does not exist.`);
1716
1737
  }
@@ -1720,26 +1741,28 @@ function modifyProfileTemplate(projectDir, apl, appFeature, logger, options = {}
1720
1741
  } catch (e) {
1721
1742
  throw new OniroError(`Error parsing ${appJsonPath}: ${e.message}`, e);
1722
1743
  }
1744
+ if (!appJson.app?.bundleName) {
1745
+ throw new OniroError("app.json5 does not contain app.bundleName.");
1746
+ }
1747
+ applyProfileTemplate(path14.join(projectDir, "signatures"), appJson.app.bundleName, apl, appFeature, logger, options);
1748
+ }
1749
+ function applyProfileTemplate(signaturesDir, bundleName, apl, appFeature, logger, options = {}) {
1750
+ const kind = options.kind ?? pickSigningKind(apl);
1751
+ logger.info(`[sign] Modifying profile template (apl=${apl}, app-feature=${appFeature}, kind=${kind})...`);
1752
+ const profileTemplatePath = path14.join(signaturesDir, "UnsgnedReleasedProfileTemplate.json");
1723
1753
  let profileTemplate;
1724
1754
  try {
1725
1755
  profileTemplate = JSON.parse(fs13.readFileSync(profileTemplatePath, "utf-8"));
1726
1756
  } catch (e) {
1727
1757
  throw new OniroError(`Error parsing ${profileTemplatePath}: ${e.message}`, e);
1728
1758
  }
1729
- if (!appJson.app?.bundleName) {
1730
- throw new OniroError("app.json5 does not contain app.bundleName.");
1731
- }
1732
1759
  if (!profileTemplate["bundle-info"]) {
1733
1760
  throw new OniroError("UnsgnedReleasedProfileTemplate.json is missing the bundle-info section.");
1734
1761
  }
1735
- profileTemplate["bundle-info"]["bundle-name"] = appJson.app.bundleName;
1762
+ profileTemplate["bundle-info"]["bundle-name"] = bundleName;
1736
1763
  profileTemplate["bundle-info"]["apl"] = apl;
1737
1764
  profileTemplate["bundle-info"]["app-feature"] = appFeature;
1738
- const distCertChainPath = path14.join(
1739
- projectDir,
1740
- "signatures",
1741
- SIGNING_KIND_CONFIG[kind].certBasename
1742
- );
1765
+ const distCertChainPath = path14.join(signaturesDir, SIGNING_KIND_CONFIG[kind].certBasename);
1743
1766
  profileTemplate["bundle-info"]["distribution-certificate"] = extractLeafCertificate(distCertChainPath);
1744
1767
  if (options.acls !== void 0) {
1745
1768
  profileTemplate.acls = profileTemplate.acls ?? {};
@@ -1750,9 +1773,8 @@ function modifyProfileTemplate(projectDir, apl, appFeature, logger, options = {}
1750
1773
  }
1751
1774
  fs13.writeFileSync(profileTemplatePath, JSON.stringify(profileTemplate, null, 2));
1752
1775
  }
1753
- function generateP7bFile(projectDir, paths, logger, passwords) {
1776
+ function generateP7bFile(signaturesDir, paths, logger, passwords) {
1754
1777
  logger.info("[sign] Generating P7b profile via hap-sign-tool...");
1755
- const signaturesDir = path14.join(projectDir, "signatures");
1756
1778
  const profileTemplatePath = path14.join(signaturesDir, "UnsgnedReleasedProfileTemplate.json");
1757
1779
  const outputProfilePath = path14.join(signaturesDir, "app1-profile.p7b");
1758
1780
  const args = [
@@ -1876,30 +1898,201 @@ function generateSigningConfigs(options) {
1876
1898
  if (!fs13.existsSync(sdkPath)) {
1877
1899
  throw new SdkNotInstalledError(String(sdkVersion), sdkPath, ALL_SDKS.find((s) => s.api === String(sdkVersion))?.version);
1878
1900
  }
1879
- const paths = {
1880
- signTool: path14.join(sdkPath, "toolchains/lib/hap-sign-tool.jar"),
1881
- keystore: path14.join(sdkPath, "toolchains/lib/OpenHarmony.p12"),
1882
- profileCert: path14.join(sdkPath, "toolchains/lib/OpenHarmonyProfileRelease.pem"),
1883
- unsignedProfileTemplate: path14.join(sdkPath, "toolchains/lib/UnsgnedReleasedProfileTemplate.json")
1884
- };
1901
+ const paths = sdkSigningFiles(sdkPath);
1885
1902
  const kind = pickSigningKind(apl);
1886
1903
  const passwords = options.passwords ?? DEFAULT_PASSWORDS;
1887
1904
  logger.info("[sign] Starting signing configuration generation...");
1888
- copyFilesToProject(projectDir, paths, kind, logger, options.applicationCertPath);
1905
+ const signaturesDir = path14.join(projectDir, "signatures");
1906
+ copySigningFiles(signaturesDir, paths, kind, logger, options.applicationCertPath);
1889
1907
  modifyProfileTemplate(projectDir, apl, appFeature, logger, {
1890
1908
  kind,
1891
1909
  acls: options.acls,
1892
1910
  appPrivilegeCapabilities: options.appPrivilegeCapabilities
1893
1911
  });
1894
- generateP7bFile(projectDir, paths, logger, passwords);
1912
+ generateP7bFile(signaturesDir, paths, logger, passwords);
1895
1913
  prepareMaterialDirectory(projectDir, logger);
1896
1914
  updateBuildProfile(projectDir, kind, logger, passwords);
1897
1915
  logger.info("[sign] Signing configuration generated successfully.");
1898
1916
  }
1899
1917
 
1900
- // src/emulator/install.ts
1918
+ // src/sign/hapInfo.ts
1919
+ var crypto3 = __toESM(require("crypto"), 1);
1901
1920
  var fs14 = __toESM(require("fs"), 1);
1902
1921
  var path15 = __toESM(require("path"), 1);
1922
+ function parseHapModuleJson(text, hapPath) {
1923
+ let parsed;
1924
+ try {
1925
+ parsed = JSON.parse(text.replace(/^\uFEFF/, ""));
1926
+ } catch (err) {
1927
+ throw new OniroError(`The module.json in ${hapPath} is not valid JSON: ${err.message}`, err);
1928
+ }
1929
+ const bundleName = parsed.app?.bundleName;
1930
+ if (typeof bundleName !== "string" || !bundleName) {
1931
+ throw new OniroError(`The module.json in ${hapPath} names no app.bundleName.`);
1932
+ }
1933
+ const requested = Array.isArray(parsed.module?.requestPermissions) ? parsed.module.requestPermissions : [];
1934
+ const { compileSdkType, targetAPIVersion } = parsed.app ?? {};
1935
+ return {
1936
+ bundleName,
1937
+ requestedPermissions: requested.map((p) => p?.name).filter((n) => typeof n === "string" && n.length > 0),
1938
+ compileSdkType: typeof compileSdkType === "string" && compileSdkType ? compileSdkType : void 0,
1939
+ targetApiVersion: typeof targetAPIVersion === "number" ? targetAPIVersion : void 0
1940
+ };
1941
+ }
1942
+ async function readHapSigningInfo(hapPath) {
1943
+ if (!fs14.existsSync(hapPath)) throw new OniroError(`${hapPath} does not exist.`);
1944
+ let moduleJson;
1945
+ try {
1946
+ moduleJson = await readZipEntry(hapPath, (n) => n === "module.json");
1947
+ if (!moduleJson && await readZipEntry(hapPath, (n) => n === "config.json")) {
1948
+ throw new OniroError(
1949
+ `${hapPath} is an FA-model HAP (config.json, no module.json). Only stage-model HAPs can be signed without their project.`
1950
+ );
1951
+ }
1952
+ } catch (err) {
1953
+ if (err instanceof OniroError) throw err;
1954
+ throw new OniroError(`Could not read ${hapPath} as a HAP: ${err.message}`, err);
1955
+ }
1956
+ if (!moduleJson) throw new OniroError(`${hapPath} holds no module.json, so it is not a HAP.`);
1957
+ return parseHapModuleJson(moduleJson.toString("utf8"), hapPath);
1958
+ }
1959
+ function defaultSignedHapPath(hapPath) {
1960
+ const dir = path15.dirname(hapPath);
1961
+ const name = path15.basename(hapPath);
1962
+ if (/-signed\.hap$/i.test(name)) return path15.join(dir, name.replace(/-signed\.hap$/i, "-resigned.hap"));
1963
+ return path15.join(dir, `${name.replace(/(-unsigned)?\.hap$/i, "")}-signed.hap`);
1964
+ }
1965
+ async function writeHapAtomically(outPath, sign) {
1966
+ const dir = path15.dirname(outPath);
1967
+ try {
1968
+ fs14.mkdirSync(dir, { recursive: true });
1969
+ } catch (err) {
1970
+ throw new OniroError(`Cannot create ${dir} for the signed HAP: ${err.message}`, err);
1971
+ }
1972
+ const tmpPath = path15.join(dir, `.${path15.basename(outPath)}.${crypto3.randomBytes(4).toString("hex")}.tmp`);
1973
+ try {
1974
+ await sign(tmpPath);
1975
+ fs14.renameSync(tmpPath, outPath);
1976
+ } finally {
1977
+ fs14.rmSync(tmpPath, { force: true });
1978
+ }
1979
+ }
1980
+
1981
+ // src/sign/signHap.ts
1982
+ var fs15 = __toESM(require("fs"), 1);
1983
+ var os8 = __toESM(require("os"), 1);
1984
+ var path16 = __toESM(require("path"), 1);
1985
+ var import_node_child_process4 = require("child_process");
1986
+
1987
+ // src/project/validators.ts
1988
+ function isValidProjectName(name) {
1989
+ if (!name) return false;
1990
+ if (name.includes("/") || name.includes("\\")) return false;
1991
+ return /^[A-Za-z0-9._-]+$/.test(name);
1992
+ }
1993
+ function isValidBundleName(bundleName) {
1994
+ return /^[A-Za-z][A-Za-z0-9_]*(\.[A-Za-z][A-Za-z0-9_]*)+$/.test(bundleName);
1995
+ }
1996
+
1997
+ // src/sign/signHap.ts
1998
+ function pickSigningSdk(sdkHome, targetApiVersion) {
1999
+ let apis;
2000
+ try {
2001
+ apis = fs15.readdirSync(sdkHome).filter((api2) => /^\d+$/.test(api2));
2002
+ } catch {
2003
+ return null;
2004
+ }
2005
+ const usable = apis.filter((api2) => fs15.existsSync(sdkSigningFiles(path16.join(sdkHome, api2)).signTool)).sort((a, b) => Number(b) - Number(a));
2006
+ const api = usable.find((a) => targetApiVersion !== void 0 && Number(a) === targetApiVersion) ?? usable[0];
2007
+ return api ? { api, sdkPath: path16.join(sdkHome, api) } : null;
2008
+ }
2009
+ async function signOpenHarmonyHap(opts) {
2010
+ const logger = opts.logger ?? noopLogger;
2011
+ const hapPath = path16.resolve(opts.hapPath);
2012
+ const outPath = path16.resolve(opts.outPath ?? defaultSignedHapPath(hapPath));
2013
+ const apl = opts.apl ?? "normal";
2014
+ if (!APL_VALUES.includes(apl)) {
2015
+ throw new OniroError(`Invalid apl '${apl}'. Expected one of: ${APL_VALUES.join(", ")}.`);
2016
+ }
2017
+ const appFeature = resolveAppFeature(apl, opts.appFeature);
2018
+ if (!APP_FEATURE_VALUES.includes(appFeature)) {
2019
+ throw new OniroError(`Invalid appFeature '${appFeature}'. Expected one of: ${APP_FEATURE_VALUES.join(", ")}.`);
2020
+ }
2021
+ const info = await readHapSigningInfo(hapPath);
2022
+ if (info.compileSdkType === "HarmonyOS") {
2023
+ throw new OniroError(
2024
+ `${hapPath} was built for HarmonyOS, which needs a certificate from AppGallery Connect: run \`oniro-app auth login\` once, then \`oniro-app sign --harmonyos --hap\`.`
2025
+ );
2026
+ }
2027
+ if (!isValidBundleName(info.bundleName)) {
2028
+ throw new OniroError(`${hapPath} names an invalid bundle name '${info.bundleName}'.`);
2029
+ }
2030
+ const sdk = pickSigningSdk(opts.sdkHome, info.targetApiVersion);
2031
+ if (!sdk) {
2032
+ throw new OniroError(
2033
+ `No OpenHarmony SDK with hap-sign-tool.jar under ${opts.sdkHome}; install one with \`oniro-app sdk install\`.`
2034
+ );
2035
+ }
2036
+ const files = sdkSigningFiles(sdk.sdkPath);
2037
+ const kind = pickSigningKind(apl);
2038
+ const passwords = opts.passwords ?? DEFAULT_PASSWORDS;
2039
+ logger.info(`[sign] Signing ${path16.basename(hapPath)} as ${info.bundleName} with the API ${sdk.api} SDK's material...`);
2040
+ const signaturesDir = fs15.mkdtempSync(path16.join(os8.tmpdir(), "oniro-sign-hap-"));
2041
+ try {
2042
+ copySigningFiles(signaturesDir, files, kind, logger, opts.applicationCertPath);
2043
+ applyProfileTemplate(signaturesDir, info.bundleName, apl, appFeature, logger, {
2044
+ kind,
2045
+ acls: opts.acls,
2046
+ appPrivilegeCapabilities: opts.appPrivilegeCapabilities
2047
+ });
2048
+ generateP7bFile(signaturesDir, files, logger, passwords);
2049
+ logger.info("[sign] Signing the HAP via hap-sign-tool...");
2050
+ const { hapKeyAlias, certBasename } = SIGNING_KIND_CONFIG[kind];
2051
+ await writeHapAtomically(outPath, (tmpPath) => {
2052
+ const args = [
2053
+ "-jar",
2054
+ files.signTool,
2055
+ "sign-app",
2056
+ "-mode",
2057
+ "localSign",
2058
+ "-keyAlias",
2059
+ hapKeyAlias,
2060
+ "-signAlg",
2061
+ "SHA256withECDSA",
2062
+ "-appCertFile",
2063
+ path16.join(signaturesDir, certBasename),
2064
+ "-profileFile",
2065
+ path16.join(signaturesDir, "app1-profile.p7b"),
2066
+ "-inFile",
2067
+ hapPath,
2068
+ "-outFile",
2069
+ tmpPath,
2070
+ "-keystoreFile",
2071
+ path16.join(signaturesDir, "OpenHarmony.p12"),
2072
+ "-keyPwd",
2073
+ passwords.key,
2074
+ "-keystorePwd",
2075
+ passwords.store
2076
+ ];
2077
+ try {
2078
+ (0, import_node_child_process4.execFileSync)("java", args, { stdio: "inherit" });
2079
+ } catch (e) {
2080
+ throw new OniroError(
2081
+ `hap-sign-tool failed to sign ${hapPath}. Ensure a JDK (with java) is on PATH. Underlying error: ${e.message}`,
2082
+ e
2083
+ );
2084
+ }
2085
+ });
2086
+ } finally {
2087
+ fs15.rmSync(signaturesDir, { recursive: true, force: true });
2088
+ }
2089
+ logger.info(`[sign] Wrote ${outPath}.`);
2090
+ return { bundleName: info.bundleName, outPath, sdkApi: sdk.api, kind };
2091
+ }
2092
+
2093
+ // src/emulator/install.ts
2094
+ var fs16 = __toESM(require("fs"), 1);
2095
+ var path17 = __toESM(require("path"), 1);
1903
2096
  async function installEmulator(opts) {
1904
2097
  const { config, progress, abortSignal } = opts;
1905
2098
  const logger = opts.logger ?? noopLogger;
@@ -1909,20 +2102,20 @@ async function installEmulator(opts) {
1909
2102
  const { dir: tmpDir, root: tmpRoot } = createInstallTempDir({
1910
2103
  config,
1911
2104
  override: opts.tmpDir,
1912
- installRoot: path15.dirname(emulatorDir),
2105
+ installRoot: path17.dirname(emulatorDir),
1913
2106
  prefix: "oniro-emulator-",
1914
2107
  logger
1915
2108
  });
1916
- const tmpZip = path15.join(tmpDir, "oniro_emulator.zip");
2109
+ const tmpZip = path17.join(tmpDir, "oniro_emulator.zip");
1917
2110
  try {
1918
- fs14.mkdirSync(emulatorDir, { recursive: true });
2111
+ fs16.mkdirSync(emulatorDir, { recursive: true });
1919
2112
  progress?.report({ message: "Downloading emulator...", increment: 0 });
1920
2113
  await downloadFile({ url, dest: tmpZip, progress, abortSignal, start: 0, range: 50, what, tmpRoot });
1921
2114
  progress?.report({ message: "Extracting emulator...", increment: 0 });
1922
2115
  await extractZipWithProgress({ zipPath: tmpZip, dest: emulatorDir, progress, start: 50, range: 45, logger });
1923
- const runSh = path15.join(emulatorDir, "images", "run.sh");
1924
- if (fs14.existsSync(runSh)) {
1925
- fs14.chmodSync(runSh, 493);
2116
+ const runSh = path17.join(emulatorDir, "images", "run.sh");
2117
+ if (fs16.existsSync(runSh)) {
2118
+ fs16.chmodSync(runSh, 493);
1926
2119
  }
1927
2120
  progress?.report({ message: "Finalizing installation...", increment: 5 });
1928
2121
  } catch (err) {
@@ -1932,24 +2125,24 @@ async function installEmulator(opts) {
1932
2125
  }
1933
2126
  }
1934
2127
  function isEmulatorInstalled(config) {
1935
- const imagesDir = path15.join(getEmulatorDir(config), "images");
1936
- return fs14.existsSync(path15.join(imagesDir, "run.sh")) || fs14.existsSync(path15.join(imagesDir, "run.bat"));
2128
+ const imagesDir = path17.join(getEmulatorDir(config), "images");
2129
+ return fs16.existsSync(path17.join(imagesDir, "run.sh")) || fs16.existsSync(path17.join(imagesDir, "run.bat"));
1937
2130
  }
1938
2131
  function removeEmulator(config) {
1939
2132
  const emulatorDir = getEmulatorDir(config);
1940
- if (fs14.existsSync(emulatorDir)) {
1941
- fs14.rmSync(emulatorDir, { recursive: true, force: true });
2133
+ if (fs16.existsSync(emulatorDir)) {
2134
+ fs16.rmSync(emulatorDir, { recursive: true, force: true });
1942
2135
  }
1943
2136
  }
1944
2137
 
1945
2138
  // src/emulator/lifecycle.ts
1946
- var fs16 = __toESM(require("fs"), 1);
1947
- var os8 = __toESM(require("os"), 1);
1948
- var path16 = __toESM(require("path"), 1);
1949
- var import_node_child_process4 = require("child_process");
2139
+ var fs18 = __toESM(require("fs"), 1);
2140
+ var os9 = __toESM(require("os"), 1);
2141
+ var path18 = __toESM(require("path"), 1);
2142
+ var import_node_child_process5 = require("child_process");
1950
2143
 
1951
2144
  // src/hdc/exec.ts
1952
- var fs15 = __toESM(require("fs"), 1);
2145
+ var fs17 = __toESM(require("fs"), 1);
1953
2146
  var import_node_string_decoder = require("string_decoder");
1954
2147
  function errorMessage(err) {
1955
2148
  return err instanceof Error ? err.message : String(err);
@@ -1958,12 +2151,12 @@ function runProcess(opts) {
1958
2151
  const { command, args, cwd, env, onOutput } = opts;
1959
2152
  const timeoutMs = opts.timeoutMs ?? 6e4;
1960
2153
  const { abortSignal } = opts;
1961
- return new Promise((resolve7, reject) => {
2154
+ return new Promise((resolve9, reject) => {
1962
2155
  if (abortSignal?.aborted) {
1963
2156
  reject(new CancelledError(`Command aborted before start: ${command}`));
1964
2157
  return;
1965
2158
  }
1966
- if (needsCmdWrapper(command) && !fs15.existsSync(command)) {
2159
+ if (needsCmdWrapper(command) && !fs17.existsSync(command)) {
1967
2160
  reject(new OniroError(`Failed to spawn ${command}: ENOENT (no such file)`));
1968
2161
  return;
1969
2162
  }
@@ -2031,7 +2224,7 @@ ${stderr.trim()}` : "")
2031
2224
  );
2032
2225
  return;
2033
2226
  }
2034
- resolve7({ code: code ?? -1, stdout, stderr });
2227
+ resolve9({ code: code ?? -1, stdout, stderr });
2035
2228
  });
2036
2229
  });
2037
2230
  }
@@ -2104,7 +2297,7 @@ function ensureRemoteOk(result, command) {
2104
2297
  }
2105
2298
 
2106
2299
  // src/emulator/lifecycle.ts
2107
- var PID_FILE = path16.join(os8.tmpdir(), "oniro_emulator.pid");
2300
+ var PID_FILE = path18.join(os9.tmpdir(), "oniro_emulator.pid");
2108
2301
  async function attemptHdcConnection(config, address = "127.0.0.1:55555", logger = noopLogger) {
2109
2302
  try {
2110
2303
  await hdcExec({ config, args: ["start", "-r"], logger });
@@ -2117,24 +2310,24 @@ async function attemptHdcConnection(config, address = "127.0.0.1:55555", logger
2117
2310
  }
2118
2311
  }
2119
2312
  function resolveLauncher(imagesPath, headless, connect) {
2120
- const runSh = path16.join(imagesPath, "run.sh");
2121
- const runBat = path16.join(imagesPath, "run.bat");
2313
+ const runSh = path18.join(imagesPath, "run.sh");
2314
+ const runBat = path18.join(imagesPath, "run.bat");
2122
2315
  const extra = [];
2123
2316
  if (headless) extra.push("--headless");
2124
2317
  if (connect) extra.push("--connect", connect);
2125
- if (os8.platform() === "win32") {
2126
- if (fs16.existsSync(runBat)) {
2318
+ if (os9.platform() === "win32") {
2319
+ if (fs18.existsSync(runBat)) {
2127
2320
  return { command: "cmd.exe", args: ["/c", runBat, ...extra], cwd: imagesPath };
2128
2321
  }
2129
- if (fs16.existsSync(runSh)) {
2322
+ if (fs18.existsSync(runSh)) {
2130
2323
  return { command: "bash", args: [runSh, ...extra], cwd: imagesPath };
2131
2324
  }
2132
2325
  throw new OniroError(`No emulator launcher (run.bat or run.sh) found in ${imagesPath}.`);
2133
2326
  }
2134
- if (fs16.existsSync(runSh)) {
2327
+ if (fs18.existsSync(runSh)) {
2135
2328
  return { command: "bash", args: [runSh, ...extra], cwd: imagesPath };
2136
2329
  }
2137
- if (fs16.existsSync(runBat)) {
2330
+ if (fs18.existsSync(runBat)) {
2138
2331
  return { command: "bash", args: [runBat, ...extra], cwd: imagesPath };
2139
2332
  }
2140
2333
  throw new OniroError(`No emulator launcher (run.sh or run.bat) found in ${imagesPath}.`);
@@ -2142,9 +2335,9 @@ function resolveLauncher(imagesPath, headless, connect) {
2142
2335
  async function startEmulator(opts) {
2143
2336
  const { config } = opts;
2144
2337
  const logger = opts.logger ?? noopLogger;
2145
- if (os8.platform() !== "win32" && fs16.existsSync(PID_FILE)) {
2338
+ if (os9.platform() !== "win32" && fs18.existsSync(PID_FILE)) {
2146
2339
  try {
2147
- const pid = Number(fs16.readFileSync(PID_FILE, "utf8").trim());
2340
+ const pid = Number(fs18.readFileSync(PID_FILE, "utf8").trim());
2148
2341
  if (Number.isFinite(pid) && pid > 0) {
2149
2342
  try {
2150
2343
  process.kill(pid, 0);
@@ -2160,35 +2353,35 @@ async function startEmulator(opts) {
2160
2353
  logger.warn(`Could not read PID file: ${err.message}`);
2161
2354
  }
2162
2355
  }
2163
- const imagesPath = path16.join(getEmulatorDir(config), "images");
2164
- if (!fs16.existsSync(imagesPath)) {
2356
+ const imagesPath = path18.join(getEmulatorDir(config), "images");
2357
+ if (!fs18.existsSync(imagesPath)) {
2165
2358
  throw new OniroError(`Emulator images directory not found at ${imagesPath}. Run \`oniro-app emulator install\` first.`);
2166
2359
  }
2167
2360
  const launcher = resolveLauncher(imagesPath, opts.headless === true, opts.connect);
2168
- const logPath = opts.logFile ?? (os8.platform() === "win32" ? "NUL" : "/dev/null");
2169
- const logFd = fs16.openSync(logPath, "a");
2361
+ const logPath = opts.logFile ?? (os9.platform() === "win32" ? "NUL" : "/dev/null");
2362
+ const logFd = fs18.openSync(logPath, "a");
2170
2363
  let child;
2171
2364
  try {
2172
- child = (0, import_node_child_process4.spawn)(launcher.command, [...launcher.args], {
2365
+ child = (0, import_node_child_process5.spawn)(launcher.command, [...launcher.args], {
2173
2366
  cwd: launcher.cwd,
2174
2367
  detached: true,
2175
2368
  stdio: ["ignore", logFd, logFd],
2176
2369
  windowsHide: true
2177
2370
  });
2178
2371
  } finally {
2179
- fs16.closeSync(logFd);
2372
+ fs18.closeSync(logFd);
2180
2373
  }
2181
2374
  if (!child.pid) {
2182
2375
  throw new OniroError(`Failed to spawn launcher (${launcher.command} ${launcher.args.join(" ")}).`);
2183
2376
  }
2184
- fs16.writeFileSync(PID_FILE, String(child.pid));
2377
+ fs18.writeFileSync(PID_FILE, String(child.pid));
2185
2378
  child.unref();
2186
2379
  logger.info(`Emulator launched (PID ${child.pid}). Logs: ${logPath}`);
2187
- const earlyExit = new Promise((resolve7) => {
2188
- child.once("exit", (code) => resolve7(code));
2189
- child.once("error", () => resolve7(-1));
2380
+ const earlyExit = new Promise((resolve9) => {
2381
+ child.once("exit", (code) => resolve9(code));
2382
+ child.once("error", () => resolve9(-1));
2190
2383
  });
2191
- const liveness = new Promise((resolve7) => setTimeout(() => resolve7("alive"), 2e3));
2384
+ const liveness = new Promise((resolve9) => setTimeout(() => resolve9("alive"), 2e3));
2192
2385
  const outcome = await Promise.race([earlyExit, liveness]);
2193
2386
  if (outcome !== "alive") {
2194
2387
  const tail = readTail(logPath, 4e3);
@@ -2228,24 +2421,24 @@ ${tail}` : ""}`);
2228
2421
  }
2229
2422
  function readTail(filePath, bytes) {
2230
2423
  try {
2231
- const stat = fs16.statSync(filePath);
2424
+ const stat = fs18.statSync(filePath);
2232
2425
  const start = Math.max(0, stat.size - bytes);
2233
- const fd = fs16.openSync(filePath, "r");
2426
+ const fd = fs18.openSync(filePath, "r");
2234
2427
  try {
2235
2428
  const buf = Buffer.alloc(stat.size - start);
2236
- fs16.readSync(fd, buf, 0, buf.length, start);
2429
+ fs18.readSync(fd, buf, 0, buf.length, start);
2237
2430
  return buf.toString("utf8");
2238
2431
  } finally {
2239
- fs16.closeSync(fd);
2432
+ fs18.closeSync(fd);
2240
2433
  }
2241
2434
  } catch {
2242
2435
  return "";
2243
2436
  }
2244
2437
  }
2245
2438
  async function stopEmulator(logger = noopLogger) {
2246
- if (fs16.existsSync(PID_FILE)) {
2439
+ if (fs18.existsSync(PID_FILE)) {
2247
2440
  try {
2248
- const pid = Number(fs16.readFileSync(PID_FILE, "utf8").trim());
2441
+ const pid = Number(fs18.readFileSync(PID_FILE, "utf8").trim());
2249
2442
  if (Number.isFinite(pid) && pid > 0) {
2250
2443
  try {
2251
2444
  process.kill(pid, "SIGTERM");
@@ -2258,20 +2451,20 @@ async function stopEmulator(logger = noopLogger) {
2258
2451
  logger.warn(`Could not read PID file: ${err.message}`);
2259
2452
  }
2260
2453
  }
2261
- const [killCmd, ...killArgs] = os8.platform() === "win32" ? ["taskkill", "/IM", "qemu-system-x86_64.exe", "/F"] : ["pkill", "-f", "qemu-system-x86_64"];
2454
+ const [killCmd, ...killArgs] = os9.platform() === "win32" ? ["taskkill", "/IM", "qemu-system-x86_64.exe", "/F"] : ["pkill", "-f", "qemu-system-x86_64"];
2262
2455
  try {
2263
2456
  await runProcess({ command: killCmd, args: killArgs, logger });
2264
2457
  } catch (err) {
2265
2458
  logger.warn(`[emulator] could not run ${killCmd}: ${err.message}`);
2266
2459
  }
2267
- fs16.rm(PID_FILE, { force: true }, (err) => {
2460
+ fs18.rm(PID_FILE, { force: true }, (err) => {
2268
2461
  if (err) logger.warn(`Could not remove PID file: ${err.message}`);
2269
2462
  });
2270
2463
  }
2271
2464
  async function isQemuRunning(logger) {
2272
- if (os8.platform() !== "win32") {
2465
+ if (os9.platform() !== "win32") {
2273
2466
  try {
2274
- const pid = Number(fs16.readFileSync(PID_FILE, "utf8").trim());
2467
+ const pid = Number(fs18.readFileSync(PID_FILE, "utf8").trim());
2275
2468
  if (Number.isFinite(pid) && pid > 0) {
2276
2469
  process.kill(pid, 0);
2277
2470
  return true;
@@ -2279,10 +2472,10 @@ async function isQemuRunning(logger) {
2279
2472
  } catch {
2280
2473
  }
2281
2474
  }
2282
- const [cmd, ...args] = os8.platform() === "win32" ? ["tasklist", "/FI", "IMAGENAME eq qemu-system-x86_64.exe", "/NH"] : ["pgrep", "-f", "qemu-system-x86_64"];
2475
+ const [cmd, ...args] = os9.platform() === "win32" ? ["tasklist", "/FI", "IMAGENAME eq qemu-system-x86_64.exe", "/NH"] : ["pgrep", "-f", "qemu-system-x86_64"];
2283
2476
  try {
2284
2477
  const res = await runProcess({ command: cmd, args, timeoutMs: 1e4, logger });
2285
- return os8.platform() === "win32" ? /qemu-system-x86_64/i.test(res.stdout) : res.code === 0;
2478
+ return os9.platform() === "win32" ? /qemu-system-x86_64/i.test(res.stdout) : res.code === 0;
2286
2479
  } catch {
2287
2480
  return false;
2288
2481
  }
@@ -2407,11 +2600,11 @@ async function paramSet(config, key, value, opts = {}) {
2407
2600
  }
2408
2601
 
2409
2602
  // src/hdc/files.ts
2410
- var path17 = __toESM(require("path"), 1);
2603
+ var path19 = __toESM(require("path"), 1);
2411
2604
  function normaliseLocalPath(local) {
2412
- const abs = path17.resolve(local);
2605
+ const abs = path19.resolve(local);
2413
2606
  const long = toLongPath(abs);
2414
- return long !== abs ? long : path17.join(toLongPath(path17.dirname(abs)), path17.basename(abs));
2607
+ return long !== abs ? long : path19.join(toLongPath(path19.dirname(abs)), path19.basename(abs));
2415
2608
  }
2416
2609
  function assertDevicePath(remote) {
2417
2610
  if (/^[A-Za-z]:[\\/]/.test(remote)) {
@@ -2430,13 +2623,13 @@ async function sendFile(opts) {
2430
2623
  assertDevicePath(opts.remote);
2431
2624
  const local = normaliseLocalPath(opts.local);
2432
2625
  const sandbox = opts.bundle ? appSandboxDir(opts.bundle) : void 0;
2433
- const remote = sandbox && !opts.remote.startsWith("/") ? path17.posix.join(sandbox, opts.remote) : opts.remote;
2626
+ const remote = sandbox && !opts.remote.startsWith("/") ? path19.posix.join(sandbox, opts.remote) : opts.remote;
2434
2627
  if (sandbox && !remote.startsWith(`${sandbox}/`)) {
2435
2628
  throw new OniroError(`${remote} is outside the sandbox of ${opts.bundle} (${sandbox}).`);
2436
2629
  }
2437
2630
  const device = { config: opts.config, deviceSerial: opts.deviceSerial, logger: opts.logger };
2438
2631
  if (sandbox) {
2439
- const mkdir = `mkdir -p ${shQuote(path17.posix.dirname(remote))}`;
2632
+ const mkdir = `mkdir -p ${shQuote(path19.posix.dirname(remote))}`;
2440
2633
  ensureRemoteOk(await shellChecked({ ...device, command: mkdir }), `hdc shell ${mkdir}`);
2441
2634
  }
2442
2635
  const res = await hdcExec({
@@ -2448,7 +2641,7 @@ async function sendFile(opts) {
2448
2641
  });
2449
2642
  ensureOk(res, `hdc file send ${local} ${remote}`);
2450
2643
  if (sandbox) {
2451
- const top = path17.posix.join(sandbox, path17.posix.relative(sandbox, remote).split("/")[0]);
2644
+ const top = path19.posix.join(sandbox, path19.posix.relative(sandbox, remote).split("/")[0]);
2452
2645
  const chown = `chown -R "$(stat -c %u:%g ${shQuote(sandbox)})" ${shQuote(top)}`;
2453
2646
  ensureRemoteOk(await shellChecked({ ...device, command: chown }), `hdc shell ${chown}`);
2454
2647
  }
@@ -2486,7 +2679,7 @@ async function getDisplaySize(config, opts = {}) {
2486
2679
 
2487
2680
  // src/hdc/wait.ts
2488
2681
  function delay(ms, signal) {
2489
- return new Promise((resolve7, reject) => {
2682
+ return new Promise((resolve9, reject) => {
2490
2683
  if (signal?.aborted) {
2491
2684
  reject(new CancelledError("Wait cancelled."));
2492
2685
  return;
@@ -2501,7 +2694,7 @@ function delay(ms, signal) {
2501
2694
  };
2502
2695
  const timer = setTimeout(() => {
2503
2696
  cleanup();
2504
- resolve7();
2697
+ resolve9();
2505
2698
  }, ms);
2506
2699
  signal?.addEventListener("abort", onAbort, { once: true });
2507
2700
  });
@@ -2530,18 +2723,18 @@ async function waitForCondition(opts) {
2530
2723
  }
2531
2724
 
2532
2725
  // src/hdc/app.ts
2533
- var fs19 = __toESM(require("fs"), 1);
2534
- var path20 = __toESM(require("path"), 1);
2726
+ var fs21 = __toESM(require("fs"), 1);
2727
+ var path22 = __toESM(require("path"), 1);
2535
2728
 
2536
2729
  // src/build/discoverHaps.ts
2537
- var fs17 = __toESM(require("fs"), 1);
2538
- var path18 = __toESM(require("path"), 1);
2730
+ var fs19 = __toESM(require("fs"), 1);
2731
+ var path20 = __toESM(require("path"), 1);
2539
2732
  function moduleOfHap(hapFileName) {
2540
2733
  const dash = hapFileName.indexOf("-");
2541
2734
  return dash > 0 ? hapFileName.slice(0, dash) : hapFileName.replace(/\.hap$/i, "");
2542
2735
  }
2543
2736
  function isAppProjectRoot(dir) {
2544
- return fs17.existsSync(path18.join(dir, "AppScope", "app.json5"));
2737
+ return fs19.existsSync(path20.join(dir, "AppScope", "app.json5"));
2545
2738
  }
2546
2739
  async function discoverHaps(opts) {
2547
2740
  const { projectDir } = opts;
@@ -2550,9 +2743,9 @@ async function discoverHaps(opts) {
2550
2743
  const result = {};
2551
2744
  const walk = async (dir, depth) => {
2552
2745
  if (depth > maxDepth) return;
2553
- const entries = await fs17.promises.readdir(dir, { withFileTypes: true }).catch(() => []);
2746
+ const entries = await fs19.promises.readdir(dir, { withFileTypes: true }).catch(() => []);
2554
2747
  for (const e of entries) {
2555
- const full = path18.join(dir, e.name);
2748
+ const full = path20.join(dir, e.name);
2556
2749
  if (e.isDirectory()) {
2557
2750
  if (e.name === "node_modules" || e.name === "oh_modules" || e.name === ".git") continue;
2558
2751
  if (skipNestedProjects && isAppProjectRoot(full)) continue;
@@ -2566,7 +2759,7 @@ async function discoverHaps(opts) {
2566
2759
  }
2567
2760
  };
2568
2761
  await walk(projectDir, 0);
2569
- const mtime = (p) => fs17.statSync(p).mtimeMs;
2762
+ const mtime = (p) => fs19.statSync(p).mtimeMs;
2570
2763
  for (const bucket of Object.values(result)) {
2571
2764
  bucket.signed.sort((a, b) => mtime(b) - mtime(a));
2572
2765
  bucket.unsigned.sort((a, b) => mtime(b) - mtime(a));
@@ -2592,16 +2785,16 @@ function pickHap(haps, module2) {
2592
2785
  }
2593
2786
 
2594
2787
  // src/hdc/project.ts
2595
- var fs18 = __toESM(require("fs"), 1);
2596
- var path19 = __toESM(require("path"), 1);
2788
+ var fs20 = __toESM(require("fs"), 1);
2789
+ var path21 = __toESM(require("path"), 1);
2597
2790
  var import_json53 = __toESM(require("json5"), 1);
2598
2791
  function readJson5(filePath) {
2599
- const content = fs18.readFileSync(filePath, "utf-8");
2792
+ const content = fs20.readFileSync(filePath, "utf-8");
2600
2793
  return import_json53.default.parse(content);
2601
2794
  }
2602
2795
  function getBundleName(projectDir) {
2603
- const appJsonPath = path19.join(projectDir, "AppScope", "app.json5");
2604
- if (!fs18.existsSync(appJsonPath)) {
2796
+ const appJsonPath = path21.join(projectDir, "AppScope", "app.json5");
2797
+ if (!fs20.existsSync(appJsonPath)) {
2605
2798
  throw new OniroError(`Could not find app.json5 at ${appJsonPath}`);
2606
2799
  }
2607
2800
  const appJson = readJson5(appJsonPath);
@@ -2611,8 +2804,8 @@ function getBundleName(projectDir) {
2611
2804
  return appJson.app.bundleName;
2612
2805
  }
2613
2806
  function getMainAbility(projectDir, moduleName = "entry", abilityName) {
2614
- const moduleJsonPath = path19.join(projectDir, moduleName, "src", "main", "module.json5");
2615
- if (!fs18.existsSync(moduleJsonPath)) {
2807
+ const moduleJsonPath = path21.join(projectDir, moduleName, "src", "main", "module.json5");
2808
+ if (!fs20.existsSync(moduleJsonPath)) {
2616
2809
  throw new OniroError(`Could not find module.json5 at ${moduleJsonPath}`);
2617
2810
  }
2618
2811
  const moduleJson = readJson5(moduleJsonPath);
@@ -2636,7 +2829,7 @@ function detectInstallFailure(output) {
2636
2829
  return /\[Fail\]|msg:\s*error|error:\s*failed to install|code:\s*95\d{5}/i.test(output) ? output.trim() : null;
2637
2830
  }
2638
2831
  function isUnsignedHap(hapPath) {
2639
- return path20.basename(hapPath).includes("unsigned");
2832
+ return path22.basename(hapPath).includes("unsigned");
2640
2833
  }
2641
2834
  function installFailureReason(output) {
2642
2835
  const code = /code:\s*(\d+)/i.exec(output);
@@ -2645,7 +2838,7 @@ function installFailureReason(output) {
2645
2838
  return message ? `code ${code[1]}: ${message}` : `code ${code[1]}`;
2646
2839
  }
2647
2840
  function unsignedHapRejection(output, hap) {
2648
- const name = path20.basename(hap.path);
2841
+ const name = path22.basename(hap.path);
2649
2842
  const reason = installFailureReason(output);
2650
2843
  const what = hap.fallback ? `No signed HAP was found, so the unsigned ${name} was tried, and the target rejected it (${reason}).` : `The target rejected the unsigned ${name} (${reason}).`;
2651
2844
  if (!/9568320|signature|pkcs7/i.test(reason)) return what;
@@ -2663,24 +2856,24 @@ ${result.stderr}`;
2663
2856
  return result;
2664
2857
  }
2665
2858
  function existingHapOrUnsigned(hapPath) {
2666
- if (fs19.existsSync(hapPath)) return { path: hapPath };
2859
+ if (fs21.existsSync(hapPath)) return { path: hapPath };
2667
2860
  const sibling = hapPath.replace(/-signed\.hap$/i, "-unsigned.hap");
2668
- return sibling !== hapPath && fs19.existsSync(sibling) ? { path: sibling, fallback: true } : void 0;
2861
+ return sibling !== hapPath && fs21.existsSync(sibling) ? { path: sibling, fallback: true } : void 0;
2669
2862
  }
2670
2863
  async function resolveInstallHap(opts, logger) {
2671
2864
  const warnIfFallback = (hap) => {
2672
- if (hap.fallback) logger.warn(`No signed HAP found; installing the unsigned ${path20.basename(hap.path)}.`);
2865
+ if (hap.fallback) logger.warn(`No signed HAP found; installing the unsigned ${path22.basename(hap.path)}.`);
2673
2866
  return hap;
2674
2867
  };
2675
2868
  const configured = opts.hapPath ?? opts.config.get("hapPath", "");
2676
2869
  const relative3 = configured || defaultPaths.hapPath;
2677
- const hapPath = path20.isAbsolute(relative3) ? relative3 : path20.join(opts.projectDir, relative3);
2870
+ const hapPath = path22.isAbsolute(relative3) ? relative3 : path22.join(opts.projectDir, relative3);
2678
2871
  if (configured) {
2679
2872
  const hap = existingHapOrUnsigned(hapPath);
2680
2873
  if (!hap) throw new OniroError(`HAP file not found at: ${hapPath}. Build the app first.`);
2681
2874
  return warnIfFallback(hap);
2682
2875
  }
2683
- if (fs19.existsSync(hapPath)) return { path: hapPath };
2876
+ if (fs21.existsSync(hapPath)) return { path: hapPath };
2684
2877
  const haps = await discoverHaps({ projectDir: opts.projectDir });
2685
2878
  const anySigned = Object.values(haps).some((m) => m.signed.length > 0);
2686
2879
  const entryUnsigned = anySigned ? void 0 : existingHapOrUnsigned(hapPath);
@@ -2791,7 +2984,7 @@ function listRunningProcesses(config, options = {}) {
2791
2984
  const logger = options.logger ?? noopLogger;
2792
2985
  const timeoutMs = options.timeoutMs ?? 1e3;
2793
2986
  const hdc = getHdcPath(config);
2794
- return new Promise((resolve7, reject) => {
2987
+ return new Promise((resolve9, reject) => {
2795
2988
  let proc;
2796
2989
  try {
2797
2990
  proc = spawnCompat(hdc, [...hdcTargetArgs(options.deviceSerial), "track-jpid"]);
@@ -2811,7 +3004,7 @@ function listRunningProcesses(config, options = {}) {
2811
3004
  if (options.targetProcessName && name === options.targetProcessName && !resolved) {
2812
3005
  resolved = true;
2813
3006
  killProcessTree(proc);
2814
- resolve7(pid);
3007
+ resolve9(pid);
2815
3008
  return;
2816
3009
  }
2817
3010
  }
@@ -2832,7 +3025,7 @@ function listRunningProcesses(config, options = {}) {
2832
3025
  if (options.targetProcessName) {
2833
3026
  reject(new OniroError(`Could not find process for bundle: ${options.targetProcessName}`));
2834
3027
  } else {
2835
- resolve7(processes);
3028
+ resolve9(processes);
2836
3029
  }
2837
3030
  }, timeoutMs);
2838
3031
  proc.on("close", () => {
@@ -2842,7 +3035,7 @@ function listRunningProcesses(config, options = {}) {
2842
3035
  if (options.targetProcessName) {
2843
3036
  reject(new OniroError(`Could not find process for bundle: ${options.targetProcessName}`));
2844
3037
  } else {
2845
- resolve7(processes);
3038
+ resolve9(processes);
2846
3039
  }
2847
3040
  });
2848
3041
  });
@@ -3011,12 +3204,12 @@ async function reboot(opts) {
3011
3204
  }
3012
3205
 
3013
3206
  // src/hdc/screen.ts
3014
- var fs20 = __toESM(require("fs"), 1);
3015
- var os9 = __toESM(require("os"), 1);
3016
- var path21 = __toESM(require("path"), 1);
3207
+ var fs22 = __toESM(require("fs"), 1);
3208
+ var os10 = __toESM(require("os"), 1);
3209
+ var path23 = __toESM(require("path"), 1);
3017
3210
  var import_node_crypto = require("crypto");
3018
3211
  function tmpName(ext) {
3019
- return path21.join(os9.tmpdir(), `oniro_screen_${(0, import_node_crypto.randomBytes)(6).toString("hex")}.${ext}`);
3212
+ return path23.join(os10.tmpdir(), `oniro_screen_${(0, import_node_crypto.randomBytes)(6).toString("hex")}.${ext}`);
3020
3213
  }
3021
3214
  async function takeScreenshot(opts) {
3022
3215
  const remote = "/data/local/tmp/oniro_screenshot.jpeg";
@@ -3026,11 +3219,11 @@ async function takeScreenshot(opts) {
3026
3219
  const local = tmpName("jpeg");
3027
3220
  try {
3028
3221
  await recvFile({ config: opts.config, remote, local, deviceSerial: opts.deviceSerial, timeoutMs });
3029
- const pixels = await fs20.promises.readFile(local);
3222
+ const pixels = await fs22.promises.readFile(local);
3030
3223
  const size = await getDisplaySize(opts.config, { deviceSerial: opts.deviceSerial }) ?? { width: 0, height: 0 };
3031
3224
  return { pixels, width: size.width, height: size.height };
3032
3225
  } finally {
3033
- fs20.promises.unlink(local).catch(() => {
3226
+ fs22.promises.unlink(local).catch(() => {
3034
3227
  });
3035
3228
  void shell({ config: opts.config, command: `rm -f ${remote}`, deviceSerial: opts.deviceSerial }).catch(() => {
3036
3229
  });
@@ -3049,9 +3242,9 @@ async function captureBurst(opts) {
3049
3242
  const local = tmpName(`burst_${i}.jpeg`);
3050
3243
  try {
3051
3244
  await recvFile({ config: opts.config, remote: `${remoteDir}/f${i}.jpeg`, local, deviceSerial: opts.deviceSerial });
3052
- frames.push(await fs20.promises.readFile(local));
3245
+ frames.push(await fs22.promises.readFile(local));
3053
3246
  } finally {
3054
- fs20.promises.unlink(local).catch(() => {
3247
+ fs22.promises.unlink(local).catch(() => {
3055
3248
  });
3056
3249
  }
3057
3250
  }
@@ -3226,9 +3419,9 @@ async function sendRawTouch(opts) {
3226
3419
  }
3227
3420
 
3228
3421
  // src/hdc/dump.ts
3229
- var fs21 = __toESM(require("fs"), 1);
3230
- var os10 = __toESM(require("os"), 1);
3231
- var path22 = __toESM(require("path"), 1);
3422
+ var fs23 = __toESM(require("fs"), 1);
3423
+ var os11 = __toESM(require("os"), 1);
3424
+ var path24 = __toESM(require("path"), 1);
3232
3425
  var import_node_crypto2 = require("crypto");
3233
3426
 
3234
3427
  // src/hdc/ime.ts
@@ -3491,10 +3684,10 @@ async function dumpLayout(opts) {
3491
3684
  if (!m) throw new OniroError(`Could not parse dumpLayout output:
3492
3685
  ${dump.stdout}`);
3493
3686
  const remote = m[1];
3494
- const local = path22.join(os10.tmpdir(), `oniro_layout_${(0, import_node_crypto2.randomBytes)(6).toString("hex")}.json`);
3687
+ const local = path24.join(os11.tmpdir(), `oniro_layout_${(0, import_node_crypto2.randomBytes)(6).toString("hex")}.json`);
3495
3688
  try {
3496
3689
  await recvFile({ config: opts.config, remote, local, deviceSerial: opts.deviceSerial, timeoutMs });
3497
- const parsed = JSON.parse(await fs21.promises.readFile(local, "utf8"));
3690
+ const parsed = JSON.parse(await fs23.promises.readFile(local, "utf8"));
3498
3691
  const display = await getDisplaySize(opts.config, { deviceSerial: opts.deviceSerial });
3499
3692
  if (opts.prune === false) {
3500
3693
  return { display, tree: parsed };
@@ -3507,7 +3700,7 @@ ${dump.stdout}`);
3507
3700
  const keyboard = kb ? { shown: true, bundle: kb.bundle, ...kb.b ? { b: kb.b } : {} } : { shown: false };
3508
3701
  return { display, tree, keyboard };
3509
3702
  } finally {
3510
- fs21.promises.unlink(local).catch(() => {
3703
+ fs23.promises.unlink(local).catch(() => {
3511
3704
  });
3512
3705
  void shell({ config: opts.config, command: `rm -f ${remote}`, deviceSerial: opts.deviceSerial }).catch(() => {
3513
3706
  });
@@ -3517,7 +3710,7 @@ ${dump.stdout}`);
3517
3710
  // src/hdc/uiActions.ts
3518
3711
  var FOCUS_SETTLE_MS = 400;
3519
3712
  var KEYBOARD_SETTLE_MS = 400;
3520
- var sleep = (ms) => new Promise((resolve7) => setTimeout(resolve7, ms));
3713
+ var sleep = (ms) => new Promise((resolve9) => setTimeout(resolve9, ms));
3521
3714
  function targetTree(layout) {
3522
3715
  const kb = layout.keyboard;
3523
3716
  return kb?.shown && kb.bundle ? withoutWindow(layout.tree, kb.bundle) : layout.tree;
@@ -3670,11 +3863,11 @@ async function waitForTarget(opts) {
3670
3863
  }
3671
3864
 
3672
3865
  // src/hdc/hilog.ts
3673
- var fs22 = __toESM(require("fs"), 1);
3866
+ var fs24 = __toESM(require("fs"), 1);
3674
3867
  function setHilogLevel(opts) {
3675
3868
  const logger = opts.logger ?? noopLogger;
3676
3869
  const hdc = getHdcPath(opts.config);
3677
- return new Promise((resolve7, reject) => {
3870
+ return new Promise((resolve9, reject) => {
3678
3871
  let child;
3679
3872
  try {
3680
3873
  child = spawnCompat(hdc, [...hdcTargetArgs(opts.deviceSerial), "shell", "hilog", "-b", opts.level]);
@@ -3694,7 +3887,7 @@ function setHilogLevel(opts) {
3694
3887
  });
3695
3888
  child.once("close", (code) => {
3696
3889
  if (code === 0) {
3697
- resolve7();
3890
+ resolve9();
3698
3891
  } else {
3699
3892
  const msg = stderr.trim() || `hdc shell hilog -b ${opts.level} exited with code ${code}`;
3700
3893
  logger.error(`[hilog] ${msg}`);
@@ -3796,7 +3989,7 @@ async function recentEntryFilter(opts) {
3796
3989
  };
3797
3990
  }
3798
3991
  function waitForLog(opts) {
3799
- return new Promise((resolve7, reject) => {
3992
+ return new Promise((resolve9, reject) => {
3800
3993
  const deadline = Date.now() + opts.timeoutMs;
3801
3994
  let settled = false;
3802
3995
  let child = null;
@@ -3817,7 +4010,7 @@ function waitForLog(opts) {
3817
4010
  if (settled) return;
3818
4011
  settled = true;
3819
4012
  cleanup();
3820
- resolve7(entry);
4013
+ resolve9(entry);
3821
4014
  };
3822
4015
  const fail = (err) => {
3823
4016
  if (settled) return;
@@ -3872,7 +4065,7 @@ function waitForLog(opts) {
3872
4065
  });
3873
4066
  }
3874
4067
  function watchLog(opts) {
3875
- return new Promise((resolve7, reject) => {
4068
+ return new Promise((resolve9, reject) => {
3876
4069
  const matches = [];
3877
4070
  let settled = false;
3878
4071
  let child = null;
@@ -3891,7 +4084,7 @@ function watchLog(opts) {
3891
4084
  if (settled) return;
3892
4085
  settled = true;
3893
4086
  cleanup();
3894
- resolve7(opts.dedup === false ? matches : dedupEntries(matches));
4087
+ resolve9(opts.dedup === false ? matches : dedupEntries(matches));
3895
4088
  };
3896
4089
  const onAbort = () => {
3897
4090
  if (settled) return;
@@ -3936,11 +4129,11 @@ async function captureLog(opts) {
3936
4129
  const res = await shell({ config: opts.config, command: "hilog -r", deviceSerial: opts.deviceSerial, timeoutMs: 1e4, logger });
3937
4130
  ensureOk(res, "hdc shell hilog -r");
3938
4131
  }
3939
- const fd = fs22.openSync(opts.file, "a");
4132
+ const fd = fs24.openSync(opts.file, "a");
3940
4133
  let lines = 0;
3941
4134
  let reconnects = 0;
3942
4135
  try {
3943
- await new Promise((resolve7, reject) => {
4136
+ await new Promise((resolve9, reject) => {
3944
4137
  const deadline = opts.durationMs !== void 0 ? Date.now() + opts.durationMs : Infinity;
3945
4138
  let settled = false;
3946
4139
  let child = null;
@@ -3949,7 +4142,7 @@ async function captureLog(opts) {
3949
4142
  let open = true;
3950
4143
  const write = (line) => {
3951
4144
  if (!open || opts.pattern && !opts.pattern.test(line)) return;
3952
- fs22.writeSync(fd, `${line}
4145
+ fs24.writeSync(fd, `${line}
3953
4146
  `);
3954
4147
  lines++;
3955
4148
  };
@@ -3970,7 +4163,7 @@ async function captureLog(opts) {
3970
4163
  if (settled) return;
3971
4164
  settled = true;
3972
4165
  cleanup();
3973
- resolve7();
4166
+ resolve9();
3974
4167
  }
3975
4168
  const fail = (err) => {
3976
4169
  if (settled) return;
@@ -4014,7 +4207,7 @@ async function captureLog(opts) {
4014
4207
  spawnStream();
4015
4208
  });
4016
4209
  } finally {
4017
- fs22.closeSync(fd);
4210
+ fs24.closeSync(fd);
4018
4211
  }
4019
4212
  return { file: opts.file, lines, reconnects };
4020
4213
  }
@@ -4042,17 +4235,17 @@ async function dumpLog(opts) {
4042
4235
  }
4043
4236
 
4044
4237
  // src/build/runHvigorw.ts
4045
- var fs24 = __toESM(require("fs"), 1);
4046
- var path24 = __toESM(require("path"), 1);
4238
+ var fs26 = __toESM(require("fs"), 1);
4239
+ var path26 = __toESM(require("path"), 1);
4047
4240
 
4048
4241
  // src/harmonyos/target.ts
4049
- var fs23 = __toESM(require("fs"), 1);
4050
- var path23 = __toESM(require("path"), 1);
4242
+ var fs25 = __toESM(require("fs"), 1);
4243
+ var path25 = __toESM(require("path"), 1);
4051
4244
  var import_json54 = __toESM(require("json5"), 1);
4052
4245
  function detectRuntimeOs(projectDir, productName) {
4053
4246
  let products = [];
4054
4247
  try {
4055
- const parsed = import_json54.default.parse(fs23.readFileSync(path23.join(projectDir, "build-profile.json5"), "utf-8"));
4248
+ const parsed = import_json54.default.parse(fs25.readFileSync(path25.join(projectDir, "build-profile.json5"), "utf-8"));
4056
4249
  if (Array.isArray(parsed.app?.products)) products = parsed.app.products;
4057
4250
  } catch {
4058
4251
  return "OpenHarmony";
@@ -4093,7 +4286,7 @@ function runHvigorw(opts) {
4093
4286
  const { config } = opts;
4094
4287
  const projectDir = toLongPath(opts.projectDir);
4095
4288
  const logger = opts.logger ?? noopLogger;
4096
- if (!fs24.existsSync(path24.join(projectDir, "build-profile.json5"))) {
4289
+ if (!fs26.existsSync(path26.join(projectDir, "build-profile.json5"))) {
4097
4290
  throw new OniroError(`Not an OpenHarmony project: ${projectDir} (build-profile.json5 not found).`);
4098
4291
  }
4099
4292
  let hvigorw;
@@ -4105,24 +4298,24 @@ function runHvigorw(opts) {
4105
4298
  hvigorw = resolved.startsWith(projectDir) ? resolved : findHarmonyOsTool(sdk, "hvigorw") ?? resolved;
4106
4299
  logger.info(`[build] Building for HarmonyOS against the SDK at ${sdk.sdkPath}.`);
4107
4300
  } else {
4108
- if (!fs24.existsSync(getCmdToolsPath(config))) {
4301
+ if (!fs26.existsSync(getCmdToolsPath(config))) {
4109
4302
  throw new CmdToolsNotInstalledError(getCmdToolsPath(config));
4110
4303
  }
4111
4304
  sdkEnv = { OHOS_BASE_SDK_HOME: getOhosBaseSdkHome(config) };
4112
4305
  hvigorw = getHvigorwPath(config, projectDir);
4113
4306
  }
4114
- if (!fs24.existsSync(hvigorw)) {
4307
+ if (!fs26.existsSync(hvigorw)) {
4115
4308
  throw new OniroError(`hvigorw wrapper not found at ${hvigorw}.`);
4116
4309
  }
4117
4310
  const args = buildHvigorwArgs(opts);
4118
4311
  if (process.platform !== "win32") {
4119
4312
  try {
4120
- fs24.chmodSync(hvigorw, 493);
4313
+ fs26.chmodSync(hvigorw, 493);
4121
4314
  } catch {
4122
4315
  }
4123
4316
  }
4124
4317
  const env = { ...process.env, ...sdkEnv };
4125
- return new Promise((resolve7, reject) => {
4318
+ return new Promise((resolve9, reject) => {
4126
4319
  if (opts.abortSignal?.aborted) {
4127
4320
  reject(new CancelledError("hvigorw build cancelled."));
4128
4321
  return;
@@ -4166,15 +4359,15 @@ function runHvigorw(opts) {
4166
4359
  settled = true;
4167
4360
  cleanup();
4168
4361
  const exitCode = code ?? 1;
4169
- if (exitCode === 0) resolve7({ exitCode });
4362
+ if (exitCode === 0) resolve9({ exitCode });
4170
4363
  else reject(new OniroError(`hvigorw exited with code ${exitCode}`));
4171
4364
  });
4172
4365
  });
4173
4366
  }
4174
4367
 
4175
4368
  // src/build/ohpm.ts
4176
- var fs25 = __toESM(require("fs"), 1);
4177
- var path25 = __toESM(require("path"), 1);
4369
+ var fs27 = __toESM(require("fs"), 1);
4370
+ var path27 = __toESM(require("path"), 1);
4178
4371
  function runOhpm(opts) {
4179
4372
  return runProcess({
4180
4373
  command: opts.ohpmPath ?? getOhpmPath(opts.config),
@@ -4187,7 +4380,7 @@ function runOhpm(opts) {
4187
4380
  });
4188
4381
  }
4189
4382
  async function ensureOhModules(opts) {
4190
- if (fs25.existsSync(path25.join(opts.projectDir, "oh_modules"))) {
4383
+ if (fs27.existsSync(path27.join(opts.projectDir, "oh_modules"))) {
4191
4384
  return { installed: false };
4192
4385
  }
4193
4386
  const res = await runOhpm({
@@ -4204,8 +4397,8 @@ async function ensureOhModules(opts) {
4204
4397
  }
4205
4398
 
4206
4399
  // src/build/codelinter.ts
4207
- var fs26 = __toESM(require("fs"), 1);
4208
- var path26 = __toESM(require("path"), 1);
4400
+ var fs28 = __toESM(require("fs"), 1);
4401
+ var path28 = __toESM(require("path"), 1);
4209
4402
  var FINDING_RE = /^(.*?):(\d+):(?:(\d+):)?\s*(error|warning|warn|suggestion|info)\b[:\s-]*(.*)$/i;
4210
4403
  var TRAILING_CODE_RE = /[([]([\w@/.\-]+)[)\]]\s*$/;
4211
4404
  var FILE_HEADER_RE = /^(.+\.\w+)\((\d+)\)$/;
@@ -4295,10 +4488,10 @@ function parseCodelinterFindings(output) {
4295
4488
  var PROJECT_ROOT_FILES = [
4296
4489
  ["build-profile.json5"],
4297
4490
  ["hvigorfile.ts", "hvigorfile.js"],
4298
- [path26.join("hvigor", "hvigor-config.json5")]
4491
+ [path28.join("hvigor", "hvigor-config.json5")]
4299
4492
  ];
4300
4493
  function missingProjectRootFiles(projectDir) {
4301
- return PROJECT_ROOT_FILES.filter((names) => !names.some((f) => fs26.existsSync(path26.join(projectDir, f)))).map(
4494
+ return PROJECT_ROOT_FILES.filter((names) => !names.some((f) => fs28.existsSync(path28.join(projectDir, f)))).map(
4302
4495
  (names) => names[0]
4303
4496
  );
4304
4497
  }
@@ -4380,32 +4573,22 @@ ${hint}`, err);
4380
4573
  return { exitCode, durationMs: Date.now() - start, discoveredHaps, warnings };
4381
4574
  }
4382
4575
 
4383
- // src/project/validators.ts
4384
- function isValidProjectName(name) {
4385
- if (!name) return false;
4386
- if (name.includes("/") || name.includes("\\")) return false;
4387
- return /^[A-Za-z0-9._-]+$/.test(name);
4388
- }
4389
- function isValidBundleName(bundleName) {
4390
- return /^[A-Za-z][A-Za-z0-9_]*(\.[A-Za-z][A-Za-z0-9_]*)+$/.test(bundleName);
4391
- }
4392
-
4393
4576
  // src/project/templates.ts
4394
- var fs27 = __toESM(require("fs"), 1);
4395
- var path27 = __toESM(require("path"), 1);
4577
+ var fs29 = __toESM(require("fs"), 1);
4578
+ var path29 = __toESM(require("path"), 1);
4396
4579
  function toHumanTemplateName(folderName) {
4397
4580
  return folderName.replace(/([a-z])([A-Z])/g, "$1 $2");
4398
4581
  }
4399
4582
  function listTemplates(templateRoot) {
4400
- if (!fs27.existsSync(templateRoot)) return [];
4401
- const entries = fs27.readdirSync(templateRoot, { withFileTypes: true });
4583
+ if (!fs29.existsSync(templateRoot)) return [];
4584
+ const entries = fs29.readdirSync(templateRoot, { withFileTypes: true });
4402
4585
  return entries.filter((e) => e.isDirectory()).map((e) => {
4403
- const dir = path27.join(templateRoot, e.name);
4404
- const metaPath = path27.join(dir, "template.json");
4586
+ const dir = path29.join(templateRoot, e.name);
4587
+ const metaPath = path29.join(dir, "template.json");
4405
4588
  let meta = {};
4406
- if (fs27.existsSync(metaPath)) {
4589
+ if (fs29.existsSync(metaPath)) {
4407
4590
  try {
4408
- meta = JSON.parse(fs27.readFileSync(metaPath, "utf8"));
4591
+ meta = JSON.parse(fs29.readFileSync(metaPath, "utf8"));
4409
4592
  } catch {
4410
4593
  }
4411
4594
  }
@@ -4420,57 +4603,57 @@ function listTemplates(templateRoot) {
4420
4603
  function validateTemplateLayout(templateDir, defaultModuleName) {
4421
4604
  const required = [
4422
4605
  "build-profile.json5",
4423
- path27.join("AppScope", "app.json5"),
4424
- path27.join(defaultModuleName, "src", "main", "module.json5"),
4425
- path27.join(defaultModuleName, "oh-package.json5"),
4606
+ path29.join("AppScope", "app.json5"),
4607
+ path29.join(defaultModuleName, "src", "main", "module.json5"),
4608
+ path29.join(defaultModuleName, "oh-package.json5"),
4426
4609
  "hvigorfile.ts"
4427
4610
  ];
4428
- return required.filter((rel) => !fs27.existsSync(path27.join(templateDir, rel)));
4611
+ return required.filter((rel) => !fs29.existsSync(path29.join(templateDir, rel)));
4429
4612
  }
4430
4613
 
4431
4614
  // src/project/createScaffold.ts
4432
- var fs29 = __toESM(require("fs"), 1);
4433
- var path28 = __toESM(require("path"), 1);
4615
+ var fs31 = __toESM(require("fs"), 1);
4616
+ var path30 = __toESM(require("path"), 1);
4434
4617
 
4435
4618
  // src/project/jsonHelpers.ts
4436
- var fs28 = __toESM(require("fs"), 1);
4619
+ var fs30 = __toESM(require("fs"), 1);
4437
4620
  var import_json55 = __toESM(require("json5"), 1);
4438
4621
  function readJson5File(filePath) {
4439
- return import_json55.default.parse(fs28.readFileSync(filePath, "utf8"));
4622
+ return import_json55.default.parse(fs30.readFileSync(filePath, "utf8"));
4440
4623
  }
4441
4624
  function writeJson5File(filePath, value) {
4442
- fs28.writeFileSync(filePath, JSON.stringify(value, null, 2) + "\n", "utf8");
4625
+ fs30.writeFileSync(filePath, JSON.stringify(value, null, 2) + "\n", "utf8");
4443
4626
  }
4444
4627
  function readJsonFile(filePath) {
4445
- return JSON.parse(fs28.readFileSync(filePath, "utf8"));
4628
+ return JSON.parse(fs30.readFileSync(filePath, "utf8"));
4446
4629
  }
4447
4630
  function writeJsonFile(filePath, value) {
4448
- fs28.writeFileSync(filePath, JSON.stringify(value, null, 2) + "\n", "utf8");
4631
+ fs30.writeFileSync(filePath, JSON.stringify(value, null, 2) + "\n", "utf8");
4449
4632
  }
4450
4633
 
4451
4634
  // src/project/createScaffold.ts
4452
4635
  var IGNORED_DIRS = /* @__PURE__ */ new Set(["oh_modules", "node_modules", "build", ".hvigor"]);
4453
4636
  async function pathExists(p) {
4454
4637
  try {
4455
- await fs29.promises.access(p);
4638
+ await fs31.promises.access(p);
4456
4639
  return true;
4457
4640
  } catch {
4458
4641
  return false;
4459
4642
  }
4460
4643
  }
4461
4644
  async function copyDirRecursive(srcDir, destDir) {
4462
- await fs29.promises.mkdir(destDir, { recursive: true });
4463
- const entries = await fs29.promises.readdir(srcDir, { withFileTypes: true });
4645
+ await fs31.promises.mkdir(destDir, { recursive: true });
4646
+ const entries = await fs31.promises.readdir(srcDir, { withFileTypes: true });
4464
4647
  for (const entry of entries) {
4465
- const src = path28.join(srcDir, entry.name);
4466
- const dest = path28.join(destDir, entry.name);
4648
+ const src = path30.join(srcDir, entry.name);
4649
+ const dest = path30.join(destDir, entry.name);
4467
4650
  if (entry.isDirectory()) {
4468
4651
  await copyDirRecursive(src, dest);
4469
4652
  } else if (entry.isSymbolicLink()) {
4470
4653
  continue;
4471
4654
  } else if (entry.isFile()) {
4472
4655
  if (entry.name === "template.json") continue;
4473
- await fs29.promises.copyFile(src, dest);
4656
+ await fs31.promises.copyFile(src, dest);
4474
4657
  }
4475
4658
  }
4476
4659
  }
@@ -4478,9 +4661,9 @@ async function normalizeJson5ToJson(projectDir, logger) {
4478
4661
  const stack = [projectDir];
4479
4662
  while (stack.length > 0) {
4480
4663
  const currentDir = stack.pop();
4481
- const entries = await fs29.promises.readdir(currentDir, { withFileTypes: true });
4664
+ const entries = await fs31.promises.readdir(currentDir, { withFileTypes: true });
4482
4665
  for (const entry of entries) {
4483
- const fullPath = path28.join(currentDir, entry.name);
4666
+ const fullPath = path30.join(currentDir, entry.name);
4484
4667
  if (entry.isDirectory()) {
4485
4668
  if (IGNORED_DIRS.has(entry.name)) continue;
4486
4669
  stack.push(fullPath);
@@ -4502,23 +4685,23 @@ function toJavaPropertiesPath(p) {
4502
4685
  function createOrUpdateLocalProperties(projectDir, sdkDir) {
4503
4686
  const content = `sdk.dir=${toJavaPropertiesPath(sdkDir)}
4504
4687
  `;
4505
- fs29.writeFileSync(path28.join(projectDir, "local.properties"), content, "utf8");
4688
+ fs31.writeFileSync(path30.join(projectDir, "local.properties"), content, "utf8");
4506
4689
  }
4507
4690
  function renameIfExists(fromPath, toPath) {
4508
- if (fs29.existsSync(fromPath) && fromPath !== toPath) {
4509
- fs29.renameSync(fromPath, toPath);
4691
+ if (fs31.existsSync(fromPath) && fromPath !== toPath) {
4692
+ fs31.renameSync(fromPath, toPath);
4510
4693
  }
4511
4694
  }
4512
4695
  function updateTemplateConfigs(projectDir, args, sdkBaseDir) {
4513
- const appJsonPath = path28.join(projectDir, "AppScope", "app.json5");
4514
- if (fs29.existsSync(appJsonPath)) {
4696
+ const appJsonPath = path30.join(projectDir, "AppScope", "app.json5");
4697
+ if (fs31.existsSync(appJsonPath)) {
4515
4698
  const appJson = readJson5File(appJsonPath);
4516
4699
  appJson.app = appJson.app ?? {};
4517
4700
  appJson.app.bundleName = args.bundleName;
4518
4701
  writeJson5File(appJsonPath, appJson);
4519
4702
  }
4520
- const stringsPath = path28.join(projectDir, "AppScope", "resources", "base", "element", "string.json");
4521
- if (fs29.existsSync(stringsPath)) {
4703
+ const stringsPath = path30.join(projectDir, "AppScope", "resources", "base", "element", "string.json");
4704
+ if (fs31.existsSync(stringsPath)) {
4522
4705
  const strings = readJsonFile(stringsPath);
4523
4706
  if (Array.isArray(strings.string)) {
4524
4707
  const appName = strings.string.find((s) => s.name === "app_name");
@@ -4526,8 +4709,8 @@ function updateTemplateConfigs(projectDir, args, sdkBaseDir) {
4526
4709
  }
4527
4710
  writeJsonFile(stringsPath, strings);
4528
4711
  }
4529
- const buildProfilePath = path28.join(projectDir, "build-profile.json5");
4530
- if (fs29.existsSync(buildProfilePath)) {
4712
+ const buildProfilePath = path30.join(projectDir, "build-profile.json5");
4713
+ if (fs31.existsSync(buildProfilePath)) {
4531
4714
  const buildProfile = readJson5File(buildProfilePath);
4532
4715
  buildProfile.app = buildProfile.app ?? {};
4533
4716
  if (Array.isArray(buildProfile.app.products) && buildProfile.app.products.length > 0) {
@@ -4540,23 +4723,23 @@ function updateTemplateConfigs(projectDir, args, sdkBaseDir) {
4540
4723
  }
4541
4724
  writeJson5File(buildProfilePath, buildProfile);
4542
4725
  }
4543
- const moduleJsonPath = path28.join(projectDir, args.moduleName, "src", "main", "module.json5");
4544
- if (fs29.existsSync(moduleJsonPath)) {
4726
+ const moduleJsonPath = path30.join(projectDir, args.moduleName, "src", "main", "module.json5");
4727
+ if (fs31.existsSync(moduleJsonPath)) {
4545
4728
  const moduleJson = readJson5File(moduleJsonPath);
4546
4729
  moduleJson.module = moduleJson.module ?? {};
4547
4730
  moduleJson.module.name = args.moduleName;
4548
4731
  writeJson5File(moduleJsonPath, moduleJson);
4549
4732
  }
4550
- const moduleOhPackagePath = path28.join(projectDir, args.moduleName, "oh-package.json5");
4551
- if (fs29.existsSync(moduleOhPackagePath)) {
4733
+ const moduleOhPackagePath = path30.join(projectDir, args.moduleName, "oh-package.json5");
4734
+ if (fs31.existsSync(moduleOhPackagePath)) {
4552
4735
  const pkg = readJson5File(moduleOhPackagePath);
4553
4736
  pkg.name = args.moduleName;
4554
4737
  writeJson5File(moduleOhPackagePath, pkg);
4555
4738
  }
4556
- const vscodeDir = path28.join(projectDir, ".vscode");
4557
- fs29.mkdirSync(vscodeDir, { recursive: true });
4739
+ const vscodeDir = path30.join(projectDir, ".vscode");
4740
+ fs31.mkdirSync(vscodeDir, { recursive: true });
4558
4741
  const hapPath = `${args.moduleName}/build/default/outputs/default/${args.moduleName}-default-signed.hap`;
4559
- writeJsonFile(path28.join(vscodeDir, "settings.json"), {
4742
+ writeJsonFile(path30.join(vscodeDir, "settings.json"), {
4560
4743
  "oniro.hapPath": hapPath,
4561
4744
  "files.associations": { "*.json5": "jsonc" }
4562
4745
  });
@@ -4573,7 +4756,7 @@ async function createScaffold(opts) {
4573
4756
  if (!opts.location || !await pathExists(opts.location)) {
4574
4757
  throw new OniroError(`Location does not exist: ${opts.location}`);
4575
4758
  }
4576
- const templateDir = path28.join(opts.templateRoot, opts.templateId);
4759
+ const templateDir = path30.join(opts.templateRoot, opts.templateId);
4577
4760
  if (!await pathExists(templateDir)) {
4578
4761
  throw new OniroError(`Template not found: ${templateDir}`);
4579
4762
  }
@@ -4608,17 +4791,17 @@ async function createScaffold(opts) {
4608
4791
  }
4609
4792
  }
4610
4793
  }
4611
- const projectDir = path28.join(opts.location, opts.projectName);
4794
+ const projectDir = path30.join(opts.location, opts.projectName);
4612
4795
  if (await pathExists(projectDir)) {
4613
4796
  if (!opts.overwrite) {
4614
4797
  throw new OniroError(`Destination already exists: ${projectDir}. Pass overwrite: true to replace it.`);
4615
4798
  }
4616
- await fs29.promises.rm(projectDir, { recursive: true, force: true });
4799
+ await fs31.promises.rm(projectDir, { recursive: true, force: true });
4617
4800
  }
4618
4801
  logger.info(`[create] Scaffolding ${opts.templateId} at ${projectDir}`);
4619
4802
  await copyDirRecursive(templateDir, projectDir);
4620
4803
  if (moduleName !== defaultModuleName) {
4621
- renameIfExists(path28.join(projectDir, defaultModuleName), path28.join(projectDir, moduleName));
4804
+ renameIfExists(path30.join(projectDir, defaultModuleName), path30.join(projectDir, moduleName));
4622
4805
  }
4623
4806
  updateTemplateConfigs(
4624
4807
  projectDir,
@@ -4635,11 +4818,11 @@ async function createScaffold(opts) {
4635
4818
  }
4636
4819
 
4637
4820
  // src/project/listModules.ts
4638
- var fs30 = __toESM(require("fs"), 1);
4639
- var path29 = __toESM(require("path"), 1);
4821
+ var fs32 = __toESM(require("fs"), 1);
4822
+ var path31 = __toESM(require("path"), 1);
4640
4823
  function listModules(opts) {
4641
- const buildProfile = path29.join(opts.projectDir, "build-profile.json5");
4642
- if (!fs30.existsSync(buildProfile)) {
4824
+ const buildProfile = path31.join(opts.projectDir, "build-profile.json5");
4825
+ if (!fs32.existsSync(buildProfile)) {
4643
4826
  throw new OniroError(`build-profile.json5 not found at ${buildProfile}`);
4644
4827
  }
4645
4828
  const parsed = readJson5File(buildProfile);
@@ -4651,12 +4834,12 @@ function listModules(opts) {
4651
4834
  }
4652
4835
 
4653
4836
  // src/project/abilities.ts
4654
- var fs31 = __toESM(require("fs"), 1);
4655
- var path30 = __toESM(require("path"), 1);
4837
+ var fs33 = __toESM(require("fs"), 1);
4838
+ var path32 = __toESM(require("path"), 1);
4656
4839
  function listAbilities(opts) {
4657
4840
  const moduleName = opts.moduleName ?? "entry";
4658
- const moduleJsonPath = path30.join(opts.projectDir, moduleName, "src", "main", "module.json5");
4659
- if (!fs31.existsSync(moduleJsonPath)) {
4841
+ const moduleJsonPath = path32.join(opts.projectDir, moduleName, "src", "main", "module.json5");
4842
+ if (!fs33.existsSync(moduleJsonPath)) {
4660
4843
  throw new OniroError(`Could not find module.json5 at ${moduleJsonPath}`);
4661
4844
  }
4662
4845
  const parsed = readJson5File(moduleJsonPath);
@@ -4664,12 +4847,12 @@ function listAbilities(opts) {
4664
4847
  }
4665
4848
 
4666
4849
  // src/project/prepareSigning.ts
4667
- var fs32 = __toESM(require("fs"), 1);
4668
- var path31 = __toESM(require("path"), 1);
4850
+ var fs34 = __toESM(require("fs"), 1);
4851
+ var path33 = __toESM(require("path"), 1);
4669
4852
  function hasSigningArtifacts(dir) {
4670
- if (!fs32.existsSync(path31.join(dir, "signatures"))) return false;
4671
- const buildProfile = path31.join(dir, "build-profile.json5");
4672
- if (!fs32.existsSync(buildProfile)) return false;
4853
+ if (!fs34.existsSync(path33.join(dir, "signatures"))) return false;
4854
+ const buildProfile = path33.join(dir, "build-profile.json5");
4855
+ if (!fs34.existsSync(buildProfile)) return false;
4673
4856
  try {
4674
4857
  const parsed = readJson5File(buildProfile);
4675
4858
  return Array.isArray(parsed.app?.signingConfigs) && parsed.app.signingConfigs.length > 0;
@@ -4748,7 +4931,6 @@ async function getDeviceInfo(opts) {
4748
4931
  }
4749
4932
 
4750
4933
  // src/install/diffHapAssets.ts
4751
- var import_node_stream_zip2 = __toESM(require("node-stream-zip"), 1);
4752
4934
  function diffEntryNames(installed, next) {
4753
4935
  const installedSet = new Set(installed);
4754
4936
  const nextSet = new Set(next);
@@ -4757,14 +4939,11 @@ function diffEntryNames(installed, next) {
4757
4939
  removedAssetPaths: [...installedSet].filter((n) => !nextSet.has(n)).sort()
4758
4940
  };
4759
4941
  }
4760
- async function listFileEntries(hapPath) {
4761
- const zip = new import_node_stream_zip2.default.async({ file: hapPath });
4762
- try {
4763
- const entries = await zip.entries();
4764
- return Object.values(entries).filter((e) => !e.isDirectory).map((e) => e.name);
4765
- } finally {
4766
- await zip.close();
4767
- }
4942
+ function listFileEntries(hapPath) {
4943
+ return withHapZip(
4944
+ hapPath,
4945
+ (zip) => Object.values(zip.entries()).filter((e) => !e.isDirectory).map((e) => e.name)
4946
+ );
4768
4947
  }
4769
4948
  async function diffHapAssets(opts) {
4770
4949
  const [installed, next] = await Promise.all([listFileEntries(opts.installedHap), listFileEntries(opts.newHap)]);
@@ -4772,8 +4951,8 @@ async function diffHapAssets(opts) {
4772
4951
  }
4773
4952
 
4774
4953
  // src/install/apply.ts
4775
- var fs33 = __toESM(require("fs"), 1);
4776
- var path32 = __toESM(require("path"), 1);
4954
+ var fs35 = __toESM(require("fs"), 1);
4955
+ var path34 = __toESM(require("path"), 1);
4777
4956
 
4778
4957
  // src/install/hapBundleName.ts
4779
4958
  function bundleNameFromHapConfig(text) {
@@ -4811,7 +4990,7 @@ async function pidOf(config, bundle, deviceSerial) {
4811
4990
  async function resolveHap(opts, logger) {
4812
4991
  let hap;
4813
4992
  if (opts.hapPath) {
4814
- const hapPath = !path32.isAbsolute(opts.hapPath) && opts.projectDir ? path32.join(opts.projectDir, opts.hapPath) : opts.hapPath;
4993
+ const hapPath = !path34.isAbsolute(opts.hapPath) && opts.projectDir ? path34.join(opts.projectDir, opts.hapPath) : opts.hapPath;
4815
4994
  hap = existingHapOrUnsigned(hapPath);
4816
4995
  if (!hap) throw new OniroError(`HAP not found at ${hapPath}.`);
4817
4996
  } else {
@@ -4820,7 +4999,7 @@ async function resolveHap(opts, logger) {
4820
4999
  if (!picked) throw new OniroError(`No HAP found${opts.module ? ` for module ${opts.module}` : ""}. Build first.`);
4821
5000
  hap = { path: picked.path, fallback: !picked.signed };
4822
5001
  }
4823
- if (hap.fallback) logger.warn(`No signed HAP found; installing the unsigned ${path32.basename(hap.path)}.`);
5002
+ if (hap.fallback) logger.warn(`No signed HAP found; installing the unsigned ${path34.basename(hap.path)}.`);
4824
5003
  return hap;
4825
5004
  }
4826
5005
  async function applyChanges(opts) {
@@ -4836,12 +5015,12 @@ async function applyChanges(opts) {
4836
5015
  });
4837
5016
  if (hapBundle && hapBundle !== bundle) {
4838
5017
  throw new OniroError(
4839
- `${path32.basename(hapPath)} (${hapPath}) is bundle ${hapBundle}, not ${bundle}, so installing it would not update ${bundle}. Choose the HAP explicitly, or name ${hapBundle} as the bundle if it is the app to update.`
5018
+ `${path34.basename(hapPath)} (${hapPath}) is bundle ${hapBundle}, not ${bundle}, so installing it would not update ${bundle}. Choose the HAP explicitly, or name ${hapBundle} as the bundle if it is the app to update.`
4840
5019
  );
4841
5020
  }
4842
5021
  logger.info(`installing ${hapPath}`);
4843
5022
  let assetsChanged = false;
4844
- if (opts.installedHapPath && fs33.existsSync(opts.installedHapPath)) {
5023
+ if (opts.installedHapPath && fs35.existsSync(opts.installedHapPath)) {
4845
5024
  try {
4846
5025
  const diff = await diffHapAssets({ installedHap: opts.installedHapPath, newHap: hapPath });
4847
5026
  assetsChanged = diff.addedAssetPaths.length > 0 || diff.removedAssetPaths.length > 0;
@@ -4874,7 +5053,7 @@ async function applyChanges(opts) {
4874
5053
  }
4875
5054
  if (!await isBundleInstalled({ config, bundle, deviceSerial })) {
4876
5055
  throw new OniroError(
4877
- `hdc reported ${path32.basename(hapPath)} as installed, but bm dump does not list ${bundle}. Check that the HAP was built from this project (its bundleName must match).`
5056
+ `hdc reported ${path34.basename(hapPath)} as installed, but bm dump does not list ${bundle}. Check that the HAP was built from this project (its bundleName must match).`
4878
5057
  );
4879
5058
  }
4880
5059
  let postInstallPid = await pidOf(config, bundle, deviceSerial);
@@ -4951,10 +5130,10 @@ var AUTH_PATHS = {
4951
5130
  };
4952
5131
 
4953
5132
  // src/harmonyos/auth/tokenStore.ts
4954
- var fs34 = __toESM(require("fs"), 1);
4955
- var os11 = __toESM(require("os"), 1);
4956
- var path33 = __toESM(require("path"), 1);
4957
- var crypto3 = __toESM(require("crypto"), 1);
5133
+ var fs36 = __toESM(require("fs"), 1);
5134
+ var os12 = __toESM(require("os"), 1);
5135
+ var path35 = __toESM(require("path"), 1);
5136
+ var crypto4 = __toESM(require("crypto"), 1);
4958
5137
  var ALGORITHM = "aes-256-gcm";
4959
5138
  var KEY_LENGTH = 32;
4960
5139
  var IV_LENGTH = 12;
@@ -4963,8 +5142,8 @@ var KEY_FILE = "kek.bin";
4963
5142
  var DIR_MODE = 448;
4964
5143
  var FILE_MODE = 384;
4965
5144
  function encrypt2(key, plaintext) {
4966
- const iv = crypto3.randomBytes(IV_LENGTH);
4967
- const cipher = crypto3.createCipheriv(ALGORITHM, key, iv);
5145
+ const iv = crypto4.randomBytes(IV_LENGTH);
5146
+ const cipher = crypto4.createCipheriv(ALGORITHM, key, iv);
4968
5147
  const ciphertext = Buffer.concat([cipher.update(plaintext), cipher.final()]);
4969
5148
  return {
4970
5149
  version: 1,
@@ -4975,7 +5154,7 @@ function encrypt2(key, plaintext) {
4975
5154
  };
4976
5155
  }
4977
5156
  function decrypt2(key, blob) {
4978
- const decipher = crypto3.createDecipheriv(ALGORITHM, key, Buffer.from(blob.iv, "base64"));
5157
+ const decipher = crypto4.createDecipheriv(ALGORITHM, key, Buffer.from(blob.iv, "base64"));
4979
5158
  decipher.setAuthTag(Buffer.from(blob.authTag, "base64"));
4980
5159
  return Buffer.concat([decipher.update(Buffer.from(blob.ciphertext, "base64")), decipher.final()]);
4981
5160
  }
@@ -4989,7 +5168,7 @@ function permissionHint(dir) {
4989
5168
  }
4990
5169
  function ensureDir(dir) {
4991
5170
  try {
4992
- fs34.mkdirSync(dir, { recursive: true, mode: DIR_MODE });
5171
+ fs36.mkdirSync(dir, { recursive: true, mode: DIR_MODE });
4993
5172
  } catch (err) {
4994
5173
  if (err.code === "EACCES") {
4995
5174
  throw new OniroError(permissionHint(dir), err);
@@ -5000,17 +5179,17 @@ function ensureDir(dir) {
5000
5179
  function createTokenStore(opts) {
5001
5180
  const logger = opts.logger ?? noopLogger;
5002
5181
  const authDir = opts.config.get("harmonyosAuthDir", defaultPaths.harmonyosAuthDir());
5003
- const tokenPath = path33.join(authDir, TOKEN_FILE);
5004
- const keyPath = path33.join(os11.homedir(), ".local", "share", "oniro-app", "keys", KEY_FILE);
5182
+ const tokenPath = path35.join(authDir, TOKEN_FILE);
5183
+ const keyPath = path35.join(os12.homedir(), ".local", "share", "oniro-app", "keys", KEY_FILE);
5005
5184
  function loadOrCreateKey() {
5006
- if (fs34.existsSync(keyPath)) {
5007
- const existing = fs34.readFileSync(keyPath);
5185
+ if (fs36.existsSync(keyPath)) {
5186
+ const existing = fs36.readFileSync(keyPath);
5008
5187
  if (existing.length === KEY_LENGTH) return existing;
5009
5188
  logger.warn("[harmonyos] The token key file was malformed; regenerating (you will need to log in again).");
5010
5189
  }
5011
- ensureDir(path33.dirname(keyPath));
5012
- const key = crypto3.randomBytes(KEY_LENGTH);
5013
- fs34.writeFileSync(keyPath, key, { mode: FILE_MODE });
5190
+ ensureDir(path35.dirname(keyPath));
5191
+ const key = crypto4.randomBytes(KEY_LENGTH);
5192
+ fs36.writeFileSync(keyPath, key, { mode: FILE_MODE });
5014
5193
  return key;
5015
5194
  }
5016
5195
  return {
@@ -5020,12 +5199,12 @@ function createTokenStore(opts) {
5020
5199
  const payload = Buffer.from(JSON.stringify(session), "utf8");
5021
5200
  const blob = encrypt2(loadOrCreateKey(), payload);
5022
5201
  ensureDir(authDir);
5023
- fs34.writeFileSync(tokenPath, JSON.stringify(blob, null, 2), { mode: FILE_MODE });
5202
+ fs36.writeFileSync(tokenPath, JSON.stringify(blob, null, 2), { mode: FILE_MODE });
5024
5203
  },
5025
5204
  load() {
5026
- if (!fs34.existsSync(tokenPath)) return null;
5205
+ if (!fs36.existsSync(tokenPath)) return null;
5027
5206
  try {
5028
- const blob = JSON.parse(fs34.readFileSync(tokenPath, "utf8"));
5207
+ const blob = JSON.parse(fs36.readFileSync(tokenPath, "utf8"));
5029
5208
  if (!isEncryptedBlob(blob)) return null;
5030
5209
  const { jwtToken, siteId } = JSON.parse(decrypt2(loadOrCreateKey(), blob).toString("utf8"));
5031
5210
  return typeof jwtToken === "string" && jwtToken && typeof siteId === "string" ? { jwtToken, siteId } : null;
@@ -5037,7 +5216,7 @@ function createTokenStore(opts) {
5037
5216
  }
5038
5217
  logger.debug(`[harmonyos] Discarding unreadable token at ${tokenPath}.`);
5039
5218
  try {
5040
- fs34.rmSync(tokenPath, { force: true });
5219
+ fs36.rmSync(tokenPath, { force: true });
5041
5220
  } catch {
5042
5221
  }
5043
5222
  return null;
@@ -5045,7 +5224,7 @@ function createTokenStore(opts) {
5045
5224
  },
5046
5225
  clear() {
5047
5226
  try {
5048
- fs34.rmSync(tokenPath, { force: true });
5227
+ fs36.rmSync(tokenPath, { force: true });
5049
5228
  } catch (err) {
5050
5229
  throw new OniroError(`Failed to remove ${tokenPath}.`, err);
5051
5230
  }
@@ -5054,7 +5233,7 @@ function createTokenStore(opts) {
5054
5233
  }
5055
5234
 
5056
5235
  // src/harmonyos/auth/session.ts
5057
- var crypto5 = __toESM(require("crypto"), 1);
5236
+ var crypto6 = __toESM(require("crypto"), 1);
5058
5237
 
5059
5238
  // src/harmonyos/http.ts
5060
5239
  var import_undici = require("undici");
@@ -5145,13 +5324,13 @@ function createHttpClient(opts = {}) {
5145
5324
 
5146
5325
  // src/harmonyos/auth/callbackServer.ts
5147
5326
  var http2 = __toESM(require("http"), 1);
5148
- var crypto4 = __toESM(require("crypto"), 1);
5327
+ var crypto5 = __toESM(require("crypto"), 1);
5149
5328
  var CALLBACK_PATH = "/callback";
5150
5329
  var MAX_BODY_BYTES = 65536;
5151
5330
  function codeMatches(code, secret) {
5152
5331
  const a = Buffer.from(code ?? "", "utf8");
5153
5332
  const b = Buffer.from(secret, "utf8");
5154
- return a.length === b.length && crypto4.timingSafeEqual(a, b);
5333
+ return a.length === b.length && crypto5.timingSafeEqual(a, b);
5155
5334
  }
5156
5335
  function isQuit(quit) {
5157
5336
  return quit === "true" || quit === "access_denied" || quit === "quit";
@@ -5159,8 +5338,8 @@ function isQuit(quit) {
5159
5338
  async function startCallbackServer(opts) {
5160
5339
  let resolveCallback;
5161
5340
  let rejectCallback;
5162
- const callback = new Promise((resolve7, reject) => {
5163
- resolveCallback = resolve7;
5341
+ const callback = new Promise((resolve9, reject) => {
5342
+ resolveCallback = resolve9;
5164
5343
  rejectCallback = reject;
5165
5344
  });
5166
5345
  callback.catch(() => {
@@ -5219,9 +5398,9 @@ async function startCallbackServer(opts) {
5219
5398
  }
5220
5399
  });
5221
5400
  server.keepAliveTimeout = 1;
5222
- await new Promise((resolve7, reject) => {
5401
+ await new Promise((resolve9, reject) => {
5223
5402
  server.once("error", (err) => reject(new OniroError("Failed to start the local login callback server.", err)));
5224
- server.listen(0, "127.0.0.1", resolve7);
5403
+ server.listen(0, "127.0.0.1", resolve9);
5225
5404
  });
5226
5405
  const port = server.address().port;
5227
5406
  return {
@@ -5237,12 +5416,12 @@ async function startCallbackServer(opts) {
5237
5416
  return Promise.race([callback, timeout]).finally(() => clearTimeout(timer));
5238
5417
  },
5239
5418
  stop() {
5240
- return new Promise((resolve7) => {
5419
+ return new Promise((resolve9) => {
5241
5420
  server.closeAllConnections?.();
5242
- const timer = setTimeout(resolve7, 100);
5421
+ const timer = setTimeout(resolve9, 100);
5243
5422
  server.close(() => {
5244
5423
  clearTimeout(timer);
5245
- resolve7();
5424
+ resolve9();
5246
5425
  });
5247
5426
  });
5248
5427
  }
@@ -5250,7 +5429,7 @@ async function startCallbackServer(opts) {
5250
5429
  }
5251
5430
 
5252
5431
  // src/harmonyos/auth/browser.ts
5253
- var import_node_child_process5 = require("child_process");
5432
+ var import_node_child_process6 = require("child_process");
5254
5433
  function isSafeUrl(url) {
5255
5434
  try {
5256
5435
  const parsed = new URL(url);
@@ -5267,18 +5446,18 @@ function openBrowser(url) {
5267
5446
  return Promise.reject(new OniroError(`Refusing to open unsafe URL: ${JSON.stringify(url)}`));
5268
5447
  }
5269
5448
  const [command, args] = process.platform === "win32" ? ["cmd", ["/c", "start", '""', escapeForCmd(url)]] : process.platform === "darwin" ? ["open", [url]] : ["xdg-open", [url]];
5270
- const child = (0, import_node_child_process5.spawn)(command, args, {
5449
+ const child = (0, import_node_child_process6.spawn)(command, args, {
5271
5450
  stdio: "ignore",
5272
5451
  shell: false,
5273
5452
  detached: true,
5274
5453
  windowsHide: true
5275
5454
  });
5276
5455
  child.unref();
5277
- return new Promise((resolve7, reject) => {
5456
+ return new Promise((resolve9, reject) => {
5278
5457
  child.on("error", (err) => reject(new OniroError(`Failed to open a browser (${command}).`, err)));
5279
5458
  child.on(
5280
5459
  "exit",
5281
- (code) => code === 0 ? resolve7() : reject(new OniroError(`Browser launcher '${command}' exited with code ${code}.`))
5460
+ (code) => code === 0 ? resolve9() : reject(new OniroError(`Browser launcher '${command}' exited with code ${code}.`))
5282
5461
  );
5283
5462
  });
5284
5463
  }
@@ -5360,7 +5539,7 @@ function createHarmonyOsSession(opts) {
5360
5539
  getDeveloperAgreement,
5361
5540
  async login(o = {}) {
5362
5541
  const start = HUAWEI_SITES.CN.loginUrl;
5363
- const clientSecret = crypto5.randomUUID().replace(/-/g, "");
5542
+ const clientSecret = crypto6.randomUUID().replace(/-/g, "");
5364
5543
  const server = await startCallbackServer({
5365
5544
  clientSecret,
5366
5545
  baseUrl: start,
@@ -5526,7 +5705,7 @@ function mapAgcError(operation, statusCode, reasonPhrase, body) {
5526
5705
  }
5527
5706
 
5528
5707
  // src/harmonyos/signing/profile.ts
5529
- var fs35 = __toESM(require("fs"), 1);
5708
+ var fs37 = __toESM(require("fs"), 1);
5530
5709
  var import_node_crypto3 = require("crypto");
5531
5710
  var import_node_forge = __toESM(require("node-forge"), 1);
5532
5711
  function extractProfileJson(buffer) {
@@ -5558,7 +5737,7 @@ function extractProfileJson(buffer) {
5558
5737
  function readProvisionProfile(profilePath) {
5559
5738
  let raw;
5560
5739
  try {
5561
- const json = extractProfileJson(fs35.readFileSync(profilePath));
5740
+ const json = extractProfileJson(fs37.readFileSync(profilePath));
5562
5741
  if (!json) return null;
5563
5742
  raw = JSON.parse(json);
5564
5743
  } catch {
@@ -5587,7 +5766,7 @@ function parseCertificate(pem) {
5587
5766
  }
5588
5767
  function keystoreHoldsKey(p12Path, keyAlias, keyPwd, certificate) {
5589
5768
  try {
5590
- const der = import_node_forge.default.util.createBuffer(fs35.readFileSync(p12Path).toString("binary"));
5769
+ const der = import_node_forge.default.util.createBuffer(fs37.readFileSync(p12Path).toString("binary"));
5591
5770
  const p12 = import_node_forge.default.pkcs12.pkcs12FromAsn1(import_node_forge.default.asn1.fromDer(der), keyPwd);
5592
5771
  const certBag = import_node_forge.default.pki.oids.certBag ?? "1.2.840.113549.1.12.10.1.3";
5593
5772
  const wanted = certificate.publicKey.export({ type: "spki", format: "der" });
@@ -5603,7 +5782,7 @@ function keystoreHoldsKey(p12Path, keyAlias, keyPwd, certificate) {
5603
5782
  }
5604
5783
  }
5605
5784
  function verifySigningMaterial(opts) {
5606
- const pems = fs35.readFileSync(opts.cerPath, "utf8").match(/-----BEGIN CERTIFICATE-----[\s\S]*?-----END CERTIFICATE-----/g);
5785
+ const pems = fs37.readFileSync(opts.cerPath, "utf8").match(/-----BEGIN CERTIFICATE-----[\s\S]*?-----END CERTIFICATE-----/g);
5607
5786
  const chain = (pems ?? []).map(parseCertificate);
5608
5787
  const leaf = chain.find((c) => !c.ca) ?? chain.at(-1);
5609
5788
  if (!leaf) throw new AgcError(SIGNING_ERRORS.CERT_INVALID);
@@ -5621,15 +5800,15 @@ function verifySigningMaterial(opts) {
5621
5800
  }
5622
5801
 
5623
5802
  // src/harmonyos/signing/autoSign.ts
5624
- var crypto9 = __toESM(require("crypto"), 1);
5625
- var fs40 = __toESM(require("fs"), 1);
5626
- var path39 = __toESM(require("path"), 1);
5803
+ var crypto10 = __toESM(require("crypto"), 1);
5804
+ var fs42 = __toESM(require("fs"), 1);
5805
+ var path41 = __toESM(require("path"), 1);
5627
5806
  var import_json57 = __toESM(require("json5"), 1);
5628
5807
 
5629
5808
  // src/harmonyos/signing/agc.ts
5630
- var crypto6 = __toESM(require("crypto"), 1);
5631
- var fs36 = __toESM(require("fs"), 1);
5632
- var path34 = __toESM(require("path"), 1);
5809
+ var crypto7 = __toESM(require("crypto"), 1);
5810
+ var fs38 = __toESM(require("fs"), 1);
5811
+ var path36 = __toESM(require("path"), 1);
5633
5812
  var ENDPOINTS = {
5634
5813
  CERT_LIST: "/api/cps/harmony-cert-manage/v1/cert/list",
5635
5814
  CERT_ADD: "/api/cps/harmony-cert-manage/v1/cert/add",
@@ -5677,7 +5856,7 @@ function createAgcClient(http3, auth) {
5677
5856
  }
5678
5857
  },
5679
5858
  async addDevice(udid, deviceKind) {
5680
- const deviceName = `oniro_app_device_${crypto6.randomBytes(4).toString("hex")}`;
5859
+ const deviceName = `oniro_app_device_${crypto7.randomBytes(4).toString("hex")}`;
5681
5860
  await call("device", "post", ENDPOINTS.DEVICE_ADD, {
5682
5861
  body: { deviceName, udid, deviceType: String(DEVICE_TYPE_CODES[deviceKind] ?? DEVICE_TYPE_CODES.phone) }
5683
5862
  });
@@ -5711,12 +5890,12 @@ function createAgcClient(http3, auth) {
5711
5890
  const target = urlsInfo?.[0];
5712
5891
  if (!target) throw mapAgcError(operation, 200, "", "");
5713
5892
  const buffer = await http3.getBinary(target.newUrl, { timeoutMs: 6e4 });
5714
- const actual = crypto6.createHash("sha256").update(buffer).digest("hex");
5893
+ const actual = crypto7.createHash("sha256").update(buffer).digest("hex");
5715
5894
  if (target.sha256 && actual !== target.sha256.toLowerCase()) {
5716
5895
  throw new ChecksumMismatchError(target.sha256, actual);
5717
5896
  }
5718
- fs36.mkdirSync(path34.dirname(dest), { recursive: true, mode: 448 });
5719
- fs36.writeFileSync(dest, buffer, { mode: 384 });
5897
+ fs38.mkdirSync(path36.dirname(dest), { recursive: true, mode: 448 });
5898
+ fs38.writeFileSync(dest, buffer, { mode: 384 });
5720
5899
  }
5721
5900
  };
5722
5901
  }
@@ -5771,23 +5950,23 @@ async function registerDevices(agc, connected, logger) {
5771
5950
  }
5772
5951
 
5773
5952
  // src/harmonyos/signing/keystore.ts
5774
- var crypto7 = __toESM(require("crypto"), 1);
5775
- var fs38 = __toESM(require("fs"), 1);
5776
- var path36 = __toESM(require("path"), 1);
5953
+ var crypto8 = __toESM(require("crypto"), 1);
5954
+ var fs40 = __toESM(require("fs"), 1);
5955
+ var path38 = __toESM(require("path"), 1);
5777
5956
 
5778
5957
  // src/harmonyos/java.ts
5779
- var fs37 = __toESM(require("fs"), 1);
5780
- var path35 = __toESM(require("path"), 1);
5958
+ var fs39 = __toESM(require("fs"), 1);
5959
+ var path37 = __toESM(require("path"), 1);
5781
5960
  var JAVA_BIN = process.platform === "win32" ? "java.exe" : "java";
5782
5961
  function findJava(sdk, env = process.env) {
5783
5962
  const candidates = [
5784
- env.JAVA_HOME && path35.join(env.JAVA_HOME, "bin", JAVA_BIN),
5785
- sdk && path35.join(sdk.installRoot, "jbr", "bin", JAVA_BIN),
5963
+ env.JAVA_HOME && path37.join(env.JAVA_HOME, "bin", JAVA_BIN),
5964
+ sdk && path37.join(sdk.installRoot, "jbr", "bin", JAVA_BIN),
5786
5965
  // macOS: the JBR is itself a bundle inside Studio's Contents.
5787
- sdk && path35.join(sdk.installRoot, "jbr", "Contents", "Home", "bin", JAVA_BIN),
5788
- ...(env.PATH ?? "").split(path35.delimiter).map((dir) => dir && path35.join(dir, JAVA_BIN))
5966
+ sdk && path37.join(sdk.installRoot, "jbr", "Contents", "Home", "bin", JAVA_BIN),
5967
+ ...(env.PATH ?? "").split(path37.delimiter).map((dir) => dir && path37.join(dir, JAVA_BIN))
5789
5968
  ];
5790
- const found = candidates.find((c) => !!c && fs37.existsSync(c));
5969
+ const found = candidates.find((c) => !!c && fs39.existsSync(c));
5791
5970
  if (found) return found;
5792
5971
  throw new OniroError(
5793
5972
  "No Java runtime found. HarmonyOS signing runs the SDK's hap-sign-tool.jar, which needs one: install a JDK and set JAVA_HOME, or install DevEco Studio, whose bundled runtime is used automatically."
@@ -5800,49 +5979,77 @@ var SIGN_ALG = "SHA256withECDSA";
5800
5979
  function redactArgs(args) {
5801
5980
  return args.map((arg, i) => args[i - 1] === "-keyPwd" || args[i - 1] === "-keystorePwd" ? "******" : arg);
5802
5981
  }
5803
- async function generateKeystoreAndCsr(opts) {
5804
- const jar = path36.join(opts.sdk.sdkPath, "default", "openharmony", "toolchains", "lib", "hap-sign-tool.jar");
5805
- if (!fs38.existsSync(jar)) {
5982
+ function hapSignToolJar(sdk) {
5983
+ const jar = path38.join(sdk.sdkPath, "default", "openharmony", "toolchains", "lib", "hap-sign-tool.jar");
5984
+ if (!fs40.existsSync(jar)) {
5806
5985
  throw new OniroError(`hap-sign-tool.jar not found at ${jar}; the HarmonyOS SDK looks incomplete.`);
5807
5986
  }
5987
+ return jar;
5988
+ }
5989
+ async function runHapSignTool(opts) {
5990
+ const jar = hapSignToolJar(opts.sdk);
5808
5991
  const java = findJava(opts.sdk);
5809
- const keyPwd = crypto7.randomBytes(12).toString("base64url").replaceAll(/[-_]/g, "");
5810
- fs38.mkdirSync(path36.dirname(opts.p12Path), { recursive: true, mode: 448 });
5811
- const run = async (what, args) => {
5812
- const argv = ["-jar", jar, ...args, "-keyAlias", KEY_ALIAS, "-keystoreFile", opts.p12Path, "-keystorePwd", keyPwd, "-keyPwd", keyPwd];
5813
- opts.logger.debug(`[harmonyos] ${java} ${redactArgs(argv).join(" ")}`);
5814
- const result = await runProcess({ command: java, args: argv, timeoutMs: 12e4, logger: opts.logger });
5815
- if (result.code !== 0) {
5816
- const detail = (result.stderr || result.stdout || "").trim();
5817
- throw new OniroError(`hap-sign-tool failed to ${what} (exit ${result.code}).${detail ? `
5992
+ const argv = ["-jar", jar, ...opts.args, "-keyAlias", KEY_ALIAS, "-keystoreFile", opts.p12Path, "-keystorePwd", opts.keyPwd, "-keyPwd", opts.keyPwd];
5993
+ opts.logger.debug(`[harmonyos] ${java} ${redactArgs(argv).join(" ")}`);
5994
+ const result = await runProcess({ command: java, args: argv, timeoutMs: 12e4, logger: opts.logger });
5995
+ if (result.code !== 0) {
5996
+ const detail = (result.stderr || result.stdout || "").trim();
5997
+ throw new OniroError(`hap-sign-tool failed to ${opts.what} (exit ${result.code}).${detail ? `
5818
5998
  ${detail}` : ""}`);
5819
- }
5820
- };
5999
+ }
6000
+ }
6001
+ async function generateKeystoreAndCsr(opts) {
6002
+ hapSignToolJar(opts.sdk);
6003
+ findJava(opts.sdk);
6004
+ const keyPwd = crypto8.randomBytes(12).toString("base64url").replaceAll(/[-_]/g, "");
6005
+ fs40.mkdirSync(path38.dirname(opts.p12Path), { recursive: true, mode: 448 });
6006
+ const run = (what, args) => runHapSignTool({ ...opts, what, args, keyPwd });
5821
6007
  opts.logger.info("[harmonyos] Generating the local keystore and certificate signing request...");
5822
6008
  await run("generate the key pair", ["generate-keypair", "-keyAlg", "ECC", "-keySize", "NIST-P-256"]);
5823
6009
  await run("generate the CSR", ["generate-csr", "-subject", "CN=DebugKey", "-signAlg", SIGN_ALG, "-outFile", opts.csrPath]);
5824
- return { keyPwd, csr: fs38.readFileSync(opts.csrPath, "utf8") };
6010
+ return { keyPwd, csr: fs40.readFileSync(opts.csrPath, "utf8") };
6011
+ }
6012
+ async function signHapFile(opts) {
6013
+ await runHapSignTool({
6014
+ ...opts,
6015
+ what: `sign ${opts.inFile}`,
6016
+ args: [
6017
+ "sign-app",
6018
+ "-mode",
6019
+ "localSign",
6020
+ "-signAlg",
6021
+ SIGN_ALG,
6022
+ "-appCertFile",
6023
+ opts.cerPath,
6024
+ "-profileFile",
6025
+ opts.profilePath,
6026
+ "-inFile",
6027
+ opts.inFile,
6028
+ "-outFile",
6029
+ opts.outFile
6030
+ ]
6031
+ });
5825
6032
  }
5826
6033
 
5827
6034
  // src/harmonyos/signing/paths.ts
5828
- var crypto8 = __toESM(require("crypto"), 1);
5829
- var path37 = __toESM(require("path"), 1);
6035
+ var crypto9 = __toESM(require("crypto"), 1);
6036
+ var path39 = __toESM(require("path"), 1);
5830
6037
  function sanitizeNamePart(raw) {
5831
6038
  const safe = (raw?.trim() ?? "").replace(/[\\/:*?"<>|=.-]+/g, "_");
5832
6039
  return safe.slice(0, 64) || "default";
5833
6040
  }
5834
6041
  function resolveSigningMaterialPaths(config, productName, projectRoot) {
5835
- const hash = crypto8.createHash("sha256").update(projectRoot, "utf8").digest("base64url").replaceAll(/[-_]/g, "");
5836
- const base = path37.join(
6042
+ const hash = crypto9.createHash("sha256").update(projectRoot, "utf8").digest("base64url").replaceAll(/[-_]/g, "");
6043
+ const base = path39.join(
5837
6044
  config.get("harmonyosSigningDir", defaultPaths.harmonyosSigningDir()),
5838
- `${sanitizeNamePart(productName)}_${path37.basename(projectRoot)}_${hash}=`
6045
+ `${sanitizeNamePart(productName)}_${path39.basename(projectRoot)}_${hash}=`
5839
6046
  );
5840
6047
  return { p12Path: `${base}.p12`, csrPath: `${base}.csr`, cerPath: `${base}.cer`, profilePath: `${base}.p7b` };
5841
6048
  }
5842
6049
 
5843
6050
  // src/harmonyos/signing/project.ts
5844
- var fs39 = __toESM(require("fs"), 1);
5845
- var path38 = __toESM(require("path"), 1);
6051
+ var fs41 = __toESM(require("fs"), 1);
6052
+ var path40 = __toESM(require("path"), 1);
5846
6053
  var import_json56 = __toESM(require("json5"), 1);
5847
6054
 
5848
6055
  // src/harmonyos/signing/aclPermissionList.ts
@@ -5910,9 +6117,9 @@ var ACL_PERMISSIONS = /* @__PURE__ */ new Set([
5910
6117
 
5911
6118
  // src/harmonyos/signing/project.ts
5912
6119
  function readRequestedPermissions(moduleConfigPath) {
5913
- if (!fs39.existsSync(moduleConfigPath)) return [];
6120
+ if (!fs41.existsSync(moduleConfigPath)) return [];
5914
6121
  try {
5915
- const parsed = import_json56.default.parse(fs39.readFileSync(moduleConfigPath, "utf-8"));
6122
+ const parsed = import_json56.default.parse(fs41.readFileSync(moduleConfigPath, "utf-8"));
5916
6123
  return (parsed.module?.requestPermissions ?? []).map((p) => p?.name).filter((n) => typeof n === "string" && n.length > 0);
5917
6124
  } catch {
5918
6125
  return [];
@@ -5921,32 +6128,38 @@ function readRequestedPermissions(moduleConfigPath) {
5921
6128
  function collectAclPermissions(projectDir, logger = noopLogger) {
5922
6129
  const requested = /* @__PURE__ */ new Set();
5923
6130
  for (const module2 of listModules({ projectDir })) {
5924
- const moduleRoot = path38.resolve(projectDir, module2.srcPath);
6131
+ const moduleRoot = path40.resolve(projectDir, module2.srcPath);
5925
6132
  for (const sourceSet of ["main", "ohosTest"]) {
5926
6133
  for (const name of readRequestedPermissions(
5927
- path38.join(moduleRoot, "src", sourceSet, "module.json5")
6134
+ path40.join(moduleRoot, "src", sourceSet, "module.json5")
5928
6135
  )) {
5929
6136
  requested.add(name);
5930
6137
  }
5931
6138
  }
5932
6139
  }
5933
- const acls = [...requested].filter((p) => ACL_PERMISSIONS.has(p)).sort();
6140
+ const acls = aclPermissionsAmong(requested);
5934
6141
  if (acls.length > 0) {
5935
6142
  logger.info(`[harmonyos] Project requests ${acls.length} ACL permission(s): ${acls.join(", ")}`);
5936
6143
  }
5937
6144
  return acls;
5938
6145
  }
6146
+ function aclPermissionsAmong(names) {
6147
+ return [...new Set(names)].filter((p) => ACL_PERMISSIONS.has(p)).sort();
6148
+ }
5939
6149
 
5940
6150
  // src/harmonyos/signing/autoSign.ts
5941
6151
  function shouldRegenerate(input) {
5942
6152
  if (input.force) return "--force was passed";
5943
- const missing = Object.values(input.paths).find((file) => !fs40.existsSync(file));
6153
+ const missing = Object.values(input.paths).find((file) => !fs42.existsSync(file));
5944
6154
  if (missing) return `${missing} is missing`;
5945
6155
  const config = input.buildProfile.app?.signingConfigs?.find((c) => c.name === input.productName);
5946
6156
  if (config?.type !== "HarmonyOS" || config.material?.profile !== input.paths.profilePath) {
5947
6157
  return "build-profile.json5 has no signingConfig for this material";
5948
6158
  }
5949
- const profile = readProvisionProfile(input.paths.profilePath);
6159
+ return profileProblem({ ...input, profilePath: input.paths.profilePath });
6160
+ }
6161
+ function profileProblem(input) {
6162
+ const profile = readProvisionProfile(input.profilePath);
5950
6163
  if (!profile) return "the provisioning profile could not be read";
5951
6164
  if (profile.bundleName !== input.bundleName) return `the profile is for bundle '${profile.bundleName}'`;
5952
6165
  if (profile.developerId && profile.developerId !== input.teamId) {
@@ -5979,25 +6192,23 @@ function writeSigningConfig(opts) {
5979
6192
  for (const product of app.products ?? []) {
5980
6193
  if (product.name === opts.productName) product.signingConfig = opts.productName;
5981
6194
  }
5982
- fs40.writeFileSync(opts.buildProfilePath, JSON.stringify(opts.buildProfile, null, 2));
6195
+ fs42.writeFileSync(opts.buildProfilePath, JSON.stringify(opts.buildProfile, null, 2));
5983
6196
  }
5984
6197
  async function harmonyOsAutoSign(opts) {
5985
6198
  const logger = opts.logger ?? noopLogger;
5986
6199
  const productName = opts.productName ?? "default";
5987
- const projectDir = path39.resolve(opts.projectDir);
5988
- const buildProfilePath = path39.join(projectDir, "build-profile.json5");
6200
+ const projectDir = path41.resolve(opts.projectDir);
6201
+ const buildProfilePath = path41.join(projectDir, "build-profile.json5");
5989
6202
  let buildProfile;
5990
6203
  try {
5991
- buildProfile = import_json57.default.parse(fs40.readFileSync(buildProfilePath, "utf-8"));
6204
+ buildProfile = import_json57.default.parse(fs42.readFileSync(buildProfilePath, "utf-8"));
5992
6205
  } catch (err) {
5993
6206
  throw new OniroError(`Could not read ${buildProfilePath}: ${err.message}`, err);
5994
6207
  }
5995
6208
  const product = buildProfile.app?.products?.find((p) => p.name === productName);
5996
6209
  if (!product) throw new OniroError(`${buildProfilePath} declares no product named '${productName}'.`);
5997
6210
  const bundleName = product.bundleName || getBundleName(projectDir);
5998
- if (!isValidBundleName(bundleName) || bundleName.split(".").length < 3 || bundleName.length < 7 || bundleName.length > 128) {
5999
- throw new AgcError(SIGNING_ERRORS.BUNDLE_NAME_INVALID);
6000
- }
6211
+ assertAgcBundleName(bundleName);
6001
6212
  const sdk = requireHarmonyOsSdk({ config: opts.config, logger });
6002
6213
  const auth = await opts.session.resolveAgcAuth({ teamId: opts.teamId });
6003
6214
  const aclPermissions = collectAclPermissions(projectDir, logger);
@@ -6026,14 +6237,47 @@ async function harmonyOsAutoSign(opts) {
6026
6237
  return result(false);
6027
6238
  }
6028
6239
  logger.info(`[harmonyos] Regenerating signing material: ${reason}.`);
6029
- const agc = createAgcClient(opts.http ?? createHttpClient({ logger }), auth);
6240
+ const keyPwd = await issueSigningMaterial({
6241
+ agc: createAgcClient(opts.http ?? createHttpClient({ logger }), auth),
6242
+ local,
6243
+ sdk,
6244
+ paths,
6245
+ bundleName,
6246
+ aclPermissions,
6247
+ teamId: auth.teamId,
6248
+ logger
6249
+ });
6250
+ const encryptedPassword = encryptPwd(keyPwd, sharedMaterialDir(paths));
6251
+ writeSigningConfig({ buildProfilePath, buildProfile, productName, paths, encryptedPassword });
6252
+ logger.info(`[harmonyos] Wrote signingConfigs['${productName}'] to ${buildProfilePath}.`);
6253
+ return result(true);
6254
+ }
6255
+ function assertAgcBundleName(bundleName) {
6256
+ if (!isValidBundleName(bundleName) || bundleName.split(".").length < 3 || bundleName.length < 7 || bundleName.length > 128) {
6257
+ throw new AgcError(SIGNING_ERRORS.BUNDLE_NAME_INVALID);
6258
+ }
6259
+ }
6260
+ function materialDirFor(paths) {
6261
+ return path41.join(path41.dirname(paths.p12Path), "material");
6262
+ }
6263
+ function sharedMaterialDir(paths) {
6264
+ const materialDir = materialDirFor(paths);
6265
+ try {
6266
+ getKey(materialDir);
6267
+ } catch {
6268
+ createMaterial(materialDir);
6269
+ }
6270
+ return materialDir;
6271
+ }
6272
+ async function issueSigningMaterial(opts) {
6273
+ const { agc, local, sdk, paths, logger } = opts;
6030
6274
  const deviceIds = await registerDevices(agc, local.devices, logger);
6031
6275
  if (local.devices.length === 0) {
6032
6276
  const cause = local.skipped.length > 0 ? "No connected device could be registered" : "No device is connected";
6033
6277
  logger.warn(`[harmonyos] ${cause}; the profile will name the ${deviceIds.length} device(s) already registered to this team.`);
6034
6278
  }
6035
- for (const file of Object.values(paths)) fs40.rmSync(file, { force: true });
6036
- const certName = `oniro_debug_${auth.teamId.replace(/[\\/.:]/g, "")}.cer`;
6279
+ for (const file of Object.values(paths)) fs42.rmSync(file, { force: true });
6280
+ const certName = `oniro_debug_${opts.teamId.replace(/[\\/.:]/g, "")}.cer`;
6037
6281
  const stale = (await agc.listCertificates()).find((c) => c.certName === certName);
6038
6282
  if (stale) {
6039
6283
  logger.info(`[harmonyos] Replacing the previous '${certName}' certificate in AppGallery Connect...`);
@@ -6047,11 +6291,11 @@ async function harmonyOsAutoSign(opts) {
6047
6291
  await agc.download("cert", cert.certObjectId, paths.cerPath);
6048
6292
  logger.info("[harmonyos] Issuing the debug provisioning profile...");
6049
6293
  const profile = await agc.addDebugProfile({
6050
- provisionName: `oniro_${crypto9.randomBytes(6).toString("hex")}`,
6051
- bundleName,
6294
+ provisionName: `oniro_${crypto10.randomBytes(6).toString("hex")}`,
6295
+ bundleName: opts.bundleName,
6052
6296
  certId: cert.id,
6053
6297
  deviceIds,
6054
- aclPermissions
6298
+ aclPermissions: opts.aclPermissions
6055
6299
  });
6056
6300
  try {
6057
6301
  await agc.download("profile", profile.provisionFileUrl, paths.profilePath);
@@ -6063,22 +6307,86 @@ async function harmonyOsAutoSign(opts) {
6063
6307
  try {
6064
6308
  verifySigningMaterial({ ...paths, keyAlias: KEY_ALIAS, keyPwd });
6065
6309
  } catch (err) {
6066
- fs40.rmSync(paths.profilePath, { force: true });
6310
+ fs42.rmSync(paths.profilePath, { force: true });
6067
6311
  throw err;
6068
6312
  }
6069
- const materialDir = path39.join(path39.dirname(paths.p12Path), "material");
6313
+ return keyPwd;
6314
+ }
6315
+
6316
+ // src/harmonyos/signing/signHap.ts
6317
+ var fs43 = __toESM(require("fs"), 1);
6318
+ var path42 = __toESM(require("path"), 1);
6319
+ function resolveHapSigningMaterialPaths(config, bundleName) {
6320
+ const paths = resolveSigningMaterialPaths(config, "hap", bundleName);
6321
+ return { ...paths, pwdPath: paths.p12Path.replace(/\.p12$/, ".pwd") };
6322
+ }
6323
+ function readStoredPassword(paths) {
6070
6324
  try {
6071
- getKey(materialDir);
6325
+ return decryptPwd(fs43.readFileSync(paths.pwdPath, "utf8").trim(), materialDirFor(paths));
6072
6326
  } catch {
6073
- createMaterial(materialDir);
6327
+ return null;
6074
6328
  }
6075
- writeSigningConfig({ buildProfilePath, buildProfile, productName, paths, encryptedPassword: encryptPwd(keyPwd, materialDir) });
6076
- logger.info(`[harmonyos] Wrote signingConfigs['${productName}'] to ${buildProfilePath}.`);
6077
- return result(true);
6329
+ }
6330
+ async function harmonyOsSignHap(opts) {
6331
+ const logger = opts.logger ?? noopLogger;
6332
+ const hapPath = path42.resolve(opts.hapPath);
6333
+ const outPath = path42.resolve(opts.outPath ?? defaultSignedHapPath(hapPath));
6334
+ const info = await readHapSigningInfo(hapPath);
6335
+ const { bundleName } = info;
6336
+ if (info.compileSdkType === "OpenHarmony") {
6337
+ throw new OniroError(
6338
+ `${hapPath} was built for OpenHarmony, so a HarmonyOS device would not run it; sign it without --harmonyos.`
6339
+ );
6340
+ }
6341
+ assertAgcBundleName(bundleName);
6342
+ const aclPermissions = aclPermissionsAmong(info.requestedPermissions);
6343
+ if (aclPermissions.length > 0) {
6344
+ logger.info(`[harmonyos] HAP requests ${aclPermissions.length} ACL permission(s): ${aclPermissions.join(", ")}`);
6345
+ }
6346
+ const sdk = requireHarmonyOsSdk({ config: opts.config, logger });
6347
+ const auth = await opts.session.resolveAgcAuth({ teamId: opts.teamId });
6348
+ const paths = resolveHapSigningMaterialPaths(opts.config, bundleName);
6349
+ const { pwdPath, ...material } = paths;
6350
+ logger.info(`[harmonyos] Signing ${path42.basename(hapPath)} as ${bundleName} (team ${auth.teamId}).`);
6351
+ const local = await collectLocalDevices(opts.config, logger);
6352
+ let keyPwd = opts.force ? null : readStoredPassword(paths);
6353
+ const missing = Object.values(material).find((file) => !fs43.existsSync(file));
6354
+ const reason = opts.force ? "--force was passed" : missing ? `${missing} is missing` : keyPwd === null ? "the stored keystore password could not be read" : profileProblem({
6355
+ profilePath: material.profilePath,
6356
+ bundleName,
6357
+ teamId: auth.teamId,
6358
+ connectedUdids: local.devices.map((d) => d.udid),
6359
+ aclPermissions
6360
+ });
6361
+ if (!reason && keyPwd !== null) {
6362
+ logger.info("[harmonyos] Reusing the existing signing material, which is still valid.");
6363
+ } else {
6364
+ logger.info(`[harmonyos] Regenerating signing material: ${reason}.`);
6365
+ fs43.rmSync(pwdPath, { force: true });
6366
+ keyPwd = await issueSigningMaterial({
6367
+ agc: createAgcClient(opts.http ?? createHttpClient({ logger }), auth),
6368
+ local,
6369
+ sdk,
6370
+ paths: material,
6371
+ bundleName,
6372
+ aclPermissions,
6373
+ teamId: auth.teamId,
6374
+ logger
6375
+ });
6376
+ fs43.writeFileSync(pwdPath, encryptPwd(keyPwd, sharedMaterialDir(material)), { mode: 384 });
6377
+ }
6378
+ const password = keyPwd;
6379
+ await writeHapAtomically(
6380
+ outPath,
6381
+ (tmpPath) => signHapFile({ sdk, inFile: hapPath, outFile: tmpPath, ...material, keyPwd: password, logger })
6382
+ );
6383
+ const deviceIds = readProvisionProfile(material.profilePath)?.deviceIds ?? [];
6384
+ logger.info(`[harmonyos] Wrote ${outPath}.`);
6385
+ return { bundleName, teamId: auth.teamId, paths: material, regenerated: !!reason, deviceIds, outPath };
6078
6386
  }
6079
6387
 
6080
6388
  // src/index.ts
6081
- var VERSION = "0.15.1";
6389
+ var VERSION = "0.16.0";
6082
6390
  // Annotate the CommonJS export names for ESM import in node:
6083
6391
  0 && (module.exports = {
6084
6392
  ALL_SDKS,
@@ -6096,6 +6404,7 @@ var VERSION = "0.15.1";
6096
6404
  OHOS_DAILY_BUILDS_URL,
6097
6405
  OHOS_URL_BASE,
6098
6406
  OniroError,
6407
+ SIGNED_HAP_CHUNK_SIZE,
6099
6408
  SIGNING_ERRORS,
6100
6409
  SdkNotInstalledError,
6101
6410
  TMP_DIR_HINT,
@@ -6131,6 +6440,7 @@ var VERSION = "0.15.1";
6131
6440
  dedupEntries,
6132
6441
  defaultContext,
6133
6442
  defaultPaths,
6443
+ defaultSignedHapPath,
6134
6444
  describeLayoutNode,
6135
6445
  detectAaStartFailure,
6136
6446
  detectCmdToolsFlavour,
@@ -6197,6 +6507,7 @@ var VERSION = "0.15.1";
6197
6507
  getSupportedSdksForUi,
6198
6508
  harmonyOsAutoSign,
6199
6509
  harmonyOsSdkLabelForApi,
6510
+ harmonyOsSignHap,
6200
6511
  hdcClientFailure,
6201
6512
  hdcExec,
6202
6513
  hdcTargetArgs,
@@ -6250,6 +6561,7 @@ var VERSION = "0.15.1";
6250
6561
  prepareSigning,
6251
6562
  pruneLayout,
6252
6563
  readHapBundleName,
6564
+ readHapSigningInfo,
6253
6565
  readJson5File,
6254
6566
  readJsonFile,
6255
6567
  readProvisionProfile,
@@ -6277,6 +6589,7 @@ var VERSION = "0.15.1";
6277
6589
  setHilogLevel,
6278
6590
  shell,
6279
6591
  shellChecked,
6592
+ signOpenHarmonyHap,
6280
6593
  spawnCompat,
6281
6594
  startEmulator,
6282
6595
  staticConfig,
@@ -6296,6 +6609,7 @@ var VERSION = "0.15.1";
6296
6609
  waitForTarget,
6297
6610
  watchLog,
6298
6611
  waypointsToRawEvents,
6612
+ withHapZip,
6299
6613
  withoutWindow,
6300
6614
  writeJson5File,
6301
6615
  writeJsonFile