@oniroproject/core 0.15.0 → 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,
@@ -163,6 +166,7 @@ __export(index_exports, {
163
166
  isOutOfSpaceError: () => isOutOfSpaceError,
164
167
  isRamBackedPath: () => isRamBackedPath,
165
168
  isUnsignedHap: () => isUnsignedHap,
169
+ isUsableDevice: () => isUsableDevice,
166
170
  isValidBundleName: () => isValidBundleName,
167
171
  isValidProjectName: () => isValidProjectName,
168
172
  killProcessTree: () => killProcessTree,
@@ -198,6 +202,7 @@ __export(index_exports, {
198
202
  prepareSigning: () => prepareSigning,
199
203
  pruneLayout: () => pruneLayout,
200
204
  readHapBundleName: () => readHapBundleName,
205
+ readHapSigningInfo: () => readHapSigningInfo,
201
206
  readJson5File: () => readJson5File,
202
207
  readJsonFile: () => readJsonFile,
203
208
  readProvisionProfile: () => readProvisionProfile,
@@ -225,6 +230,7 @@ __export(index_exports, {
225
230
  setHilogLevel: () => setHilogLevel,
226
231
  shell: () => shell,
227
232
  shellChecked: () => shellChecked,
233
+ signOpenHarmonyHap: () => signOpenHarmonyHap,
228
234
  spawnCompat: () => spawnCompat,
229
235
  startEmulator: () => startEmulator,
230
236
  staticConfig: () => staticConfig,
@@ -244,6 +250,7 @@ __export(index_exports, {
244
250
  waitForTarget: () => waitForTarget,
245
251
  watchLog: () => watchLog,
246
252
  waypointsToRawEvents: () => waypointsToRawEvents,
253
+ withHapZip: () => withHapZip,
247
254
  withoutWindow: () => withoutWindow,
248
255
  writeJson5File: () => writeJson5File,
249
256
  writeJsonFile: () => writeJsonFile
@@ -833,14 +840,14 @@ var pipelineAsync = (0, import_node_util.promisify)(import_node_stream.pipeline)
833
840
  async function downloadFile(opts) {
834
841
  const { url, dest, progress, abortSignal } = opts;
835
842
  const proto = url.startsWith("https") ? https : http;
836
- return new Promise((resolve7, reject) => {
843
+ return new Promise((resolve9, reject) => {
837
844
  const s = Math.max(0, Math.min(100, opts.start ?? 0));
838
845
  const r = Math.max(0, Math.min(100 - s, opts.range ?? 100));
839
846
  let settled = false;
840
847
  const done = (err) => {
841
848
  if (settled) return;
842
849
  settled = true;
843
- err ? reject(err) : resolve7();
850
+ err ? reject(err) : resolve9();
844
851
  };
845
852
  if (abortSignal?.aborted) {
846
853
  done(new CancelledError("Download cancelled before start."));
@@ -1018,15 +1025,26 @@ async function extractZipWithProgress(opts) {
1018
1025
  async function extractTarball(tarPath, dest, strip = 0, filter) {
1019
1026
  await tar.x({ file: tarPath, cwd: dest, strip, ...filter ? { filter: (p) => filter(p) } : {} });
1020
1027
  }
1021
- async function readZipEntry(zipPath, match) {
1022
- 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()));
1023
1032
  try {
1024
- const entry = Object.values(await zip.entries()).find((e) => !e.isDirectory && match(e.name));
1025
- 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) });
1026
1038
  } finally {
1027
- await zip.close();
1039
+ await close();
1028
1040
  }
1029
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
+ }
1030
1048
 
1031
1049
  // src/sdk/list.ts
1032
1050
  var fs7 = __toESM(require("fs"), 1);
@@ -1677,9 +1695,16 @@ var SIGNING_KIND_CONFIG = {
1677
1695
  certBasename: "OpenHarmonyApplication.cer"
1678
1696
  }
1679
1697
  };
1680
- function copyFilesToProject(projectDir, paths, kind, logger, applicationCertPath) {
1681
- logger.info("[sign] Copying signing material into project...");
1682
- 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}...`);
1683
1708
  fs13.mkdirSync(signaturesDir, { recursive: true });
1684
1709
  fs13.copyFileSync(paths.keystore, path14.join(signaturesDir, "OpenHarmony.p12"));
1685
1710
  fs13.copyFileSync(paths.profileCert, path14.join(signaturesDir, "OpenHarmonyProfileRelease.pem"));
@@ -1706,10 +1731,7 @@ function extractLeafCertificate(chainPath) {
1706
1731
  return parts[2].trim() + "\n-----END CERTIFICATE-----\n";
1707
1732
  }
1708
1733
  function modifyProfileTemplate(projectDir, apl, appFeature, logger, options = {}) {
1709
- const kind = options.kind ?? pickSigningKind(apl);
1710
- logger.info(`[sign] Modifying profile template (apl=${apl}, app-feature=${appFeature}, kind=${kind})...`);
1711
1734
  const appJsonPath = path14.join(projectDir, "AppScope/app.json5");
1712
- const profileTemplatePath = path14.join(projectDir, "signatures/UnsgnedReleasedProfileTemplate.json");
1713
1735
  if (!fs13.existsSync(appJsonPath)) {
1714
1736
  throw new OniroError(`${appJsonPath} does not exist.`);
1715
1737
  }
@@ -1719,26 +1741,28 @@ function modifyProfileTemplate(projectDir, apl, appFeature, logger, options = {}
1719
1741
  } catch (e) {
1720
1742
  throw new OniroError(`Error parsing ${appJsonPath}: ${e.message}`, e);
1721
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");
1722
1753
  let profileTemplate;
1723
1754
  try {
1724
1755
  profileTemplate = JSON.parse(fs13.readFileSync(profileTemplatePath, "utf-8"));
1725
1756
  } catch (e) {
1726
1757
  throw new OniroError(`Error parsing ${profileTemplatePath}: ${e.message}`, e);
1727
1758
  }
1728
- if (!appJson.app?.bundleName) {
1729
- throw new OniroError("app.json5 does not contain app.bundleName.");
1730
- }
1731
1759
  if (!profileTemplate["bundle-info"]) {
1732
1760
  throw new OniroError("UnsgnedReleasedProfileTemplate.json is missing the bundle-info section.");
1733
1761
  }
1734
- profileTemplate["bundle-info"]["bundle-name"] = appJson.app.bundleName;
1762
+ profileTemplate["bundle-info"]["bundle-name"] = bundleName;
1735
1763
  profileTemplate["bundle-info"]["apl"] = apl;
1736
1764
  profileTemplate["bundle-info"]["app-feature"] = appFeature;
1737
- const distCertChainPath = path14.join(
1738
- projectDir,
1739
- "signatures",
1740
- SIGNING_KIND_CONFIG[kind].certBasename
1741
- );
1765
+ const distCertChainPath = path14.join(signaturesDir, SIGNING_KIND_CONFIG[kind].certBasename);
1742
1766
  profileTemplate["bundle-info"]["distribution-certificate"] = extractLeafCertificate(distCertChainPath);
1743
1767
  if (options.acls !== void 0) {
1744
1768
  profileTemplate.acls = profileTemplate.acls ?? {};
@@ -1749,9 +1773,8 @@ function modifyProfileTemplate(projectDir, apl, appFeature, logger, options = {}
1749
1773
  }
1750
1774
  fs13.writeFileSync(profileTemplatePath, JSON.stringify(profileTemplate, null, 2));
1751
1775
  }
1752
- function generateP7bFile(projectDir, paths, logger, passwords) {
1776
+ function generateP7bFile(signaturesDir, paths, logger, passwords) {
1753
1777
  logger.info("[sign] Generating P7b profile via hap-sign-tool...");
1754
- const signaturesDir = path14.join(projectDir, "signatures");
1755
1778
  const profileTemplatePath = path14.join(signaturesDir, "UnsgnedReleasedProfileTemplate.json");
1756
1779
  const outputProfilePath = path14.join(signaturesDir, "app1-profile.p7b");
1757
1780
  const args = [
@@ -1875,30 +1898,201 @@ function generateSigningConfigs(options) {
1875
1898
  if (!fs13.existsSync(sdkPath)) {
1876
1899
  throw new SdkNotInstalledError(String(sdkVersion), sdkPath, ALL_SDKS.find((s) => s.api === String(sdkVersion))?.version);
1877
1900
  }
1878
- const paths = {
1879
- signTool: path14.join(sdkPath, "toolchains/lib/hap-sign-tool.jar"),
1880
- keystore: path14.join(sdkPath, "toolchains/lib/OpenHarmony.p12"),
1881
- profileCert: path14.join(sdkPath, "toolchains/lib/OpenHarmonyProfileRelease.pem"),
1882
- unsignedProfileTemplate: path14.join(sdkPath, "toolchains/lib/UnsgnedReleasedProfileTemplate.json")
1883
- };
1901
+ const paths = sdkSigningFiles(sdkPath);
1884
1902
  const kind = pickSigningKind(apl);
1885
1903
  const passwords = options.passwords ?? DEFAULT_PASSWORDS;
1886
1904
  logger.info("[sign] Starting signing configuration generation...");
1887
- copyFilesToProject(projectDir, paths, kind, logger, options.applicationCertPath);
1905
+ const signaturesDir = path14.join(projectDir, "signatures");
1906
+ copySigningFiles(signaturesDir, paths, kind, logger, options.applicationCertPath);
1888
1907
  modifyProfileTemplate(projectDir, apl, appFeature, logger, {
1889
1908
  kind,
1890
1909
  acls: options.acls,
1891
1910
  appPrivilegeCapabilities: options.appPrivilegeCapabilities
1892
1911
  });
1893
- generateP7bFile(projectDir, paths, logger, passwords);
1912
+ generateP7bFile(signaturesDir, paths, logger, passwords);
1894
1913
  prepareMaterialDirectory(projectDir, logger);
1895
1914
  updateBuildProfile(projectDir, kind, logger, passwords);
1896
1915
  logger.info("[sign] Signing configuration generated successfully.");
1897
1916
  }
1898
1917
 
1899
- // src/emulator/install.ts
1918
+ // src/sign/hapInfo.ts
1919
+ var crypto3 = __toESM(require("crypto"), 1);
1900
1920
  var fs14 = __toESM(require("fs"), 1);
1901
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);
1902
2096
  async function installEmulator(opts) {
1903
2097
  const { config, progress, abortSignal } = opts;
1904
2098
  const logger = opts.logger ?? noopLogger;
@@ -1908,20 +2102,20 @@ async function installEmulator(opts) {
1908
2102
  const { dir: tmpDir, root: tmpRoot } = createInstallTempDir({
1909
2103
  config,
1910
2104
  override: opts.tmpDir,
1911
- installRoot: path15.dirname(emulatorDir),
2105
+ installRoot: path17.dirname(emulatorDir),
1912
2106
  prefix: "oniro-emulator-",
1913
2107
  logger
1914
2108
  });
1915
- const tmpZip = path15.join(tmpDir, "oniro_emulator.zip");
2109
+ const tmpZip = path17.join(tmpDir, "oniro_emulator.zip");
1916
2110
  try {
1917
- fs14.mkdirSync(emulatorDir, { recursive: true });
2111
+ fs16.mkdirSync(emulatorDir, { recursive: true });
1918
2112
  progress?.report({ message: "Downloading emulator...", increment: 0 });
1919
2113
  await downloadFile({ url, dest: tmpZip, progress, abortSignal, start: 0, range: 50, what, tmpRoot });
1920
2114
  progress?.report({ message: "Extracting emulator...", increment: 0 });
1921
2115
  await extractZipWithProgress({ zipPath: tmpZip, dest: emulatorDir, progress, start: 50, range: 45, logger });
1922
- const runSh = path15.join(emulatorDir, "images", "run.sh");
1923
- if (fs14.existsSync(runSh)) {
1924
- fs14.chmodSync(runSh, 493);
2116
+ const runSh = path17.join(emulatorDir, "images", "run.sh");
2117
+ if (fs16.existsSync(runSh)) {
2118
+ fs16.chmodSync(runSh, 493);
1925
2119
  }
1926
2120
  progress?.report({ message: "Finalizing installation...", increment: 5 });
1927
2121
  } catch (err) {
@@ -1931,24 +2125,24 @@ async function installEmulator(opts) {
1931
2125
  }
1932
2126
  }
1933
2127
  function isEmulatorInstalled(config) {
1934
- const imagesDir = path15.join(getEmulatorDir(config), "images");
1935
- 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"));
1936
2130
  }
1937
2131
  function removeEmulator(config) {
1938
2132
  const emulatorDir = getEmulatorDir(config);
1939
- if (fs14.existsSync(emulatorDir)) {
1940
- fs14.rmSync(emulatorDir, { recursive: true, force: true });
2133
+ if (fs16.existsSync(emulatorDir)) {
2134
+ fs16.rmSync(emulatorDir, { recursive: true, force: true });
1941
2135
  }
1942
2136
  }
1943
2137
 
1944
2138
  // src/emulator/lifecycle.ts
1945
- var fs16 = __toESM(require("fs"), 1);
1946
- var os8 = __toESM(require("os"), 1);
1947
- var path16 = __toESM(require("path"), 1);
1948
- 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");
1949
2143
 
1950
2144
  // src/hdc/exec.ts
1951
- var fs15 = __toESM(require("fs"), 1);
2145
+ var fs17 = __toESM(require("fs"), 1);
1952
2146
  var import_node_string_decoder = require("string_decoder");
1953
2147
  function errorMessage(err) {
1954
2148
  return err instanceof Error ? err.message : String(err);
@@ -1957,12 +2151,12 @@ function runProcess(opts) {
1957
2151
  const { command, args, cwd, env, onOutput } = opts;
1958
2152
  const timeoutMs = opts.timeoutMs ?? 6e4;
1959
2153
  const { abortSignal } = opts;
1960
- return new Promise((resolve7, reject) => {
2154
+ return new Promise((resolve9, reject) => {
1961
2155
  if (abortSignal?.aborted) {
1962
2156
  reject(new CancelledError(`Command aborted before start: ${command}`));
1963
2157
  return;
1964
2158
  }
1965
- if (needsCmdWrapper(command) && !fs15.existsSync(command)) {
2159
+ if (needsCmdWrapper(command) && !fs17.existsSync(command)) {
1966
2160
  reject(new OniroError(`Failed to spawn ${command}: ENOENT (no such file)`));
1967
2161
  return;
1968
2162
  }
@@ -2030,7 +2224,7 @@ ${stderr.trim()}` : "")
2030
2224
  );
2031
2225
  return;
2032
2226
  }
2033
- resolve7({ code: code ?? -1, stdout, stderr });
2227
+ resolve9({ code: code ?? -1, stdout, stderr });
2034
2228
  });
2035
2229
  });
2036
2230
  }
@@ -2103,7 +2297,7 @@ function ensureRemoteOk(result, command) {
2103
2297
  }
2104
2298
 
2105
2299
  // src/emulator/lifecycle.ts
2106
- var PID_FILE = path16.join(os8.tmpdir(), "oniro_emulator.pid");
2300
+ var PID_FILE = path18.join(os9.tmpdir(), "oniro_emulator.pid");
2107
2301
  async function attemptHdcConnection(config, address = "127.0.0.1:55555", logger = noopLogger) {
2108
2302
  try {
2109
2303
  await hdcExec({ config, args: ["start", "-r"], logger });
@@ -2116,24 +2310,24 @@ async function attemptHdcConnection(config, address = "127.0.0.1:55555", logger
2116
2310
  }
2117
2311
  }
2118
2312
  function resolveLauncher(imagesPath, headless, connect) {
2119
- const runSh = path16.join(imagesPath, "run.sh");
2120
- const runBat = path16.join(imagesPath, "run.bat");
2313
+ const runSh = path18.join(imagesPath, "run.sh");
2314
+ const runBat = path18.join(imagesPath, "run.bat");
2121
2315
  const extra = [];
2122
2316
  if (headless) extra.push("--headless");
2123
2317
  if (connect) extra.push("--connect", connect);
2124
- if (os8.platform() === "win32") {
2125
- if (fs16.existsSync(runBat)) {
2318
+ if (os9.platform() === "win32") {
2319
+ if (fs18.existsSync(runBat)) {
2126
2320
  return { command: "cmd.exe", args: ["/c", runBat, ...extra], cwd: imagesPath };
2127
2321
  }
2128
- if (fs16.existsSync(runSh)) {
2322
+ if (fs18.existsSync(runSh)) {
2129
2323
  return { command: "bash", args: [runSh, ...extra], cwd: imagesPath };
2130
2324
  }
2131
2325
  throw new OniroError(`No emulator launcher (run.bat or run.sh) found in ${imagesPath}.`);
2132
2326
  }
2133
- if (fs16.existsSync(runSh)) {
2327
+ if (fs18.existsSync(runSh)) {
2134
2328
  return { command: "bash", args: [runSh, ...extra], cwd: imagesPath };
2135
2329
  }
2136
- if (fs16.existsSync(runBat)) {
2330
+ if (fs18.existsSync(runBat)) {
2137
2331
  return { command: "bash", args: [runBat, ...extra], cwd: imagesPath };
2138
2332
  }
2139
2333
  throw new OniroError(`No emulator launcher (run.sh or run.bat) found in ${imagesPath}.`);
@@ -2141,9 +2335,9 @@ function resolveLauncher(imagesPath, headless, connect) {
2141
2335
  async function startEmulator(opts) {
2142
2336
  const { config } = opts;
2143
2337
  const logger = opts.logger ?? noopLogger;
2144
- if (os8.platform() !== "win32" && fs16.existsSync(PID_FILE)) {
2338
+ if (os9.platform() !== "win32" && fs18.existsSync(PID_FILE)) {
2145
2339
  try {
2146
- const pid = Number(fs16.readFileSync(PID_FILE, "utf8").trim());
2340
+ const pid = Number(fs18.readFileSync(PID_FILE, "utf8").trim());
2147
2341
  if (Number.isFinite(pid) && pid > 0) {
2148
2342
  try {
2149
2343
  process.kill(pid, 0);
@@ -2159,35 +2353,35 @@ async function startEmulator(opts) {
2159
2353
  logger.warn(`Could not read PID file: ${err.message}`);
2160
2354
  }
2161
2355
  }
2162
- const imagesPath = path16.join(getEmulatorDir(config), "images");
2163
- if (!fs16.existsSync(imagesPath)) {
2356
+ const imagesPath = path18.join(getEmulatorDir(config), "images");
2357
+ if (!fs18.existsSync(imagesPath)) {
2164
2358
  throw new OniroError(`Emulator images directory not found at ${imagesPath}. Run \`oniro-app emulator install\` first.`);
2165
2359
  }
2166
2360
  const launcher = resolveLauncher(imagesPath, opts.headless === true, opts.connect);
2167
- const logPath = opts.logFile ?? (os8.platform() === "win32" ? "NUL" : "/dev/null");
2168
- const logFd = fs16.openSync(logPath, "a");
2361
+ const logPath = opts.logFile ?? (os9.platform() === "win32" ? "NUL" : "/dev/null");
2362
+ const logFd = fs18.openSync(logPath, "a");
2169
2363
  let child;
2170
2364
  try {
2171
- child = (0, import_node_child_process4.spawn)(launcher.command, [...launcher.args], {
2365
+ child = (0, import_node_child_process5.spawn)(launcher.command, [...launcher.args], {
2172
2366
  cwd: launcher.cwd,
2173
2367
  detached: true,
2174
2368
  stdio: ["ignore", logFd, logFd],
2175
2369
  windowsHide: true
2176
2370
  });
2177
2371
  } finally {
2178
- fs16.closeSync(logFd);
2372
+ fs18.closeSync(logFd);
2179
2373
  }
2180
2374
  if (!child.pid) {
2181
2375
  throw new OniroError(`Failed to spawn launcher (${launcher.command} ${launcher.args.join(" ")}).`);
2182
2376
  }
2183
- fs16.writeFileSync(PID_FILE, String(child.pid));
2377
+ fs18.writeFileSync(PID_FILE, String(child.pid));
2184
2378
  child.unref();
2185
2379
  logger.info(`Emulator launched (PID ${child.pid}). Logs: ${logPath}`);
2186
- const earlyExit = new Promise((resolve7) => {
2187
- child.once("exit", (code) => resolve7(code));
2188
- child.once("error", () => resolve7(-1));
2380
+ const earlyExit = new Promise((resolve9) => {
2381
+ child.once("exit", (code) => resolve9(code));
2382
+ child.once("error", () => resolve9(-1));
2189
2383
  });
2190
- const liveness = new Promise((resolve7) => setTimeout(() => resolve7("alive"), 2e3));
2384
+ const liveness = new Promise((resolve9) => setTimeout(() => resolve9("alive"), 2e3));
2191
2385
  const outcome = await Promise.race([earlyExit, liveness]);
2192
2386
  if (outcome !== "alive") {
2193
2387
  const tail = readTail(logPath, 4e3);
@@ -2227,24 +2421,24 @@ ${tail}` : ""}`);
2227
2421
  }
2228
2422
  function readTail(filePath, bytes) {
2229
2423
  try {
2230
- const stat = fs16.statSync(filePath);
2424
+ const stat = fs18.statSync(filePath);
2231
2425
  const start = Math.max(0, stat.size - bytes);
2232
- const fd = fs16.openSync(filePath, "r");
2426
+ const fd = fs18.openSync(filePath, "r");
2233
2427
  try {
2234
2428
  const buf = Buffer.alloc(stat.size - start);
2235
- fs16.readSync(fd, buf, 0, buf.length, start);
2429
+ fs18.readSync(fd, buf, 0, buf.length, start);
2236
2430
  return buf.toString("utf8");
2237
2431
  } finally {
2238
- fs16.closeSync(fd);
2432
+ fs18.closeSync(fd);
2239
2433
  }
2240
2434
  } catch {
2241
2435
  return "";
2242
2436
  }
2243
2437
  }
2244
2438
  async function stopEmulator(logger = noopLogger) {
2245
- if (fs16.existsSync(PID_FILE)) {
2439
+ if (fs18.existsSync(PID_FILE)) {
2246
2440
  try {
2247
- const pid = Number(fs16.readFileSync(PID_FILE, "utf8").trim());
2441
+ const pid = Number(fs18.readFileSync(PID_FILE, "utf8").trim());
2248
2442
  if (Number.isFinite(pid) && pid > 0) {
2249
2443
  try {
2250
2444
  process.kill(pid, "SIGTERM");
@@ -2257,20 +2451,20 @@ async function stopEmulator(logger = noopLogger) {
2257
2451
  logger.warn(`Could not read PID file: ${err.message}`);
2258
2452
  }
2259
2453
  }
2260
- 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"];
2261
2455
  try {
2262
2456
  await runProcess({ command: killCmd, args: killArgs, logger });
2263
2457
  } catch (err) {
2264
2458
  logger.warn(`[emulator] could not run ${killCmd}: ${err.message}`);
2265
2459
  }
2266
- fs16.rm(PID_FILE, { force: true }, (err) => {
2460
+ fs18.rm(PID_FILE, { force: true }, (err) => {
2267
2461
  if (err) logger.warn(`Could not remove PID file: ${err.message}`);
2268
2462
  });
2269
2463
  }
2270
2464
  async function isQemuRunning(logger) {
2271
- if (os8.platform() !== "win32") {
2465
+ if (os9.platform() !== "win32") {
2272
2466
  try {
2273
- const pid = Number(fs16.readFileSync(PID_FILE, "utf8").trim());
2467
+ const pid = Number(fs18.readFileSync(PID_FILE, "utf8").trim());
2274
2468
  if (Number.isFinite(pid) && pid > 0) {
2275
2469
  process.kill(pid, 0);
2276
2470
  return true;
@@ -2278,10 +2472,10 @@ async function isQemuRunning(logger) {
2278
2472
  } catch {
2279
2473
  }
2280
2474
  }
2281
- 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"];
2282
2476
  try {
2283
2477
  const res = await runProcess({ command: cmd, args, timeoutMs: 1e4, logger });
2284
- 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;
2285
2479
  } catch {
2286
2480
  return false;
2287
2481
  }
@@ -2342,12 +2536,13 @@ async function listDevices(config, opts = {}) {
2342
2536
  });
2343
2537
  return parseDeviceList(res.stdout);
2344
2538
  }
2539
+ function isUsableDevice(device) {
2540
+ return device.status === "Connected" || device.status === "Unknown";
2541
+ }
2345
2542
  async function selectDevice(config, serial, opts = {}) {
2346
2543
  const resolved = resolveDeviceSerial(serial);
2347
2544
  if (resolved) return resolved;
2348
- const usable = (await listDevices(config, opts)).filter(
2349
- (d) => d.status === "Connected" || d.status === "Unknown"
2350
- );
2545
+ const usable = (await listDevices(config, opts)).filter(isUsableDevice);
2351
2546
  if (usable.length === 1) return usable[0].serial;
2352
2547
  if (usable.length === 0) {
2353
2548
  throw new OniroError("No connected device found. Connect a device/emulator or set ONIRO_DEVICE_SERIAL.");
@@ -2405,11 +2600,11 @@ async function paramSet(config, key, value, opts = {}) {
2405
2600
  }
2406
2601
 
2407
2602
  // src/hdc/files.ts
2408
- var path17 = __toESM(require("path"), 1);
2603
+ var path19 = __toESM(require("path"), 1);
2409
2604
  function normaliseLocalPath(local) {
2410
- const abs = path17.resolve(local);
2605
+ const abs = path19.resolve(local);
2411
2606
  const long = toLongPath(abs);
2412
- 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));
2413
2608
  }
2414
2609
  function assertDevicePath(remote) {
2415
2610
  if (/^[A-Za-z]:[\\/]/.test(remote)) {
@@ -2428,13 +2623,13 @@ async function sendFile(opts) {
2428
2623
  assertDevicePath(opts.remote);
2429
2624
  const local = normaliseLocalPath(opts.local);
2430
2625
  const sandbox = opts.bundle ? appSandboxDir(opts.bundle) : void 0;
2431
- 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;
2432
2627
  if (sandbox && !remote.startsWith(`${sandbox}/`)) {
2433
2628
  throw new OniroError(`${remote} is outside the sandbox of ${opts.bundle} (${sandbox}).`);
2434
2629
  }
2435
2630
  const device = { config: opts.config, deviceSerial: opts.deviceSerial, logger: opts.logger };
2436
2631
  if (sandbox) {
2437
- const mkdir = `mkdir -p ${shQuote(path17.posix.dirname(remote))}`;
2632
+ const mkdir = `mkdir -p ${shQuote(path19.posix.dirname(remote))}`;
2438
2633
  ensureRemoteOk(await shellChecked({ ...device, command: mkdir }), `hdc shell ${mkdir}`);
2439
2634
  }
2440
2635
  const res = await hdcExec({
@@ -2446,7 +2641,7 @@ async function sendFile(opts) {
2446
2641
  });
2447
2642
  ensureOk(res, `hdc file send ${local} ${remote}`);
2448
2643
  if (sandbox) {
2449
- 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]);
2450
2645
  const chown = `chown -R "$(stat -c %u:%g ${shQuote(sandbox)})" ${shQuote(top)}`;
2451
2646
  ensureRemoteOk(await shellChecked({ ...device, command: chown }), `hdc shell ${chown}`);
2452
2647
  }
@@ -2484,7 +2679,7 @@ async function getDisplaySize(config, opts = {}) {
2484
2679
 
2485
2680
  // src/hdc/wait.ts
2486
2681
  function delay(ms, signal) {
2487
- return new Promise((resolve7, reject) => {
2682
+ return new Promise((resolve9, reject) => {
2488
2683
  if (signal?.aborted) {
2489
2684
  reject(new CancelledError("Wait cancelled."));
2490
2685
  return;
@@ -2499,7 +2694,7 @@ function delay(ms, signal) {
2499
2694
  };
2500
2695
  const timer = setTimeout(() => {
2501
2696
  cleanup();
2502
- resolve7();
2697
+ resolve9();
2503
2698
  }, ms);
2504
2699
  signal?.addEventListener("abort", onAbort, { once: true });
2505
2700
  });
@@ -2528,18 +2723,18 @@ async function waitForCondition(opts) {
2528
2723
  }
2529
2724
 
2530
2725
  // src/hdc/app.ts
2531
- var fs19 = __toESM(require("fs"), 1);
2532
- var path20 = __toESM(require("path"), 1);
2726
+ var fs21 = __toESM(require("fs"), 1);
2727
+ var path22 = __toESM(require("path"), 1);
2533
2728
 
2534
2729
  // src/build/discoverHaps.ts
2535
- var fs17 = __toESM(require("fs"), 1);
2536
- var path18 = __toESM(require("path"), 1);
2730
+ var fs19 = __toESM(require("fs"), 1);
2731
+ var path20 = __toESM(require("path"), 1);
2537
2732
  function moduleOfHap(hapFileName) {
2538
2733
  const dash = hapFileName.indexOf("-");
2539
2734
  return dash > 0 ? hapFileName.slice(0, dash) : hapFileName.replace(/\.hap$/i, "");
2540
2735
  }
2541
2736
  function isAppProjectRoot(dir) {
2542
- return fs17.existsSync(path18.join(dir, "AppScope", "app.json5"));
2737
+ return fs19.existsSync(path20.join(dir, "AppScope", "app.json5"));
2543
2738
  }
2544
2739
  async function discoverHaps(opts) {
2545
2740
  const { projectDir } = opts;
@@ -2548,9 +2743,9 @@ async function discoverHaps(opts) {
2548
2743
  const result = {};
2549
2744
  const walk = async (dir, depth) => {
2550
2745
  if (depth > maxDepth) return;
2551
- const entries = await fs17.promises.readdir(dir, { withFileTypes: true }).catch(() => []);
2746
+ const entries = await fs19.promises.readdir(dir, { withFileTypes: true }).catch(() => []);
2552
2747
  for (const e of entries) {
2553
- const full = path18.join(dir, e.name);
2748
+ const full = path20.join(dir, e.name);
2554
2749
  if (e.isDirectory()) {
2555
2750
  if (e.name === "node_modules" || e.name === "oh_modules" || e.name === ".git") continue;
2556
2751
  if (skipNestedProjects && isAppProjectRoot(full)) continue;
@@ -2564,7 +2759,7 @@ async function discoverHaps(opts) {
2564
2759
  }
2565
2760
  };
2566
2761
  await walk(projectDir, 0);
2567
- const mtime = (p) => fs17.statSync(p).mtimeMs;
2762
+ const mtime = (p) => fs19.statSync(p).mtimeMs;
2568
2763
  for (const bucket of Object.values(result)) {
2569
2764
  bucket.signed.sort((a, b) => mtime(b) - mtime(a));
2570
2765
  bucket.unsigned.sort((a, b) => mtime(b) - mtime(a));
@@ -2590,16 +2785,16 @@ function pickHap(haps, module2) {
2590
2785
  }
2591
2786
 
2592
2787
  // src/hdc/project.ts
2593
- var fs18 = __toESM(require("fs"), 1);
2594
- var path19 = __toESM(require("path"), 1);
2788
+ var fs20 = __toESM(require("fs"), 1);
2789
+ var path21 = __toESM(require("path"), 1);
2595
2790
  var import_json53 = __toESM(require("json5"), 1);
2596
2791
  function readJson5(filePath) {
2597
- const content = fs18.readFileSync(filePath, "utf-8");
2792
+ const content = fs20.readFileSync(filePath, "utf-8");
2598
2793
  return import_json53.default.parse(content);
2599
2794
  }
2600
2795
  function getBundleName(projectDir) {
2601
- const appJsonPath = path19.join(projectDir, "AppScope", "app.json5");
2602
- if (!fs18.existsSync(appJsonPath)) {
2796
+ const appJsonPath = path21.join(projectDir, "AppScope", "app.json5");
2797
+ if (!fs20.existsSync(appJsonPath)) {
2603
2798
  throw new OniroError(`Could not find app.json5 at ${appJsonPath}`);
2604
2799
  }
2605
2800
  const appJson = readJson5(appJsonPath);
@@ -2609,8 +2804,8 @@ function getBundleName(projectDir) {
2609
2804
  return appJson.app.bundleName;
2610
2805
  }
2611
2806
  function getMainAbility(projectDir, moduleName = "entry", abilityName) {
2612
- const moduleJsonPath = path19.join(projectDir, moduleName, "src", "main", "module.json5");
2613
- if (!fs18.existsSync(moduleJsonPath)) {
2807
+ const moduleJsonPath = path21.join(projectDir, moduleName, "src", "main", "module.json5");
2808
+ if (!fs20.existsSync(moduleJsonPath)) {
2614
2809
  throw new OniroError(`Could not find module.json5 at ${moduleJsonPath}`);
2615
2810
  }
2616
2811
  const moduleJson = readJson5(moduleJsonPath);
@@ -2634,7 +2829,7 @@ function detectInstallFailure(output) {
2634
2829
  return /\[Fail\]|msg:\s*error|error:\s*failed to install|code:\s*95\d{5}/i.test(output) ? output.trim() : null;
2635
2830
  }
2636
2831
  function isUnsignedHap(hapPath) {
2637
- return path20.basename(hapPath).includes("unsigned");
2832
+ return path22.basename(hapPath).includes("unsigned");
2638
2833
  }
2639
2834
  function installFailureReason(output) {
2640
2835
  const code = /code:\s*(\d+)/i.exec(output);
@@ -2643,7 +2838,7 @@ function installFailureReason(output) {
2643
2838
  return message ? `code ${code[1]}: ${message}` : `code ${code[1]}`;
2644
2839
  }
2645
2840
  function unsignedHapRejection(output, hap) {
2646
- const name = path20.basename(hap.path);
2841
+ const name = path22.basename(hap.path);
2647
2842
  const reason = installFailureReason(output);
2648
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}).`;
2649
2844
  if (!/9568320|signature|pkcs7/i.test(reason)) return what;
@@ -2661,24 +2856,24 @@ ${result.stderr}`;
2661
2856
  return result;
2662
2857
  }
2663
2858
  function existingHapOrUnsigned(hapPath) {
2664
- if (fs19.existsSync(hapPath)) return { path: hapPath };
2859
+ if (fs21.existsSync(hapPath)) return { path: hapPath };
2665
2860
  const sibling = hapPath.replace(/-signed\.hap$/i, "-unsigned.hap");
2666
- 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;
2667
2862
  }
2668
2863
  async function resolveInstallHap(opts, logger) {
2669
2864
  const warnIfFallback = (hap) => {
2670
- 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)}.`);
2671
2866
  return hap;
2672
2867
  };
2673
2868
  const configured = opts.hapPath ?? opts.config.get("hapPath", "");
2674
2869
  const relative3 = configured || defaultPaths.hapPath;
2675
- const hapPath = path20.isAbsolute(relative3) ? relative3 : path20.join(opts.projectDir, relative3);
2870
+ const hapPath = path22.isAbsolute(relative3) ? relative3 : path22.join(opts.projectDir, relative3);
2676
2871
  if (configured) {
2677
2872
  const hap = existingHapOrUnsigned(hapPath);
2678
2873
  if (!hap) throw new OniroError(`HAP file not found at: ${hapPath}. Build the app first.`);
2679
2874
  return warnIfFallback(hap);
2680
2875
  }
2681
- if (fs19.existsSync(hapPath)) return { path: hapPath };
2876
+ if (fs21.existsSync(hapPath)) return { path: hapPath };
2682
2877
  const haps = await discoverHaps({ projectDir: opts.projectDir });
2683
2878
  const anySigned = Object.values(haps).some((m) => m.signed.length > 0);
2684
2879
  const entryUnsigned = anySigned ? void 0 : existingHapOrUnsigned(hapPath);
@@ -2789,7 +2984,7 @@ function listRunningProcesses(config, options = {}) {
2789
2984
  const logger = options.logger ?? noopLogger;
2790
2985
  const timeoutMs = options.timeoutMs ?? 1e3;
2791
2986
  const hdc = getHdcPath(config);
2792
- return new Promise((resolve7, reject) => {
2987
+ return new Promise((resolve9, reject) => {
2793
2988
  let proc;
2794
2989
  try {
2795
2990
  proc = spawnCompat(hdc, [...hdcTargetArgs(options.deviceSerial), "track-jpid"]);
@@ -2809,7 +3004,7 @@ function listRunningProcesses(config, options = {}) {
2809
3004
  if (options.targetProcessName && name === options.targetProcessName && !resolved) {
2810
3005
  resolved = true;
2811
3006
  killProcessTree(proc);
2812
- resolve7(pid);
3007
+ resolve9(pid);
2813
3008
  return;
2814
3009
  }
2815
3010
  }
@@ -2830,7 +3025,7 @@ function listRunningProcesses(config, options = {}) {
2830
3025
  if (options.targetProcessName) {
2831
3026
  reject(new OniroError(`Could not find process for bundle: ${options.targetProcessName}`));
2832
3027
  } else {
2833
- resolve7(processes);
3028
+ resolve9(processes);
2834
3029
  }
2835
3030
  }, timeoutMs);
2836
3031
  proc.on("close", () => {
@@ -2840,7 +3035,7 @@ function listRunningProcesses(config, options = {}) {
2840
3035
  if (options.targetProcessName) {
2841
3036
  reject(new OniroError(`Could not find process for bundle: ${options.targetProcessName}`));
2842
3037
  } else {
2843
- resolve7(processes);
3038
+ resolve9(processes);
2844
3039
  }
2845
3040
  });
2846
3041
  });
@@ -3009,12 +3204,12 @@ async function reboot(opts) {
3009
3204
  }
3010
3205
 
3011
3206
  // src/hdc/screen.ts
3012
- var fs20 = __toESM(require("fs"), 1);
3013
- var os9 = __toESM(require("os"), 1);
3014
- 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);
3015
3210
  var import_node_crypto = require("crypto");
3016
3211
  function tmpName(ext) {
3017
- 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}`);
3018
3213
  }
3019
3214
  async function takeScreenshot(opts) {
3020
3215
  const remote = "/data/local/tmp/oniro_screenshot.jpeg";
@@ -3024,11 +3219,11 @@ async function takeScreenshot(opts) {
3024
3219
  const local = tmpName("jpeg");
3025
3220
  try {
3026
3221
  await recvFile({ config: opts.config, remote, local, deviceSerial: opts.deviceSerial, timeoutMs });
3027
- const pixels = await fs20.promises.readFile(local);
3222
+ const pixels = await fs22.promises.readFile(local);
3028
3223
  const size = await getDisplaySize(opts.config, { deviceSerial: opts.deviceSerial }) ?? { width: 0, height: 0 };
3029
3224
  return { pixels, width: size.width, height: size.height };
3030
3225
  } finally {
3031
- fs20.promises.unlink(local).catch(() => {
3226
+ fs22.promises.unlink(local).catch(() => {
3032
3227
  });
3033
3228
  void shell({ config: opts.config, command: `rm -f ${remote}`, deviceSerial: opts.deviceSerial }).catch(() => {
3034
3229
  });
@@ -3047,9 +3242,9 @@ async function captureBurst(opts) {
3047
3242
  const local = tmpName(`burst_${i}.jpeg`);
3048
3243
  try {
3049
3244
  await recvFile({ config: opts.config, remote: `${remoteDir}/f${i}.jpeg`, local, deviceSerial: opts.deviceSerial });
3050
- frames.push(await fs20.promises.readFile(local));
3245
+ frames.push(await fs22.promises.readFile(local));
3051
3246
  } finally {
3052
- fs20.promises.unlink(local).catch(() => {
3247
+ fs22.promises.unlink(local).catch(() => {
3053
3248
  });
3054
3249
  }
3055
3250
  }
@@ -3224,9 +3419,9 @@ async function sendRawTouch(opts) {
3224
3419
  }
3225
3420
 
3226
3421
  // src/hdc/dump.ts
3227
- var fs21 = __toESM(require("fs"), 1);
3228
- var os10 = __toESM(require("os"), 1);
3229
- 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);
3230
3425
  var import_node_crypto2 = require("crypto");
3231
3426
 
3232
3427
  // src/hdc/ime.ts
@@ -3489,10 +3684,10 @@ async function dumpLayout(opts) {
3489
3684
  if (!m) throw new OniroError(`Could not parse dumpLayout output:
3490
3685
  ${dump.stdout}`);
3491
3686
  const remote = m[1];
3492
- 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`);
3493
3688
  try {
3494
3689
  await recvFile({ config: opts.config, remote, local, deviceSerial: opts.deviceSerial, timeoutMs });
3495
- const parsed = JSON.parse(await fs21.promises.readFile(local, "utf8"));
3690
+ const parsed = JSON.parse(await fs23.promises.readFile(local, "utf8"));
3496
3691
  const display = await getDisplaySize(opts.config, { deviceSerial: opts.deviceSerial });
3497
3692
  if (opts.prune === false) {
3498
3693
  return { display, tree: parsed };
@@ -3505,7 +3700,7 @@ ${dump.stdout}`);
3505
3700
  const keyboard = kb ? { shown: true, bundle: kb.bundle, ...kb.b ? { b: kb.b } : {} } : { shown: false };
3506
3701
  return { display, tree, keyboard };
3507
3702
  } finally {
3508
- fs21.promises.unlink(local).catch(() => {
3703
+ fs23.promises.unlink(local).catch(() => {
3509
3704
  });
3510
3705
  void shell({ config: opts.config, command: `rm -f ${remote}`, deviceSerial: opts.deviceSerial }).catch(() => {
3511
3706
  });
@@ -3515,7 +3710,7 @@ ${dump.stdout}`);
3515
3710
  // src/hdc/uiActions.ts
3516
3711
  var FOCUS_SETTLE_MS = 400;
3517
3712
  var KEYBOARD_SETTLE_MS = 400;
3518
- var sleep = (ms) => new Promise((resolve7) => setTimeout(resolve7, ms));
3713
+ var sleep = (ms) => new Promise((resolve9) => setTimeout(resolve9, ms));
3519
3714
  function targetTree(layout) {
3520
3715
  const kb = layout.keyboard;
3521
3716
  return kb?.shown && kb.bundle ? withoutWindow(layout.tree, kb.bundle) : layout.tree;
@@ -3668,11 +3863,11 @@ async function waitForTarget(opts) {
3668
3863
  }
3669
3864
 
3670
3865
  // src/hdc/hilog.ts
3671
- var fs22 = __toESM(require("fs"), 1);
3866
+ var fs24 = __toESM(require("fs"), 1);
3672
3867
  function setHilogLevel(opts) {
3673
3868
  const logger = opts.logger ?? noopLogger;
3674
3869
  const hdc = getHdcPath(opts.config);
3675
- return new Promise((resolve7, reject) => {
3870
+ return new Promise((resolve9, reject) => {
3676
3871
  let child;
3677
3872
  try {
3678
3873
  child = spawnCompat(hdc, [...hdcTargetArgs(opts.deviceSerial), "shell", "hilog", "-b", opts.level]);
@@ -3692,7 +3887,7 @@ function setHilogLevel(opts) {
3692
3887
  });
3693
3888
  child.once("close", (code) => {
3694
3889
  if (code === 0) {
3695
- resolve7();
3890
+ resolve9();
3696
3891
  } else {
3697
3892
  const msg = stderr.trim() || `hdc shell hilog -b ${opts.level} exited with code ${code}`;
3698
3893
  logger.error(`[hilog] ${msg}`);
@@ -3794,7 +3989,7 @@ async function recentEntryFilter(opts) {
3794
3989
  };
3795
3990
  }
3796
3991
  function waitForLog(opts) {
3797
- return new Promise((resolve7, reject) => {
3992
+ return new Promise((resolve9, reject) => {
3798
3993
  const deadline = Date.now() + opts.timeoutMs;
3799
3994
  let settled = false;
3800
3995
  let child = null;
@@ -3815,7 +4010,7 @@ function waitForLog(opts) {
3815
4010
  if (settled) return;
3816
4011
  settled = true;
3817
4012
  cleanup();
3818
- resolve7(entry);
4013
+ resolve9(entry);
3819
4014
  };
3820
4015
  const fail = (err) => {
3821
4016
  if (settled) return;
@@ -3870,7 +4065,7 @@ function waitForLog(opts) {
3870
4065
  });
3871
4066
  }
3872
4067
  function watchLog(opts) {
3873
- return new Promise((resolve7, reject) => {
4068
+ return new Promise((resolve9, reject) => {
3874
4069
  const matches = [];
3875
4070
  let settled = false;
3876
4071
  let child = null;
@@ -3889,7 +4084,7 @@ function watchLog(opts) {
3889
4084
  if (settled) return;
3890
4085
  settled = true;
3891
4086
  cleanup();
3892
- resolve7(opts.dedup === false ? matches : dedupEntries(matches));
4087
+ resolve9(opts.dedup === false ? matches : dedupEntries(matches));
3893
4088
  };
3894
4089
  const onAbort = () => {
3895
4090
  if (settled) return;
@@ -3934,11 +4129,11 @@ async function captureLog(opts) {
3934
4129
  const res = await shell({ config: opts.config, command: "hilog -r", deviceSerial: opts.deviceSerial, timeoutMs: 1e4, logger });
3935
4130
  ensureOk(res, "hdc shell hilog -r");
3936
4131
  }
3937
- const fd = fs22.openSync(opts.file, "a");
4132
+ const fd = fs24.openSync(opts.file, "a");
3938
4133
  let lines = 0;
3939
4134
  let reconnects = 0;
3940
4135
  try {
3941
- await new Promise((resolve7, reject) => {
4136
+ await new Promise((resolve9, reject) => {
3942
4137
  const deadline = opts.durationMs !== void 0 ? Date.now() + opts.durationMs : Infinity;
3943
4138
  let settled = false;
3944
4139
  let child = null;
@@ -3947,7 +4142,7 @@ async function captureLog(opts) {
3947
4142
  let open = true;
3948
4143
  const write = (line) => {
3949
4144
  if (!open || opts.pattern && !opts.pattern.test(line)) return;
3950
- fs22.writeSync(fd, `${line}
4145
+ fs24.writeSync(fd, `${line}
3951
4146
  `);
3952
4147
  lines++;
3953
4148
  };
@@ -3968,7 +4163,7 @@ async function captureLog(opts) {
3968
4163
  if (settled) return;
3969
4164
  settled = true;
3970
4165
  cleanup();
3971
- resolve7();
4166
+ resolve9();
3972
4167
  }
3973
4168
  const fail = (err) => {
3974
4169
  if (settled) return;
@@ -4012,7 +4207,7 @@ async function captureLog(opts) {
4012
4207
  spawnStream();
4013
4208
  });
4014
4209
  } finally {
4015
- fs22.closeSync(fd);
4210
+ fs24.closeSync(fd);
4016
4211
  }
4017
4212
  return { file: opts.file, lines, reconnects };
4018
4213
  }
@@ -4040,17 +4235,17 @@ async function dumpLog(opts) {
4040
4235
  }
4041
4236
 
4042
4237
  // src/build/runHvigorw.ts
4043
- var fs24 = __toESM(require("fs"), 1);
4044
- var path24 = __toESM(require("path"), 1);
4238
+ var fs26 = __toESM(require("fs"), 1);
4239
+ var path26 = __toESM(require("path"), 1);
4045
4240
 
4046
4241
  // src/harmonyos/target.ts
4047
- var fs23 = __toESM(require("fs"), 1);
4048
- var path23 = __toESM(require("path"), 1);
4242
+ var fs25 = __toESM(require("fs"), 1);
4243
+ var path25 = __toESM(require("path"), 1);
4049
4244
  var import_json54 = __toESM(require("json5"), 1);
4050
4245
  function detectRuntimeOs(projectDir, productName) {
4051
4246
  let products = [];
4052
4247
  try {
4053
- 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"));
4054
4249
  if (Array.isArray(parsed.app?.products)) products = parsed.app.products;
4055
4250
  } catch {
4056
4251
  return "OpenHarmony";
@@ -4091,7 +4286,7 @@ function runHvigorw(opts) {
4091
4286
  const { config } = opts;
4092
4287
  const projectDir = toLongPath(opts.projectDir);
4093
4288
  const logger = opts.logger ?? noopLogger;
4094
- if (!fs24.existsSync(path24.join(projectDir, "build-profile.json5"))) {
4289
+ if (!fs26.existsSync(path26.join(projectDir, "build-profile.json5"))) {
4095
4290
  throw new OniroError(`Not an OpenHarmony project: ${projectDir} (build-profile.json5 not found).`);
4096
4291
  }
4097
4292
  let hvigorw;
@@ -4103,24 +4298,24 @@ function runHvigorw(opts) {
4103
4298
  hvigorw = resolved.startsWith(projectDir) ? resolved : findHarmonyOsTool(sdk, "hvigorw") ?? resolved;
4104
4299
  logger.info(`[build] Building for HarmonyOS against the SDK at ${sdk.sdkPath}.`);
4105
4300
  } else {
4106
- if (!fs24.existsSync(getCmdToolsPath(config))) {
4301
+ if (!fs26.existsSync(getCmdToolsPath(config))) {
4107
4302
  throw new CmdToolsNotInstalledError(getCmdToolsPath(config));
4108
4303
  }
4109
4304
  sdkEnv = { OHOS_BASE_SDK_HOME: getOhosBaseSdkHome(config) };
4110
4305
  hvigorw = getHvigorwPath(config, projectDir);
4111
4306
  }
4112
- if (!fs24.existsSync(hvigorw)) {
4307
+ if (!fs26.existsSync(hvigorw)) {
4113
4308
  throw new OniroError(`hvigorw wrapper not found at ${hvigorw}.`);
4114
4309
  }
4115
4310
  const args = buildHvigorwArgs(opts);
4116
4311
  if (process.platform !== "win32") {
4117
4312
  try {
4118
- fs24.chmodSync(hvigorw, 493);
4313
+ fs26.chmodSync(hvigorw, 493);
4119
4314
  } catch {
4120
4315
  }
4121
4316
  }
4122
4317
  const env = { ...process.env, ...sdkEnv };
4123
- return new Promise((resolve7, reject) => {
4318
+ return new Promise((resolve9, reject) => {
4124
4319
  if (opts.abortSignal?.aborted) {
4125
4320
  reject(new CancelledError("hvigorw build cancelled."));
4126
4321
  return;
@@ -4164,15 +4359,15 @@ function runHvigorw(opts) {
4164
4359
  settled = true;
4165
4360
  cleanup();
4166
4361
  const exitCode = code ?? 1;
4167
- if (exitCode === 0) resolve7({ exitCode });
4362
+ if (exitCode === 0) resolve9({ exitCode });
4168
4363
  else reject(new OniroError(`hvigorw exited with code ${exitCode}`));
4169
4364
  });
4170
4365
  });
4171
4366
  }
4172
4367
 
4173
4368
  // src/build/ohpm.ts
4174
- var fs25 = __toESM(require("fs"), 1);
4175
- var path25 = __toESM(require("path"), 1);
4369
+ var fs27 = __toESM(require("fs"), 1);
4370
+ var path27 = __toESM(require("path"), 1);
4176
4371
  function runOhpm(opts) {
4177
4372
  return runProcess({
4178
4373
  command: opts.ohpmPath ?? getOhpmPath(opts.config),
@@ -4185,7 +4380,7 @@ function runOhpm(opts) {
4185
4380
  });
4186
4381
  }
4187
4382
  async function ensureOhModules(opts) {
4188
- if (fs25.existsSync(path25.join(opts.projectDir, "oh_modules"))) {
4383
+ if (fs27.existsSync(path27.join(opts.projectDir, "oh_modules"))) {
4189
4384
  return { installed: false };
4190
4385
  }
4191
4386
  const res = await runOhpm({
@@ -4202,8 +4397,8 @@ async function ensureOhModules(opts) {
4202
4397
  }
4203
4398
 
4204
4399
  // src/build/codelinter.ts
4205
- var fs26 = __toESM(require("fs"), 1);
4206
- var path26 = __toESM(require("path"), 1);
4400
+ var fs28 = __toESM(require("fs"), 1);
4401
+ var path28 = __toESM(require("path"), 1);
4207
4402
  var FINDING_RE = /^(.*?):(\d+):(?:(\d+):)?\s*(error|warning|warn|suggestion|info)\b[:\s-]*(.*)$/i;
4208
4403
  var TRAILING_CODE_RE = /[([]([\w@/.\-]+)[)\]]\s*$/;
4209
4404
  var FILE_HEADER_RE = /^(.+\.\w+)\((\d+)\)$/;
@@ -4293,10 +4488,10 @@ function parseCodelinterFindings(output) {
4293
4488
  var PROJECT_ROOT_FILES = [
4294
4489
  ["build-profile.json5"],
4295
4490
  ["hvigorfile.ts", "hvigorfile.js"],
4296
- [path26.join("hvigor", "hvigor-config.json5")]
4491
+ [path28.join("hvigor", "hvigor-config.json5")]
4297
4492
  ];
4298
4493
  function missingProjectRootFiles(projectDir) {
4299
- 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(
4300
4495
  (names) => names[0]
4301
4496
  );
4302
4497
  }
@@ -4378,32 +4573,22 @@ ${hint}`, err);
4378
4573
  return { exitCode, durationMs: Date.now() - start, discoveredHaps, warnings };
4379
4574
  }
4380
4575
 
4381
- // src/project/validators.ts
4382
- function isValidProjectName(name) {
4383
- if (!name) return false;
4384
- if (name.includes("/") || name.includes("\\")) return false;
4385
- return /^[A-Za-z0-9._-]+$/.test(name);
4386
- }
4387
- function isValidBundleName(bundleName) {
4388
- return /^[A-Za-z][A-Za-z0-9_]*(\.[A-Za-z][A-Za-z0-9_]*)+$/.test(bundleName);
4389
- }
4390
-
4391
4576
  // src/project/templates.ts
4392
- var fs27 = __toESM(require("fs"), 1);
4393
- var path27 = __toESM(require("path"), 1);
4577
+ var fs29 = __toESM(require("fs"), 1);
4578
+ var path29 = __toESM(require("path"), 1);
4394
4579
  function toHumanTemplateName(folderName) {
4395
4580
  return folderName.replace(/([a-z])([A-Z])/g, "$1 $2");
4396
4581
  }
4397
4582
  function listTemplates(templateRoot) {
4398
- if (!fs27.existsSync(templateRoot)) return [];
4399
- const entries = fs27.readdirSync(templateRoot, { withFileTypes: true });
4583
+ if (!fs29.existsSync(templateRoot)) return [];
4584
+ const entries = fs29.readdirSync(templateRoot, { withFileTypes: true });
4400
4585
  return entries.filter((e) => e.isDirectory()).map((e) => {
4401
- const dir = path27.join(templateRoot, e.name);
4402
- const metaPath = path27.join(dir, "template.json");
4586
+ const dir = path29.join(templateRoot, e.name);
4587
+ const metaPath = path29.join(dir, "template.json");
4403
4588
  let meta = {};
4404
- if (fs27.existsSync(metaPath)) {
4589
+ if (fs29.existsSync(metaPath)) {
4405
4590
  try {
4406
- meta = JSON.parse(fs27.readFileSync(metaPath, "utf8"));
4591
+ meta = JSON.parse(fs29.readFileSync(metaPath, "utf8"));
4407
4592
  } catch {
4408
4593
  }
4409
4594
  }
@@ -4418,57 +4603,57 @@ function listTemplates(templateRoot) {
4418
4603
  function validateTemplateLayout(templateDir, defaultModuleName) {
4419
4604
  const required = [
4420
4605
  "build-profile.json5",
4421
- path27.join("AppScope", "app.json5"),
4422
- path27.join(defaultModuleName, "src", "main", "module.json5"),
4423
- 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"),
4424
4609
  "hvigorfile.ts"
4425
4610
  ];
4426
- return required.filter((rel) => !fs27.existsSync(path27.join(templateDir, rel)));
4611
+ return required.filter((rel) => !fs29.existsSync(path29.join(templateDir, rel)));
4427
4612
  }
4428
4613
 
4429
4614
  // src/project/createScaffold.ts
4430
- var fs29 = __toESM(require("fs"), 1);
4431
- var path28 = __toESM(require("path"), 1);
4615
+ var fs31 = __toESM(require("fs"), 1);
4616
+ var path30 = __toESM(require("path"), 1);
4432
4617
 
4433
4618
  // src/project/jsonHelpers.ts
4434
- var fs28 = __toESM(require("fs"), 1);
4619
+ var fs30 = __toESM(require("fs"), 1);
4435
4620
  var import_json55 = __toESM(require("json5"), 1);
4436
4621
  function readJson5File(filePath) {
4437
- return import_json55.default.parse(fs28.readFileSync(filePath, "utf8"));
4622
+ return import_json55.default.parse(fs30.readFileSync(filePath, "utf8"));
4438
4623
  }
4439
4624
  function writeJson5File(filePath, value) {
4440
- fs28.writeFileSync(filePath, JSON.stringify(value, null, 2) + "\n", "utf8");
4625
+ fs30.writeFileSync(filePath, JSON.stringify(value, null, 2) + "\n", "utf8");
4441
4626
  }
4442
4627
  function readJsonFile(filePath) {
4443
- return JSON.parse(fs28.readFileSync(filePath, "utf8"));
4628
+ return JSON.parse(fs30.readFileSync(filePath, "utf8"));
4444
4629
  }
4445
4630
  function writeJsonFile(filePath, value) {
4446
- fs28.writeFileSync(filePath, JSON.stringify(value, null, 2) + "\n", "utf8");
4631
+ fs30.writeFileSync(filePath, JSON.stringify(value, null, 2) + "\n", "utf8");
4447
4632
  }
4448
4633
 
4449
4634
  // src/project/createScaffold.ts
4450
4635
  var IGNORED_DIRS = /* @__PURE__ */ new Set(["oh_modules", "node_modules", "build", ".hvigor"]);
4451
4636
  async function pathExists(p) {
4452
4637
  try {
4453
- await fs29.promises.access(p);
4638
+ await fs31.promises.access(p);
4454
4639
  return true;
4455
4640
  } catch {
4456
4641
  return false;
4457
4642
  }
4458
4643
  }
4459
4644
  async function copyDirRecursive(srcDir, destDir) {
4460
- await fs29.promises.mkdir(destDir, { recursive: true });
4461
- 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 });
4462
4647
  for (const entry of entries) {
4463
- const src = path28.join(srcDir, entry.name);
4464
- const dest = path28.join(destDir, entry.name);
4648
+ const src = path30.join(srcDir, entry.name);
4649
+ const dest = path30.join(destDir, entry.name);
4465
4650
  if (entry.isDirectory()) {
4466
4651
  await copyDirRecursive(src, dest);
4467
4652
  } else if (entry.isSymbolicLink()) {
4468
4653
  continue;
4469
4654
  } else if (entry.isFile()) {
4470
4655
  if (entry.name === "template.json") continue;
4471
- await fs29.promises.copyFile(src, dest);
4656
+ await fs31.promises.copyFile(src, dest);
4472
4657
  }
4473
4658
  }
4474
4659
  }
@@ -4476,9 +4661,9 @@ async function normalizeJson5ToJson(projectDir, logger) {
4476
4661
  const stack = [projectDir];
4477
4662
  while (stack.length > 0) {
4478
4663
  const currentDir = stack.pop();
4479
- const entries = await fs29.promises.readdir(currentDir, { withFileTypes: true });
4664
+ const entries = await fs31.promises.readdir(currentDir, { withFileTypes: true });
4480
4665
  for (const entry of entries) {
4481
- const fullPath = path28.join(currentDir, entry.name);
4666
+ const fullPath = path30.join(currentDir, entry.name);
4482
4667
  if (entry.isDirectory()) {
4483
4668
  if (IGNORED_DIRS.has(entry.name)) continue;
4484
4669
  stack.push(fullPath);
@@ -4500,23 +4685,23 @@ function toJavaPropertiesPath(p) {
4500
4685
  function createOrUpdateLocalProperties(projectDir, sdkDir) {
4501
4686
  const content = `sdk.dir=${toJavaPropertiesPath(sdkDir)}
4502
4687
  `;
4503
- fs29.writeFileSync(path28.join(projectDir, "local.properties"), content, "utf8");
4688
+ fs31.writeFileSync(path30.join(projectDir, "local.properties"), content, "utf8");
4504
4689
  }
4505
4690
  function renameIfExists(fromPath, toPath) {
4506
- if (fs29.existsSync(fromPath) && fromPath !== toPath) {
4507
- fs29.renameSync(fromPath, toPath);
4691
+ if (fs31.existsSync(fromPath) && fromPath !== toPath) {
4692
+ fs31.renameSync(fromPath, toPath);
4508
4693
  }
4509
4694
  }
4510
4695
  function updateTemplateConfigs(projectDir, args, sdkBaseDir) {
4511
- const appJsonPath = path28.join(projectDir, "AppScope", "app.json5");
4512
- if (fs29.existsSync(appJsonPath)) {
4696
+ const appJsonPath = path30.join(projectDir, "AppScope", "app.json5");
4697
+ if (fs31.existsSync(appJsonPath)) {
4513
4698
  const appJson = readJson5File(appJsonPath);
4514
4699
  appJson.app = appJson.app ?? {};
4515
4700
  appJson.app.bundleName = args.bundleName;
4516
4701
  writeJson5File(appJsonPath, appJson);
4517
4702
  }
4518
- const stringsPath = path28.join(projectDir, "AppScope", "resources", "base", "element", "string.json");
4519
- if (fs29.existsSync(stringsPath)) {
4703
+ const stringsPath = path30.join(projectDir, "AppScope", "resources", "base", "element", "string.json");
4704
+ if (fs31.existsSync(stringsPath)) {
4520
4705
  const strings = readJsonFile(stringsPath);
4521
4706
  if (Array.isArray(strings.string)) {
4522
4707
  const appName = strings.string.find((s) => s.name === "app_name");
@@ -4524,8 +4709,8 @@ function updateTemplateConfigs(projectDir, args, sdkBaseDir) {
4524
4709
  }
4525
4710
  writeJsonFile(stringsPath, strings);
4526
4711
  }
4527
- const buildProfilePath = path28.join(projectDir, "build-profile.json5");
4528
- if (fs29.existsSync(buildProfilePath)) {
4712
+ const buildProfilePath = path30.join(projectDir, "build-profile.json5");
4713
+ if (fs31.existsSync(buildProfilePath)) {
4529
4714
  const buildProfile = readJson5File(buildProfilePath);
4530
4715
  buildProfile.app = buildProfile.app ?? {};
4531
4716
  if (Array.isArray(buildProfile.app.products) && buildProfile.app.products.length > 0) {
@@ -4538,23 +4723,23 @@ function updateTemplateConfigs(projectDir, args, sdkBaseDir) {
4538
4723
  }
4539
4724
  writeJson5File(buildProfilePath, buildProfile);
4540
4725
  }
4541
- const moduleJsonPath = path28.join(projectDir, args.moduleName, "src", "main", "module.json5");
4542
- if (fs29.existsSync(moduleJsonPath)) {
4726
+ const moduleJsonPath = path30.join(projectDir, args.moduleName, "src", "main", "module.json5");
4727
+ if (fs31.existsSync(moduleJsonPath)) {
4543
4728
  const moduleJson = readJson5File(moduleJsonPath);
4544
4729
  moduleJson.module = moduleJson.module ?? {};
4545
4730
  moduleJson.module.name = args.moduleName;
4546
4731
  writeJson5File(moduleJsonPath, moduleJson);
4547
4732
  }
4548
- const moduleOhPackagePath = path28.join(projectDir, args.moduleName, "oh-package.json5");
4549
- if (fs29.existsSync(moduleOhPackagePath)) {
4733
+ const moduleOhPackagePath = path30.join(projectDir, args.moduleName, "oh-package.json5");
4734
+ if (fs31.existsSync(moduleOhPackagePath)) {
4550
4735
  const pkg = readJson5File(moduleOhPackagePath);
4551
4736
  pkg.name = args.moduleName;
4552
4737
  writeJson5File(moduleOhPackagePath, pkg);
4553
4738
  }
4554
- const vscodeDir = path28.join(projectDir, ".vscode");
4555
- fs29.mkdirSync(vscodeDir, { recursive: true });
4739
+ const vscodeDir = path30.join(projectDir, ".vscode");
4740
+ fs31.mkdirSync(vscodeDir, { recursive: true });
4556
4741
  const hapPath = `${args.moduleName}/build/default/outputs/default/${args.moduleName}-default-signed.hap`;
4557
- writeJsonFile(path28.join(vscodeDir, "settings.json"), {
4742
+ writeJsonFile(path30.join(vscodeDir, "settings.json"), {
4558
4743
  "oniro.hapPath": hapPath,
4559
4744
  "files.associations": { "*.json5": "jsonc" }
4560
4745
  });
@@ -4571,7 +4756,7 @@ async function createScaffold(opts) {
4571
4756
  if (!opts.location || !await pathExists(opts.location)) {
4572
4757
  throw new OniroError(`Location does not exist: ${opts.location}`);
4573
4758
  }
4574
- const templateDir = path28.join(opts.templateRoot, opts.templateId);
4759
+ const templateDir = path30.join(opts.templateRoot, opts.templateId);
4575
4760
  if (!await pathExists(templateDir)) {
4576
4761
  throw new OniroError(`Template not found: ${templateDir}`);
4577
4762
  }
@@ -4606,17 +4791,17 @@ async function createScaffold(opts) {
4606
4791
  }
4607
4792
  }
4608
4793
  }
4609
- const projectDir = path28.join(opts.location, opts.projectName);
4794
+ const projectDir = path30.join(opts.location, opts.projectName);
4610
4795
  if (await pathExists(projectDir)) {
4611
4796
  if (!opts.overwrite) {
4612
4797
  throw new OniroError(`Destination already exists: ${projectDir}. Pass overwrite: true to replace it.`);
4613
4798
  }
4614
- await fs29.promises.rm(projectDir, { recursive: true, force: true });
4799
+ await fs31.promises.rm(projectDir, { recursive: true, force: true });
4615
4800
  }
4616
4801
  logger.info(`[create] Scaffolding ${opts.templateId} at ${projectDir}`);
4617
4802
  await copyDirRecursive(templateDir, projectDir);
4618
4803
  if (moduleName !== defaultModuleName) {
4619
- renameIfExists(path28.join(projectDir, defaultModuleName), path28.join(projectDir, moduleName));
4804
+ renameIfExists(path30.join(projectDir, defaultModuleName), path30.join(projectDir, moduleName));
4620
4805
  }
4621
4806
  updateTemplateConfigs(
4622
4807
  projectDir,
@@ -4633,11 +4818,11 @@ async function createScaffold(opts) {
4633
4818
  }
4634
4819
 
4635
4820
  // src/project/listModules.ts
4636
- var fs30 = __toESM(require("fs"), 1);
4637
- var path29 = __toESM(require("path"), 1);
4821
+ var fs32 = __toESM(require("fs"), 1);
4822
+ var path31 = __toESM(require("path"), 1);
4638
4823
  function listModules(opts) {
4639
- const buildProfile = path29.join(opts.projectDir, "build-profile.json5");
4640
- if (!fs30.existsSync(buildProfile)) {
4824
+ const buildProfile = path31.join(opts.projectDir, "build-profile.json5");
4825
+ if (!fs32.existsSync(buildProfile)) {
4641
4826
  throw new OniroError(`build-profile.json5 not found at ${buildProfile}`);
4642
4827
  }
4643
4828
  const parsed = readJson5File(buildProfile);
@@ -4649,12 +4834,12 @@ function listModules(opts) {
4649
4834
  }
4650
4835
 
4651
4836
  // src/project/abilities.ts
4652
- var fs31 = __toESM(require("fs"), 1);
4653
- var path30 = __toESM(require("path"), 1);
4837
+ var fs33 = __toESM(require("fs"), 1);
4838
+ var path32 = __toESM(require("path"), 1);
4654
4839
  function listAbilities(opts) {
4655
4840
  const moduleName = opts.moduleName ?? "entry";
4656
- const moduleJsonPath = path30.join(opts.projectDir, moduleName, "src", "main", "module.json5");
4657
- if (!fs31.existsSync(moduleJsonPath)) {
4841
+ const moduleJsonPath = path32.join(opts.projectDir, moduleName, "src", "main", "module.json5");
4842
+ if (!fs33.existsSync(moduleJsonPath)) {
4658
4843
  throw new OniroError(`Could not find module.json5 at ${moduleJsonPath}`);
4659
4844
  }
4660
4845
  const parsed = readJson5File(moduleJsonPath);
@@ -4662,12 +4847,12 @@ function listAbilities(opts) {
4662
4847
  }
4663
4848
 
4664
4849
  // src/project/prepareSigning.ts
4665
- var fs32 = __toESM(require("fs"), 1);
4666
- var path31 = __toESM(require("path"), 1);
4850
+ var fs34 = __toESM(require("fs"), 1);
4851
+ var path33 = __toESM(require("path"), 1);
4667
4852
  function hasSigningArtifacts(dir) {
4668
- if (!fs32.existsSync(path31.join(dir, "signatures"))) return false;
4669
- const buildProfile = path31.join(dir, "build-profile.json5");
4670
- 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;
4671
4856
  try {
4672
4857
  const parsed = readJson5File(buildProfile);
4673
4858
  return Array.isArray(parsed.app?.signingConfigs) && parsed.app.signingConfigs.length > 0;
@@ -4746,7 +4931,6 @@ async function getDeviceInfo(opts) {
4746
4931
  }
4747
4932
 
4748
4933
  // src/install/diffHapAssets.ts
4749
- var import_node_stream_zip2 = __toESM(require("node-stream-zip"), 1);
4750
4934
  function diffEntryNames(installed, next) {
4751
4935
  const installedSet = new Set(installed);
4752
4936
  const nextSet = new Set(next);
@@ -4755,14 +4939,11 @@ function diffEntryNames(installed, next) {
4755
4939
  removedAssetPaths: [...installedSet].filter((n) => !nextSet.has(n)).sort()
4756
4940
  };
4757
4941
  }
4758
- async function listFileEntries(hapPath) {
4759
- const zip = new import_node_stream_zip2.default.async({ file: hapPath });
4760
- try {
4761
- const entries = await zip.entries();
4762
- return Object.values(entries).filter((e) => !e.isDirectory).map((e) => e.name);
4763
- } finally {
4764
- await zip.close();
4765
- }
4942
+ function listFileEntries(hapPath) {
4943
+ return withHapZip(
4944
+ hapPath,
4945
+ (zip) => Object.values(zip.entries()).filter((e) => !e.isDirectory).map((e) => e.name)
4946
+ );
4766
4947
  }
4767
4948
  async function diffHapAssets(opts) {
4768
4949
  const [installed, next] = await Promise.all([listFileEntries(opts.installedHap), listFileEntries(opts.newHap)]);
@@ -4770,8 +4951,8 @@ async function diffHapAssets(opts) {
4770
4951
  }
4771
4952
 
4772
4953
  // src/install/apply.ts
4773
- var fs33 = __toESM(require("fs"), 1);
4774
- var path32 = __toESM(require("path"), 1);
4954
+ var fs35 = __toESM(require("fs"), 1);
4955
+ var path34 = __toESM(require("path"), 1);
4775
4956
 
4776
4957
  // src/install/hapBundleName.ts
4777
4958
  function bundleNameFromHapConfig(text) {
@@ -4809,7 +4990,7 @@ async function pidOf(config, bundle, deviceSerial) {
4809
4990
  async function resolveHap(opts, logger) {
4810
4991
  let hap;
4811
4992
  if (opts.hapPath) {
4812
- 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;
4813
4994
  hap = existingHapOrUnsigned(hapPath);
4814
4995
  if (!hap) throw new OniroError(`HAP not found at ${hapPath}.`);
4815
4996
  } else {
@@ -4818,7 +4999,7 @@ async function resolveHap(opts, logger) {
4818
4999
  if (!picked) throw new OniroError(`No HAP found${opts.module ? ` for module ${opts.module}` : ""}. Build first.`);
4819
5000
  hap = { path: picked.path, fallback: !picked.signed };
4820
5001
  }
4821
- 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)}.`);
4822
5003
  return hap;
4823
5004
  }
4824
5005
  async function applyChanges(opts) {
@@ -4834,12 +5015,12 @@ async function applyChanges(opts) {
4834
5015
  });
4835
5016
  if (hapBundle && hapBundle !== bundle) {
4836
5017
  throw new OniroError(
4837
- `${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.`
4838
5019
  );
4839
5020
  }
4840
5021
  logger.info(`installing ${hapPath}`);
4841
5022
  let assetsChanged = false;
4842
- if (opts.installedHapPath && fs33.existsSync(opts.installedHapPath)) {
5023
+ if (opts.installedHapPath && fs35.existsSync(opts.installedHapPath)) {
4843
5024
  try {
4844
5025
  const diff = await diffHapAssets({ installedHap: opts.installedHapPath, newHap: hapPath });
4845
5026
  assetsChanged = diff.addedAssetPaths.length > 0 || diff.removedAssetPaths.length > 0;
@@ -4872,7 +5053,7 @@ async function applyChanges(opts) {
4872
5053
  }
4873
5054
  if (!await isBundleInstalled({ config, bundle, deviceSerial })) {
4874
5055
  throw new OniroError(
4875
- `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).`
4876
5057
  );
4877
5058
  }
4878
5059
  let postInstallPid = await pidOf(config, bundle, deviceSerial);
@@ -4949,10 +5130,10 @@ var AUTH_PATHS = {
4949
5130
  };
4950
5131
 
4951
5132
  // src/harmonyos/auth/tokenStore.ts
4952
- var fs34 = __toESM(require("fs"), 1);
4953
- var os11 = __toESM(require("os"), 1);
4954
- var path33 = __toESM(require("path"), 1);
4955
- 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);
4956
5137
  var ALGORITHM = "aes-256-gcm";
4957
5138
  var KEY_LENGTH = 32;
4958
5139
  var IV_LENGTH = 12;
@@ -4961,8 +5142,8 @@ var KEY_FILE = "kek.bin";
4961
5142
  var DIR_MODE = 448;
4962
5143
  var FILE_MODE = 384;
4963
5144
  function encrypt2(key, plaintext) {
4964
- const iv = crypto3.randomBytes(IV_LENGTH);
4965
- const cipher = crypto3.createCipheriv(ALGORITHM, key, iv);
5145
+ const iv = crypto4.randomBytes(IV_LENGTH);
5146
+ const cipher = crypto4.createCipheriv(ALGORITHM, key, iv);
4966
5147
  const ciphertext = Buffer.concat([cipher.update(plaintext), cipher.final()]);
4967
5148
  return {
4968
5149
  version: 1,
@@ -4973,7 +5154,7 @@ function encrypt2(key, plaintext) {
4973
5154
  };
4974
5155
  }
4975
5156
  function decrypt2(key, blob) {
4976
- const decipher = crypto3.createDecipheriv(ALGORITHM, key, Buffer.from(blob.iv, "base64"));
5157
+ const decipher = crypto4.createDecipheriv(ALGORITHM, key, Buffer.from(blob.iv, "base64"));
4977
5158
  decipher.setAuthTag(Buffer.from(blob.authTag, "base64"));
4978
5159
  return Buffer.concat([decipher.update(Buffer.from(blob.ciphertext, "base64")), decipher.final()]);
4979
5160
  }
@@ -4987,7 +5168,7 @@ function permissionHint(dir) {
4987
5168
  }
4988
5169
  function ensureDir(dir) {
4989
5170
  try {
4990
- fs34.mkdirSync(dir, { recursive: true, mode: DIR_MODE });
5171
+ fs36.mkdirSync(dir, { recursive: true, mode: DIR_MODE });
4991
5172
  } catch (err) {
4992
5173
  if (err.code === "EACCES") {
4993
5174
  throw new OniroError(permissionHint(dir), err);
@@ -4998,17 +5179,17 @@ function ensureDir(dir) {
4998
5179
  function createTokenStore(opts) {
4999
5180
  const logger = opts.logger ?? noopLogger;
5000
5181
  const authDir = opts.config.get("harmonyosAuthDir", defaultPaths.harmonyosAuthDir());
5001
- const tokenPath = path33.join(authDir, TOKEN_FILE);
5002
- 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);
5003
5184
  function loadOrCreateKey() {
5004
- if (fs34.existsSync(keyPath)) {
5005
- const existing = fs34.readFileSync(keyPath);
5185
+ if (fs36.existsSync(keyPath)) {
5186
+ const existing = fs36.readFileSync(keyPath);
5006
5187
  if (existing.length === KEY_LENGTH) return existing;
5007
5188
  logger.warn("[harmonyos] The token key file was malformed; regenerating (you will need to log in again).");
5008
5189
  }
5009
- ensureDir(path33.dirname(keyPath));
5010
- const key = crypto3.randomBytes(KEY_LENGTH);
5011
- 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 });
5012
5193
  return key;
5013
5194
  }
5014
5195
  return {
@@ -5018,12 +5199,12 @@ function createTokenStore(opts) {
5018
5199
  const payload = Buffer.from(JSON.stringify(session), "utf8");
5019
5200
  const blob = encrypt2(loadOrCreateKey(), payload);
5020
5201
  ensureDir(authDir);
5021
- fs34.writeFileSync(tokenPath, JSON.stringify(blob, null, 2), { mode: FILE_MODE });
5202
+ fs36.writeFileSync(tokenPath, JSON.stringify(blob, null, 2), { mode: FILE_MODE });
5022
5203
  },
5023
5204
  load() {
5024
- if (!fs34.existsSync(tokenPath)) return null;
5205
+ if (!fs36.existsSync(tokenPath)) return null;
5025
5206
  try {
5026
- const blob = JSON.parse(fs34.readFileSync(tokenPath, "utf8"));
5207
+ const blob = JSON.parse(fs36.readFileSync(tokenPath, "utf8"));
5027
5208
  if (!isEncryptedBlob(blob)) return null;
5028
5209
  const { jwtToken, siteId } = JSON.parse(decrypt2(loadOrCreateKey(), blob).toString("utf8"));
5029
5210
  return typeof jwtToken === "string" && jwtToken && typeof siteId === "string" ? { jwtToken, siteId } : null;
@@ -5035,7 +5216,7 @@ function createTokenStore(opts) {
5035
5216
  }
5036
5217
  logger.debug(`[harmonyos] Discarding unreadable token at ${tokenPath}.`);
5037
5218
  try {
5038
- fs34.rmSync(tokenPath, { force: true });
5219
+ fs36.rmSync(tokenPath, { force: true });
5039
5220
  } catch {
5040
5221
  }
5041
5222
  return null;
@@ -5043,7 +5224,7 @@ function createTokenStore(opts) {
5043
5224
  },
5044
5225
  clear() {
5045
5226
  try {
5046
- fs34.rmSync(tokenPath, { force: true });
5227
+ fs36.rmSync(tokenPath, { force: true });
5047
5228
  } catch (err) {
5048
5229
  throw new OniroError(`Failed to remove ${tokenPath}.`, err);
5049
5230
  }
@@ -5052,7 +5233,7 @@ function createTokenStore(opts) {
5052
5233
  }
5053
5234
 
5054
5235
  // src/harmonyos/auth/session.ts
5055
- var crypto5 = __toESM(require("crypto"), 1);
5236
+ var crypto6 = __toESM(require("crypto"), 1);
5056
5237
 
5057
5238
  // src/harmonyos/http.ts
5058
5239
  var import_undici = require("undici");
@@ -5143,13 +5324,13 @@ function createHttpClient(opts = {}) {
5143
5324
 
5144
5325
  // src/harmonyos/auth/callbackServer.ts
5145
5326
  var http2 = __toESM(require("http"), 1);
5146
- var crypto4 = __toESM(require("crypto"), 1);
5327
+ var crypto5 = __toESM(require("crypto"), 1);
5147
5328
  var CALLBACK_PATH = "/callback";
5148
5329
  var MAX_BODY_BYTES = 65536;
5149
5330
  function codeMatches(code, secret) {
5150
5331
  const a = Buffer.from(code ?? "", "utf8");
5151
5332
  const b = Buffer.from(secret, "utf8");
5152
- return a.length === b.length && crypto4.timingSafeEqual(a, b);
5333
+ return a.length === b.length && crypto5.timingSafeEqual(a, b);
5153
5334
  }
5154
5335
  function isQuit(quit) {
5155
5336
  return quit === "true" || quit === "access_denied" || quit === "quit";
@@ -5157,8 +5338,8 @@ function isQuit(quit) {
5157
5338
  async function startCallbackServer(opts) {
5158
5339
  let resolveCallback;
5159
5340
  let rejectCallback;
5160
- const callback = new Promise((resolve7, reject) => {
5161
- resolveCallback = resolve7;
5341
+ const callback = new Promise((resolve9, reject) => {
5342
+ resolveCallback = resolve9;
5162
5343
  rejectCallback = reject;
5163
5344
  });
5164
5345
  callback.catch(() => {
@@ -5217,9 +5398,9 @@ async function startCallbackServer(opts) {
5217
5398
  }
5218
5399
  });
5219
5400
  server.keepAliveTimeout = 1;
5220
- await new Promise((resolve7, reject) => {
5401
+ await new Promise((resolve9, reject) => {
5221
5402
  server.once("error", (err) => reject(new OniroError("Failed to start the local login callback server.", err)));
5222
- server.listen(0, "127.0.0.1", resolve7);
5403
+ server.listen(0, "127.0.0.1", resolve9);
5223
5404
  });
5224
5405
  const port = server.address().port;
5225
5406
  return {
@@ -5235,12 +5416,12 @@ async function startCallbackServer(opts) {
5235
5416
  return Promise.race([callback, timeout]).finally(() => clearTimeout(timer));
5236
5417
  },
5237
5418
  stop() {
5238
- return new Promise((resolve7) => {
5419
+ return new Promise((resolve9) => {
5239
5420
  server.closeAllConnections?.();
5240
- const timer = setTimeout(resolve7, 100);
5421
+ const timer = setTimeout(resolve9, 100);
5241
5422
  server.close(() => {
5242
5423
  clearTimeout(timer);
5243
- resolve7();
5424
+ resolve9();
5244
5425
  });
5245
5426
  });
5246
5427
  }
@@ -5248,7 +5429,7 @@ async function startCallbackServer(opts) {
5248
5429
  }
5249
5430
 
5250
5431
  // src/harmonyos/auth/browser.ts
5251
- var import_node_child_process5 = require("child_process");
5432
+ var import_node_child_process6 = require("child_process");
5252
5433
  function isSafeUrl(url) {
5253
5434
  try {
5254
5435
  const parsed = new URL(url);
@@ -5265,18 +5446,18 @@ function openBrowser(url) {
5265
5446
  return Promise.reject(new OniroError(`Refusing to open unsafe URL: ${JSON.stringify(url)}`));
5266
5447
  }
5267
5448
  const [command, args] = process.platform === "win32" ? ["cmd", ["/c", "start", '""', escapeForCmd(url)]] : process.platform === "darwin" ? ["open", [url]] : ["xdg-open", [url]];
5268
- const child = (0, import_node_child_process5.spawn)(command, args, {
5449
+ const child = (0, import_node_child_process6.spawn)(command, args, {
5269
5450
  stdio: "ignore",
5270
5451
  shell: false,
5271
5452
  detached: true,
5272
5453
  windowsHide: true
5273
5454
  });
5274
5455
  child.unref();
5275
- return new Promise((resolve7, reject) => {
5456
+ return new Promise((resolve9, reject) => {
5276
5457
  child.on("error", (err) => reject(new OniroError(`Failed to open a browser (${command}).`, err)));
5277
5458
  child.on(
5278
5459
  "exit",
5279
- (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}.`))
5280
5461
  );
5281
5462
  });
5282
5463
  }
@@ -5358,7 +5539,7 @@ function createHarmonyOsSession(opts) {
5358
5539
  getDeveloperAgreement,
5359
5540
  async login(o = {}) {
5360
5541
  const start = HUAWEI_SITES.CN.loginUrl;
5361
- const clientSecret = crypto5.randomUUID().replace(/-/g, "");
5542
+ const clientSecret = crypto6.randomUUID().replace(/-/g, "");
5362
5543
  const server = await startCallbackServer({
5363
5544
  clientSecret,
5364
5545
  baseUrl: start,
@@ -5479,7 +5660,7 @@ var SIGNING_ERRORS = {
5479
5660
  DEVICE_ADD: "AppGallery Connect did not register the device.",
5480
5661
  DEVICE_LIMIT: "This team has reached its device limit. Remove an unused device in AppGallery Connect and retry.",
5481
5662
  DEVICE_NAME_REPEAT: "A device with that name is already registered. Retry.",
5482
- DEVICE_NONE: "No device is connected and none is registered to this team, but a debug profile must name at least one. Connect a device and retry.",
5663
+ DEVICE_NONE: "This team has no registered device and none could be registered from here, but a debug profile must name at least one. Connect a device that reports a UDID (`hdc shell bm get -u`), or register one in AppGallery Connect, and retry.",
5483
5664
  PROFILE_ADD: "AppGallery Connect did not issue the provisioning profile.",
5484
5665
  PROFILE_NAME_REPEAT: "A provisioning profile with that name already exists in AppGallery Connect. Retry.",
5485
5666
  PROVISION_LIMIT: "This team has reached its debug-profile limit. Delete unused profiles in AppGallery Connect and retry.",
@@ -5524,7 +5705,7 @@ function mapAgcError(operation, statusCode, reasonPhrase, body) {
5524
5705
  }
5525
5706
 
5526
5707
  // src/harmonyos/signing/profile.ts
5527
- var fs35 = __toESM(require("fs"), 1);
5708
+ var fs37 = __toESM(require("fs"), 1);
5528
5709
  var import_node_crypto3 = require("crypto");
5529
5710
  var import_node_forge = __toESM(require("node-forge"), 1);
5530
5711
  function extractProfileJson(buffer) {
@@ -5556,7 +5737,7 @@ function extractProfileJson(buffer) {
5556
5737
  function readProvisionProfile(profilePath) {
5557
5738
  let raw;
5558
5739
  try {
5559
- const json = extractProfileJson(fs35.readFileSync(profilePath));
5740
+ const json = extractProfileJson(fs37.readFileSync(profilePath));
5560
5741
  if (!json) return null;
5561
5742
  raw = JSON.parse(json);
5562
5743
  } catch {
@@ -5585,7 +5766,7 @@ function parseCertificate(pem) {
5585
5766
  }
5586
5767
  function keystoreHoldsKey(p12Path, keyAlias, keyPwd, certificate) {
5587
5768
  try {
5588
- 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"));
5589
5770
  const p12 = import_node_forge.default.pkcs12.pkcs12FromAsn1(import_node_forge.default.asn1.fromDer(der), keyPwd);
5590
5771
  const certBag = import_node_forge.default.pki.oids.certBag ?? "1.2.840.113549.1.12.10.1.3";
5591
5772
  const wanted = certificate.publicKey.export({ type: "spki", format: "der" });
@@ -5601,7 +5782,7 @@ function keystoreHoldsKey(p12Path, keyAlias, keyPwd, certificate) {
5601
5782
  }
5602
5783
  }
5603
5784
  function verifySigningMaterial(opts) {
5604
- 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);
5605
5786
  const chain = (pems ?? []).map(parseCertificate);
5606
5787
  const leaf = chain.find((c) => !c.ca) ?? chain.at(-1);
5607
5788
  if (!leaf) throw new AgcError(SIGNING_ERRORS.CERT_INVALID);
@@ -5619,15 +5800,15 @@ function verifySigningMaterial(opts) {
5619
5800
  }
5620
5801
 
5621
5802
  // src/harmonyos/signing/autoSign.ts
5622
- var crypto9 = __toESM(require("crypto"), 1);
5623
- var fs40 = __toESM(require("fs"), 1);
5624
- 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);
5625
5806
  var import_json57 = __toESM(require("json5"), 1);
5626
5807
 
5627
5808
  // src/harmonyos/signing/agc.ts
5628
- var crypto6 = __toESM(require("crypto"), 1);
5629
- var fs36 = __toESM(require("fs"), 1);
5630
- 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);
5631
5812
  var ENDPOINTS = {
5632
5813
  CERT_LIST: "/api/cps/harmony-cert-manage/v1/cert/list",
5633
5814
  CERT_ADD: "/api/cps/harmony-cert-manage/v1/cert/add",
@@ -5675,7 +5856,7 @@ function createAgcClient(http3, auth) {
5675
5856
  }
5676
5857
  },
5677
5858
  async addDevice(udid, deviceKind) {
5678
- const deviceName = `oniro_app_device_${crypto6.randomBytes(4).toString("hex")}`;
5859
+ const deviceName = `oniro_app_device_${crypto7.randomBytes(4).toString("hex")}`;
5679
5860
  await call("device", "post", ENDPOINTS.DEVICE_ADD, {
5680
5861
  body: { deviceName, udid, deviceType: String(DEVICE_TYPE_CODES[deviceKind] ?? DEVICE_TYPE_CODES.phone) }
5681
5862
  });
@@ -5709,12 +5890,12 @@ function createAgcClient(http3, auth) {
5709
5890
  const target = urlsInfo?.[0];
5710
5891
  if (!target) throw mapAgcError(operation, 200, "", "");
5711
5892
  const buffer = await http3.getBinary(target.newUrl, { timeoutMs: 6e4 });
5712
- const actual = crypto6.createHash("sha256").update(buffer).digest("hex");
5893
+ const actual = crypto7.createHash("sha256").update(buffer).digest("hex");
5713
5894
  if (target.sha256 && actual !== target.sha256.toLowerCase()) {
5714
5895
  throw new ChecksumMismatchError(target.sha256, actual);
5715
5896
  }
5716
- fs36.mkdirSync(path34.dirname(dest), { recursive: true, mode: 448 });
5717
- fs36.writeFileSync(dest, buffer, { mode: 384 });
5897
+ fs38.mkdirSync(path36.dirname(dest), { recursive: true, mode: 448 });
5898
+ fs38.writeFileSync(dest, buffer, { mode: 384 });
5718
5899
  }
5719
5900
  };
5720
5901
  }
@@ -5728,21 +5909,31 @@ function parseDeviceKind(stdout) {
5728
5909
  }
5729
5910
  async function collectLocalDevices(config, logger) {
5730
5911
  const devices = [];
5912
+ const skipped = [];
5731
5913
  for (const target of await listDevices(config, { logger, timeoutMs: 15e3 })) {
5732
- if (target.status !== "Connected") continue;
5914
+ const skip = (why) => {
5915
+ logger.warn(`[harmonyos] ${target.serial} ${why}`);
5916
+ skipped.push(target.serial);
5917
+ };
5918
+ if (!isUsableDevice(target)) {
5919
+ skip(
5920
+ target.status === "Unauthorized" ? "is unauthorized; accept the debugging prompt on the device to register it." : `is ${target.status.toLowerCase()}; skipping it.`
5921
+ );
5922
+ continue;
5923
+ }
5733
5924
  const shell2 = async (...args) => (await hdcExec({ config, deviceSerial: target.serial, args: ["shell", ...args], timeoutMs: 15e3, logger })).stdout;
5734
5925
  try {
5735
5926
  const udid = parseUdid(await shell2("bm", "get", "-u"));
5736
5927
  if (!udid) {
5737
- logger.warn(`[harmonyos] ${target.serial} did not report a UDID; skipping it.`);
5928
+ skip("did not report a UDID (`bm get -u`), so it cannot be registered.");
5738
5929
  continue;
5739
5930
  }
5740
5931
  devices.push({ serial: target.serial, udid, kind: parseDeviceKind(await shell2("getprop", "hw_sc.build.os.deviceType")) });
5741
5932
  } catch (err) {
5742
- logger.warn(`[harmonyos] Could not read ${target.serial}: ${err.message}`);
5933
+ skip(`could not be read: ${err.message}`);
5743
5934
  }
5744
5935
  }
5745
- return devices;
5936
+ return { devices, skipped };
5746
5937
  }
5747
5938
  async function registerDevices(agc, connected, logger) {
5748
5939
  let devices = await agc.listDevices();
@@ -5755,32 +5946,27 @@ async function registerDevices(agc, connected, logger) {
5755
5946
  if (unregistered.length > 0) devices = await agc.listDevices();
5756
5947
  const deviceIds = devices.map((d) => d.id);
5757
5948
  if (deviceIds.length === 0) throw new AgcError(SIGNING_ERRORS.DEVICE_NONE);
5758
- if (connected.length === 0) {
5759
- logger.warn(
5760
- `[harmonyos] No device is connected; the profile will name the ${deviceIds.length} device(s) already registered to this team.`
5761
- );
5762
- }
5763
5949
  return deviceIds;
5764
5950
  }
5765
5951
 
5766
5952
  // src/harmonyos/signing/keystore.ts
5767
- var crypto7 = __toESM(require("crypto"), 1);
5768
- var fs38 = __toESM(require("fs"), 1);
5769
- 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);
5770
5956
 
5771
5957
  // src/harmonyos/java.ts
5772
- var fs37 = __toESM(require("fs"), 1);
5773
- var path35 = __toESM(require("path"), 1);
5958
+ var fs39 = __toESM(require("fs"), 1);
5959
+ var path37 = __toESM(require("path"), 1);
5774
5960
  var JAVA_BIN = process.platform === "win32" ? "java.exe" : "java";
5775
5961
  function findJava(sdk, env = process.env) {
5776
5962
  const candidates = [
5777
- env.JAVA_HOME && path35.join(env.JAVA_HOME, "bin", JAVA_BIN),
5778
- 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),
5779
5965
  // macOS: the JBR is itself a bundle inside Studio's Contents.
5780
- sdk && path35.join(sdk.installRoot, "jbr", "Contents", "Home", "bin", JAVA_BIN),
5781
- ...(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))
5782
5968
  ];
5783
- const found = candidates.find((c) => !!c && fs37.existsSync(c));
5969
+ const found = candidates.find((c) => !!c && fs39.existsSync(c));
5784
5970
  if (found) return found;
5785
5971
  throw new OniroError(
5786
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."
@@ -5793,49 +5979,77 @@ var SIGN_ALG = "SHA256withECDSA";
5793
5979
  function redactArgs(args) {
5794
5980
  return args.map((arg, i) => args[i - 1] === "-keyPwd" || args[i - 1] === "-keystorePwd" ? "******" : arg);
5795
5981
  }
5796
- async function generateKeystoreAndCsr(opts) {
5797
- const jar = path36.join(opts.sdk.sdkPath, "default", "openharmony", "toolchains", "lib", "hap-sign-tool.jar");
5798
- 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)) {
5799
5985
  throw new OniroError(`hap-sign-tool.jar not found at ${jar}; the HarmonyOS SDK looks incomplete.`);
5800
5986
  }
5987
+ return jar;
5988
+ }
5989
+ async function runHapSignTool(opts) {
5990
+ const jar = hapSignToolJar(opts.sdk);
5801
5991
  const java = findJava(opts.sdk);
5802
- const keyPwd = crypto7.randomBytes(12).toString("base64url").replaceAll(/[-_]/g, "");
5803
- fs38.mkdirSync(path36.dirname(opts.p12Path), { recursive: true, mode: 448 });
5804
- const run = async (what, args) => {
5805
- const argv = ["-jar", jar, ...args, "-keyAlias", KEY_ALIAS, "-keystoreFile", opts.p12Path, "-keystorePwd", keyPwd, "-keyPwd", keyPwd];
5806
- opts.logger.debug(`[harmonyos] ${java} ${redactArgs(argv).join(" ")}`);
5807
- const result = await runProcess({ command: java, args: argv, timeoutMs: 12e4, logger: opts.logger });
5808
- if (result.code !== 0) {
5809
- const detail = (result.stderr || result.stdout || "").trim();
5810
- 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 ? `
5811
5998
  ${detail}` : ""}`);
5812
- }
5813
- };
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 });
5814
6007
  opts.logger.info("[harmonyos] Generating the local keystore and certificate signing request...");
5815
6008
  await run("generate the key pair", ["generate-keypair", "-keyAlg", "ECC", "-keySize", "NIST-P-256"]);
5816
6009
  await run("generate the CSR", ["generate-csr", "-subject", "CN=DebugKey", "-signAlg", SIGN_ALG, "-outFile", opts.csrPath]);
5817
- 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
+ });
5818
6032
  }
5819
6033
 
5820
6034
  // src/harmonyos/signing/paths.ts
5821
- var crypto8 = __toESM(require("crypto"), 1);
5822
- var path37 = __toESM(require("path"), 1);
6035
+ var crypto9 = __toESM(require("crypto"), 1);
6036
+ var path39 = __toESM(require("path"), 1);
5823
6037
  function sanitizeNamePart(raw) {
5824
6038
  const safe = (raw?.trim() ?? "").replace(/[\\/:*?"<>|=.-]+/g, "_");
5825
6039
  return safe.slice(0, 64) || "default";
5826
6040
  }
5827
6041
  function resolveSigningMaterialPaths(config, productName, projectRoot) {
5828
- const hash = crypto8.createHash("sha256").update(projectRoot, "utf8").digest("base64url").replaceAll(/[-_]/g, "");
5829
- const base = path37.join(
6042
+ const hash = crypto9.createHash("sha256").update(projectRoot, "utf8").digest("base64url").replaceAll(/[-_]/g, "");
6043
+ const base = path39.join(
5830
6044
  config.get("harmonyosSigningDir", defaultPaths.harmonyosSigningDir()),
5831
- `${sanitizeNamePart(productName)}_${path37.basename(projectRoot)}_${hash}=`
6045
+ `${sanitizeNamePart(productName)}_${path39.basename(projectRoot)}_${hash}=`
5832
6046
  );
5833
6047
  return { p12Path: `${base}.p12`, csrPath: `${base}.csr`, cerPath: `${base}.cer`, profilePath: `${base}.p7b` };
5834
6048
  }
5835
6049
 
5836
6050
  // src/harmonyos/signing/project.ts
5837
- var fs39 = __toESM(require("fs"), 1);
5838
- var path38 = __toESM(require("path"), 1);
6051
+ var fs41 = __toESM(require("fs"), 1);
6052
+ var path40 = __toESM(require("path"), 1);
5839
6053
  var import_json56 = __toESM(require("json5"), 1);
5840
6054
 
5841
6055
  // src/harmonyos/signing/aclPermissionList.ts
@@ -5903,9 +6117,9 @@ var ACL_PERMISSIONS = /* @__PURE__ */ new Set([
5903
6117
 
5904
6118
  // src/harmonyos/signing/project.ts
5905
6119
  function readRequestedPermissions(moduleConfigPath) {
5906
- if (!fs39.existsSync(moduleConfigPath)) return [];
6120
+ if (!fs41.existsSync(moduleConfigPath)) return [];
5907
6121
  try {
5908
- const parsed = import_json56.default.parse(fs39.readFileSync(moduleConfigPath, "utf-8"));
6122
+ const parsed = import_json56.default.parse(fs41.readFileSync(moduleConfigPath, "utf-8"));
5909
6123
  return (parsed.module?.requestPermissions ?? []).map((p) => p?.name).filter((n) => typeof n === "string" && n.length > 0);
5910
6124
  } catch {
5911
6125
  return [];
@@ -5914,32 +6128,38 @@ function readRequestedPermissions(moduleConfigPath) {
5914
6128
  function collectAclPermissions(projectDir, logger = noopLogger) {
5915
6129
  const requested = /* @__PURE__ */ new Set();
5916
6130
  for (const module2 of listModules({ projectDir })) {
5917
- const moduleRoot = path38.resolve(projectDir, module2.srcPath);
6131
+ const moduleRoot = path40.resolve(projectDir, module2.srcPath);
5918
6132
  for (const sourceSet of ["main", "ohosTest"]) {
5919
6133
  for (const name of readRequestedPermissions(
5920
- path38.join(moduleRoot, "src", sourceSet, "module.json5")
6134
+ path40.join(moduleRoot, "src", sourceSet, "module.json5")
5921
6135
  )) {
5922
6136
  requested.add(name);
5923
6137
  }
5924
6138
  }
5925
6139
  }
5926
- const acls = [...requested].filter((p) => ACL_PERMISSIONS.has(p)).sort();
6140
+ const acls = aclPermissionsAmong(requested);
5927
6141
  if (acls.length > 0) {
5928
6142
  logger.info(`[harmonyos] Project requests ${acls.length} ACL permission(s): ${acls.join(", ")}`);
5929
6143
  }
5930
6144
  return acls;
5931
6145
  }
6146
+ function aclPermissionsAmong(names) {
6147
+ return [...new Set(names)].filter((p) => ACL_PERMISSIONS.has(p)).sort();
6148
+ }
5932
6149
 
5933
6150
  // src/harmonyos/signing/autoSign.ts
5934
6151
  function shouldRegenerate(input) {
5935
6152
  if (input.force) return "--force was passed";
5936
- const missing = Object.values(input.paths).find((file) => !fs40.existsSync(file));
6153
+ const missing = Object.values(input.paths).find((file) => !fs42.existsSync(file));
5937
6154
  if (missing) return `${missing} is missing`;
5938
6155
  const config = input.buildProfile.app?.signingConfigs?.find((c) => c.name === input.productName);
5939
6156
  if (config?.type !== "HarmonyOS" || config.material?.profile !== input.paths.profilePath) {
5940
6157
  return "build-profile.json5 has no signingConfig for this material";
5941
6158
  }
5942
- 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);
5943
6163
  if (!profile) return "the provisioning profile could not be read";
5944
6164
  if (profile.bundleName !== input.bundleName) return `the profile is for bundle '${profile.bundleName}'`;
5945
6165
  if (profile.developerId && profile.developerId !== input.teamId) {
@@ -5972,31 +6192,29 @@ function writeSigningConfig(opts) {
5972
6192
  for (const product of app.products ?? []) {
5973
6193
  if (product.name === opts.productName) product.signingConfig = opts.productName;
5974
6194
  }
5975
- fs40.writeFileSync(opts.buildProfilePath, JSON.stringify(opts.buildProfile, null, 2));
6195
+ fs42.writeFileSync(opts.buildProfilePath, JSON.stringify(opts.buildProfile, null, 2));
5976
6196
  }
5977
6197
  async function harmonyOsAutoSign(opts) {
5978
6198
  const logger = opts.logger ?? noopLogger;
5979
6199
  const productName = opts.productName ?? "default";
5980
- const projectDir = path39.resolve(opts.projectDir);
5981
- const buildProfilePath = path39.join(projectDir, "build-profile.json5");
6200
+ const projectDir = path41.resolve(opts.projectDir);
6201
+ const buildProfilePath = path41.join(projectDir, "build-profile.json5");
5982
6202
  let buildProfile;
5983
6203
  try {
5984
- buildProfile = import_json57.default.parse(fs40.readFileSync(buildProfilePath, "utf-8"));
6204
+ buildProfile = import_json57.default.parse(fs42.readFileSync(buildProfilePath, "utf-8"));
5985
6205
  } catch (err) {
5986
6206
  throw new OniroError(`Could not read ${buildProfilePath}: ${err.message}`, err);
5987
6207
  }
5988
6208
  const product = buildProfile.app?.products?.find((p) => p.name === productName);
5989
6209
  if (!product) throw new OniroError(`${buildProfilePath} declares no product named '${productName}'.`);
5990
6210
  const bundleName = product.bundleName || getBundleName(projectDir);
5991
- if (!isValidBundleName(bundleName) || bundleName.split(".").length < 3 || bundleName.length < 7 || bundleName.length > 128) {
5992
- throw new AgcError(SIGNING_ERRORS.BUNDLE_NAME_INVALID);
5993
- }
6211
+ assertAgcBundleName(bundleName);
5994
6212
  const sdk = requireHarmonyOsSdk({ config: opts.config, logger });
5995
6213
  const auth = await opts.session.resolveAgcAuth({ teamId: opts.teamId });
5996
6214
  const aclPermissions = collectAclPermissions(projectDir, logger);
5997
6215
  const paths = resolveSigningMaterialPaths(opts.config, productName, projectDir);
5998
6216
  logger.info(`[harmonyos] Signing ${bundleName} (product '${productName}', team ${auth.teamId}).`);
5999
- const connected = await collectLocalDevices(opts.config, logger);
6217
+ const local = await collectLocalDevices(opts.config, logger);
6000
6218
  const result = (regenerated) => ({
6001
6219
  bundleName,
6002
6220
  teamId: auth.teamId,
@@ -6010,7 +6228,7 @@ async function harmonyOsAutoSign(opts) {
6010
6228
  productName,
6011
6229
  bundleName,
6012
6230
  teamId: auth.teamId,
6013
- connectedUdids: connected.map((d) => d.udid),
6231
+ connectedUdids: local.devices.map((d) => d.udid),
6014
6232
  aclPermissions,
6015
6233
  force: opts.force
6016
6234
  });
@@ -6019,10 +6237,47 @@ async function harmonyOsAutoSign(opts) {
6019
6237
  return result(false);
6020
6238
  }
6021
6239
  logger.info(`[harmonyos] Regenerating signing material: ${reason}.`);
6022
- const agc = createAgcClient(opts.http ?? createHttpClient({ logger }), auth);
6023
- const deviceIds = await registerDevices(agc, connected, logger);
6024
- for (const file of Object.values(paths)) fs40.rmSync(file, { force: true });
6025
- const certName = `oniro_debug_${auth.teamId.replace(/[\\/.:]/g, "")}.cer`;
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;
6274
+ const deviceIds = await registerDevices(agc, local.devices, logger);
6275
+ if (local.devices.length === 0) {
6276
+ const cause = local.skipped.length > 0 ? "No connected device could be registered" : "No device is connected";
6277
+ logger.warn(`[harmonyos] ${cause}; the profile will name the ${deviceIds.length} device(s) already registered to this team.`);
6278
+ }
6279
+ for (const file of Object.values(paths)) fs42.rmSync(file, { force: true });
6280
+ const certName = `oniro_debug_${opts.teamId.replace(/[\\/.:]/g, "")}.cer`;
6026
6281
  const stale = (await agc.listCertificates()).find((c) => c.certName === certName);
6027
6282
  if (stale) {
6028
6283
  logger.info(`[harmonyos] Replacing the previous '${certName}' certificate in AppGallery Connect...`);
@@ -6036,11 +6291,11 @@ async function harmonyOsAutoSign(opts) {
6036
6291
  await agc.download("cert", cert.certObjectId, paths.cerPath);
6037
6292
  logger.info("[harmonyos] Issuing the debug provisioning profile...");
6038
6293
  const profile = await agc.addDebugProfile({
6039
- provisionName: `oniro_${crypto9.randomBytes(6).toString("hex")}`,
6040
- bundleName,
6294
+ provisionName: `oniro_${crypto10.randomBytes(6).toString("hex")}`,
6295
+ bundleName: opts.bundleName,
6041
6296
  certId: cert.id,
6042
6297
  deviceIds,
6043
- aclPermissions
6298
+ aclPermissions: opts.aclPermissions
6044
6299
  });
6045
6300
  try {
6046
6301
  await agc.download("profile", profile.provisionFileUrl, paths.profilePath);
@@ -6052,22 +6307,86 @@ async function harmonyOsAutoSign(opts) {
6052
6307
  try {
6053
6308
  verifySigningMaterial({ ...paths, keyAlias: KEY_ALIAS, keyPwd });
6054
6309
  } catch (err) {
6055
- fs40.rmSync(paths.profilePath, { force: true });
6310
+ fs42.rmSync(paths.profilePath, { force: true });
6056
6311
  throw err;
6057
6312
  }
6058
- 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) {
6059
6324
  try {
6060
- getKey(materialDir);
6325
+ return decryptPwd(fs43.readFileSync(paths.pwdPath, "utf8").trim(), materialDirFor(paths));
6061
6326
  } catch {
6062
- createMaterial(materialDir);
6327
+ return null;
6063
6328
  }
6064
- writeSigningConfig({ buildProfilePath, buildProfile, productName, paths, encryptedPassword: encryptPwd(keyPwd, materialDir) });
6065
- logger.info(`[harmonyos] Wrote signingConfigs['${productName}'] to ${buildProfilePath}.`);
6066
- 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 };
6067
6386
  }
6068
6387
 
6069
6388
  // src/index.ts
6070
- var VERSION = "0.15.0";
6389
+ var VERSION = "0.16.0";
6071
6390
  // Annotate the CommonJS export names for ESM import in node:
6072
6391
  0 && (module.exports = {
6073
6392
  ALL_SDKS,
@@ -6085,6 +6404,7 @@ var VERSION = "0.15.0";
6085
6404
  OHOS_DAILY_BUILDS_URL,
6086
6405
  OHOS_URL_BASE,
6087
6406
  OniroError,
6407
+ SIGNED_HAP_CHUNK_SIZE,
6088
6408
  SIGNING_ERRORS,
6089
6409
  SdkNotInstalledError,
6090
6410
  TMP_DIR_HINT,
@@ -6120,6 +6440,7 @@ var VERSION = "0.15.0";
6120
6440
  dedupEntries,
6121
6441
  defaultContext,
6122
6442
  defaultPaths,
6443
+ defaultSignedHapPath,
6123
6444
  describeLayoutNode,
6124
6445
  detectAaStartFailure,
6125
6446
  detectCmdToolsFlavour,
@@ -6186,6 +6507,7 @@ var VERSION = "0.15.0";
6186
6507
  getSupportedSdksForUi,
6187
6508
  harmonyOsAutoSign,
6188
6509
  harmonyOsSdkLabelForApi,
6510
+ harmonyOsSignHap,
6189
6511
  hdcClientFailure,
6190
6512
  hdcExec,
6191
6513
  hdcTargetArgs,
@@ -6203,6 +6525,7 @@ var VERSION = "0.15.0";
6203
6525
  isOutOfSpaceError,
6204
6526
  isRamBackedPath,
6205
6527
  isUnsignedHap,
6528
+ isUsableDevice,
6206
6529
  isValidBundleName,
6207
6530
  isValidProjectName,
6208
6531
  killProcessTree,
@@ -6238,6 +6561,7 @@ var VERSION = "0.15.0";
6238
6561
  prepareSigning,
6239
6562
  pruneLayout,
6240
6563
  readHapBundleName,
6564
+ readHapSigningInfo,
6241
6565
  readJson5File,
6242
6566
  readJsonFile,
6243
6567
  readProvisionProfile,
@@ -6265,6 +6589,7 @@ var VERSION = "0.15.0";
6265
6589
  setHilogLevel,
6266
6590
  shell,
6267
6591
  shellChecked,
6592
+ signOpenHarmonyHap,
6268
6593
  spawnCompat,
6269
6594
  startEmulator,
6270
6595
  staticConfig,
@@ -6284,6 +6609,7 @@ var VERSION = "0.15.0";
6284
6609
  waitForTarget,
6285
6610
  watchLog,
6286
6611
  waypointsToRawEvents,
6612
+ withHapZip,
6287
6613
  withoutWindow,
6288
6614
  writeJson5File,
6289
6615
  writeJsonFile