@oniroproject/core 0.15.1 → 0.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -581,14 +581,14 @@ var pipelineAsync = promisify(pipeline);
581
581
  async function downloadFile(opts) {
582
582
  const { url, dest, progress, abortSignal } = opts;
583
583
  const proto = url.startsWith("https") ? https : http;
584
- return new Promise((resolve7, reject) => {
584
+ return new Promise((resolve9, reject) => {
585
585
  const s = Math.max(0, Math.min(100, opts.start ?? 0));
586
586
  const r = Math.max(0, Math.min(100 - s, opts.range ?? 100));
587
587
  let settled = false;
588
588
  const done = (err) => {
589
589
  if (settled) return;
590
590
  settled = true;
591
- err ? reject(err) : resolve7();
591
+ err ? reject(err) : resolve9();
592
592
  };
593
593
  if (abortSignal?.aborted) {
594
594
  done(new CancelledError("Download cancelled before start."));
@@ -766,15 +766,26 @@ async function extractZipWithProgress(opts) {
766
766
  async function extractTarball(tarPath, dest, strip = 0, filter) {
767
767
  await tar.x({ file: tarPath, cwd: dest, strip, ...filter ? { filter: (p) => filter(p) } : {} });
768
768
  }
769
- async function readZipEntry(zipPath, match) {
770
- const zip = new StreamZip.async({ file: zipPath });
769
+ var SIGNED_HAP_CHUNK_SIZE = 128 * 1024;
770
+ async function withHapZip(zipPath, use) {
771
+ const zip = new StreamZip({ file: zipPath, chunkSize: SIGNED_HAP_CHUNK_SIZE, storeEntries: true });
772
+ const close = () => new Promise((resolve9) => zip.close(() => resolve9()));
771
773
  try {
772
- const entry = Object.values(await zip.entries()).find((e) => !e.isDirectory && match(e.name));
773
- return entry ? await zip.entryData(entry) : void 0;
774
+ await new Promise((resolve9, reject) => {
775
+ zip.once("ready", () => resolve9());
776
+ zip.once("error", (err) => reject(err));
777
+ });
778
+ return await use({ entries: () => zip.entries(), entryData: (name) => zip.entryDataSync(name) });
774
779
  } finally {
775
- await zip.close();
780
+ await close();
776
781
  }
777
782
  }
783
+ function readZipEntry(zipPath, match) {
784
+ return withHapZip(zipPath, (zip) => {
785
+ const entry = Object.values(zip.entries()).find((e) => !e.isDirectory && match(e.name));
786
+ return entry ? zip.entryData(entry.name) : void 0;
787
+ });
788
+ }
778
789
 
779
790
  // src/sdk/list.ts
780
791
  import * as fs7 from "fs";
@@ -1425,9 +1436,16 @@ var SIGNING_KIND_CONFIG = {
1425
1436
  certBasename: "OpenHarmonyApplication.cer"
1426
1437
  }
1427
1438
  };
1428
- function copyFilesToProject(projectDir, paths, kind, logger, applicationCertPath) {
1429
- logger.info("[sign] Copying signing material into project...");
1430
- const signaturesDir = path14.join(projectDir, "signatures");
1439
+ function sdkSigningFiles(sdkPath) {
1440
+ return {
1441
+ signTool: path14.join(sdkPath, "toolchains/lib/hap-sign-tool.jar"),
1442
+ keystore: path14.join(sdkPath, "toolchains/lib/OpenHarmony.p12"),
1443
+ profileCert: path14.join(sdkPath, "toolchains/lib/OpenHarmonyProfileRelease.pem"),
1444
+ unsignedProfileTemplate: path14.join(sdkPath, "toolchains/lib/UnsgnedReleasedProfileTemplate.json")
1445
+ };
1446
+ }
1447
+ function copySigningFiles(signaturesDir, paths, kind, logger, applicationCertPath) {
1448
+ logger.info(`[sign] Copying signing material into ${signaturesDir}...`);
1431
1449
  fs13.mkdirSync(signaturesDir, { recursive: true });
1432
1450
  fs13.copyFileSync(paths.keystore, path14.join(signaturesDir, "OpenHarmony.p12"));
1433
1451
  fs13.copyFileSync(paths.profileCert, path14.join(signaturesDir, "OpenHarmonyProfileRelease.pem"));
@@ -1454,10 +1472,7 @@ function extractLeafCertificate(chainPath) {
1454
1472
  return parts[2].trim() + "\n-----END CERTIFICATE-----\n";
1455
1473
  }
1456
1474
  function modifyProfileTemplate(projectDir, apl, appFeature, logger, options = {}) {
1457
- const kind = options.kind ?? pickSigningKind(apl);
1458
- logger.info(`[sign] Modifying profile template (apl=${apl}, app-feature=${appFeature}, kind=${kind})...`);
1459
1475
  const appJsonPath = path14.join(projectDir, "AppScope/app.json5");
1460
- const profileTemplatePath = path14.join(projectDir, "signatures/UnsgnedReleasedProfileTemplate.json");
1461
1476
  if (!fs13.existsSync(appJsonPath)) {
1462
1477
  throw new OniroError(`${appJsonPath} does not exist.`);
1463
1478
  }
@@ -1467,26 +1482,28 @@ function modifyProfileTemplate(projectDir, apl, appFeature, logger, options = {}
1467
1482
  } catch (e) {
1468
1483
  throw new OniroError(`Error parsing ${appJsonPath}: ${e.message}`, e);
1469
1484
  }
1485
+ if (!appJson.app?.bundleName) {
1486
+ throw new OniroError("app.json5 does not contain app.bundleName.");
1487
+ }
1488
+ applyProfileTemplate(path14.join(projectDir, "signatures"), appJson.app.bundleName, apl, appFeature, logger, options);
1489
+ }
1490
+ function applyProfileTemplate(signaturesDir, bundleName, apl, appFeature, logger, options = {}) {
1491
+ const kind = options.kind ?? pickSigningKind(apl);
1492
+ logger.info(`[sign] Modifying profile template (apl=${apl}, app-feature=${appFeature}, kind=${kind})...`);
1493
+ const profileTemplatePath = path14.join(signaturesDir, "UnsgnedReleasedProfileTemplate.json");
1470
1494
  let profileTemplate;
1471
1495
  try {
1472
1496
  profileTemplate = JSON.parse(fs13.readFileSync(profileTemplatePath, "utf-8"));
1473
1497
  } catch (e) {
1474
1498
  throw new OniroError(`Error parsing ${profileTemplatePath}: ${e.message}`, e);
1475
1499
  }
1476
- if (!appJson.app?.bundleName) {
1477
- throw new OniroError("app.json5 does not contain app.bundleName.");
1478
- }
1479
1500
  if (!profileTemplate["bundle-info"]) {
1480
1501
  throw new OniroError("UnsgnedReleasedProfileTemplate.json is missing the bundle-info section.");
1481
1502
  }
1482
- profileTemplate["bundle-info"]["bundle-name"] = appJson.app.bundleName;
1503
+ profileTemplate["bundle-info"]["bundle-name"] = bundleName;
1483
1504
  profileTemplate["bundle-info"]["apl"] = apl;
1484
1505
  profileTemplate["bundle-info"]["app-feature"] = appFeature;
1485
- const distCertChainPath = path14.join(
1486
- projectDir,
1487
- "signatures",
1488
- SIGNING_KIND_CONFIG[kind].certBasename
1489
- );
1506
+ const distCertChainPath = path14.join(signaturesDir, SIGNING_KIND_CONFIG[kind].certBasename);
1490
1507
  profileTemplate["bundle-info"]["distribution-certificate"] = extractLeafCertificate(distCertChainPath);
1491
1508
  if (options.acls !== void 0) {
1492
1509
  profileTemplate.acls = profileTemplate.acls ?? {};
@@ -1497,9 +1514,8 @@ function modifyProfileTemplate(projectDir, apl, appFeature, logger, options = {}
1497
1514
  }
1498
1515
  fs13.writeFileSync(profileTemplatePath, JSON.stringify(profileTemplate, null, 2));
1499
1516
  }
1500
- function generateP7bFile(projectDir, paths, logger, passwords) {
1517
+ function generateP7bFile(signaturesDir, paths, logger, passwords) {
1501
1518
  logger.info("[sign] Generating P7b profile via hap-sign-tool...");
1502
- const signaturesDir = path14.join(projectDir, "signatures");
1503
1519
  const profileTemplatePath = path14.join(signaturesDir, "UnsgnedReleasedProfileTemplate.json");
1504
1520
  const outputProfilePath = path14.join(signaturesDir, "app1-profile.p7b");
1505
1521
  const args = [
@@ -1623,30 +1639,201 @@ function generateSigningConfigs(options) {
1623
1639
  if (!fs13.existsSync(sdkPath)) {
1624
1640
  throw new SdkNotInstalledError(String(sdkVersion), sdkPath, ALL_SDKS.find((s) => s.api === String(sdkVersion))?.version);
1625
1641
  }
1626
- const paths = {
1627
- signTool: path14.join(sdkPath, "toolchains/lib/hap-sign-tool.jar"),
1628
- keystore: path14.join(sdkPath, "toolchains/lib/OpenHarmony.p12"),
1629
- profileCert: path14.join(sdkPath, "toolchains/lib/OpenHarmonyProfileRelease.pem"),
1630
- unsignedProfileTemplate: path14.join(sdkPath, "toolchains/lib/UnsgnedReleasedProfileTemplate.json")
1631
- };
1642
+ const paths = sdkSigningFiles(sdkPath);
1632
1643
  const kind = pickSigningKind(apl);
1633
1644
  const passwords = options.passwords ?? DEFAULT_PASSWORDS;
1634
1645
  logger.info("[sign] Starting signing configuration generation...");
1635
- copyFilesToProject(projectDir, paths, kind, logger, options.applicationCertPath);
1646
+ const signaturesDir = path14.join(projectDir, "signatures");
1647
+ copySigningFiles(signaturesDir, paths, kind, logger, options.applicationCertPath);
1636
1648
  modifyProfileTemplate(projectDir, apl, appFeature, logger, {
1637
1649
  kind,
1638
1650
  acls: options.acls,
1639
1651
  appPrivilegeCapabilities: options.appPrivilegeCapabilities
1640
1652
  });
1641
- generateP7bFile(projectDir, paths, logger, passwords);
1653
+ generateP7bFile(signaturesDir, paths, logger, passwords);
1642
1654
  prepareMaterialDirectory(projectDir, logger);
1643
1655
  updateBuildProfile(projectDir, kind, logger, passwords);
1644
1656
  logger.info("[sign] Signing configuration generated successfully.");
1645
1657
  }
1646
1658
 
1647
- // src/emulator/install.ts
1659
+ // src/sign/hapInfo.ts
1660
+ import * as crypto3 from "crypto";
1648
1661
  import * as fs14 from "fs";
1649
1662
  import * as path15 from "path";
1663
+ function parseHapModuleJson(text, hapPath) {
1664
+ let parsed;
1665
+ try {
1666
+ parsed = JSON.parse(text.replace(/^\uFEFF/, ""));
1667
+ } catch (err) {
1668
+ throw new OniroError(`The module.json in ${hapPath} is not valid JSON: ${err.message}`, err);
1669
+ }
1670
+ const bundleName = parsed.app?.bundleName;
1671
+ if (typeof bundleName !== "string" || !bundleName) {
1672
+ throw new OniroError(`The module.json in ${hapPath} names no app.bundleName.`);
1673
+ }
1674
+ const requested = Array.isArray(parsed.module?.requestPermissions) ? parsed.module.requestPermissions : [];
1675
+ const { compileSdkType, targetAPIVersion } = parsed.app ?? {};
1676
+ return {
1677
+ bundleName,
1678
+ requestedPermissions: requested.map((p) => p?.name).filter((n) => typeof n === "string" && n.length > 0),
1679
+ compileSdkType: typeof compileSdkType === "string" && compileSdkType ? compileSdkType : void 0,
1680
+ targetApiVersion: typeof targetAPIVersion === "number" ? targetAPIVersion : void 0
1681
+ };
1682
+ }
1683
+ async function readHapSigningInfo(hapPath) {
1684
+ if (!fs14.existsSync(hapPath)) throw new OniroError(`${hapPath} does not exist.`);
1685
+ let moduleJson;
1686
+ try {
1687
+ moduleJson = await readZipEntry(hapPath, (n) => n === "module.json");
1688
+ if (!moduleJson && await readZipEntry(hapPath, (n) => n === "config.json")) {
1689
+ throw new OniroError(
1690
+ `${hapPath} is an FA-model HAP (config.json, no module.json). Only stage-model HAPs can be signed without their project.`
1691
+ );
1692
+ }
1693
+ } catch (err) {
1694
+ if (err instanceof OniroError) throw err;
1695
+ throw new OniroError(`Could not read ${hapPath} as a HAP: ${err.message}`, err);
1696
+ }
1697
+ if (!moduleJson) throw new OniroError(`${hapPath} holds no module.json, so it is not a HAP.`);
1698
+ return parseHapModuleJson(moduleJson.toString("utf8"), hapPath);
1699
+ }
1700
+ function defaultSignedHapPath(hapPath) {
1701
+ const dir = path15.dirname(hapPath);
1702
+ const name = path15.basename(hapPath);
1703
+ if (/-signed\.hap$/i.test(name)) return path15.join(dir, name.replace(/-signed\.hap$/i, "-resigned.hap"));
1704
+ return path15.join(dir, `${name.replace(/(-unsigned)?\.hap$/i, "")}-signed.hap`);
1705
+ }
1706
+ async function writeHapAtomically(outPath, sign) {
1707
+ const dir = path15.dirname(outPath);
1708
+ try {
1709
+ fs14.mkdirSync(dir, { recursive: true });
1710
+ } catch (err) {
1711
+ throw new OniroError(`Cannot create ${dir} for the signed HAP: ${err.message}`, err);
1712
+ }
1713
+ const tmpPath = path15.join(dir, `.${path15.basename(outPath)}.${crypto3.randomBytes(4).toString("hex")}.tmp`);
1714
+ try {
1715
+ await sign(tmpPath);
1716
+ fs14.renameSync(tmpPath, outPath);
1717
+ } finally {
1718
+ fs14.rmSync(tmpPath, { force: true });
1719
+ }
1720
+ }
1721
+
1722
+ // src/sign/signHap.ts
1723
+ import * as fs15 from "fs";
1724
+ import * as os8 from "os";
1725
+ import * as path16 from "path";
1726
+ import { execFileSync as execFileSync2 } from "child_process";
1727
+
1728
+ // src/project/validators.ts
1729
+ function isValidProjectName(name) {
1730
+ if (!name) return false;
1731
+ if (name.includes("/") || name.includes("\\")) return false;
1732
+ return /^[A-Za-z0-9._-]+$/.test(name);
1733
+ }
1734
+ function isValidBundleName(bundleName) {
1735
+ return /^[A-Za-z][A-Za-z0-9_]*(\.[A-Za-z][A-Za-z0-9_]*)+$/.test(bundleName);
1736
+ }
1737
+
1738
+ // src/sign/signHap.ts
1739
+ function pickSigningSdk(sdkHome, targetApiVersion) {
1740
+ let apis;
1741
+ try {
1742
+ apis = fs15.readdirSync(sdkHome).filter((api2) => /^\d+$/.test(api2));
1743
+ } catch {
1744
+ return null;
1745
+ }
1746
+ const usable = apis.filter((api2) => fs15.existsSync(sdkSigningFiles(path16.join(sdkHome, api2)).signTool)).sort((a, b) => Number(b) - Number(a));
1747
+ const api = usable.find((a) => targetApiVersion !== void 0 && Number(a) === targetApiVersion) ?? usable[0];
1748
+ return api ? { api, sdkPath: path16.join(sdkHome, api) } : null;
1749
+ }
1750
+ async function signOpenHarmonyHap(opts) {
1751
+ const logger = opts.logger ?? noopLogger;
1752
+ const hapPath = path16.resolve(opts.hapPath);
1753
+ const outPath = path16.resolve(opts.outPath ?? defaultSignedHapPath(hapPath));
1754
+ const apl = opts.apl ?? "normal";
1755
+ if (!APL_VALUES.includes(apl)) {
1756
+ throw new OniroError(`Invalid apl '${apl}'. Expected one of: ${APL_VALUES.join(", ")}.`);
1757
+ }
1758
+ const appFeature = resolveAppFeature(apl, opts.appFeature);
1759
+ if (!APP_FEATURE_VALUES.includes(appFeature)) {
1760
+ throw new OniroError(`Invalid appFeature '${appFeature}'. Expected one of: ${APP_FEATURE_VALUES.join(", ")}.`);
1761
+ }
1762
+ const info = await readHapSigningInfo(hapPath);
1763
+ if (info.compileSdkType === "HarmonyOS") {
1764
+ throw new OniroError(
1765
+ `${hapPath} was built for HarmonyOS, which needs a certificate from AppGallery Connect: run \`oniro-app auth login\` once, then \`oniro-app sign --harmonyos --hap\`.`
1766
+ );
1767
+ }
1768
+ if (!isValidBundleName(info.bundleName)) {
1769
+ throw new OniroError(`${hapPath} names an invalid bundle name '${info.bundleName}'.`);
1770
+ }
1771
+ const sdk = pickSigningSdk(opts.sdkHome, info.targetApiVersion);
1772
+ if (!sdk) {
1773
+ throw new OniroError(
1774
+ `No OpenHarmony SDK with hap-sign-tool.jar under ${opts.sdkHome}; install one with \`oniro-app sdk install\`.`
1775
+ );
1776
+ }
1777
+ const files = sdkSigningFiles(sdk.sdkPath);
1778
+ const kind = pickSigningKind(apl);
1779
+ const passwords = opts.passwords ?? DEFAULT_PASSWORDS;
1780
+ logger.info(`[sign] Signing ${path16.basename(hapPath)} as ${info.bundleName} with the API ${sdk.api} SDK's material...`);
1781
+ const signaturesDir = fs15.mkdtempSync(path16.join(os8.tmpdir(), "oniro-sign-hap-"));
1782
+ try {
1783
+ copySigningFiles(signaturesDir, files, kind, logger, opts.applicationCertPath);
1784
+ applyProfileTemplate(signaturesDir, info.bundleName, apl, appFeature, logger, {
1785
+ kind,
1786
+ acls: opts.acls,
1787
+ appPrivilegeCapabilities: opts.appPrivilegeCapabilities
1788
+ });
1789
+ generateP7bFile(signaturesDir, files, logger, passwords);
1790
+ logger.info("[sign] Signing the HAP via hap-sign-tool...");
1791
+ const { hapKeyAlias, certBasename } = SIGNING_KIND_CONFIG[kind];
1792
+ await writeHapAtomically(outPath, (tmpPath) => {
1793
+ const args = [
1794
+ "-jar",
1795
+ files.signTool,
1796
+ "sign-app",
1797
+ "-mode",
1798
+ "localSign",
1799
+ "-keyAlias",
1800
+ hapKeyAlias,
1801
+ "-signAlg",
1802
+ "SHA256withECDSA",
1803
+ "-appCertFile",
1804
+ path16.join(signaturesDir, certBasename),
1805
+ "-profileFile",
1806
+ path16.join(signaturesDir, "app1-profile.p7b"),
1807
+ "-inFile",
1808
+ hapPath,
1809
+ "-outFile",
1810
+ tmpPath,
1811
+ "-keystoreFile",
1812
+ path16.join(signaturesDir, "OpenHarmony.p12"),
1813
+ "-keyPwd",
1814
+ passwords.key,
1815
+ "-keystorePwd",
1816
+ passwords.store
1817
+ ];
1818
+ try {
1819
+ execFileSync2("java", args, { stdio: "inherit" });
1820
+ } catch (e) {
1821
+ throw new OniroError(
1822
+ `hap-sign-tool failed to sign ${hapPath}. Ensure a JDK (with java) is on PATH. Underlying error: ${e.message}`,
1823
+ e
1824
+ );
1825
+ }
1826
+ });
1827
+ } finally {
1828
+ fs15.rmSync(signaturesDir, { recursive: true, force: true });
1829
+ }
1830
+ logger.info(`[sign] Wrote ${outPath}.`);
1831
+ return { bundleName: info.bundleName, outPath, sdkApi: sdk.api, kind };
1832
+ }
1833
+
1834
+ // src/emulator/install.ts
1835
+ import * as fs16 from "fs";
1836
+ import * as path17 from "path";
1650
1837
  async function installEmulator(opts) {
1651
1838
  const { config, progress, abortSignal } = opts;
1652
1839
  const logger = opts.logger ?? noopLogger;
@@ -1656,20 +1843,20 @@ async function installEmulator(opts) {
1656
1843
  const { dir: tmpDir, root: tmpRoot } = createInstallTempDir({
1657
1844
  config,
1658
1845
  override: opts.tmpDir,
1659
- installRoot: path15.dirname(emulatorDir),
1846
+ installRoot: path17.dirname(emulatorDir),
1660
1847
  prefix: "oniro-emulator-",
1661
1848
  logger
1662
1849
  });
1663
- const tmpZip = path15.join(tmpDir, "oniro_emulator.zip");
1850
+ const tmpZip = path17.join(tmpDir, "oniro_emulator.zip");
1664
1851
  try {
1665
- fs14.mkdirSync(emulatorDir, { recursive: true });
1852
+ fs16.mkdirSync(emulatorDir, { recursive: true });
1666
1853
  progress?.report({ message: "Downloading emulator...", increment: 0 });
1667
1854
  await downloadFile({ url, dest: tmpZip, progress, abortSignal, start: 0, range: 50, what, tmpRoot });
1668
1855
  progress?.report({ message: "Extracting emulator...", increment: 0 });
1669
1856
  await extractZipWithProgress({ zipPath: tmpZip, dest: emulatorDir, progress, start: 50, range: 45, logger });
1670
- const runSh = path15.join(emulatorDir, "images", "run.sh");
1671
- if (fs14.existsSync(runSh)) {
1672
- fs14.chmodSync(runSh, 493);
1857
+ const runSh = path17.join(emulatorDir, "images", "run.sh");
1858
+ if (fs16.existsSync(runSh)) {
1859
+ fs16.chmodSync(runSh, 493);
1673
1860
  }
1674
1861
  progress?.report({ message: "Finalizing installation...", increment: 5 });
1675
1862
  } catch (err) {
@@ -1679,24 +1866,24 @@ async function installEmulator(opts) {
1679
1866
  }
1680
1867
  }
1681
1868
  function isEmulatorInstalled(config) {
1682
- const imagesDir = path15.join(getEmulatorDir(config), "images");
1683
- return fs14.existsSync(path15.join(imagesDir, "run.sh")) || fs14.existsSync(path15.join(imagesDir, "run.bat"));
1869
+ const imagesDir = path17.join(getEmulatorDir(config), "images");
1870
+ return fs16.existsSync(path17.join(imagesDir, "run.sh")) || fs16.existsSync(path17.join(imagesDir, "run.bat"));
1684
1871
  }
1685
1872
  function removeEmulator(config) {
1686
1873
  const emulatorDir = getEmulatorDir(config);
1687
- if (fs14.existsSync(emulatorDir)) {
1688
- fs14.rmSync(emulatorDir, { recursive: true, force: true });
1874
+ if (fs16.existsSync(emulatorDir)) {
1875
+ fs16.rmSync(emulatorDir, { recursive: true, force: true });
1689
1876
  }
1690
1877
  }
1691
1878
 
1692
1879
  // src/emulator/lifecycle.ts
1693
- import * as fs16 from "fs";
1694
- import * as os8 from "os";
1695
- import * as path16 from "path";
1880
+ import * as fs18 from "fs";
1881
+ import * as os9 from "os";
1882
+ import * as path18 from "path";
1696
1883
  import { spawn as spawn2 } from "child_process";
1697
1884
 
1698
1885
  // src/hdc/exec.ts
1699
- import * as fs15 from "fs";
1886
+ import * as fs17 from "fs";
1700
1887
  import { StringDecoder } from "string_decoder";
1701
1888
  function errorMessage(err) {
1702
1889
  return err instanceof Error ? err.message : String(err);
@@ -1705,12 +1892,12 @@ function runProcess(opts) {
1705
1892
  const { command, args, cwd, env, onOutput } = opts;
1706
1893
  const timeoutMs = opts.timeoutMs ?? 6e4;
1707
1894
  const { abortSignal } = opts;
1708
- return new Promise((resolve7, reject) => {
1895
+ return new Promise((resolve9, reject) => {
1709
1896
  if (abortSignal?.aborted) {
1710
1897
  reject(new CancelledError(`Command aborted before start: ${command}`));
1711
1898
  return;
1712
1899
  }
1713
- if (needsCmdWrapper(command) && !fs15.existsSync(command)) {
1900
+ if (needsCmdWrapper(command) && !fs17.existsSync(command)) {
1714
1901
  reject(new OniroError(`Failed to spawn ${command}: ENOENT (no such file)`));
1715
1902
  return;
1716
1903
  }
@@ -1778,7 +1965,7 @@ ${stderr.trim()}` : "")
1778
1965
  );
1779
1966
  return;
1780
1967
  }
1781
- resolve7({ code: code ?? -1, stdout, stderr });
1968
+ resolve9({ code: code ?? -1, stdout, stderr });
1782
1969
  });
1783
1970
  });
1784
1971
  }
@@ -1851,7 +2038,7 @@ function ensureRemoteOk(result, command) {
1851
2038
  }
1852
2039
 
1853
2040
  // src/emulator/lifecycle.ts
1854
- var PID_FILE = path16.join(os8.tmpdir(), "oniro_emulator.pid");
2041
+ var PID_FILE = path18.join(os9.tmpdir(), "oniro_emulator.pid");
1855
2042
  async function attemptHdcConnection(config, address = "127.0.0.1:55555", logger = noopLogger) {
1856
2043
  try {
1857
2044
  await hdcExec({ config, args: ["start", "-r"], logger });
@@ -1864,24 +2051,24 @@ async function attemptHdcConnection(config, address = "127.0.0.1:55555", logger
1864
2051
  }
1865
2052
  }
1866
2053
  function resolveLauncher(imagesPath, headless, connect) {
1867
- const runSh = path16.join(imagesPath, "run.sh");
1868
- const runBat = path16.join(imagesPath, "run.bat");
2054
+ const runSh = path18.join(imagesPath, "run.sh");
2055
+ const runBat = path18.join(imagesPath, "run.bat");
1869
2056
  const extra = [];
1870
2057
  if (headless) extra.push("--headless");
1871
2058
  if (connect) extra.push("--connect", connect);
1872
- if (os8.platform() === "win32") {
1873
- if (fs16.existsSync(runBat)) {
2059
+ if (os9.platform() === "win32") {
2060
+ if (fs18.existsSync(runBat)) {
1874
2061
  return { command: "cmd.exe", args: ["/c", runBat, ...extra], cwd: imagesPath };
1875
2062
  }
1876
- if (fs16.existsSync(runSh)) {
2063
+ if (fs18.existsSync(runSh)) {
1877
2064
  return { command: "bash", args: [runSh, ...extra], cwd: imagesPath };
1878
2065
  }
1879
2066
  throw new OniroError(`No emulator launcher (run.bat or run.sh) found in ${imagesPath}.`);
1880
2067
  }
1881
- if (fs16.existsSync(runSh)) {
2068
+ if (fs18.existsSync(runSh)) {
1882
2069
  return { command: "bash", args: [runSh, ...extra], cwd: imagesPath };
1883
2070
  }
1884
- if (fs16.existsSync(runBat)) {
2071
+ if (fs18.existsSync(runBat)) {
1885
2072
  return { command: "bash", args: [runBat, ...extra], cwd: imagesPath };
1886
2073
  }
1887
2074
  throw new OniroError(`No emulator launcher (run.sh or run.bat) found in ${imagesPath}.`);
@@ -1889,9 +2076,9 @@ function resolveLauncher(imagesPath, headless, connect) {
1889
2076
  async function startEmulator(opts) {
1890
2077
  const { config } = opts;
1891
2078
  const logger = opts.logger ?? noopLogger;
1892
- if (os8.platform() !== "win32" && fs16.existsSync(PID_FILE)) {
2079
+ if (os9.platform() !== "win32" && fs18.existsSync(PID_FILE)) {
1893
2080
  try {
1894
- const pid = Number(fs16.readFileSync(PID_FILE, "utf8").trim());
2081
+ const pid = Number(fs18.readFileSync(PID_FILE, "utf8").trim());
1895
2082
  if (Number.isFinite(pid) && pid > 0) {
1896
2083
  try {
1897
2084
  process.kill(pid, 0);
@@ -1907,13 +2094,13 @@ async function startEmulator(opts) {
1907
2094
  logger.warn(`Could not read PID file: ${err.message}`);
1908
2095
  }
1909
2096
  }
1910
- const imagesPath = path16.join(getEmulatorDir(config), "images");
1911
- if (!fs16.existsSync(imagesPath)) {
2097
+ const imagesPath = path18.join(getEmulatorDir(config), "images");
2098
+ if (!fs18.existsSync(imagesPath)) {
1912
2099
  throw new OniroError(`Emulator images directory not found at ${imagesPath}. Run \`oniro-app emulator install\` first.`);
1913
2100
  }
1914
2101
  const launcher = resolveLauncher(imagesPath, opts.headless === true, opts.connect);
1915
- const logPath = opts.logFile ?? (os8.platform() === "win32" ? "NUL" : "/dev/null");
1916
- const logFd = fs16.openSync(logPath, "a");
2102
+ const logPath = opts.logFile ?? (os9.platform() === "win32" ? "NUL" : "/dev/null");
2103
+ const logFd = fs18.openSync(logPath, "a");
1917
2104
  let child;
1918
2105
  try {
1919
2106
  child = spawn2(launcher.command, [...launcher.args], {
@@ -1923,19 +2110,19 @@ async function startEmulator(opts) {
1923
2110
  windowsHide: true
1924
2111
  });
1925
2112
  } finally {
1926
- fs16.closeSync(logFd);
2113
+ fs18.closeSync(logFd);
1927
2114
  }
1928
2115
  if (!child.pid) {
1929
2116
  throw new OniroError(`Failed to spawn launcher (${launcher.command} ${launcher.args.join(" ")}).`);
1930
2117
  }
1931
- fs16.writeFileSync(PID_FILE, String(child.pid));
2118
+ fs18.writeFileSync(PID_FILE, String(child.pid));
1932
2119
  child.unref();
1933
2120
  logger.info(`Emulator launched (PID ${child.pid}). Logs: ${logPath}`);
1934
- const earlyExit = new Promise((resolve7) => {
1935
- child.once("exit", (code) => resolve7(code));
1936
- child.once("error", () => resolve7(-1));
2121
+ const earlyExit = new Promise((resolve9) => {
2122
+ child.once("exit", (code) => resolve9(code));
2123
+ child.once("error", () => resolve9(-1));
1937
2124
  });
1938
- const liveness = new Promise((resolve7) => setTimeout(() => resolve7("alive"), 2e3));
2125
+ const liveness = new Promise((resolve9) => setTimeout(() => resolve9("alive"), 2e3));
1939
2126
  const outcome = await Promise.race([earlyExit, liveness]);
1940
2127
  if (outcome !== "alive") {
1941
2128
  const tail = readTail(logPath, 4e3);
@@ -1975,24 +2162,24 @@ ${tail}` : ""}`);
1975
2162
  }
1976
2163
  function readTail(filePath, bytes) {
1977
2164
  try {
1978
- const stat = fs16.statSync(filePath);
2165
+ const stat = fs18.statSync(filePath);
1979
2166
  const start = Math.max(0, stat.size - bytes);
1980
- const fd = fs16.openSync(filePath, "r");
2167
+ const fd = fs18.openSync(filePath, "r");
1981
2168
  try {
1982
2169
  const buf = Buffer.alloc(stat.size - start);
1983
- fs16.readSync(fd, buf, 0, buf.length, start);
2170
+ fs18.readSync(fd, buf, 0, buf.length, start);
1984
2171
  return buf.toString("utf8");
1985
2172
  } finally {
1986
- fs16.closeSync(fd);
2173
+ fs18.closeSync(fd);
1987
2174
  }
1988
2175
  } catch {
1989
2176
  return "";
1990
2177
  }
1991
2178
  }
1992
2179
  async function stopEmulator(logger = noopLogger) {
1993
- if (fs16.existsSync(PID_FILE)) {
2180
+ if (fs18.existsSync(PID_FILE)) {
1994
2181
  try {
1995
- const pid = Number(fs16.readFileSync(PID_FILE, "utf8").trim());
2182
+ const pid = Number(fs18.readFileSync(PID_FILE, "utf8").trim());
1996
2183
  if (Number.isFinite(pid) && pid > 0) {
1997
2184
  try {
1998
2185
  process.kill(pid, "SIGTERM");
@@ -2005,20 +2192,20 @@ async function stopEmulator(logger = noopLogger) {
2005
2192
  logger.warn(`Could not read PID file: ${err.message}`);
2006
2193
  }
2007
2194
  }
2008
- const [killCmd, ...killArgs] = os8.platform() === "win32" ? ["taskkill", "/IM", "qemu-system-x86_64.exe", "/F"] : ["pkill", "-f", "qemu-system-x86_64"];
2195
+ const [killCmd, ...killArgs] = os9.platform() === "win32" ? ["taskkill", "/IM", "qemu-system-x86_64.exe", "/F"] : ["pkill", "-f", "qemu-system-x86_64"];
2009
2196
  try {
2010
2197
  await runProcess({ command: killCmd, args: killArgs, logger });
2011
2198
  } catch (err) {
2012
2199
  logger.warn(`[emulator] could not run ${killCmd}: ${err.message}`);
2013
2200
  }
2014
- fs16.rm(PID_FILE, { force: true }, (err) => {
2201
+ fs18.rm(PID_FILE, { force: true }, (err) => {
2015
2202
  if (err) logger.warn(`Could not remove PID file: ${err.message}`);
2016
2203
  });
2017
2204
  }
2018
2205
  async function isQemuRunning(logger) {
2019
- if (os8.platform() !== "win32") {
2206
+ if (os9.platform() !== "win32") {
2020
2207
  try {
2021
- const pid = Number(fs16.readFileSync(PID_FILE, "utf8").trim());
2208
+ const pid = Number(fs18.readFileSync(PID_FILE, "utf8").trim());
2022
2209
  if (Number.isFinite(pid) && pid > 0) {
2023
2210
  process.kill(pid, 0);
2024
2211
  return true;
@@ -2026,10 +2213,10 @@ async function isQemuRunning(logger) {
2026
2213
  } catch {
2027
2214
  }
2028
2215
  }
2029
- const [cmd, ...args] = os8.platform() === "win32" ? ["tasklist", "/FI", "IMAGENAME eq qemu-system-x86_64.exe", "/NH"] : ["pgrep", "-f", "qemu-system-x86_64"];
2216
+ const [cmd, ...args] = os9.platform() === "win32" ? ["tasklist", "/FI", "IMAGENAME eq qemu-system-x86_64.exe", "/NH"] : ["pgrep", "-f", "qemu-system-x86_64"];
2030
2217
  try {
2031
2218
  const res = await runProcess({ command: cmd, args, timeoutMs: 1e4, logger });
2032
- return os8.platform() === "win32" ? /qemu-system-x86_64/i.test(res.stdout) : res.code === 0;
2219
+ return os9.platform() === "win32" ? /qemu-system-x86_64/i.test(res.stdout) : res.code === 0;
2033
2220
  } catch {
2034
2221
  return false;
2035
2222
  }
@@ -2154,11 +2341,11 @@ async function paramSet(config, key, value, opts = {}) {
2154
2341
  }
2155
2342
 
2156
2343
  // src/hdc/files.ts
2157
- import * as path17 from "path";
2344
+ import * as path19 from "path";
2158
2345
  function normaliseLocalPath(local) {
2159
- const abs = path17.resolve(local);
2346
+ const abs = path19.resolve(local);
2160
2347
  const long = toLongPath(abs);
2161
- return long !== abs ? long : path17.join(toLongPath(path17.dirname(abs)), path17.basename(abs));
2348
+ return long !== abs ? long : path19.join(toLongPath(path19.dirname(abs)), path19.basename(abs));
2162
2349
  }
2163
2350
  function assertDevicePath(remote) {
2164
2351
  if (/^[A-Za-z]:[\\/]/.test(remote)) {
@@ -2177,13 +2364,13 @@ async function sendFile(opts) {
2177
2364
  assertDevicePath(opts.remote);
2178
2365
  const local = normaliseLocalPath(opts.local);
2179
2366
  const sandbox = opts.bundle ? appSandboxDir(opts.bundle) : void 0;
2180
- const remote = sandbox && !opts.remote.startsWith("/") ? path17.posix.join(sandbox, opts.remote) : opts.remote;
2367
+ const remote = sandbox && !opts.remote.startsWith("/") ? path19.posix.join(sandbox, opts.remote) : opts.remote;
2181
2368
  if (sandbox && !remote.startsWith(`${sandbox}/`)) {
2182
2369
  throw new OniroError(`${remote} is outside the sandbox of ${opts.bundle} (${sandbox}).`);
2183
2370
  }
2184
2371
  const device = { config: opts.config, deviceSerial: opts.deviceSerial, logger: opts.logger };
2185
2372
  if (sandbox) {
2186
- const mkdir = `mkdir -p ${shQuote(path17.posix.dirname(remote))}`;
2373
+ const mkdir = `mkdir -p ${shQuote(path19.posix.dirname(remote))}`;
2187
2374
  ensureRemoteOk(await shellChecked({ ...device, command: mkdir }), `hdc shell ${mkdir}`);
2188
2375
  }
2189
2376
  const res = await hdcExec({
@@ -2195,7 +2382,7 @@ async function sendFile(opts) {
2195
2382
  });
2196
2383
  ensureOk(res, `hdc file send ${local} ${remote}`);
2197
2384
  if (sandbox) {
2198
- const top = path17.posix.join(sandbox, path17.posix.relative(sandbox, remote).split("/")[0]);
2385
+ const top = path19.posix.join(sandbox, path19.posix.relative(sandbox, remote).split("/")[0]);
2199
2386
  const chown = `chown -R "$(stat -c %u:%g ${shQuote(sandbox)})" ${shQuote(top)}`;
2200
2387
  ensureRemoteOk(await shellChecked({ ...device, command: chown }), `hdc shell ${chown}`);
2201
2388
  }
@@ -2233,7 +2420,7 @@ async function getDisplaySize(config, opts = {}) {
2233
2420
 
2234
2421
  // src/hdc/wait.ts
2235
2422
  function delay(ms, signal) {
2236
- return new Promise((resolve7, reject) => {
2423
+ return new Promise((resolve9, reject) => {
2237
2424
  if (signal?.aborted) {
2238
2425
  reject(new CancelledError("Wait cancelled."));
2239
2426
  return;
@@ -2248,7 +2435,7 @@ function delay(ms, signal) {
2248
2435
  };
2249
2436
  const timer = setTimeout(() => {
2250
2437
  cleanup();
2251
- resolve7();
2438
+ resolve9();
2252
2439
  }, ms);
2253
2440
  signal?.addEventListener("abort", onAbort, { once: true });
2254
2441
  });
@@ -2277,18 +2464,18 @@ async function waitForCondition(opts) {
2277
2464
  }
2278
2465
 
2279
2466
  // src/hdc/app.ts
2280
- import * as fs19 from "fs";
2281
- import * as path20 from "path";
2467
+ import * as fs21 from "fs";
2468
+ import * as path22 from "path";
2282
2469
 
2283
2470
  // src/build/discoverHaps.ts
2284
- import * as fs17 from "fs";
2285
- import * as path18 from "path";
2471
+ import * as fs19 from "fs";
2472
+ import * as path20 from "path";
2286
2473
  function moduleOfHap(hapFileName) {
2287
2474
  const dash = hapFileName.indexOf("-");
2288
2475
  return dash > 0 ? hapFileName.slice(0, dash) : hapFileName.replace(/\.hap$/i, "");
2289
2476
  }
2290
2477
  function isAppProjectRoot(dir) {
2291
- return fs17.existsSync(path18.join(dir, "AppScope", "app.json5"));
2478
+ return fs19.existsSync(path20.join(dir, "AppScope", "app.json5"));
2292
2479
  }
2293
2480
  async function discoverHaps(opts) {
2294
2481
  const { projectDir } = opts;
@@ -2297,9 +2484,9 @@ async function discoverHaps(opts) {
2297
2484
  const result = {};
2298
2485
  const walk = async (dir, depth) => {
2299
2486
  if (depth > maxDepth) return;
2300
- const entries = await fs17.promises.readdir(dir, { withFileTypes: true }).catch(() => []);
2487
+ const entries = await fs19.promises.readdir(dir, { withFileTypes: true }).catch(() => []);
2301
2488
  for (const e of entries) {
2302
- const full = path18.join(dir, e.name);
2489
+ const full = path20.join(dir, e.name);
2303
2490
  if (e.isDirectory()) {
2304
2491
  if (e.name === "node_modules" || e.name === "oh_modules" || e.name === ".git") continue;
2305
2492
  if (skipNestedProjects && isAppProjectRoot(full)) continue;
@@ -2313,7 +2500,7 @@ async function discoverHaps(opts) {
2313
2500
  }
2314
2501
  };
2315
2502
  await walk(projectDir, 0);
2316
- const mtime = (p) => fs17.statSync(p).mtimeMs;
2503
+ const mtime = (p) => fs19.statSync(p).mtimeMs;
2317
2504
  for (const bucket of Object.values(result)) {
2318
2505
  bucket.signed.sort((a, b) => mtime(b) - mtime(a));
2319
2506
  bucket.unsigned.sort((a, b) => mtime(b) - mtime(a));
@@ -2339,16 +2526,16 @@ function pickHap(haps, module) {
2339
2526
  }
2340
2527
 
2341
2528
  // src/hdc/project.ts
2342
- import * as fs18 from "fs";
2343
- import * as path19 from "path";
2529
+ import * as fs20 from "fs";
2530
+ import * as path21 from "path";
2344
2531
  import JSON53 from "json5";
2345
2532
  function readJson5(filePath) {
2346
- const content = fs18.readFileSync(filePath, "utf-8");
2533
+ const content = fs20.readFileSync(filePath, "utf-8");
2347
2534
  return JSON53.parse(content);
2348
2535
  }
2349
2536
  function getBundleName(projectDir) {
2350
- const appJsonPath = path19.join(projectDir, "AppScope", "app.json5");
2351
- if (!fs18.existsSync(appJsonPath)) {
2537
+ const appJsonPath = path21.join(projectDir, "AppScope", "app.json5");
2538
+ if (!fs20.existsSync(appJsonPath)) {
2352
2539
  throw new OniroError(`Could not find app.json5 at ${appJsonPath}`);
2353
2540
  }
2354
2541
  const appJson = readJson5(appJsonPath);
@@ -2358,8 +2545,8 @@ function getBundleName(projectDir) {
2358
2545
  return appJson.app.bundleName;
2359
2546
  }
2360
2547
  function getMainAbility(projectDir, moduleName = "entry", abilityName) {
2361
- const moduleJsonPath = path19.join(projectDir, moduleName, "src", "main", "module.json5");
2362
- if (!fs18.existsSync(moduleJsonPath)) {
2548
+ const moduleJsonPath = path21.join(projectDir, moduleName, "src", "main", "module.json5");
2549
+ if (!fs20.existsSync(moduleJsonPath)) {
2363
2550
  throw new OniroError(`Could not find module.json5 at ${moduleJsonPath}`);
2364
2551
  }
2365
2552
  const moduleJson = readJson5(moduleJsonPath);
@@ -2383,7 +2570,7 @@ function detectInstallFailure(output) {
2383
2570
  return /\[Fail\]|msg:\s*error|error:\s*failed to install|code:\s*95\d{5}/i.test(output) ? output.trim() : null;
2384
2571
  }
2385
2572
  function isUnsignedHap(hapPath) {
2386
- return path20.basename(hapPath).includes("unsigned");
2573
+ return path22.basename(hapPath).includes("unsigned");
2387
2574
  }
2388
2575
  function installFailureReason(output) {
2389
2576
  const code = /code:\s*(\d+)/i.exec(output);
@@ -2392,7 +2579,7 @@ function installFailureReason(output) {
2392
2579
  return message ? `code ${code[1]}: ${message}` : `code ${code[1]}`;
2393
2580
  }
2394
2581
  function unsignedHapRejection(output, hap) {
2395
- const name = path20.basename(hap.path);
2582
+ const name = path22.basename(hap.path);
2396
2583
  const reason = installFailureReason(output);
2397
2584
  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}).`;
2398
2585
  if (!/9568320|signature|pkcs7/i.test(reason)) return what;
@@ -2410,24 +2597,24 @@ ${result.stderr}`;
2410
2597
  return result;
2411
2598
  }
2412
2599
  function existingHapOrUnsigned(hapPath) {
2413
- if (fs19.existsSync(hapPath)) return { path: hapPath };
2600
+ if (fs21.existsSync(hapPath)) return { path: hapPath };
2414
2601
  const sibling = hapPath.replace(/-signed\.hap$/i, "-unsigned.hap");
2415
- return sibling !== hapPath && fs19.existsSync(sibling) ? { path: sibling, fallback: true } : void 0;
2602
+ return sibling !== hapPath && fs21.existsSync(sibling) ? { path: sibling, fallback: true } : void 0;
2416
2603
  }
2417
2604
  async function resolveInstallHap(opts, logger) {
2418
2605
  const warnIfFallback = (hap) => {
2419
- if (hap.fallback) logger.warn(`No signed HAP found; installing the unsigned ${path20.basename(hap.path)}.`);
2606
+ if (hap.fallback) logger.warn(`No signed HAP found; installing the unsigned ${path22.basename(hap.path)}.`);
2420
2607
  return hap;
2421
2608
  };
2422
2609
  const configured = opts.hapPath ?? opts.config.get("hapPath", "");
2423
2610
  const relative3 = configured || defaultPaths.hapPath;
2424
- const hapPath = path20.isAbsolute(relative3) ? relative3 : path20.join(opts.projectDir, relative3);
2611
+ const hapPath = path22.isAbsolute(relative3) ? relative3 : path22.join(opts.projectDir, relative3);
2425
2612
  if (configured) {
2426
2613
  const hap = existingHapOrUnsigned(hapPath);
2427
2614
  if (!hap) throw new OniroError(`HAP file not found at: ${hapPath}. Build the app first.`);
2428
2615
  return warnIfFallback(hap);
2429
2616
  }
2430
- if (fs19.existsSync(hapPath)) return { path: hapPath };
2617
+ if (fs21.existsSync(hapPath)) return { path: hapPath };
2431
2618
  const haps = await discoverHaps({ projectDir: opts.projectDir });
2432
2619
  const anySigned = Object.values(haps).some((m) => m.signed.length > 0);
2433
2620
  const entryUnsigned = anySigned ? void 0 : existingHapOrUnsigned(hapPath);
@@ -2538,7 +2725,7 @@ function listRunningProcesses(config, options = {}) {
2538
2725
  const logger = options.logger ?? noopLogger;
2539
2726
  const timeoutMs = options.timeoutMs ?? 1e3;
2540
2727
  const hdc = getHdcPath(config);
2541
- return new Promise((resolve7, reject) => {
2728
+ return new Promise((resolve9, reject) => {
2542
2729
  let proc;
2543
2730
  try {
2544
2731
  proc = spawnCompat(hdc, [...hdcTargetArgs(options.deviceSerial), "track-jpid"]);
@@ -2558,7 +2745,7 @@ function listRunningProcesses(config, options = {}) {
2558
2745
  if (options.targetProcessName && name === options.targetProcessName && !resolved) {
2559
2746
  resolved = true;
2560
2747
  killProcessTree(proc);
2561
- resolve7(pid);
2748
+ resolve9(pid);
2562
2749
  return;
2563
2750
  }
2564
2751
  }
@@ -2579,7 +2766,7 @@ function listRunningProcesses(config, options = {}) {
2579
2766
  if (options.targetProcessName) {
2580
2767
  reject(new OniroError(`Could not find process for bundle: ${options.targetProcessName}`));
2581
2768
  } else {
2582
- resolve7(processes);
2769
+ resolve9(processes);
2583
2770
  }
2584
2771
  }, timeoutMs);
2585
2772
  proc.on("close", () => {
@@ -2589,7 +2776,7 @@ function listRunningProcesses(config, options = {}) {
2589
2776
  if (options.targetProcessName) {
2590
2777
  reject(new OniroError(`Could not find process for bundle: ${options.targetProcessName}`));
2591
2778
  } else {
2592
- resolve7(processes);
2779
+ resolve9(processes);
2593
2780
  }
2594
2781
  });
2595
2782
  });
@@ -2758,12 +2945,12 @@ async function reboot(opts) {
2758
2945
  }
2759
2946
 
2760
2947
  // src/hdc/screen.ts
2761
- import * as fs20 from "fs";
2762
- import * as os9 from "os";
2763
- import * as path21 from "path";
2764
- import { randomBytes as randomBytes2 } from "crypto";
2948
+ import * as fs22 from "fs";
2949
+ import * as os10 from "os";
2950
+ import * as path23 from "path";
2951
+ import { randomBytes as randomBytes3 } from "crypto";
2765
2952
  function tmpName(ext) {
2766
- return path21.join(os9.tmpdir(), `oniro_screen_${randomBytes2(6).toString("hex")}.${ext}`);
2953
+ return path23.join(os10.tmpdir(), `oniro_screen_${randomBytes3(6).toString("hex")}.${ext}`);
2767
2954
  }
2768
2955
  async function takeScreenshot(opts) {
2769
2956
  const remote = "/data/local/tmp/oniro_screenshot.jpeg";
@@ -2773,11 +2960,11 @@ async function takeScreenshot(opts) {
2773
2960
  const local = tmpName("jpeg");
2774
2961
  try {
2775
2962
  await recvFile({ config: opts.config, remote, local, deviceSerial: opts.deviceSerial, timeoutMs });
2776
- const pixels = await fs20.promises.readFile(local);
2963
+ const pixels = await fs22.promises.readFile(local);
2777
2964
  const size = await getDisplaySize(opts.config, { deviceSerial: opts.deviceSerial }) ?? { width: 0, height: 0 };
2778
2965
  return { pixels, width: size.width, height: size.height };
2779
2966
  } finally {
2780
- fs20.promises.unlink(local).catch(() => {
2967
+ fs22.promises.unlink(local).catch(() => {
2781
2968
  });
2782
2969
  void shell({ config: opts.config, command: `rm -f ${remote}`, deviceSerial: opts.deviceSerial }).catch(() => {
2783
2970
  });
@@ -2785,7 +2972,7 @@ async function takeScreenshot(opts) {
2785
2972
  }
2786
2973
  async function captureBurst(opts) {
2787
2974
  const count = Math.max(1, Math.floor(opts.count));
2788
- const remoteDir = `/data/local/tmp/oniro_burst_${randomBytes2(6).toString("hex")}`;
2975
+ const remoteDir = `/data/local/tmp/oniro_burst_${randomBytes3(6).toString("hex")}`;
2789
2976
  const sleepSec = opts.intervalMs > 0 ? (opts.intervalMs / 1e3).toFixed(3) : "0";
2790
2977
  const loop = `mkdir -p ${remoteDir}; i=0; while [ $i -lt ${count} ]; do snapshot_display -f ${remoteDir}/f$i.jpeg; [ ${count} -gt 1 ] && sleep ${sleepSec}; i=$((i+1)); done`;
2791
2978
  const cap = await shell({ config: opts.config, command: loop, deviceSerial: opts.deviceSerial, timeoutMs: opts.timeoutMs ?? 12e4 });
@@ -2796,9 +2983,9 @@ async function captureBurst(opts) {
2796
2983
  const local = tmpName(`burst_${i}.jpeg`);
2797
2984
  try {
2798
2985
  await recvFile({ config: opts.config, remote: `${remoteDir}/f${i}.jpeg`, local, deviceSerial: opts.deviceSerial });
2799
- frames.push(await fs20.promises.readFile(local));
2986
+ frames.push(await fs22.promises.readFile(local));
2800
2987
  } finally {
2801
- fs20.promises.unlink(local).catch(() => {
2988
+ fs22.promises.unlink(local).catch(() => {
2802
2989
  });
2803
2990
  }
2804
2991
  }
@@ -2973,10 +3160,10 @@ async function sendRawTouch(opts) {
2973
3160
  }
2974
3161
 
2975
3162
  // src/hdc/dump.ts
2976
- import * as fs21 from "fs";
2977
- import * as os10 from "os";
2978
- import * as path22 from "path";
2979
- import { randomBytes as randomBytes3 } from "crypto";
3163
+ import * as fs23 from "fs";
3164
+ import * as os11 from "os";
3165
+ import * as path24 from "path";
3166
+ import { randomBytes as randomBytes4 } from "crypto";
2980
3167
 
2981
3168
  // src/hdc/ime.ts
2982
3169
  var INPUT_METHOD_SA_ID = 3703;
@@ -3238,10 +3425,10 @@ async function dumpLayout(opts) {
3238
3425
  if (!m) throw new OniroError(`Could not parse dumpLayout output:
3239
3426
  ${dump.stdout}`);
3240
3427
  const remote = m[1];
3241
- const local = path22.join(os10.tmpdir(), `oniro_layout_${randomBytes3(6).toString("hex")}.json`);
3428
+ const local = path24.join(os11.tmpdir(), `oniro_layout_${randomBytes4(6).toString("hex")}.json`);
3242
3429
  try {
3243
3430
  await recvFile({ config: opts.config, remote, local, deviceSerial: opts.deviceSerial, timeoutMs });
3244
- const parsed = JSON.parse(await fs21.promises.readFile(local, "utf8"));
3431
+ const parsed = JSON.parse(await fs23.promises.readFile(local, "utf8"));
3245
3432
  const display = await getDisplaySize(opts.config, { deviceSerial: opts.deviceSerial });
3246
3433
  if (opts.prune === false) {
3247
3434
  return { display, tree: parsed };
@@ -3254,7 +3441,7 @@ ${dump.stdout}`);
3254
3441
  const keyboard = kb ? { shown: true, bundle: kb.bundle, ...kb.b ? { b: kb.b } : {} } : { shown: false };
3255
3442
  return { display, tree, keyboard };
3256
3443
  } finally {
3257
- fs21.promises.unlink(local).catch(() => {
3444
+ fs23.promises.unlink(local).catch(() => {
3258
3445
  });
3259
3446
  void shell({ config: opts.config, command: `rm -f ${remote}`, deviceSerial: opts.deviceSerial }).catch(() => {
3260
3447
  });
@@ -3264,7 +3451,7 @@ ${dump.stdout}`);
3264
3451
  // src/hdc/uiActions.ts
3265
3452
  var FOCUS_SETTLE_MS = 400;
3266
3453
  var KEYBOARD_SETTLE_MS = 400;
3267
- var sleep = (ms) => new Promise((resolve7) => setTimeout(resolve7, ms));
3454
+ var sleep = (ms) => new Promise((resolve9) => setTimeout(resolve9, ms));
3268
3455
  function targetTree(layout) {
3269
3456
  const kb = layout.keyboard;
3270
3457
  return kb?.shown && kb.bundle ? withoutWindow(layout.tree, kb.bundle) : layout.tree;
@@ -3417,11 +3604,11 @@ async function waitForTarget(opts) {
3417
3604
  }
3418
3605
 
3419
3606
  // src/hdc/hilog.ts
3420
- import * as fs22 from "fs";
3607
+ import * as fs24 from "fs";
3421
3608
  function setHilogLevel(opts) {
3422
3609
  const logger = opts.logger ?? noopLogger;
3423
3610
  const hdc = getHdcPath(opts.config);
3424
- return new Promise((resolve7, reject) => {
3611
+ return new Promise((resolve9, reject) => {
3425
3612
  let child;
3426
3613
  try {
3427
3614
  child = spawnCompat(hdc, [...hdcTargetArgs(opts.deviceSerial), "shell", "hilog", "-b", opts.level]);
@@ -3441,7 +3628,7 @@ function setHilogLevel(opts) {
3441
3628
  });
3442
3629
  child.once("close", (code) => {
3443
3630
  if (code === 0) {
3444
- resolve7();
3631
+ resolve9();
3445
3632
  } else {
3446
3633
  const msg = stderr.trim() || `hdc shell hilog -b ${opts.level} exited with code ${code}`;
3447
3634
  logger.error(`[hilog] ${msg}`);
@@ -3543,7 +3730,7 @@ async function recentEntryFilter(opts) {
3543
3730
  };
3544
3731
  }
3545
3732
  function waitForLog(opts) {
3546
- return new Promise((resolve7, reject) => {
3733
+ return new Promise((resolve9, reject) => {
3547
3734
  const deadline = Date.now() + opts.timeoutMs;
3548
3735
  let settled = false;
3549
3736
  let child = null;
@@ -3564,7 +3751,7 @@ function waitForLog(opts) {
3564
3751
  if (settled) return;
3565
3752
  settled = true;
3566
3753
  cleanup();
3567
- resolve7(entry);
3754
+ resolve9(entry);
3568
3755
  };
3569
3756
  const fail = (err) => {
3570
3757
  if (settled) return;
@@ -3619,7 +3806,7 @@ function waitForLog(opts) {
3619
3806
  });
3620
3807
  }
3621
3808
  function watchLog(opts) {
3622
- return new Promise((resolve7, reject) => {
3809
+ return new Promise((resolve9, reject) => {
3623
3810
  const matches = [];
3624
3811
  let settled = false;
3625
3812
  let child = null;
@@ -3638,7 +3825,7 @@ function watchLog(opts) {
3638
3825
  if (settled) return;
3639
3826
  settled = true;
3640
3827
  cleanup();
3641
- resolve7(opts.dedup === false ? matches : dedupEntries(matches));
3828
+ resolve9(opts.dedup === false ? matches : dedupEntries(matches));
3642
3829
  };
3643
3830
  const onAbort = () => {
3644
3831
  if (settled) return;
@@ -3683,11 +3870,11 @@ async function captureLog(opts) {
3683
3870
  const res = await shell({ config: opts.config, command: "hilog -r", deviceSerial: opts.deviceSerial, timeoutMs: 1e4, logger });
3684
3871
  ensureOk(res, "hdc shell hilog -r");
3685
3872
  }
3686
- const fd = fs22.openSync(opts.file, "a");
3873
+ const fd = fs24.openSync(opts.file, "a");
3687
3874
  let lines = 0;
3688
3875
  let reconnects = 0;
3689
3876
  try {
3690
- await new Promise((resolve7, reject) => {
3877
+ await new Promise((resolve9, reject) => {
3691
3878
  const deadline = opts.durationMs !== void 0 ? Date.now() + opts.durationMs : Infinity;
3692
3879
  let settled = false;
3693
3880
  let child = null;
@@ -3696,7 +3883,7 @@ async function captureLog(opts) {
3696
3883
  let open = true;
3697
3884
  const write = (line) => {
3698
3885
  if (!open || opts.pattern && !opts.pattern.test(line)) return;
3699
- fs22.writeSync(fd, `${line}
3886
+ fs24.writeSync(fd, `${line}
3700
3887
  `);
3701
3888
  lines++;
3702
3889
  };
@@ -3717,7 +3904,7 @@ async function captureLog(opts) {
3717
3904
  if (settled) return;
3718
3905
  settled = true;
3719
3906
  cleanup();
3720
- resolve7();
3907
+ resolve9();
3721
3908
  }
3722
3909
  const fail = (err) => {
3723
3910
  if (settled) return;
@@ -3761,7 +3948,7 @@ async function captureLog(opts) {
3761
3948
  spawnStream();
3762
3949
  });
3763
3950
  } finally {
3764
- fs22.closeSync(fd);
3951
+ fs24.closeSync(fd);
3765
3952
  }
3766
3953
  return { file: opts.file, lines, reconnects };
3767
3954
  }
@@ -3789,17 +3976,17 @@ async function dumpLog(opts) {
3789
3976
  }
3790
3977
 
3791
3978
  // src/build/runHvigorw.ts
3792
- import * as fs24 from "fs";
3793
- import * as path24 from "path";
3979
+ import * as fs26 from "fs";
3980
+ import * as path26 from "path";
3794
3981
 
3795
3982
  // src/harmonyos/target.ts
3796
- import * as fs23 from "fs";
3797
- import * as path23 from "path";
3983
+ import * as fs25 from "fs";
3984
+ import * as path25 from "path";
3798
3985
  import JSON54 from "json5";
3799
3986
  function detectRuntimeOs(projectDir, productName) {
3800
3987
  let products = [];
3801
3988
  try {
3802
- const parsed = JSON54.parse(fs23.readFileSync(path23.join(projectDir, "build-profile.json5"), "utf-8"));
3989
+ const parsed = JSON54.parse(fs25.readFileSync(path25.join(projectDir, "build-profile.json5"), "utf-8"));
3803
3990
  if (Array.isArray(parsed.app?.products)) products = parsed.app.products;
3804
3991
  } catch {
3805
3992
  return "OpenHarmony";
@@ -3840,7 +4027,7 @@ function runHvigorw(opts) {
3840
4027
  const { config } = opts;
3841
4028
  const projectDir = toLongPath(opts.projectDir);
3842
4029
  const logger = opts.logger ?? noopLogger;
3843
- if (!fs24.existsSync(path24.join(projectDir, "build-profile.json5"))) {
4030
+ if (!fs26.existsSync(path26.join(projectDir, "build-profile.json5"))) {
3844
4031
  throw new OniroError(`Not an OpenHarmony project: ${projectDir} (build-profile.json5 not found).`);
3845
4032
  }
3846
4033
  let hvigorw;
@@ -3852,24 +4039,24 @@ function runHvigorw(opts) {
3852
4039
  hvigorw = resolved.startsWith(projectDir) ? resolved : findHarmonyOsTool(sdk, "hvigorw") ?? resolved;
3853
4040
  logger.info(`[build] Building for HarmonyOS against the SDK at ${sdk.sdkPath}.`);
3854
4041
  } else {
3855
- if (!fs24.existsSync(getCmdToolsPath(config))) {
4042
+ if (!fs26.existsSync(getCmdToolsPath(config))) {
3856
4043
  throw new CmdToolsNotInstalledError(getCmdToolsPath(config));
3857
4044
  }
3858
4045
  sdkEnv = { OHOS_BASE_SDK_HOME: getOhosBaseSdkHome(config) };
3859
4046
  hvigorw = getHvigorwPath(config, projectDir);
3860
4047
  }
3861
- if (!fs24.existsSync(hvigorw)) {
4048
+ if (!fs26.existsSync(hvigorw)) {
3862
4049
  throw new OniroError(`hvigorw wrapper not found at ${hvigorw}.`);
3863
4050
  }
3864
4051
  const args = buildHvigorwArgs(opts);
3865
4052
  if (process.platform !== "win32") {
3866
4053
  try {
3867
- fs24.chmodSync(hvigorw, 493);
4054
+ fs26.chmodSync(hvigorw, 493);
3868
4055
  } catch {
3869
4056
  }
3870
4057
  }
3871
4058
  const env = { ...process.env, ...sdkEnv };
3872
- return new Promise((resolve7, reject) => {
4059
+ return new Promise((resolve9, reject) => {
3873
4060
  if (opts.abortSignal?.aborted) {
3874
4061
  reject(new CancelledError("hvigorw build cancelled."));
3875
4062
  return;
@@ -3913,15 +4100,15 @@ function runHvigorw(opts) {
3913
4100
  settled = true;
3914
4101
  cleanup();
3915
4102
  const exitCode = code ?? 1;
3916
- if (exitCode === 0) resolve7({ exitCode });
4103
+ if (exitCode === 0) resolve9({ exitCode });
3917
4104
  else reject(new OniroError(`hvigorw exited with code ${exitCode}`));
3918
4105
  });
3919
4106
  });
3920
4107
  }
3921
4108
 
3922
4109
  // src/build/ohpm.ts
3923
- import * as fs25 from "fs";
3924
- import * as path25 from "path";
4110
+ import * as fs27 from "fs";
4111
+ import * as path27 from "path";
3925
4112
  function runOhpm(opts) {
3926
4113
  return runProcess({
3927
4114
  command: opts.ohpmPath ?? getOhpmPath(opts.config),
@@ -3934,7 +4121,7 @@ function runOhpm(opts) {
3934
4121
  });
3935
4122
  }
3936
4123
  async function ensureOhModules(opts) {
3937
- if (fs25.existsSync(path25.join(opts.projectDir, "oh_modules"))) {
4124
+ if (fs27.existsSync(path27.join(opts.projectDir, "oh_modules"))) {
3938
4125
  return { installed: false };
3939
4126
  }
3940
4127
  const res = await runOhpm({
@@ -3951,8 +4138,8 @@ async function ensureOhModules(opts) {
3951
4138
  }
3952
4139
 
3953
4140
  // src/build/codelinter.ts
3954
- import * as fs26 from "fs";
3955
- import * as path26 from "path";
4141
+ import * as fs28 from "fs";
4142
+ import * as path28 from "path";
3956
4143
  var FINDING_RE = /^(.*?):(\d+):(?:(\d+):)?\s*(error|warning|warn|suggestion|info)\b[:\s-]*(.*)$/i;
3957
4144
  var TRAILING_CODE_RE = /[([]([\w@/.\-]+)[)\]]\s*$/;
3958
4145
  var FILE_HEADER_RE = /^(.+\.\w+)\((\d+)\)$/;
@@ -4042,10 +4229,10 @@ function parseCodelinterFindings(output) {
4042
4229
  var PROJECT_ROOT_FILES = [
4043
4230
  ["build-profile.json5"],
4044
4231
  ["hvigorfile.ts", "hvigorfile.js"],
4045
- [path26.join("hvigor", "hvigor-config.json5")]
4232
+ [path28.join("hvigor", "hvigor-config.json5")]
4046
4233
  ];
4047
4234
  function missingProjectRootFiles(projectDir) {
4048
- return PROJECT_ROOT_FILES.filter((names) => !names.some((f) => fs26.existsSync(path26.join(projectDir, f)))).map(
4235
+ return PROJECT_ROOT_FILES.filter((names) => !names.some((f) => fs28.existsSync(path28.join(projectDir, f)))).map(
4049
4236
  (names) => names[0]
4050
4237
  );
4051
4238
  }
@@ -4127,32 +4314,22 @@ ${hint}`, err);
4127
4314
  return { exitCode, durationMs: Date.now() - start, discoveredHaps, warnings };
4128
4315
  }
4129
4316
 
4130
- // src/project/validators.ts
4131
- function isValidProjectName(name) {
4132
- if (!name) return false;
4133
- if (name.includes("/") || name.includes("\\")) return false;
4134
- return /^[A-Za-z0-9._-]+$/.test(name);
4135
- }
4136
- function isValidBundleName(bundleName) {
4137
- return /^[A-Za-z][A-Za-z0-9_]*(\.[A-Za-z][A-Za-z0-9_]*)+$/.test(bundleName);
4138
- }
4139
-
4140
4317
  // src/project/templates.ts
4141
- import * as fs27 from "fs";
4142
- import * as path27 from "path";
4318
+ import * as fs29 from "fs";
4319
+ import * as path29 from "path";
4143
4320
  function toHumanTemplateName(folderName) {
4144
4321
  return folderName.replace(/([a-z])([A-Z])/g, "$1 $2");
4145
4322
  }
4146
4323
  function listTemplates(templateRoot) {
4147
- if (!fs27.existsSync(templateRoot)) return [];
4148
- const entries = fs27.readdirSync(templateRoot, { withFileTypes: true });
4324
+ if (!fs29.existsSync(templateRoot)) return [];
4325
+ const entries = fs29.readdirSync(templateRoot, { withFileTypes: true });
4149
4326
  return entries.filter((e) => e.isDirectory()).map((e) => {
4150
- const dir = path27.join(templateRoot, e.name);
4151
- const metaPath = path27.join(dir, "template.json");
4327
+ const dir = path29.join(templateRoot, e.name);
4328
+ const metaPath = path29.join(dir, "template.json");
4152
4329
  let meta = {};
4153
- if (fs27.existsSync(metaPath)) {
4330
+ if (fs29.existsSync(metaPath)) {
4154
4331
  try {
4155
- meta = JSON.parse(fs27.readFileSync(metaPath, "utf8"));
4332
+ meta = JSON.parse(fs29.readFileSync(metaPath, "utf8"));
4156
4333
  } catch {
4157
4334
  }
4158
4335
  }
@@ -4167,57 +4344,57 @@ function listTemplates(templateRoot) {
4167
4344
  function validateTemplateLayout(templateDir, defaultModuleName) {
4168
4345
  const required = [
4169
4346
  "build-profile.json5",
4170
- path27.join("AppScope", "app.json5"),
4171
- path27.join(defaultModuleName, "src", "main", "module.json5"),
4172
- path27.join(defaultModuleName, "oh-package.json5"),
4347
+ path29.join("AppScope", "app.json5"),
4348
+ path29.join(defaultModuleName, "src", "main", "module.json5"),
4349
+ path29.join(defaultModuleName, "oh-package.json5"),
4173
4350
  "hvigorfile.ts"
4174
4351
  ];
4175
- return required.filter((rel) => !fs27.existsSync(path27.join(templateDir, rel)));
4352
+ return required.filter((rel) => !fs29.existsSync(path29.join(templateDir, rel)));
4176
4353
  }
4177
4354
 
4178
4355
  // src/project/createScaffold.ts
4179
- import * as fs29 from "fs";
4180
- import * as path28 from "path";
4356
+ import * as fs31 from "fs";
4357
+ import * as path30 from "path";
4181
4358
 
4182
4359
  // src/project/jsonHelpers.ts
4183
- import * as fs28 from "fs";
4360
+ import * as fs30 from "fs";
4184
4361
  import JSON55 from "json5";
4185
4362
  function readJson5File(filePath) {
4186
- return JSON55.parse(fs28.readFileSync(filePath, "utf8"));
4363
+ return JSON55.parse(fs30.readFileSync(filePath, "utf8"));
4187
4364
  }
4188
4365
  function writeJson5File(filePath, value) {
4189
- fs28.writeFileSync(filePath, JSON.stringify(value, null, 2) + "\n", "utf8");
4366
+ fs30.writeFileSync(filePath, JSON.stringify(value, null, 2) + "\n", "utf8");
4190
4367
  }
4191
4368
  function readJsonFile(filePath) {
4192
- return JSON.parse(fs28.readFileSync(filePath, "utf8"));
4369
+ return JSON.parse(fs30.readFileSync(filePath, "utf8"));
4193
4370
  }
4194
4371
  function writeJsonFile(filePath, value) {
4195
- fs28.writeFileSync(filePath, JSON.stringify(value, null, 2) + "\n", "utf8");
4372
+ fs30.writeFileSync(filePath, JSON.stringify(value, null, 2) + "\n", "utf8");
4196
4373
  }
4197
4374
 
4198
4375
  // src/project/createScaffold.ts
4199
4376
  var IGNORED_DIRS = /* @__PURE__ */ new Set(["oh_modules", "node_modules", "build", ".hvigor"]);
4200
4377
  async function pathExists(p) {
4201
4378
  try {
4202
- await fs29.promises.access(p);
4379
+ await fs31.promises.access(p);
4203
4380
  return true;
4204
4381
  } catch {
4205
4382
  return false;
4206
4383
  }
4207
4384
  }
4208
4385
  async function copyDirRecursive(srcDir, destDir) {
4209
- await fs29.promises.mkdir(destDir, { recursive: true });
4210
- const entries = await fs29.promises.readdir(srcDir, { withFileTypes: true });
4386
+ await fs31.promises.mkdir(destDir, { recursive: true });
4387
+ const entries = await fs31.promises.readdir(srcDir, { withFileTypes: true });
4211
4388
  for (const entry of entries) {
4212
- const src = path28.join(srcDir, entry.name);
4213
- const dest = path28.join(destDir, entry.name);
4389
+ const src = path30.join(srcDir, entry.name);
4390
+ const dest = path30.join(destDir, entry.name);
4214
4391
  if (entry.isDirectory()) {
4215
4392
  await copyDirRecursive(src, dest);
4216
4393
  } else if (entry.isSymbolicLink()) {
4217
4394
  continue;
4218
4395
  } else if (entry.isFile()) {
4219
4396
  if (entry.name === "template.json") continue;
4220
- await fs29.promises.copyFile(src, dest);
4397
+ await fs31.promises.copyFile(src, dest);
4221
4398
  }
4222
4399
  }
4223
4400
  }
@@ -4225,9 +4402,9 @@ async function normalizeJson5ToJson(projectDir, logger) {
4225
4402
  const stack = [projectDir];
4226
4403
  while (stack.length > 0) {
4227
4404
  const currentDir = stack.pop();
4228
- const entries = await fs29.promises.readdir(currentDir, { withFileTypes: true });
4405
+ const entries = await fs31.promises.readdir(currentDir, { withFileTypes: true });
4229
4406
  for (const entry of entries) {
4230
- const fullPath = path28.join(currentDir, entry.name);
4407
+ const fullPath = path30.join(currentDir, entry.name);
4231
4408
  if (entry.isDirectory()) {
4232
4409
  if (IGNORED_DIRS.has(entry.name)) continue;
4233
4410
  stack.push(fullPath);
@@ -4249,23 +4426,23 @@ function toJavaPropertiesPath(p) {
4249
4426
  function createOrUpdateLocalProperties(projectDir, sdkDir) {
4250
4427
  const content = `sdk.dir=${toJavaPropertiesPath(sdkDir)}
4251
4428
  `;
4252
- fs29.writeFileSync(path28.join(projectDir, "local.properties"), content, "utf8");
4429
+ fs31.writeFileSync(path30.join(projectDir, "local.properties"), content, "utf8");
4253
4430
  }
4254
4431
  function renameIfExists(fromPath, toPath) {
4255
- if (fs29.existsSync(fromPath) && fromPath !== toPath) {
4256
- fs29.renameSync(fromPath, toPath);
4432
+ if (fs31.existsSync(fromPath) && fromPath !== toPath) {
4433
+ fs31.renameSync(fromPath, toPath);
4257
4434
  }
4258
4435
  }
4259
4436
  function updateTemplateConfigs(projectDir, args, sdkBaseDir) {
4260
- const appJsonPath = path28.join(projectDir, "AppScope", "app.json5");
4261
- if (fs29.existsSync(appJsonPath)) {
4437
+ const appJsonPath = path30.join(projectDir, "AppScope", "app.json5");
4438
+ if (fs31.existsSync(appJsonPath)) {
4262
4439
  const appJson = readJson5File(appJsonPath);
4263
4440
  appJson.app = appJson.app ?? {};
4264
4441
  appJson.app.bundleName = args.bundleName;
4265
4442
  writeJson5File(appJsonPath, appJson);
4266
4443
  }
4267
- const stringsPath = path28.join(projectDir, "AppScope", "resources", "base", "element", "string.json");
4268
- if (fs29.existsSync(stringsPath)) {
4444
+ const stringsPath = path30.join(projectDir, "AppScope", "resources", "base", "element", "string.json");
4445
+ if (fs31.existsSync(stringsPath)) {
4269
4446
  const strings = readJsonFile(stringsPath);
4270
4447
  if (Array.isArray(strings.string)) {
4271
4448
  const appName = strings.string.find((s) => s.name === "app_name");
@@ -4273,8 +4450,8 @@ function updateTemplateConfigs(projectDir, args, sdkBaseDir) {
4273
4450
  }
4274
4451
  writeJsonFile(stringsPath, strings);
4275
4452
  }
4276
- const buildProfilePath = path28.join(projectDir, "build-profile.json5");
4277
- if (fs29.existsSync(buildProfilePath)) {
4453
+ const buildProfilePath = path30.join(projectDir, "build-profile.json5");
4454
+ if (fs31.existsSync(buildProfilePath)) {
4278
4455
  const buildProfile = readJson5File(buildProfilePath);
4279
4456
  buildProfile.app = buildProfile.app ?? {};
4280
4457
  if (Array.isArray(buildProfile.app.products) && buildProfile.app.products.length > 0) {
@@ -4287,23 +4464,23 @@ function updateTemplateConfigs(projectDir, args, sdkBaseDir) {
4287
4464
  }
4288
4465
  writeJson5File(buildProfilePath, buildProfile);
4289
4466
  }
4290
- const moduleJsonPath = path28.join(projectDir, args.moduleName, "src", "main", "module.json5");
4291
- if (fs29.existsSync(moduleJsonPath)) {
4467
+ const moduleJsonPath = path30.join(projectDir, args.moduleName, "src", "main", "module.json5");
4468
+ if (fs31.existsSync(moduleJsonPath)) {
4292
4469
  const moduleJson = readJson5File(moduleJsonPath);
4293
4470
  moduleJson.module = moduleJson.module ?? {};
4294
4471
  moduleJson.module.name = args.moduleName;
4295
4472
  writeJson5File(moduleJsonPath, moduleJson);
4296
4473
  }
4297
- const moduleOhPackagePath = path28.join(projectDir, args.moduleName, "oh-package.json5");
4298
- if (fs29.existsSync(moduleOhPackagePath)) {
4474
+ const moduleOhPackagePath = path30.join(projectDir, args.moduleName, "oh-package.json5");
4475
+ if (fs31.existsSync(moduleOhPackagePath)) {
4299
4476
  const pkg = readJson5File(moduleOhPackagePath);
4300
4477
  pkg.name = args.moduleName;
4301
4478
  writeJson5File(moduleOhPackagePath, pkg);
4302
4479
  }
4303
- const vscodeDir = path28.join(projectDir, ".vscode");
4304
- fs29.mkdirSync(vscodeDir, { recursive: true });
4480
+ const vscodeDir = path30.join(projectDir, ".vscode");
4481
+ fs31.mkdirSync(vscodeDir, { recursive: true });
4305
4482
  const hapPath = `${args.moduleName}/build/default/outputs/default/${args.moduleName}-default-signed.hap`;
4306
- writeJsonFile(path28.join(vscodeDir, "settings.json"), {
4483
+ writeJsonFile(path30.join(vscodeDir, "settings.json"), {
4307
4484
  "oniro.hapPath": hapPath,
4308
4485
  "files.associations": { "*.json5": "jsonc" }
4309
4486
  });
@@ -4320,7 +4497,7 @@ async function createScaffold(opts) {
4320
4497
  if (!opts.location || !await pathExists(opts.location)) {
4321
4498
  throw new OniroError(`Location does not exist: ${opts.location}`);
4322
4499
  }
4323
- const templateDir = path28.join(opts.templateRoot, opts.templateId);
4500
+ const templateDir = path30.join(opts.templateRoot, opts.templateId);
4324
4501
  if (!await pathExists(templateDir)) {
4325
4502
  throw new OniroError(`Template not found: ${templateDir}`);
4326
4503
  }
@@ -4355,17 +4532,17 @@ async function createScaffold(opts) {
4355
4532
  }
4356
4533
  }
4357
4534
  }
4358
- const projectDir = path28.join(opts.location, opts.projectName);
4535
+ const projectDir = path30.join(opts.location, opts.projectName);
4359
4536
  if (await pathExists(projectDir)) {
4360
4537
  if (!opts.overwrite) {
4361
4538
  throw new OniroError(`Destination already exists: ${projectDir}. Pass overwrite: true to replace it.`);
4362
4539
  }
4363
- await fs29.promises.rm(projectDir, { recursive: true, force: true });
4540
+ await fs31.promises.rm(projectDir, { recursive: true, force: true });
4364
4541
  }
4365
4542
  logger.info(`[create] Scaffolding ${opts.templateId} at ${projectDir}`);
4366
4543
  await copyDirRecursive(templateDir, projectDir);
4367
4544
  if (moduleName !== defaultModuleName) {
4368
- renameIfExists(path28.join(projectDir, defaultModuleName), path28.join(projectDir, moduleName));
4545
+ renameIfExists(path30.join(projectDir, defaultModuleName), path30.join(projectDir, moduleName));
4369
4546
  }
4370
4547
  updateTemplateConfigs(
4371
4548
  projectDir,
@@ -4382,11 +4559,11 @@ async function createScaffold(opts) {
4382
4559
  }
4383
4560
 
4384
4561
  // src/project/listModules.ts
4385
- import * as fs30 from "fs";
4386
- import * as path29 from "path";
4562
+ import * as fs32 from "fs";
4563
+ import * as path31 from "path";
4387
4564
  function listModules(opts) {
4388
- const buildProfile = path29.join(opts.projectDir, "build-profile.json5");
4389
- if (!fs30.existsSync(buildProfile)) {
4565
+ const buildProfile = path31.join(opts.projectDir, "build-profile.json5");
4566
+ if (!fs32.existsSync(buildProfile)) {
4390
4567
  throw new OniroError(`build-profile.json5 not found at ${buildProfile}`);
4391
4568
  }
4392
4569
  const parsed = readJson5File(buildProfile);
@@ -4398,12 +4575,12 @@ function listModules(opts) {
4398
4575
  }
4399
4576
 
4400
4577
  // src/project/abilities.ts
4401
- import * as fs31 from "fs";
4402
- import * as path30 from "path";
4578
+ import * as fs33 from "fs";
4579
+ import * as path32 from "path";
4403
4580
  function listAbilities(opts) {
4404
4581
  const moduleName = opts.moduleName ?? "entry";
4405
- const moduleJsonPath = path30.join(opts.projectDir, moduleName, "src", "main", "module.json5");
4406
- if (!fs31.existsSync(moduleJsonPath)) {
4582
+ const moduleJsonPath = path32.join(opts.projectDir, moduleName, "src", "main", "module.json5");
4583
+ if (!fs33.existsSync(moduleJsonPath)) {
4407
4584
  throw new OniroError(`Could not find module.json5 at ${moduleJsonPath}`);
4408
4585
  }
4409
4586
  const parsed = readJson5File(moduleJsonPath);
@@ -4411,12 +4588,12 @@ function listAbilities(opts) {
4411
4588
  }
4412
4589
 
4413
4590
  // src/project/prepareSigning.ts
4414
- import * as fs32 from "fs";
4415
- import * as path31 from "path";
4591
+ import * as fs34 from "fs";
4592
+ import * as path33 from "path";
4416
4593
  function hasSigningArtifacts(dir) {
4417
- if (!fs32.existsSync(path31.join(dir, "signatures"))) return false;
4418
- const buildProfile = path31.join(dir, "build-profile.json5");
4419
- if (!fs32.existsSync(buildProfile)) return false;
4594
+ if (!fs34.existsSync(path33.join(dir, "signatures"))) return false;
4595
+ const buildProfile = path33.join(dir, "build-profile.json5");
4596
+ if (!fs34.existsSync(buildProfile)) return false;
4420
4597
  try {
4421
4598
  const parsed = readJson5File(buildProfile);
4422
4599
  return Array.isArray(parsed.app?.signingConfigs) && parsed.app.signingConfigs.length > 0;
@@ -4495,7 +4672,6 @@ async function getDeviceInfo(opts) {
4495
4672
  }
4496
4673
 
4497
4674
  // src/install/diffHapAssets.ts
4498
- import StreamZip2 from "node-stream-zip";
4499
4675
  function diffEntryNames(installed, next) {
4500
4676
  const installedSet = new Set(installed);
4501
4677
  const nextSet = new Set(next);
@@ -4504,14 +4680,11 @@ function diffEntryNames(installed, next) {
4504
4680
  removedAssetPaths: [...installedSet].filter((n) => !nextSet.has(n)).sort()
4505
4681
  };
4506
4682
  }
4507
- async function listFileEntries(hapPath) {
4508
- const zip = new StreamZip2.async({ file: hapPath });
4509
- try {
4510
- const entries = await zip.entries();
4511
- return Object.values(entries).filter((e) => !e.isDirectory).map((e) => e.name);
4512
- } finally {
4513
- await zip.close();
4514
- }
4683
+ function listFileEntries(hapPath) {
4684
+ return withHapZip(
4685
+ hapPath,
4686
+ (zip) => Object.values(zip.entries()).filter((e) => !e.isDirectory).map((e) => e.name)
4687
+ );
4515
4688
  }
4516
4689
  async function diffHapAssets(opts) {
4517
4690
  const [installed, next] = await Promise.all([listFileEntries(opts.installedHap), listFileEntries(opts.newHap)]);
@@ -4519,8 +4692,8 @@ async function diffHapAssets(opts) {
4519
4692
  }
4520
4693
 
4521
4694
  // src/install/apply.ts
4522
- import * as fs33 from "fs";
4523
- import * as path32 from "path";
4695
+ import * as fs35 from "fs";
4696
+ import * as path34 from "path";
4524
4697
 
4525
4698
  // src/install/hapBundleName.ts
4526
4699
  function bundleNameFromHapConfig(text) {
@@ -4558,7 +4731,7 @@ async function pidOf(config, bundle, deviceSerial) {
4558
4731
  async function resolveHap(opts, logger) {
4559
4732
  let hap;
4560
4733
  if (opts.hapPath) {
4561
- const hapPath = !path32.isAbsolute(opts.hapPath) && opts.projectDir ? path32.join(opts.projectDir, opts.hapPath) : opts.hapPath;
4734
+ const hapPath = !path34.isAbsolute(opts.hapPath) && opts.projectDir ? path34.join(opts.projectDir, opts.hapPath) : opts.hapPath;
4562
4735
  hap = existingHapOrUnsigned(hapPath);
4563
4736
  if (!hap) throw new OniroError(`HAP not found at ${hapPath}.`);
4564
4737
  } else {
@@ -4567,7 +4740,7 @@ async function resolveHap(opts, logger) {
4567
4740
  if (!picked) throw new OniroError(`No HAP found${opts.module ? ` for module ${opts.module}` : ""}. Build first.`);
4568
4741
  hap = { path: picked.path, fallback: !picked.signed };
4569
4742
  }
4570
- if (hap.fallback) logger.warn(`No signed HAP found; installing the unsigned ${path32.basename(hap.path)}.`);
4743
+ if (hap.fallback) logger.warn(`No signed HAP found; installing the unsigned ${path34.basename(hap.path)}.`);
4571
4744
  return hap;
4572
4745
  }
4573
4746
  async function applyChanges(opts) {
@@ -4583,12 +4756,12 @@ async function applyChanges(opts) {
4583
4756
  });
4584
4757
  if (hapBundle && hapBundle !== bundle) {
4585
4758
  throw new OniroError(
4586
- `${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.`
4759
+ `${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.`
4587
4760
  );
4588
4761
  }
4589
4762
  logger.info(`installing ${hapPath}`);
4590
4763
  let assetsChanged = false;
4591
- if (opts.installedHapPath && fs33.existsSync(opts.installedHapPath)) {
4764
+ if (opts.installedHapPath && fs35.existsSync(opts.installedHapPath)) {
4592
4765
  try {
4593
4766
  const diff = await diffHapAssets({ installedHap: opts.installedHapPath, newHap: hapPath });
4594
4767
  assetsChanged = diff.addedAssetPaths.length > 0 || diff.removedAssetPaths.length > 0;
@@ -4621,7 +4794,7 @@ async function applyChanges(opts) {
4621
4794
  }
4622
4795
  if (!await isBundleInstalled({ config, bundle, deviceSerial })) {
4623
4796
  throw new OniroError(
4624
- `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).`
4797
+ `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).`
4625
4798
  );
4626
4799
  }
4627
4800
  let postInstallPid = await pidOf(config, bundle, deviceSerial);
@@ -4698,10 +4871,10 @@ var AUTH_PATHS = {
4698
4871
  };
4699
4872
 
4700
4873
  // src/harmonyos/auth/tokenStore.ts
4701
- import * as fs34 from "fs";
4702
- import * as os11 from "os";
4703
- import * as path33 from "path";
4704
- import * as crypto3 from "crypto";
4874
+ import * as fs36 from "fs";
4875
+ import * as os12 from "os";
4876
+ import * as path35 from "path";
4877
+ import * as crypto4 from "crypto";
4705
4878
  var ALGORITHM = "aes-256-gcm";
4706
4879
  var KEY_LENGTH = 32;
4707
4880
  var IV_LENGTH = 12;
@@ -4710,8 +4883,8 @@ var KEY_FILE = "kek.bin";
4710
4883
  var DIR_MODE = 448;
4711
4884
  var FILE_MODE = 384;
4712
4885
  function encrypt2(key, plaintext) {
4713
- const iv = crypto3.randomBytes(IV_LENGTH);
4714
- const cipher = crypto3.createCipheriv(ALGORITHM, key, iv);
4886
+ const iv = crypto4.randomBytes(IV_LENGTH);
4887
+ const cipher = crypto4.createCipheriv(ALGORITHM, key, iv);
4715
4888
  const ciphertext = Buffer.concat([cipher.update(plaintext), cipher.final()]);
4716
4889
  return {
4717
4890
  version: 1,
@@ -4722,7 +4895,7 @@ function encrypt2(key, plaintext) {
4722
4895
  };
4723
4896
  }
4724
4897
  function decrypt2(key, blob) {
4725
- const decipher = crypto3.createDecipheriv(ALGORITHM, key, Buffer.from(blob.iv, "base64"));
4898
+ const decipher = crypto4.createDecipheriv(ALGORITHM, key, Buffer.from(blob.iv, "base64"));
4726
4899
  decipher.setAuthTag(Buffer.from(blob.authTag, "base64"));
4727
4900
  return Buffer.concat([decipher.update(Buffer.from(blob.ciphertext, "base64")), decipher.final()]);
4728
4901
  }
@@ -4736,7 +4909,7 @@ function permissionHint(dir) {
4736
4909
  }
4737
4910
  function ensureDir(dir) {
4738
4911
  try {
4739
- fs34.mkdirSync(dir, { recursive: true, mode: DIR_MODE });
4912
+ fs36.mkdirSync(dir, { recursive: true, mode: DIR_MODE });
4740
4913
  } catch (err) {
4741
4914
  if (err.code === "EACCES") {
4742
4915
  throw new OniroError(permissionHint(dir), err);
@@ -4747,17 +4920,17 @@ function ensureDir(dir) {
4747
4920
  function createTokenStore(opts) {
4748
4921
  const logger = opts.logger ?? noopLogger;
4749
4922
  const authDir = opts.config.get("harmonyosAuthDir", defaultPaths.harmonyosAuthDir());
4750
- const tokenPath = path33.join(authDir, TOKEN_FILE);
4751
- const keyPath = path33.join(os11.homedir(), ".local", "share", "oniro-app", "keys", KEY_FILE);
4923
+ const tokenPath = path35.join(authDir, TOKEN_FILE);
4924
+ const keyPath = path35.join(os12.homedir(), ".local", "share", "oniro-app", "keys", KEY_FILE);
4752
4925
  function loadOrCreateKey() {
4753
- if (fs34.existsSync(keyPath)) {
4754
- const existing = fs34.readFileSync(keyPath);
4926
+ if (fs36.existsSync(keyPath)) {
4927
+ const existing = fs36.readFileSync(keyPath);
4755
4928
  if (existing.length === KEY_LENGTH) return existing;
4756
4929
  logger.warn("[harmonyos] The token key file was malformed; regenerating (you will need to log in again).");
4757
4930
  }
4758
- ensureDir(path33.dirname(keyPath));
4759
- const key = crypto3.randomBytes(KEY_LENGTH);
4760
- fs34.writeFileSync(keyPath, key, { mode: FILE_MODE });
4931
+ ensureDir(path35.dirname(keyPath));
4932
+ const key = crypto4.randomBytes(KEY_LENGTH);
4933
+ fs36.writeFileSync(keyPath, key, { mode: FILE_MODE });
4761
4934
  return key;
4762
4935
  }
4763
4936
  return {
@@ -4767,12 +4940,12 @@ function createTokenStore(opts) {
4767
4940
  const payload = Buffer.from(JSON.stringify(session), "utf8");
4768
4941
  const blob = encrypt2(loadOrCreateKey(), payload);
4769
4942
  ensureDir(authDir);
4770
- fs34.writeFileSync(tokenPath, JSON.stringify(blob, null, 2), { mode: FILE_MODE });
4943
+ fs36.writeFileSync(tokenPath, JSON.stringify(blob, null, 2), { mode: FILE_MODE });
4771
4944
  },
4772
4945
  load() {
4773
- if (!fs34.existsSync(tokenPath)) return null;
4946
+ if (!fs36.existsSync(tokenPath)) return null;
4774
4947
  try {
4775
- const blob = JSON.parse(fs34.readFileSync(tokenPath, "utf8"));
4948
+ const blob = JSON.parse(fs36.readFileSync(tokenPath, "utf8"));
4776
4949
  if (!isEncryptedBlob(blob)) return null;
4777
4950
  const { jwtToken, siteId } = JSON.parse(decrypt2(loadOrCreateKey(), blob).toString("utf8"));
4778
4951
  return typeof jwtToken === "string" && jwtToken && typeof siteId === "string" ? { jwtToken, siteId } : null;
@@ -4784,7 +4957,7 @@ function createTokenStore(opts) {
4784
4957
  }
4785
4958
  logger.debug(`[harmonyos] Discarding unreadable token at ${tokenPath}.`);
4786
4959
  try {
4787
- fs34.rmSync(tokenPath, { force: true });
4960
+ fs36.rmSync(tokenPath, { force: true });
4788
4961
  } catch {
4789
4962
  }
4790
4963
  return null;
@@ -4792,7 +4965,7 @@ function createTokenStore(opts) {
4792
4965
  },
4793
4966
  clear() {
4794
4967
  try {
4795
- fs34.rmSync(tokenPath, { force: true });
4968
+ fs36.rmSync(tokenPath, { force: true });
4796
4969
  } catch (err) {
4797
4970
  throw new OniroError(`Failed to remove ${tokenPath}.`, err);
4798
4971
  }
@@ -4801,7 +4974,7 @@ function createTokenStore(opts) {
4801
4974
  }
4802
4975
 
4803
4976
  // src/harmonyos/auth/session.ts
4804
- import * as crypto5 from "crypto";
4977
+ import * as crypto6 from "crypto";
4805
4978
 
4806
4979
  // src/harmonyos/http.ts
4807
4980
  import { EnvHttpProxyAgent, fetch } from "undici";
@@ -4892,13 +5065,13 @@ function createHttpClient(opts = {}) {
4892
5065
 
4893
5066
  // src/harmonyos/auth/callbackServer.ts
4894
5067
  import * as http2 from "http";
4895
- import * as crypto4 from "crypto";
5068
+ import * as crypto5 from "crypto";
4896
5069
  var CALLBACK_PATH = "/callback";
4897
5070
  var MAX_BODY_BYTES = 65536;
4898
5071
  function codeMatches(code, secret) {
4899
5072
  const a = Buffer.from(code ?? "", "utf8");
4900
5073
  const b = Buffer.from(secret, "utf8");
4901
- return a.length === b.length && crypto4.timingSafeEqual(a, b);
5074
+ return a.length === b.length && crypto5.timingSafeEqual(a, b);
4902
5075
  }
4903
5076
  function isQuit(quit) {
4904
5077
  return quit === "true" || quit === "access_denied" || quit === "quit";
@@ -4906,8 +5079,8 @@ function isQuit(quit) {
4906
5079
  async function startCallbackServer(opts) {
4907
5080
  let resolveCallback;
4908
5081
  let rejectCallback;
4909
- const callback = new Promise((resolve7, reject) => {
4910
- resolveCallback = resolve7;
5082
+ const callback = new Promise((resolve9, reject) => {
5083
+ resolveCallback = resolve9;
4911
5084
  rejectCallback = reject;
4912
5085
  });
4913
5086
  callback.catch(() => {
@@ -4966,9 +5139,9 @@ async function startCallbackServer(opts) {
4966
5139
  }
4967
5140
  });
4968
5141
  server.keepAliveTimeout = 1;
4969
- await new Promise((resolve7, reject) => {
5142
+ await new Promise((resolve9, reject) => {
4970
5143
  server.once("error", (err) => reject(new OniroError("Failed to start the local login callback server.", err)));
4971
- server.listen(0, "127.0.0.1", resolve7);
5144
+ server.listen(0, "127.0.0.1", resolve9);
4972
5145
  });
4973
5146
  const port = server.address().port;
4974
5147
  return {
@@ -4984,12 +5157,12 @@ async function startCallbackServer(opts) {
4984
5157
  return Promise.race([callback, timeout]).finally(() => clearTimeout(timer));
4985
5158
  },
4986
5159
  stop() {
4987
- return new Promise((resolve7) => {
5160
+ return new Promise((resolve9) => {
4988
5161
  server.closeAllConnections?.();
4989
- const timer = setTimeout(resolve7, 100);
5162
+ const timer = setTimeout(resolve9, 100);
4990
5163
  server.close(() => {
4991
5164
  clearTimeout(timer);
4992
- resolve7();
5165
+ resolve9();
4993
5166
  });
4994
5167
  });
4995
5168
  }
@@ -5021,11 +5194,11 @@ function openBrowser(url) {
5021
5194
  windowsHide: true
5022
5195
  });
5023
5196
  child.unref();
5024
- return new Promise((resolve7, reject) => {
5197
+ return new Promise((resolve9, reject) => {
5025
5198
  child.on("error", (err) => reject(new OniroError(`Failed to open a browser (${command}).`, err)));
5026
5199
  child.on(
5027
5200
  "exit",
5028
- (code) => code === 0 ? resolve7() : reject(new OniroError(`Browser launcher '${command}' exited with code ${code}.`))
5201
+ (code) => code === 0 ? resolve9() : reject(new OniroError(`Browser launcher '${command}' exited with code ${code}.`))
5029
5202
  );
5030
5203
  });
5031
5204
  }
@@ -5107,7 +5280,7 @@ function createHarmonyOsSession(opts) {
5107
5280
  getDeveloperAgreement,
5108
5281
  async login(o = {}) {
5109
5282
  const start = HUAWEI_SITES.CN.loginUrl;
5110
- const clientSecret = crypto5.randomUUID().replace(/-/g, "");
5283
+ const clientSecret = crypto6.randomUUID().replace(/-/g, "");
5111
5284
  const server = await startCallbackServer({
5112
5285
  clientSecret,
5113
5286
  baseUrl: start,
@@ -5273,7 +5446,7 @@ function mapAgcError(operation, statusCode, reasonPhrase, body) {
5273
5446
  }
5274
5447
 
5275
5448
  // src/harmonyos/signing/profile.ts
5276
- import * as fs35 from "fs";
5449
+ import * as fs37 from "fs";
5277
5450
  import { X509Certificate } from "crypto";
5278
5451
  import forge from "node-forge";
5279
5452
  function extractProfileJson(buffer) {
@@ -5305,7 +5478,7 @@ function extractProfileJson(buffer) {
5305
5478
  function readProvisionProfile(profilePath) {
5306
5479
  let raw;
5307
5480
  try {
5308
- const json = extractProfileJson(fs35.readFileSync(profilePath));
5481
+ const json = extractProfileJson(fs37.readFileSync(profilePath));
5309
5482
  if (!json) return null;
5310
5483
  raw = JSON.parse(json);
5311
5484
  } catch {
@@ -5334,7 +5507,7 @@ function parseCertificate(pem) {
5334
5507
  }
5335
5508
  function keystoreHoldsKey(p12Path, keyAlias, keyPwd, certificate) {
5336
5509
  try {
5337
- const der = forge.util.createBuffer(fs35.readFileSync(p12Path).toString("binary"));
5510
+ const der = forge.util.createBuffer(fs37.readFileSync(p12Path).toString("binary"));
5338
5511
  const p12 = forge.pkcs12.pkcs12FromAsn1(forge.asn1.fromDer(der), keyPwd);
5339
5512
  const certBag = forge.pki.oids.certBag ?? "1.2.840.113549.1.12.10.1.3";
5340
5513
  const wanted = certificate.publicKey.export({ type: "spki", format: "der" });
@@ -5350,7 +5523,7 @@ function keystoreHoldsKey(p12Path, keyAlias, keyPwd, certificate) {
5350
5523
  }
5351
5524
  }
5352
5525
  function verifySigningMaterial(opts) {
5353
- const pems = fs35.readFileSync(opts.cerPath, "utf8").match(/-----BEGIN CERTIFICATE-----[\s\S]*?-----END CERTIFICATE-----/g);
5526
+ const pems = fs37.readFileSync(opts.cerPath, "utf8").match(/-----BEGIN CERTIFICATE-----[\s\S]*?-----END CERTIFICATE-----/g);
5354
5527
  const chain = (pems ?? []).map(parseCertificate);
5355
5528
  const leaf = chain.find((c) => !c.ca) ?? chain.at(-1);
5356
5529
  if (!leaf) throw new AgcError(SIGNING_ERRORS.CERT_INVALID);
@@ -5368,15 +5541,15 @@ function verifySigningMaterial(opts) {
5368
5541
  }
5369
5542
 
5370
5543
  // src/harmonyos/signing/autoSign.ts
5371
- import * as crypto9 from "crypto";
5372
- import * as fs40 from "fs";
5373
- import * as path39 from "path";
5544
+ import * as crypto10 from "crypto";
5545
+ import * as fs42 from "fs";
5546
+ import * as path41 from "path";
5374
5547
  import JSON57 from "json5";
5375
5548
 
5376
5549
  // src/harmonyos/signing/agc.ts
5377
- import * as crypto6 from "crypto";
5378
- import * as fs36 from "fs";
5379
- import * as path34 from "path";
5550
+ import * as crypto7 from "crypto";
5551
+ import * as fs38 from "fs";
5552
+ import * as path36 from "path";
5380
5553
  var ENDPOINTS = {
5381
5554
  CERT_LIST: "/api/cps/harmony-cert-manage/v1/cert/list",
5382
5555
  CERT_ADD: "/api/cps/harmony-cert-manage/v1/cert/add",
@@ -5424,7 +5597,7 @@ function createAgcClient(http3, auth) {
5424
5597
  }
5425
5598
  },
5426
5599
  async addDevice(udid, deviceKind) {
5427
- const deviceName = `oniro_app_device_${crypto6.randomBytes(4).toString("hex")}`;
5600
+ const deviceName = `oniro_app_device_${crypto7.randomBytes(4).toString("hex")}`;
5428
5601
  await call("device", "post", ENDPOINTS.DEVICE_ADD, {
5429
5602
  body: { deviceName, udid, deviceType: String(DEVICE_TYPE_CODES[deviceKind] ?? DEVICE_TYPE_CODES.phone) }
5430
5603
  });
@@ -5458,12 +5631,12 @@ function createAgcClient(http3, auth) {
5458
5631
  const target = urlsInfo?.[0];
5459
5632
  if (!target) throw mapAgcError(operation, 200, "", "");
5460
5633
  const buffer = await http3.getBinary(target.newUrl, { timeoutMs: 6e4 });
5461
- const actual = crypto6.createHash("sha256").update(buffer).digest("hex");
5634
+ const actual = crypto7.createHash("sha256").update(buffer).digest("hex");
5462
5635
  if (target.sha256 && actual !== target.sha256.toLowerCase()) {
5463
5636
  throw new ChecksumMismatchError(target.sha256, actual);
5464
5637
  }
5465
- fs36.mkdirSync(path34.dirname(dest), { recursive: true, mode: 448 });
5466
- fs36.writeFileSync(dest, buffer, { mode: 384 });
5638
+ fs38.mkdirSync(path36.dirname(dest), { recursive: true, mode: 448 });
5639
+ fs38.writeFileSync(dest, buffer, { mode: 384 });
5467
5640
  }
5468
5641
  };
5469
5642
  }
@@ -5518,23 +5691,23 @@ async function registerDevices(agc, connected, logger) {
5518
5691
  }
5519
5692
 
5520
5693
  // src/harmonyos/signing/keystore.ts
5521
- import * as crypto7 from "crypto";
5522
- import * as fs38 from "fs";
5523
- import * as path36 from "path";
5694
+ import * as crypto8 from "crypto";
5695
+ import * as fs40 from "fs";
5696
+ import * as path38 from "path";
5524
5697
 
5525
5698
  // src/harmonyos/java.ts
5526
- import * as fs37 from "fs";
5527
- import * as path35 from "path";
5699
+ import * as fs39 from "fs";
5700
+ import * as path37 from "path";
5528
5701
  var JAVA_BIN = process.platform === "win32" ? "java.exe" : "java";
5529
5702
  function findJava(sdk, env = process.env) {
5530
5703
  const candidates = [
5531
- env.JAVA_HOME && path35.join(env.JAVA_HOME, "bin", JAVA_BIN),
5532
- sdk && path35.join(sdk.installRoot, "jbr", "bin", JAVA_BIN),
5704
+ env.JAVA_HOME && path37.join(env.JAVA_HOME, "bin", JAVA_BIN),
5705
+ sdk && path37.join(sdk.installRoot, "jbr", "bin", JAVA_BIN),
5533
5706
  // macOS: the JBR is itself a bundle inside Studio's Contents.
5534
- sdk && path35.join(sdk.installRoot, "jbr", "Contents", "Home", "bin", JAVA_BIN),
5535
- ...(env.PATH ?? "").split(path35.delimiter).map((dir) => dir && path35.join(dir, JAVA_BIN))
5707
+ sdk && path37.join(sdk.installRoot, "jbr", "Contents", "Home", "bin", JAVA_BIN),
5708
+ ...(env.PATH ?? "").split(path37.delimiter).map((dir) => dir && path37.join(dir, JAVA_BIN))
5536
5709
  ];
5537
- const found = candidates.find((c) => !!c && fs37.existsSync(c));
5710
+ const found = candidates.find((c) => !!c && fs39.existsSync(c));
5538
5711
  if (found) return found;
5539
5712
  throw new OniroError(
5540
5713
  "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."
@@ -5547,49 +5720,77 @@ var SIGN_ALG = "SHA256withECDSA";
5547
5720
  function redactArgs(args) {
5548
5721
  return args.map((arg, i) => args[i - 1] === "-keyPwd" || args[i - 1] === "-keystorePwd" ? "******" : arg);
5549
5722
  }
5550
- async function generateKeystoreAndCsr(opts) {
5551
- const jar = path36.join(opts.sdk.sdkPath, "default", "openharmony", "toolchains", "lib", "hap-sign-tool.jar");
5552
- if (!fs38.existsSync(jar)) {
5723
+ function hapSignToolJar(sdk) {
5724
+ const jar = path38.join(sdk.sdkPath, "default", "openharmony", "toolchains", "lib", "hap-sign-tool.jar");
5725
+ if (!fs40.existsSync(jar)) {
5553
5726
  throw new OniroError(`hap-sign-tool.jar not found at ${jar}; the HarmonyOS SDK looks incomplete.`);
5554
5727
  }
5728
+ return jar;
5729
+ }
5730
+ async function runHapSignTool(opts) {
5731
+ const jar = hapSignToolJar(opts.sdk);
5555
5732
  const java = findJava(opts.sdk);
5556
- const keyPwd = crypto7.randomBytes(12).toString("base64url").replaceAll(/[-_]/g, "");
5557
- fs38.mkdirSync(path36.dirname(opts.p12Path), { recursive: true, mode: 448 });
5558
- const run = async (what, args) => {
5559
- const argv = ["-jar", jar, ...args, "-keyAlias", KEY_ALIAS, "-keystoreFile", opts.p12Path, "-keystorePwd", keyPwd, "-keyPwd", keyPwd];
5560
- opts.logger.debug(`[harmonyos] ${java} ${redactArgs(argv).join(" ")}`);
5561
- const result = await runProcess({ command: java, args: argv, timeoutMs: 12e4, logger: opts.logger });
5562
- if (result.code !== 0) {
5563
- const detail = (result.stderr || result.stdout || "").trim();
5564
- throw new OniroError(`hap-sign-tool failed to ${what} (exit ${result.code}).${detail ? `
5733
+ const argv = ["-jar", jar, ...opts.args, "-keyAlias", KEY_ALIAS, "-keystoreFile", opts.p12Path, "-keystorePwd", opts.keyPwd, "-keyPwd", opts.keyPwd];
5734
+ opts.logger.debug(`[harmonyos] ${java} ${redactArgs(argv).join(" ")}`);
5735
+ const result = await runProcess({ command: java, args: argv, timeoutMs: 12e4, logger: opts.logger });
5736
+ if (result.code !== 0) {
5737
+ const detail = (result.stderr || result.stdout || "").trim();
5738
+ throw new OniroError(`hap-sign-tool failed to ${opts.what} (exit ${result.code}).${detail ? `
5565
5739
  ${detail}` : ""}`);
5566
- }
5567
- };
5740
+ }
5741
+ }
5742
+ async function generateKeystoreAndCsr(opts) {
5743
+ hapSignToolJar(opts.sdk);
5744
+ findJava(opts.sdk);
5745
+ const keyPwd = crypto8.randomBytes(12).toString("base64url").replaceAll(/[-_]/g, "");
5746
+ fs40.mkdirSync(path38.dirname(opts.p12Path), { recursive: true, mode: 448 });
5747
+ const run = (what, args) => runHapSignTool({ ...opts, what, args, keyPwd });
5568
5748
  opts.logger.info("[harmonyos] Generating the local keystore and certificate signing request...");
5569
5749
  await run("generate the key pair", ["generate-keypair", "-keyAlg", "ECC", "-keySize", "NIST-P-256"]);
5570
5750
  await run("generate the CSR", ["generate-csr", "-subject", "CN=DebugKey", "-signAlg", SIGN_ALG, "-outFile", opts.csrPath]);
5571
- return { keyPwd, csr: fs38.readFileSync(opts.csrPath, "utf8") };
5751
+ return { keyPwd, csr: fs40.readFileSync(opts.csrPath, "utf8") };
5752
+ }
5753
+ async function signHapFile(opts) {
5754
+ await runHapSignTool({
5755
+ ...opts,
5756
+ what: `sign ${opts.inFile}`,
5757
+ args: [
5758
+ "sign-app",
5759
+ "-mode",
5760
+ "localSign",
5761
+ "-signAlg",
5762
+ SIGN_ALG,
5763
+ "-appCertFile",
5764
+ opts.cerPath,
5765
+ "-profileFile",
5766
+ opts.profilePath,
5767
+ "-inFile",
5768
+ opts.inFile,
5769
+ "-outFile",
5770
+ opts.outFile
5771
+ ]
5772
+ });
5572
5773
  }
5573
5774
 
5574
5775
  // src/harmonyos/signing/paths.ts
5575
- import * as crypto8 from "crypto";
5576
- import * as path37 from "path";
5776
+ import * as crypto9 from "crypto";
5777
+ import * as path39 from "path";
5577
5778
  function sanitizeNamePart(raw) {
5578
5779
  const safe = (raw?.trim() ?? "").replace(/[\\/:*?"<>|=.-]+/g, "_");
5579
5780
  return safe.slice(0, 64) || "default";
5580
5781
  }
5581
5782
  function resolveSigningMaterialPaths(config, productName, projectRoot) {
5582
- const hash = crypto8.createHash("sha256").update(projectRoot, "utf8").digest("base64url").replaceAll(/[-_]/g, "");
5583
- const base = path37.join(
5783
+ const hash = crypto9.createHash("sha256").update(projectRoot, "utf8").digest("base64url").replaceAll(/[-_]/g, "");
5784
+ const base = path39.join(
5584
5785
  config.get("harmonyosSigningDir", defaultPaths.harmonyosSigningDir()),
5585
- `${sanitizeNamePart(productName)}_${path37.basename(projectRoot)}_${hash}=`
5786
+ `${sanitizeNamePart(productName)}_${path39.basename(projectRoot)}_${hash}=`
5586
5787
  );
5587
5788
  return { p12Path: `${base}.p12`, csrPath: `${base}.csr`, cerPath: `${base}.cer`, profilePath: `${base}.p7b` };
5588
5789
  }
5589
5790
 
5590
5791
  // src/harmonyos/signing/project.ts
5591
- import * as fs39 from "fs";
5592
- import * as path38 from "path";
5792
+ import * as fs41 from "fs";
5793
+ import * as path40 from "path";
5593
5794
  import JSON56 from "json5";
5594
5795
 
5595
5796
  // src/harmonyos/signing/aclPermissionList.ts
@@ -5657,9 +5858,9 @@ var ACL_PERMISSIONS = /* @__PURE__ */ new Set([
5657
5858
 
5658
5859
  // src/harmonyos/signing/project.ts
5659
5860
  function readRequestedPermissions(moduleConfigPath) {
5660
- if (!fs39.existsSync(moduleConfigPath)) return [];
5861
+ if (!fs41.existsSync(moduleConfigPath)) return [];
5661
5862
  try {
5662
- const parsed = JSON56.parse(fs39.readFileSync(moduleConfigPath, "utf-8"));
5863
+ const parsed = JSON56.parse(fs41.readFileSync(moduleConfigPath, "utf-8"));
5663
5864
  return (parsed.module?.requestPermissions ?? []).map((p) => p?.name).filter((n) => typeof n === "string" && n.length > 0);
5664
5865
  } catch {
5665
5866
  return [];
@@ -5668,32 +5869,38 @@ function readRequestedPermissions(moduleConfigPath) {
5668
5869
  function collectAclPermissions(projectDir, logger = noopLogger) {
5669
5870
  const requested = /* @__PURE__ */ new Set();
5670
5871
  for (const module of listModules({ projectDir })) {
5671
- const moduleRoot = path38.resolve(projectDir, module.srcPath);
5872
+ const moduleRoot = path40.resolve(projectDir, module.srcPath);
5672
5873
  for (const sourceSet of ["main", "ohosTest"]) {
5673
5874
  for (const name of readRequestedPermissions(
5674
- path38.join(moduleRoot, "src", sourceSet, "module.json5")
5875
+ path40.join(moduleRoot, "src", sourceSet, "module.json5")
5675
5876
  )) {
5676
5877
  requested.add(name);
5677
5878
  }
5678
5879
  }
5679
5880
  }
5680
- const acls = [...requested].filter((p) => ACL_PERMISSIONS.has(p)).sort();
5881
+ const acls = aclPermissionsAmong(requested);
5681
5882
  if (acls.length > 0) {
5682
5883
  logger.info(`[harmonyos] Project requests ${acls.length} ACL permission(s): ${acls.join(", ")}`);
5683
5884
  }
5684
5885
  return acls;
5685
5886
  }
5887
+ function aclPermissionsAmong(names) {
5888
+ return [...new Set(names)].filter((p) => ACL_PERMISSIONS.has(p)).sort();
5889
+ }
5686
5890
 
5687
5891
  // src/harmonyos/signing/autoSign.ts
5688
5892
  function shouldRegenerate(input) {
5689
5893
  if (input.force) return "--force was passed";
5690
- const missing = Object.values(input.paths).find((file) => !fs40.existsSync(file));
5894
+ const missing = Object.values(input.paths).find((file) => !fs42.existsSync(file));
5691
5895
  if (missing) return `${missing} is missing`;
5692
5896
  const config = input.buildProfile.app?.signingConfigs?.find((c) => c.name === input.productName);
5693
5897
  if (config?.type !== "HarmonyOS" || config.material?.profile !== input.paths.profilePath) {
5694
5898
  return "build-profile.json5 has no signingConfig for this material";
5695
5899
  }
5696
- const profile = readProvisionProfile(input.paths.profilePath);
5900
+ return profileProblem({ ...input, profilePath: input.paths.profilePath });
5901
+ }
5902
+ function profileProblem(input) {
5903
+ const profile = readProvisionProfile(input.profilePath);
5697
5904
  if (!profile) return "the provisioning profile could not be read";
5698
5905
  if (profile.bundleName !== input.bundleName) return `the profile is for bundle '${profile.bundleName}'`;
5699
5906
  if (profile.developerId && profile.developerId !== input.teamId) {
@@ -5726,25 +5933,23 @@ function writeSigningConfig(opts) {
5726
5933
  for (const product of app.products ?? []) {
5727
5934
  if (product.name === opts.productName) product.signingConfig = opts.productName;
5728
5935
  }
5729
- fs40.writeFileSync(opts.buildProfilePath, JSON.stringify(opts.buildProfile, null, 2));
5936
+ fs42.writeFileSync(opts.buildProfilePath, JSON.stringify(opts.buildProfile, null, 2));
5730
5937
  }
5731
5938
  async function harmonyOsAutoSign(opts) {
5732
5939
  const logger = opts.logger ?? noopLogger;
5733
5940
  const productName = opts.productName ?? "default";
5734
- const projectDir = path39.resolve(opts.projectDir);
5735
- const buildProfilePath = path39.join(projectDir, "build-profile.json5");
5941
+ const projectDir = path41.resolve(opts.projectDir);
5942
+ const buildProfilePath = path41.join(projectDir, "build-profile.json5");
5736
5943
  let buildProfile;
5737
5944
  try {
5738
- buildProfile = JSON57.parse(fs40.readFileSync(buildProfilePath, "utf-8"));
5945
+ buildProfile = JSON57.parse(fs42.readFileSync(buildProfilePath, "utf-8"));
5739
5946
  } catch (err) {
5740
5947
  throw new OniroError(`Could not read ${buildProfilePath}: ${err.message}`, err);
5741
5948
  }
5742
5949
  const product = buildProfile.app?.products?.find((p) => p.name === productName);
5743
5950
  if (!product) throw new OniroError(`${buildProfilePath} declares no product named '${productName}'.`);
5744
5951
  const bundleName = product.bundleName || getBundleName(projectDir);
5745
- if (!isValidBundleName(bundleName) || bundleName.split(".").length < 3 || bundleName.length < 7 || bundleName.length > 128) {
5746
- throw new AgcError(SIGNING_ERRORS.BUNDLE_NAME_INVALID);
5747
- }
5952
+ assertAgcBundleName(bundleName);
5748
5953
  const sdk = requireHarmonyOsSdk({ config: opts.config, logger });
5749
5954
  const auth = await opts.session.resolveAgcAuth({ teamId: opts.teamId });
5750
5955
  const aclPermissions = collectAclPermissions(projectDir, logger);
@@ -5773,14 +5978,47 @@ async function harmonyOsAutoSign(opts) {
5773
5978
  return result(false);
5774
5979
  }
5775
5980
  logger.info(`[harmonyos] Regenerating signing material: ${reason}.`);
5776
- const agc = createAgcClient(opts.http ?? createHttpClient({ logger }), auth);
5981
+ const keyPwd = await issueSigningMaterial({
5982
+ agc: createAgcClient(opts.http ?? createHttpClient({ logger }), auth),
5983
+ local,
5984
+ sdk,
5985
+ paths,
5986
+ bundleName,
5987
+ aclPermissions,
5988
+ teamId: auth.teamId,
5989
+ logger
5990
+ });
5991
+ const encryptedPassword = encryptPwd(keyPwd, sharedMaterialDir(paths));
5992
+ writeSigningConfig({ buildProfilePath, buildProfile, productName, paths, encryptedPassword });
5993
+ logger.info(`[harmonyos] Wrote signingConfigs['${productName}'] to ${buildProfilePath}.`);
5994
+ return result(true);
5995
+ }
5996
+ function assertAgcBundleName(bundleName) {
5997
+ if (!isValidBundleName(bundleName) || bundleName.split(".").length < 3 || bundleName.length < 7 || bundleName.length > 128) {
5998
+ throw new AgcError(SIGNING_ERRORS.BUNDLE_NAME_INVALID);
5999
+ }
6000
+ }
6001
+ function materialDirFor(paths) {
6002
+ return path41.join(path41.dirname(paths.p12Path), "material");
6003
+ }
6004
+ function sharedMaterialDir(paths) {
6005
+ const materialDir = materialDirFor(paths);
6006
+ try {
6007
+ getKey(materialDir);
6008
+ } catch {
6009
+ createMaterial(materialDir);
6010
+ }
6011
+ return materialDir;
6012
+ }
6013
+ async function issueSigningMaterial(opts) {
6014
+ const { agc, local, sdk, paths, logger } = opts;
5777
6015
  const deviceIds = await registerDevices(agc, local.devices, logger);
5778
6016
  if (local.devices.length === 0) {
5779
6017
  const cause = local.skipped.length > 0 ? "No connected device could be registered" : "No device is connected";
5780
6018
  logger.warn(`[harmonyos] ${cause}; the profile will name the ${deviceIds.length} device(s) already registered to this team.`);
5781
6019
  }
5782
- for (const file of Object.values(paths)) fs40.rmSync(file, { force: true });
5783
- const certName = `oniro_debug_${auth.teamId.replace(/[\\/.:]/g, "")}.cer`;
6020
+ for (const file of Object.values(paths)) fs42.rmSync(file, { force: true });
6021
+ const certName = `oniro_debug_${opts.teamId.replace(/[\\/.:]/g, "")}.cer`;
5784
6022
  const stale = (await agc.listCertificates()).find((c) => c.certName === certName);
5785
6023
  if (stale) {
5786
6024
  logger.info(`[harmonyos] Replacing the previous '${certName}' certificate in AppGallery Connect...`);
@@ -5794,11 +6032,11 @@ async function harmonyOsAutoSign(opts) {
5794
6032
  await agc.download("cert", cert.certObjectId, paths.cerPath);
5795
6033
  logger.info("[harmonyos] Issuing the debug provisioning profile...");
5796
6034
  const profile = await agc.addDebugProfile({
5797
- provisionName: `oniro_${crypto9.randomBytes(6).toString("hex")}`,
5798
- bundleName,
6035
+ provisionName: `oniro_${crypto10.randomBytes(6).toString("hex")}`,
6036
+ bundleName: opts.bundleName,
5799
6037
  certId: cert.id,
5800
6038
  deviceIds,
5801
- aclPermissions
6039
+ aclPermissions: opts.aclPermissions
5802
6040
  });
5803
6041
  try {
5804
6042
  await agc.download("profile", profile.provisionFileUrl, paths.profilePath);
@@ -5810,22 +6048,86 @@ async function harmonyOsAutoSign(opts) {
5810
6048
  try {
5811
6049
  verifySigningMaterial({ ...paths, keyAlias: KEY_ALIAS, keyPwd });
5812
6050
  } catch (err) {
5813
- fs40.rmSync(paths.profilePath, { force: true });
6051
+ fs42.rmSync(paths.profilePath, { force: true });
5814
6052
  throw err;
5815
6053
  }
5816
- const materialDir = path39.join(path39.dirname(paths.p12Path), "material");
6054
+ return keyPwd;
6055
+ }
6056
+
6057
+ // src/harmonyos/signing/signHap.ts
6058
+ import * as fs43 from "fs";
6059
+ import * as path42 from "path";
6060
+ function resolveHapSigningMaterialPaths(config, bundleName) {
6061
+ const paths = resolveSigningMaterialPaths(config, "hap", bundleName);
6062
+ return { ...paths, pwdPath: paths.p12Path.replace(/\.p12$/, ".pwd") };
6063
+ }
6064
+ function readStoredPassword(paths) {
5817
6065
  try {
5818
- getKey(materialDir);
6066
+ return decryptPwd(fs43.readFileSync(paths.pwdPath, "utf8").trim(), materialDirFor(paths));
5819
6067
  } catch {
5820
- createMaterial(materialDir);
6068
+ return null;
5821
6069
  }
5822
- writeSigningConfig({ buildProfilePath, buildProfile, productName, paths, encryptedPassword: encryptPwd(keyPwd, materialDir) });
5823
- logger.info(`[harmonyos] Wrote signingConfigs['${productName}'] to ${buildProfilePath}.`);
5824
- return result(true);
6070
+ }
6071
+ async function harmonyOsSignHap(opts) {
6072
+ const logger = opts.logger ?? noopLogger;
6073
+ const hapPath = path42.resolve(opts.hapPath);
6074
+ const outPath = path42.resolve(opts.outPath ?? defaultSignedHapPath(hapPath));
6075
+ const info = await readHapSigningInfo(hapPath);
6076
+ const { bundleName } = info;
6077
+ if (info.compileSdkType === "OpenHarmony") {
6078
+ throw new OniroError(
6079
+ `${hapPath} was built for OpenHarmony, so a HarmonyOS device would not run it; sign it without --harmonyos.`
6080
+ );
6081
+ }
6082
+ assertAgcBundleName(bundleName);
6083
+ const aclPermissions = aclPermissionsAmong(info.requestedPermissions);
6084
+ if (aclPermissions.length > 0) {
6085
+ logger.info(`[harmonyos] HAP requests ${aclPermissions.length} ACL permission(s): ${aclPermissions.join(", ")}`);
6086
+ }
6087
+ const sdk = requireHarmonyOsSdk({ config: opts.config, logger });
6088
+ const auth = await opts.session.resolveAgcAuth({ teamId: opts.teamId });
6089
+ const paths = resolveHapSigningMaterialPaths(opts.config, bundleName);
6090
+ const { pwdPath, ...material } = paths;
6091
+ logger.info(`[harmonyos] Signing ${path42.basename(hapPath)} as ${bundleName} (team ${auth.teamId}).`);
6092
+ const local = await collectLocalDevices(opts.config, logger);
6093
+ let keyPwd = opts.force ? null : readStoredPassword(paths);
6094
+ const missing = Object.values(material).find((file) => !fs43.existsSync(file));
6095
+ const reason = opts.force ? "--force was passed" : missing ? `${missing} is missing` : keyPwd === null ? "the stored keystore password could not be read" : profileProblem({
6096
+ profilePath: material.profilePath,
6097
+ bundleName,
6098
+ teamId: auth.teamId,
6099
+ connectedUdids: local.devices.map((d) => d.udid),
6100
+ aclPermissions
6101
+ });
6102
+ if (!reason && keyPwd !== null) {
6103
+ logger.info("[harmonyos] Reusing the existing signing material, which is still valid.");
6104
+ } else {
6105
+ logger.info(`[harmonyos] Regenerating signing material: ${reason}.`);
6106
+ fs43.rmSync(pwdPath, { force: true });
6107
+ keyPwd = await issueSigningMaterial({
6108
+ agc: createAgcClient(opts.http ?? createHttpClient({ logger }), auth),
6109
+ local,
6110
+ sdk,
6111
+ paths: material,
6112
+ bundleName,
6113
+ aclPermissions,
6114
+ teamId: auth.teamId,
6115
+ logger
6116
+ });
6117
+ fs43.writeFileSync(pwdPath, encryptPwd(keyPwd, sharedMaterialDir(material)), { mode: 384 });
6118
+ }
6119
+ const password = keyPwd;
6120
+ await writeHapAtomically(
6121
+ outPath,
6122
+ (tmpPath) => signHapFile({ sdk, inFile: hapPath, outFile: tmpPath, ...material, keyPwd: password, logger })
6123
+ );
6124
+ const deviceIds = readProvisionProfile(material.profilePath)?.deviceIds ?? [];
6125
+ logger.info(`[harmonyos] Wrote ${outPath}.`);
6126
+ return { bundleName, teamId: auth.teamId, paths: material, regenerated: !!reason, deviceIds, outPath };
5825
6127
  }
5826
6128
 
5827
6129
  // src/index.ts
5828
- var VERSION = "0.15.1";
6130
+ var VERSION = "0.16.0";
5829
6131
  export {
5830
6132
  ALL_SDKS,
5831
6133
  APL_VALUES,
@@ -5842,6 +6144,7 @@ export {
5842
6144
  OHOS_DAILY_BUILDS_URL,
5843
6145
  OHOS_URL_BASE,
5844
6146
  OniroError,
6147
+ SIGNED_HAP_CHUNK_SIZE,
5845
6148
  SIGNING_ERRORS,
5846
6149
  SdkNotInstalledError,
5847
6150
  TMP_DIR_HINT,
@@ -5877,6 +6180,7 @@ export {
5877
6180
  dedupEntries,
5878
6181
  defaultContext,
5879
6182
  defaultPaths,
6183
+ defaultSignedHapPath,
5880
6184
  describeLayoutNode,
5881
6185
  detectAaStartFailure,
5882
6186
  detectCmdToolsFlavour,
@@ -5943,6 +6247,7 @@ export {
5943
6247
  getSupportedSdksForUi,
5944
6248
  harmonyOsAutoSign,
5945
6249
  harmonyOsSdkLabelForApi,
6250
+ harmonyOsSignHap,
5946
6251
  hdcClientFailure,
5947
6252
  hdcExec,
5948
6253
  hdcTargetArgs,
@@ -5996,6 +6301,7 @@ export {
5996
6301
  prepareSigning,
5997
6302
  pruneLayout,
5998
6303
  readHapBundleName,
6304
+ readHapSigningInfo,
5999
6305
  readJson5File,
6000
6306
  readJsonFile,
6001
6307
  readProvisionProfile,
@@ -6023,6 +6329,7 @@ export {
6023
6329
  setHilogLevel,
6024
6330
  shell,
6025
6331
  shellChecked,
6332
+ signOpenHarmonyHap,
6026
6333
  spawnCompat,
6027
6334
  startEmulator,
6028
6335
  staticConfig,
@@ -6042,6 +6349,7 @@ export {
6042
6349
  waitForTarget,
6043
6350
  watchLog,
6044
6351
  waypointsToRawEvents,
6352
+ withHapZip,
6045
6353
  withoutWindow,
6046
6354
  writeJson5File,
6047
6355
  writeJsonFile