@oniroproject/core 0.15.0 → 0.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.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
  }
@@ -2090,12 +2277,13 @@ async function listDevices(config, opts = {}) {
2090
2277
  });
2091
2278
  return parseDeviceList(res.stdout);
2092
2279
  }
2280
+ function isUsableDevice(device) {
2281
+ return device.status === "Connected" || device.status === "Unknown";
2282
+ }
2093
2283
  async function selectDevice(config, serial, opts = {}) {
2094
2284
  const resolved = resolveDeviceSerial(serial);
2095
2285
  if (resolved) return resolved;
2096
- const usable = (await listDevices(config, opts)).filter(
2097
- (d) => d.status === "Connected" || d.status === "Unknown"
2098
- );
2286
+ const usable = (await listDevices(config, opts)).filter(isUsableDevice);
2099
2287
  if (usable.length === 1) return usable[0].serial;
2100
2288
  if (usable.length === 0) {
2101
2289
  throw new OniroError("No connected device found. Connect a device/emulator or set ONIRO_DEVICE_SERIAL.");
@@ -2153,11 +2341,11 @@ async function paramSet(config, key, value, opts = {}) {
2153
2341
  }
2154
2342
 
2155
2343
  // src/hdc/files.ts
2156
- import * as path17 from "path";
2344
+ import * as path19 from "path";
2157
2345
  function normaliseLocalPath(local) {
2158
- const abs = path17.resolve(local);
2346
+ const abs = path19.resolve(local);
2159
2347
  const long = toLongPath(abs);
2160
- 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));
2161
2349
  }
2162
2350
  function assertDevicePath(remote) {
2163
2351
  if (/^[A-Za-z]:[\\/]/.test(remote)) {
@@ -2176,13 +2364,13 @@ async function sendFile(opts) {
2176
2364
  assertDevicePath(opts.remote);
2177
2365
  const local = normaliseLocalPath(opts.local);
2178
2366
  const sandbox = opts.bundle ? appSandboxDir(opts.bundle) : void 0;
2179
- 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;
2180
2368
  if (sandbox && !remote.startsWith(`${sandbox}/`)) {
2181
2369
  throw new OniroError(`${remote} is outside the sandbox of ${opts.bundle} (${sandbox}).`);
2182
2370
  }
2183
2371
  const device = { config: opts.config, deviceSerial: opts.deviceSerial, logger: opts.logger };
2184
2372
  if (sandbox) {
2185
- const mkdir = `mkdir -p ${shQuote(path17.posix.dirname(remote))}`;
2373
+ const mkdir = `mkdir -p ${shQuote(path19.posix.dirname(remote))}`;
2186
2374
  ensureRemoteOk(await shellChecked({ ...device, command: mkdir }), `hdc shell ${mkdir}`);
2187
2375
  }
2188
2376
  const res = await hdcExec({
@@ -2194,7 +2382,7 @@ async function sendFile(opts) {
2194
2382
  });
2195
2383
  ensureOk(res, `hdc file send ${local} ${remote}`);
2196
2384
  if (sandbox) {
2197
- 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]);
2198
2386
  const chown = `chown -R "$(stat -c %u:%g ${shQuote(sandbox)})" ${shQuote(top)}`;
2199
2387
  ensureRemoteOk(await shellChecked({ ...device, command: chown }), `hdc shell ${chown}`);
2200
2388
  }
@@ -2232,7 +2420,7 @@ async function getDisplaySize(config, opts = {}) {
2232
2420
 
2233
2421
  // src/hdc/wait.ts
2234
2422
  function delay(ms, signal) {
2235
- return new Promise((resolve7, reject) => {
2423
+ return new Promise((resolve9, reject) => {
2236
2424
  if (signal?.aborted) {
2237
2425
  reject(new CancelledError("Wait cancelled."));
2238
2426
  return;
@@ -2247,7 +2435,7 @@ function delay(ms, signal) {
2247
2435
  };
2248
2436
  const timer = setTimeout(() => {
2249
2437
  cleanup();
2250
- resolve7();
2438
+ resolve9();
2251
2439
  }, ms);
2252
2440
  signal?.addEventListener("abort", onAbort, { once: true });
2253
2441
  });
@@ -2276,18 +2464,18 @@ async function waitForCondition(opts) {
2276
2464
  }
2277
2465
 
2278
2466
  // src/hdc/app.ts
2279
- import * as fs19 from "fs";
2280
- import * as path20 from "path";
2467
+ import * as fs21 from "fs";
2468
+ import * as path22 from "path";
2281
2469
 
2282
2470
  // src/build/discoverHaps.ts
2283
- import * as fs17 from "fs";
2284
- import * as path18 from "path";
2471
+ import * as fs19 from "fs";
2472
+ import * as path20 from "path";
2285
2473
  function moduleOfHap(hapFileName) {
2286
2474
  const dash = hapFileName.indexOf("-");
2287
2475
  return dash > 0 ? hapFileName.slice(0, dash) : hapFileName.replace(/\.hap$/i, "");
2288
2476
  }
2289
2477
  function isAppProjectRoot(dir) {
2290
- return fs17.existsSync(path18.join(dir, "AppScope", "app.json5"));
2478
+ return fs19.existsSync(path20.join(dir, "AppScope", "app.json5"));
2291
2479
  }
2292
2480
  async function discoverHaps(opts) {
2293
2481
  const { projectDir } = opts;
@@ -2296,9 +2484,9 @@ async function discoverHaps(opts) {
2296
2484
  const result = {};
2297
2485
  const walk = async (dir, depth) => {
2298
2486
  if (depth > maxDepth) return;
2299
- const entries = await fs17.promises.readdir(dir, { withFileTypes: true }).catch(() => []);
2487
+ const entries = await fs19.promises.readdir(dir, { withFileTypes: true }).catch(() => []);
2300
2488
  for (const e of entries) {
2301
- const full = path18.join(dir, e.name);
2489
+ const full = path20.join(dir, e.name);
2302
2490
  if (e.isDirectory()) {
2303
2491
  if (e.name === "node_modules" || e.name === "oh_modules" || e.name === ".git") continue;
2304
2492
  if (skipNestedProjects && isAppProjectRoot(full)) continue;
@@ -2312,7 +2500,7 @@ async function discoverHaps(opts) {
2312
2500
  }
2313
2501
  };
2314
2502
  await walk(projectDir, 0);
2315
- const mtime = (p) => fs17.statSync(p).mtimeMs;
2503
+ const mtime = (p) => fs19.statSync(p).mtimeMs;
2316
2504
  for (const bucket of Object.values(result)) {
2317
2505
  bucket.signed.sort((a, b) => mtime(b) - mtime(a));
2318
2506
  bucket.unsigned.sort((a, b) => mtime(b) - mtime(a));
@@ -2338,16 +2526,16 @@ function pickHap(haps, module) {
2338
2526
  }
2339
2527
 
2340
2528
  // src/hdc/project.ts
2341
- import * as fs18 from "fs";
2342
- import * as path19 from "path";
2529
+ import * as fs20 from "fs";
2530
+ import * as path21 from "path";
2343
2531
  import JSON53 from "json5";
2344
2532
  function readJson5(filePath) {
2345
- const content = fs18.readFileSync(filePath, "utf-8");
2533
+ const content = fs20.readFileSync(filePath, "utf-8");
2346
2534
  return JSON53.parse(content);
2347
2535
  }
2348
2536
  function getBundleName(projectDir) {
2349
- const appJsonPath = path19.join(projectDir, "AppScope", "app.json5");
2350
- if (!fs18.existsSync(appJsonPath)) {
2537
+ const appJsonPath = path21.join(projectDir, "AppScope", "app.json5");
2538
+ if (!fs20.existsSync(appJsonPath)) {
2351
2539
  throw new OniroError(`Could not find app.json5 at ${appJsonPath}`);
2352
2540
  }
2353
2541
  const appJson = readJson5(appJsonPath);
@@ -2357,8 +2545,8 @@ function getBundleName(projectDir) {
2357
2545
  return appJson.app.bundleName;
2358
2546
  }
2359
2547
  function getMainAbility(projectDir, moduleName = "entry", abilityName) {
2360
- const moduleJsonPath = path19.join(projectDir, moduleName, "src", "main", "module.json5");
2361
- if (!fs18.existsSync(moduleJsonPath)) {
2548
+ const moduleJsonPath = path21.join(projectDir, moduleName, "src", "main", "module.json5");
2549
+ if (!fs20.existsSync(moduleJsonPath)) {
2362
2550
  throw new OniroError(`Could not find module.json5 at ${moduleJsonPath}`);
2363
2551
  }
2364
2552
  const moduleJson = readJson5(moduleJsonPath);
@@ -2382,7 +2570,7 @@ function detectInstallFailure(output) {
2382
2570
  return /\[Fail\]|msg:\s*error|error:\s*failed to install|code:\s*95\d{5}/i.test(output) ? output.trim() : null;
2383
2571
  }
2384
2572
  function isUnsignedHap(hapPath) {
2385
- return path20.basename(hapPath).includes("unsigned");
2573
+ return path22.basename(hapPath).includes("unsigned");
2386
2574
  }
2387
2575
  function installFailureReason(output) {
2388
2576
  const code = /code:\s*(\d+)/i.exec(output);
@@ -2391,7 +2579,7 @@ function installFailureReason(output) {
2391
2579
  return message ? `code ${code[1]}: ${message}` : `code ${code[1]}`;
2392
2580
  }
2393
2581
  function unsignedHapRejection(output, hap) {
2394
- const name = path20.basename(hap.path);
2582
+ const name = path22.basename(hap.path);
2395
2583
  const reason = installFailureReason(output);
2396
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}).`;
2397
2585
  if (!/9568320|signature|pkcs7/i.test(reason)) return what;
@@ -2409,24 +2597,24 @@ ${result.stderr}`;
2409
2597
  return result;
2410
2598
  }
2411
2599
  function existingHapOrUnsigned(hapPath) {
2412
- if (fs19.existsSync(hapPath)) return { path: hapPath };
2600
+ if (fs21.existsSync(hapPath)) return { path: hapPath };
2413
2601
  const sibling = hapPath.replace(/-signed\.hap$/i, "-unsigned.hap");
2414
- 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;
2415
2603
  }
2416
2604
  async function resolveInstallHap(opts, logger) {
2417
2605
  const warnIfFallback = (hap) => {
2418
- 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)}.`);
2419
2607
  return hap;
2420
2608
  };
2421
2609
  const configured = opts.hapPath ?? opts.config.get("hapPath", "");
2422
2610
  const relative3 = configured || defaultPaths.hapPath;
2423
- const hapPath = path20.isAbsolute(relative3) ? relative3 : path20.join(opts.projectDir, relative3);
2611
+ const hapPath = path22.isAbsolute(relative3) ? relative3 : path22.join(opts.projectDir, relative3);
2424
2612
  if (configured) {
2425
2613
  const hap = existingHapOrUnsigned(hapPath);
2426
2614
  if (!hap) throw new OniroError(`HAP file not found at: ${hapPath}. Build the app first.`);
2427
2615
  return warnIfFallback(hap);
2428
2616
  }
2429
- if (fs19.existsSync(hapPath)) return { path: hapPath };
2617
+ if (fs21.existsSync(hapPath)) return { path: hapPath };
2430
2618
  const haps = await discoverHaps({ projectDir: opts.projectDir });
2431
2619
  const anySigned = Object.values(haps).some((m) => m.signed.length > 0);
2432
2620
  const entryUnsigned = anySigned ? void 0 : existingHapOrUnsigned(hapPath);
@@ -2537,7 +2725,7 @@ function listRunningProcesses(config, options = {}) {
2537
2725
  const logger = options.logger ?? noopLogger;
2538
2726
  const timeoutMs = options.timeoutMs ?? 1e3;
2539
2727
  const hdc = getHdcPath(config);
2540
- return new Promise((resolve7, reject) => {
2728
+ return new Promise((resolve9, reject) => {
2541
2729
  let proc;
2542
2730
  try {
2543
2731
  proc = spawnCompat(hdc, [...hdcTargetArgs(options.deviceSerial), "track-jpid"]);
@@ -2557,7 +2745,7 @@ function listRunningProcesses(config, options = {}) {
2557
2745
  if (options.targetProcessName && name === options.targetProcessName && !resolved) {
2558
2746
  resolved = true;
2559
2747
  killProcessTree(proc);
2560
- resolve7(pid);
2748
+ resolve9(pid);
2561
2749
  return;
2562
2750
  }
2563
2751
  }
@@ -2578,7 +2766,7 @@ function listRunningProcesses(config, options = {}) {
2578
2766
  if (options.targetProcessName) {
2579
2767
  reject(new OniroError(`Could not find process for bundle: ${options.targetProcessName}`));
2580
2768
  } else {
2581
- resolve7(processes);
2769
+ resolve9(processes);
2582
2770
  }
2583
2771
  }, timeoutMs);
2584
2772
  proc.on("close", () => {
@@ -2588,7 +2776,7 @@ function listRunningProcesses(config, options = {}) {
2588
2776
  if (options.targetProcessName) {
2589
2777
  reject(new OniroError(`Could not find process for bundle: ${options.targetProcessName}`));
2590
2778
  } else {
2591
- resolve7(processes);
2779
+ resolve9(processes);
2592
2780
  }
2593
2781
  });
2594
2782
  });
@@ -2757,12 +2945,12 @@ async function reboot(opts) {
2757
2945
  }
2758
2946
 
2759
2947
  // src/hdc/screen.ts
2760
- import * as fs20 from "fs";
2761
- import * as os9 from "os";
2762
- import * as path21 from "path";
2763
- 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";
2764
2952
  function tmpName(ext) {
2765
- 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}`);
2766
2954
  }
2767
2955
  async function takeScreenshot(opts) {
2768
2956
  const remote = "/data/local/tmp/oniro_screenshot.jpeg";
@@ -2772,11 +2960,11 @@ async function takeScreenshot(opts) {
2772
2960
  const local = tmpName("jpeg");
2773
2961
  try {
2774
2962
  await recvFile({ config: opts.config, remote, local, deviceSerial: opts.deviceSerial, timeoutMs });
2775
- const pixels = await fs20.promises.readFile(local);
2963
+ const pixels = await fs22.promises.readFile(local);
2776
2964
  const size = await getDisplaySize(opts.config, { deviceSerial: opts.deviceSerial }) ?? { width: 0, height: 0 };
2777
2965
  return { pixels, width: size.width, height: size.height };
2778
2966
  } finally {
2779
- fs20.promises.unlink(local).catch(() => {
2967
+ fs22.promises.unlink(local).catch(() => {
2780
2968
  });
2781
2969
  void shell({ config: opts.config, command: `rm -f ${remote}`, deviceSerial: opts.deviceSerial }).catch(() => {
2782
2970
  });
@@ -2784,7 +2972,7 @@ async function takeScreenshot(opts) {
2784
2972
  }
2785
2973
  async function captureBurst(opts) {
2786
2974
  const count = Math.max(1, Math.floor(opts.count));
2787
- const remoteDir = `/data/local/tmp/oniro_burst_${randomBytes2(6).toString("hex")}`;
2975
+ const remoteDir = `/data/local/tmp/oniro_burst_${randomBytes3(6).toString("hex")}`;
2788
2976
  const sleepSec = opts.intervalMs > 0 ? (opts.intervalMs / 1e3).toFixed(3) : "0";
2789
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`;
2790
2978
  const cap = await shell({ config: opts.config, command: loop, deviceSerial: opts.deviceSerial, timeoutMs: opts.timeoutMs ?? 12e4 });
@@ -2795,9 +2983,9 @@ async function captureBurst(opts) {
2795
2983
  const local = tmpName(`burst_${i}.jpeg`);
2796
2984
  try {
2797
2985
  await recvFile({ config: opts.config, remote: `${remoteDir}/f${i}.jpeg`, local, deviceSerial: opts.deviceSerial });
2798
- frames.push(await fs20.promises.readFile(local));
2986
+ frames.push(await fs22.promises.readFile(local));
2799
2987
  } finally {
2800
- fs20.promises.unlink(local).catch(() => {
2988
+ fs22.promises.unlink(local).catch(() => {
2801
2989
  });
2802
2990
  }
2803
2991
  }
@@ -2972,10 +3160,10 @@ async function sendRawTouch(opts) {
2972
3160
  }
2973
3161
 
2974
3162
  // src/hdc/dump.ts
2975
- import * as fs21 from "fs";
2976
- import * as os10 from "os";
2977
- import * as path22 from "path";
2978
- 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";
2979
3167
 
2980
3168
  // src/hdc/ime.ts
2981
3169
  var INPUT_METHOD_SA_ID = 3703;
@@ -3237,10 +3425,10 @@ async function dumpLayout(opts) {
3237
3425
  if (!m) throw new OniroError(`Could not parse dumpLayout output:
3238
3426
  ${dump.stdout}`);
3239
3427
  const remote = m[1];
3240
- 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`);
3241
3429
  try {
3242
3430
  await recvFile({ config: opts.config, remote, local, deviceSerial: opts.deviceSerial, timeoutMs });
3243
- const parsed = JSON.parse(await fs21.promises.readFile(local, "utf8"));
3431
+ const parsed = JSON.parse(await fs23.promises.readFile(local, "utf8"));
3244
3432
  const display = await getDisplaySize(opts.config, { deviceSerial: opts.deviceSerial });
3245
3433
  if (opts.prune === false) {
3246
3434
  return { display, tree: parsed };
@@ -3253,7 +3441,7 @@ ${dump.stdout}`);
3253
3441
  const keyboard = kb ? { shown: true, bundle: kb.bundle, ...kb.b ? { b: kb.b } : {} } : { shown: false };
3254
3442
  return { display, tree, keyboard };
3255
3443
  } finally {
3256
- fs21.promises.unlink(local).catch(() => {
3444
+ fs23.promises.unlink(local).catch(() => {
3257
3445
  });
3258
3446
  void shell({ config: opts.config, command: `rm -f ${remote}`, deviceSerial: opts.deviceSerial }).catch(() => {
3259
3447
  });
@@ -3263,7 +3451,7 @@ ${dump.stdout}`);
3263
3451
  // src/hdc/uiActions.ts
3264
3452
  var FOCUS_SETTLE_MS = 400;
3265
3453
  var KEYBOARD_SETTLE_MS = 400;
3266
- var sleep = (ms) => new Promise((resolve7) => setTimeout(resolve7, ms));
3454
+ var sleep = (ms) => new Promise((resolve9) => setTimeout(resolve9, ms));
3267
3455
  function targetTree(layout) {
3268
3456
  const kb = layout.keyboard;
3269
3457
  return kb?.shown && kb.bundle ? withoutWindow(layout.tree, kb.bundle) : layout.tree;
@@ -3416,11 +3604,11 @@ async function waitForTarget(opts) {
3416
3604
  }
3417
3605
 
3418
3606
  // src/hdc/hilog.ts
3419
- import * as fs22 from "fs";
3607
+ import * as fs24 from "fs";
3420
3608
  function setHilogLevel(opts) {
3421
3609
  const logger = opts.logger ?? noopLogger;
3422
3610
  const hdc = getHdcPath(opts.config);
3423
- return new Promise((resolve7, reject) => {
3611
+ return new Promise((resolve9, reject) => {
3424
3612
  let child;
3425
3613
  try {
3426
3614
  child = spawnCompat(hdc, [...hdcTargetArgs(opts.deviceSerial), "shell", "hilog", "-b", opts.level]);
@@ -3440,7 +3628,7 @@ function setHilogLevel(opts) {
3440
3628
  });
3441
3629
  child.once("close", (code) => {
3442
3630
  if (code === 0) {
3443
- resolve7();
3631
+ resolve9();
3444
3632
  } else {
3445
3633
  const msg = stderr.trim() || `hdc shell hilog -b ${opts.level} exited with code ${code}`;
3446
3634
  logger.error(`[hilog] ${msg}`);
@@ -3542,7 +3730,7 @@ async function recentEntryFilter(opts) {
3542
3730
  };
3543
3731
  }
3544
3732
  function waitForLog(opts) {
3545
- return new Promise((resolve7, reject) => {
3733
+ return new Promise((resolve9, reject) => {
3546
3734
  const deadline = Date.now() + opts.timeoutMs;
3547
3735
  let settled = false;
3548
3736
  let child = null;
@@ -3563,7 +3751,7 @@ function waitForLog(opts) {
3563
3751
  if (settled) return;
3564
3752
  settled = true;
3565
3753
  cleanup();
3566
- resolve7(entry);
3754
+ resolve9(entry);
3567
3755
  };
3568
3756
  const fail = (err) => {
3569
3757
  if (settled) return;
@@ -3618,7 +3806,7 @@ function waitForLog(opts) {
3618
3806
  });
3619
3807
  }
3620
3808
  function watchLog(opts) {
3621
- return new Promise((resolve7, reject) => {
3809
+ return new Promise((resolve9, reject) => {
3622
3810
  const matches = [];
3623
3811
  let settled = false;
3624
3812
  let child = null;
@@ -3637,7 +3825,7 @@ function watchLog(opts) {
3637
3825
  if (settled) return;
3638
3826
  settled = true;
3639
3827
  cleanup();
3640
- resolve7(opts.dedup === false ? matches : dedupEntries(matches));
3828
+ resolve9(opts.dedup === false ? matches : dedupEntries(matches));
3641
3829
  };
3642
3830
  const onAbort = () => {
3643
3831
  if (settled) return;
@@ -3682,11 +3870,11 @@ async function captureLog(opts) {
3682
3870
  const res = await shell({ config: opts.config, command: "hilog -r", deviceSerial: opts.deviceSerial, timeoutMs: 1e4, logger });
3683
3871
  ensureOk(res, "hdc shell hilog -r");
3684
3872
  }
3685
- const fd = fs22.openSync(opts.file, "a");
3873
+ const fd = fs24.openSync(opts.file, "a");
3686
3874
  let lines = 0;
3687
3875
  let reconnects = 0;
3688
3876
  try {
3689
- await new Promise((resolve7, reject) => {
3877
+ await new Promise((resolve9, reject) => {
3690
3878
  const deadline = opts.durationMs !== void 0 ? Date.now() + opts.durationMs : Infinity;
3691
3879
  let settled = false;
3692
3880
  let child = null;
@@ -3695,7 +3883,7 @@ async function captureLog(opts) {
3695
3883
  let open = true;
3696
3884
  const write = (line) => {
3697
3885
  if (!open || opts.pattern && !opts.pattern.test(line)) return;
3698
- fs22.writeSync(fd, `${line}
3886
+ fs24.writeSync(fd, `${line}
3699
3887
  `);
3700
3888
  lines++;
3701
3889
  };
@@ -3716,7 +3904,7 @@ async function captureLog(opts) {
3716
3904
  if (settled) return;
3717
3905
  settled = true;
3718
3906
  cleanup();
3719
- resolve7();
3907
+ resolve9();
3720
3908
  }
3721
3909
  const fail = (err) => {
3722
3910
  if (settled) return;
@@ -3760,7 +3948,7 @@ async function captureLog(opts) {
3760
3948
  spawnStream();
3761
3949
  });
3762
3950
  } finally {
3763
- fs22.closeSync(fd);
3951
+ fs24.closeSync(fd);
3764
3952
  }
3765
3953
  return { file: opts.file, lines, reconnects };
3766
3954
  }
@@ -3788,17 +3976,17 @@ async function dumpLog(opts) {
3788
3976
  }
3789
3977
 
3790
3978
  // src/build/runHvigorw.ts
3791
- import * as fs24 from "fs";
3792
- import * as path24 from "path";
3979
+ import * as fs26 from "fs";
3980
+ import * as path26 from "path";
3793
3981
 
3794
3982
  // src/harmonyos/target.ts
3795
- import * as fs23 from "fs";
3796
- import * as path23 from "path";
3983
+ import * as fs25 from "fs";
3984
+ import * as path25 from "path";
3797
3985
  import JSON54 from "json5";
3798
3986
  function detectRuntimeOs(projectDir, productName) {
3799
3987
  let products = [];
3800
3988
  try {
3801
- 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"));
3802
3990
  if (Array.isArray(parsed.app?.products)) products = parsed.app.products;
3803
3991
  } catch {
3804
3992
  return "OpenHarmony";
@@ -3839,7 +4027,7 @@ function runHvigorw(opts) {
3839
4027
  const { config } = opts;
3840
4028
  const projectDir = toLongPath(opts.projectDir);
3841
4029
  const logger = opts.logger ?? noopLogger;
3842
- if (!fs24.existsSync(path24.join(projectDir, "build-profile.json5"))) {
4030
+ if (!fs26.existsSync(path26.join(projectDir, "build-profile.json5"))) {
3843
4031
  throw new OniroError(`Not an OpenHarmony project: ${projectDir} (build-profile.json5 not found).`);
3844
4032
  }
3845
4033
  let hvigorw;
@@ -3851,24 +4039,24 @@ function runHvigorw(opts) {
3851
4039
  hvigorw = resolved.startsWith(projectDir) ? resolved : findHarmonyOsTool(sdk, "hvigorw") ?? resolved;
3852
4040
  logger.info(`[build] Building for HarmonyOS against the SDK at ${sdk.sdkPath}.`);
3853
4041
  } else {
3854
- if (!fs24.existsSync(getCmdToolsPath(config))) {
4042
+ if (!fs26.existsSync(getCmdToolsPath(config))) {
3855
4043
  throw new CmdToolsNotInstalledError(getCmdToolsPath(config));
3856
4044
  }
3857
4045
  sdkEnv = { OHOS_BASE_SDK_HOME: getOhosBaseSdkHome(config) };
3858
4046
  hvigorw = getHvigorwPath(config, projectDir);
3859
4047
  }
3860
- if (!fs24.existsSync(hvigorw)) {
4048
+ if (!fs26.existsSync(hvigorw)) {
3861
4049
  throw new OniroError(`hvigorw wrapper not found at ${hvigorw}.`);
3862
4050
  }
3863
4051
  const args = buildHvigorwArgs(opts);
3864
4052
  if (process.platform !== "win32") {
3865
4053
  try {
3866
- fs24.chmodSync(hvigorw, 493);
4054
+ fs26.chmodSync(hvigorw, 493);
3867
4055
  } catch {
3868
4056
  }
3869
4057
  }
3870
4058
  const env = { ...process.env, ...sdkEnv };
3871
- return new Promise((resolve7, reject) => {
4059
+ return new Promise((resolve9, reject) => {
3872
4060
  if (opts.abortSignal?.aborted) {
3873
4061
  reject(new CancelledError("hvigorw build cancelled."));
3874
4062
  return;
@@ -3912,15 +4100,15 @@ function runHvigorw(opts) {
3912
4100
  settled = true;
3913
4101
  cleanup();
3914
4102
  const exitCode = code ?? 1;
3915
- if (exitCode === 0) resolve7({ exitCode });
4103
+ if (exitCode === 0) resolve9({ exitCode });
3916
4104
  else reject(new OniroError(`hvigorw exited with code ${exitCode}`));
3917
4105
  });
3918
4106
  });
3919
4107
  }
3920
4108
 
3921
4109
  // src/build/ohpm.ts
3922
- import * as fs25 from "fs";
3923
- import * as path25 from "path";
4110
+ import * as fs27 from "fs";
4111
+ import * as path27 from "path";
3924
4112
  function runOhpm(opts) {
3925
4113
  return runProcess({
3926
4114
  command: opts.ohpmPath ?? getOhpmPath(opts.config),
@@ -3933,7 +4121,7 @@ function runOhpm(opts) {
3933
4121
  });
3934
4122
  }
3935
4123
  async function ensureOhModules(opts) {
3936
- if (fs25.existsSync(path25.join(opts.projectDir, "oh_modules"))) {
4124
+ if (fs27.existsSync(path27.join(opts.projectDir, "oh_modules"))) {
3937
4125
  return { installed: false };
3938
4126
  }
3939
4127
  const res = await runOhpm({
@@ -3950,8 +4138,8 @@ async function ensureOhModules(opts) {
3950
4138
  }
3951
4139
 
3952
4140
  // src/build/codelinter.ts
3953
- import * as fs26 from "fs";
3954
- import * as path26 from "path";
4141
+ import * as fs28 from "fs";
4142
+ import * as path28 from "path";
3955
4143
  var FINDING_RE = /^(.*?):(\d+):(?:(\d+):)?\s*(error|warning|warn|suggestion|info)\b[:\s-]*(.*)$/i;
3956
4144
  var TRAILING_CODE_RE = /[([]([\w@/.\-]+)[)\]]\s*$/;
3957
4145
  var FILE_HEADER_RE = /^(.+\.\w+)\((\d+)\)$/;
@@ -4041,10 +4229,10 @@ function parseCodelinterFindings(output) {
4041
4229
  var PROJECT_ROOT_FILES = [
4042
4230
  ["build-profile.json5"],
4043
4231
  ["hvigorfile.ts", "hvigorfile.js"],
4044
- [path26.join("hvigor", "hvigor-config.json5")]
4232
+ [path28.join("hvigor", "hvigor-config.json5")]
4045
4233
  ];
4046
4234
  function missingProjectRootFiles(projectDir) {
4047
- 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(
4048
4236
  (names) => names[0]
4049
4237
  );
4050
4238
  }
@@ -4126,32 +4314,22 @@ ${hint}`, err);
4126
4314
  return { exitCode, durationMs: Date.now() - start, discoveredHaps, warnings };
4127
4315
  }
4128
4316
 
4129
- // src/project/validators.ts
4130
- function isValidProjectName(name) {
4131
- if (!name) return false;
4132
- if (name.includes("/") || name.includes("\\")) return false;
4133
- return /^[A-Za-z0-9._-]+$/.test(name);
4134
- }
4135
- function isValidBundleName(bundleName) {
4136
- return /^[A-Za-z][A-Za-z0-9_]*(\.[A-Za-z][A-Za-z0-9_]*)+$/.test(bundleName);
4137
- }
4138
-
4139
4317
  // src/project/templates.ts
4140
- import * as fs27 from "fs";
4141
- import * as path27 from "path";
4318
+ import * as fs29 from "fs";
4319
+ import * as path29 from "path";
4142
4320
  function toHumanTemplateName(folderName) {
4143
4321
  return folderName.replace(/([a-z])([A-Z])/g, "$1 $2");
4144
4322
  }
4145
4323
  function listTemplates(templateRoot) {
4146
- if (!fs27.existsSync(templateRoot)) return [];
4147
- const entries = fs27.readdirSync(templateRoot, { withFileTypes: true });
4324
+ if (!fs29.existsSync(templateRoot)) return [];
4325
+ const entries = fs29.readdirSync(templateRoot, { withFileTypes: true });
4148
4326
  return entries.filter((e) => e.isDirectory()).map((e) => {
4149
- const dir = path27.join(templateRoot, e.name);
4150
- const metaPath = path27.join(dir, "template.json");
4327
+ const dir = path29.join(templateRoot, e.name);
4328
+ const metaPath = path29.join(dir, "template.json");
4151
4329
  let meta = {};
4152
- if (fs27.existsSync(metaPath)) {
4330
+ if (fs29.existsSync(metaPath)) {
4153
4331
  try {
4154
- meta = JSON.parse(fs27.readFileSync(metaPath, "utf8"));
4332
+ meta = JSON.parse(fs29.readFileSync(metaPath, "utf8"));
4155
4333
  } catch {
4156
4334
  }
4157
4335
  }
@@ -4166,57 +4344,57 @@ function listTemplates(templateRoot) {
4166
4344
  function validateTemplateLayout(templateDir, defaultModuleName) {
4167
4345
  const required = [
4168
4346
  "build-profile.json5",
4169
- path27.join("AppScope", "app.json5"),
4170
- path27.join(defaultModuleName, "src", "main", "module.json5"),
4171
- 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"),
4172
4350
  "hvigorfile.ts"
4173
4351
  ];
4174
- return required.filter((rel) => !fs27.existsSync(path27.join(templateDir, rel)));
4352
+ return required.filter((rel) => !fs29.existsSync(path29.join(templateDir, rel)));
4175
4353
  }
4176
4354
 
4177
4355
  // src/project/createScaffold.ts
4178
- import * as fs29 from "fs";
4179
- import * as path28 from "path";
4356
+ import * as fs31 from "fs";
4357
+ import * as path30 from "path";
4180
4358
 
4181
4359
  // src/project/jsonHelpers.ts
4182
- import * as fs28 from "fs";
4360
+ import * as fs30 from "fs";
4183
4361
  import JSON55 from "json5";
4184
4362
  function readJson5File(filePath) {
4185
- return JSON55.parse(fs28.readFileSync(filePath, "utf8"));
4363
+ return JSON55.parse(fs30.readFileSync(filePath, "utf8"));
4186
4364
  }
4187
4365
  function writeJson5File(filePath, value) {
4188
- fs28.writeFileSync(filePath, JSON.stringify(value, null, 2) + "\n", "utf8");
4366
+ fs30.writeFileSync(filePath, JSON.stringify(value, null, 2) + "\n", "utf8");
4189
4367
  }
4190
4368
  function readJsonFile(filePath) {
4191
- return JSON.parse(fs28.readFileSync(filePath, "utf8"));
4369
+ return JSON.parse(fs30.readFileSync(filePath, "utf8"));
4192
4370
  }
4193
4371
  function writeJsonFile(filePath, value) {
4194
- fs28.writeFileSync(filePath, JSON.stringify(value, null, 2) + "\n", "utf8");
4372
+ fs30.writeFileSync(filePath, JSON.stringify(value, null, 2) + "\n", "utf8");
4195
4373
  }
4196
4374
 
4197
4375
  // src/project/createScaffold.ts
4198
4376
  var IGNORED_DIRS = /* @__PURE__ */ new Set(["oh_modules", "node_modules", "build", ".hvigor"]);
4199
4377
  async function pathExists(p) {
4200
4378
  try {
4201
- await fs29.promises.access(p);
4379
+ await fs31.promises.access(p);
4202
4380
  return true;
4203
4381
  } catch {
4204
4382
  return false;
4205
4383
  }
4206
4384
  }
4207
4385
  async function copyDirRecursive(srcDir, destDir) {
4208
- await fs29.promises.mkdir(destDir, { recursive: true });
4209
- 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 });
4210
4388
  for (const entry of entries) {
4211
- const src = path28.join(srcDir, entry.name);
4212
- const dest = path28.join(destDir, entry.name);
4389
+ const src = path30.join(srcDir, entry.name);
4390
+ const dest = path30.join(destDir, entry.name);
4213
4391
  if (entry.isDirectory()) {
4214
4392
  await copyDirRecursive(src, dest);
4215
4393
  } else if (entry.isSymbolicLink()) {
4216
4394
  continue;
4217
4395
  } else if (entry.isFile()) {
4218
4396
  if (entry.name === "template.json") continue;
4219
- await fs29.promises.copyFile(src, dest);
4397
+ await fs31.promises.copyFile(src, dest);
4220
4398
  }
4221
4399
  }
4222
4400
  }
@@ -4224,9 +4402,9 @@ async function normalizeJson5ToJson(projectDir, logger) {
4224
4402
  const stack = [projectDir];
4225
4403
  while (stack.length > 0) {
4226
4404
  const currentDir = stack.pop();
4227
- const entries = await fs29.promises.readdir(currentDir, { withFileTypes: true });
4405
+ const entries = await fs31.promises.readdir(currentDir, { withFileTypes: true });
4228
4406
  for (const entry of entries) {
4229
- const fullPath = path28.join(currentDir, entry.name);
4407
+ const fullPath = path30.join(currentDir, entry.name);
4230
4408
  if (entry.isDirectory()) {
4231
4409
  if (IGNORED_DIRS.has(entry.name)) continue;
4232
4410
  stack.push(fullPath);
@@ -4248,23 +4426,23 @@ function toJavaPropertiesPath(p) {
4248
4426
  function createOrUpdateLocalProperties(projectDir, sdkDir) {
4249
4427
  const content = `sdk.dir=${toJavaPropertiesPath(sdkDir)}
4250
4428
  `;
4251
- fs29.writeFileSync(path28.join(projectDir, "local.properties"), content, "utf8");
4429
+ fs31.writeFileSync(path30.join(projectDir, "local.properties"), content, "utf8");
4252
4430
  }
4253
4431
  function renameIfExists(fromPath, toPath) {
4254
- if (fs29.existsSync(fromPath) && fromPath !== toPath) {
4255
- fs29.renameSync(fromPath, toPath);
4432
+ if (fs31.existsSync(fromPath) && fromPath !== toPath) {
4433
+ fs31.renameSync(fromPath, toPath);
4256
4434
  }
4257
4435
  }
4258
4436
  function updateTemplateConfigs(projectDir, args, sdkBaseDir) {
4259
- const appJsonPath = path28.join(projectDir, "AppScope", "app.json5");
4260
- if (fs29.existsSync(appJsonPath)) {
4437
+ const appJsonPath = path30.join(projectDir, "AppScope", "app.json5");
4438
+ if (fs31.existsSync(appJsonPath)) {
4261
4439
  const appJson = readJson5File(appJsonPath);
4262
4440
  appJson.app = appJson.app ?? {};
4263
4441
  appJson.app.bundleName = args.bundleName;
4264
4442
  writeJson5File(appJsonPath, appJson);
4265
4443
  }
4266
- const stringsPath = path28.join(projectDir, "AppScope", "resources", "base", "element", "string.json");
4267
- if (fs29.existsSync(stringsPath)) {
4444
+ const stringsPath = path30.join(projectDir, "AppScope", "resources", "base", "element", "string.json");
4445
+ if (fs31.existsSync(stringsPath)) {
4268
4446
  const strings = readJsonFile(stringsPath);
4269
4447
  if (Array.isArray(strings.string)) {
4270
4448
  const appName = strings.string.find((s) => s.name === "app_name");
@@ -4272,8 +4450,8 @@ function updateTemplateConfigs(projectDir, args, sdkBaseDir) {
4272
4450
  }
4273
4451
  writeJsonFile(stringsPath, strings);
4274
4452
  }
4275
- const buildProfilePath = path28.join(projectDir, "build-profile.json5");
4276
- if (fs29.existsSync(buildProfilePath)) {
4453
+ const buildProfilePath = path30.join(projectDir, "build-profile.json5");
4454
+ if (fs31.existsSync(buildProfilePath)) {
4277
4455
  const buildProfile = readJson5File(buildProfilePath);
4278
4456
  buildProfile.app = buildProfile.app ?? {};
4279
4457
  if (Array.isArray(buildProfile.app.products) && buildProfile.app.products.length > 0) {
@@ -4286,23 +4464,23 @@ function updateTemplateConfigs(projectDir, args, sdkBaseDir) {
4286
4464
  }
4287
4465
  writeJson5File(buildProfilePath, buildProfile);
4288
4466
  }
4289
- const moduleJsonPath = path28.join(projectDir, args.moduleName, "src", "main", "module.json5");
4290
- if (fs29.existsSync(moduleJsonPath)) {
4467
+ const moduleJsonPath = path30.join(projectDir, args.moduleName, "src", "main", "module.json5");
4468
+ if (fs31.existsSync(moduleJsonPath)) {
4291
4469
  const moduleJson = readJson5File(moduleJsonPath);
4292
4470
  moduleJson.module = moduleJson.module ?? {};
4293
4471
  moduleJson.module.name = args.moduleName;
4294
4472
  writeJson5File(moduleJsonPath, moduleJson);
4295
4473
  }
4296
- const moduleOhPackagePath = path28.join(projectDir, args.moduleName, "oh-package.json5");
4297
- if (fs29.existsSync(moduleOhPackagePath)) {
4474
+ const moduleOhPackagePath = path30.join(projectDir, args.moduleName, "oh-package.json5");
4475
+ if (fs31.existsSync(moduleOhPackagePath)) {
4298
4476
  const pkg = readJson5File(moduleOhPackagePath);
4299
4477
  pkg.name = args.moduleName;
4300
4478
  writeJson5File(moduleOhPackagePath, pkg);
4301
4479
  }
4302
- const vscodeDir = path28.join(projectDir, ".vscode");
4303
- fs29.mkdirSync(vscodeDir, { recursive: true });
4480
+ const vscodeDir = path30.join(projectDir, ".vscode");
4481
+ fs31.mkdirSync(vscodeDir, { recursive: true });
4304
4482
  const hapPath = `${args.moduleName}/build/default/outputs/default/${args.moduleName}-default-signed.hap`;
4305
- writeJsonFile(path28.join(vscodeDir, "settings.json"), {
4483
+ writeJsonFile(path30.join(vscodeDir, "settings.json"), {
4306
4484
  "oniro.hapPath": hapPath,
4307
4485
  "files.associations": { "*.json5": "jsonc" }
4308
4486
  });
@@ -4319,7 +4497,7 @@ async function createScaffold(opts) {
4319
4497
  if (!opts.location || !await pathExists(opts.location)) {
4320
4498
  throw new OniroError(`Location does not exist: ${opts.location}`);
4321
4499
  }
4322
- const templateDir = path28.join(opts.templateRoot, opts.templateId);
4500
+ const templateDir = path30.join(opts.templateRoot, opts.templateId);
4323
4501
  if (!await pathExists(templateDir)) {
4324
4502
  throw new OniroError(`Template not found: ${templateDir}`);
4325
4503
  }
@@ -4354,17 +4532,17 @@ async function createScaffold(opts) {
4354
4532
  }
4355
4533
  }
4356
4534
  }
4357
- const projectDir = path28.join(opts.location, opts.projectName);
4535
+ const projectDir = path30.join(opts.location, opts.projectName);
4358
4536
  if (await pathExists(projectDir)) {
4359
4537
  if (!opts.overwrite) {
4360
4538
  throw new OniroError(`Destination already exists: ${projectDir}. Pass overwrite: true to replace it.`);
4361
4539
  }
4362
- await fs29.promises.rm(projectDir, { recursive: true, force: true });
4540
+ await fs31.promises.rm(projectDir, { recursive: true, force: true });
4363
4541
  }
4364
4542
  logger.info(`[create] Scaffolding ${opts.templateId} at ${projectDir}`);
4365
4543
  await copyDirRecursive(templateDir, projectDir);
4366
4544
  if (moduleName !== defaultModuleName) {
4367
- renameIfExists(path28.join(projectDir, defaultModuleName), path28.join(projectDir, moduleName));
4545
+ renameIfExists(path30.join(projectDir, defaultModuleName), path30.join(projectDir, moduleName));
4368
4546
  }
4369
4547
  updateTemplateConfigs(
4370
4548
  projectDir,
@@ -4381,11 +4559,11 @@ async function createScaffold(opts) {
4381
4559
  }
4382
4560
 
4383
4561
  // src/project/listModules.ts
4384
- import * as fs30 from "fs";
4385
- import * as path29 from "path";
4562
+ import * as fs32 from "fs";
4563
+ import * as path31 from "path";
4386
4564
  function listModules(opts) {
4387
- const buildProfile = path29.join(opts.projectDir, "build-profile.json5");
4388
- if (!fs30.existsSync(buildProfile)) {
4565
+ const buildProfile = path31.join(opts.projectDir, "build-profile.json5");
4566
+ if (!fs32.existsSync(buildProfile)) {
4389
4567
  throw new OniroError(`build-profile.json5 not found at ${buildProfile}`);
4390
4568
  }
4391
4569
  const parsed = readJson5File(buildProfile);
@@ -4397,12 +4575,12 @@ function listModules(opts) {
4397
4575
  }
4398
4576
 
4399
4577
  // src/project/abilities.ts
4400
- import * as fs31 from "fs";
4401
- import * as path30 from "path";
4578
+ import * as fs33 from "fs";
4579
+ import * as path32 from "path";
4402
4580
  function listAbilities(opts) {
4403
4581
  const moduleName = opts.moduleName ?? "entry";
4404
- const moduleJsonPath = path30.join(opts.projectDir, moduleName, "src", "main", "module.json5");
4405
- if (!fs31.existsSync(moduleJsonPath)) {
4582
+ const moduleJsonPath = path32.join(opts.projectDir, moduleName, "src", "main", "module.json5");
4583
+ if (!fs33.existsSync(moduleJsonPath)) {
4406
4584
  throw new OniroError(`Could not find module.json5 at ${moduleJsonPath}`);
4407
4585
  }
4408
4586
  const parsed = readJson5File(moduleJsonPath);
@@ -4410,12 +4588,12 @@ function listAbilities(opts) {
4410
4588
  }
4411
4589
 
4412
4590
  // src/project/prepareSigning.ts
4413
- import * as fs32 from "fs";
4414
- import * as path31 from "path";
4591
+ import * as fs34 from "fs";
4592
+ import * as path33 from "path";
4415
4593
  function hasSigningArtifacts(dir) {
4416
- if (!fs32.existsSync(path31.join(dir, "signatures"))) return false;
4417
- const buildProfile = path31.join(dir, "build-profile.json5");
4418
- 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;
4419
4597
  try {
4420
4598
  const parsed = readJson5File(buildProfile);
4421
4599
  return Array.isArray(parsed.app?.signingConfigs) && parsed.app.signingConfigs.length > 0;
@@ -4494,7 +4672,6 @@ async function getDeviceInfo(opts) {
4494
4672
  }
4495
4673
 
4496
4674
  // src/install/diffHapAssets.ts
4497
- import StreamZip2 from "node-stream-zip";
4498
4675
  function diffEntryNames(installed, next) {
4499
4676
  const installedSet = new Set(installed);
4500
4677
  const nextSet = new Set(next);
@@ -4503,14 +4680,11 @@ function diffEntryNames(installed, next) {
4503
4680
  removedAssetPaths: [...installedSet].filter((n) => !nextSet.has(n)).sort()
4504
4681
  };
4505
4682
  }
4506
- async function listFileEntries(hapPath) {
4507
- const zip = new StreamZip2.async({ file: hapPath });
4508
- try {
4509
- const entries = await zip.entries();
4510
- return Object.values(entries).filter((e) => !e.isDirectory).map((e) => e.name);
4511
- } finally {
4512
- await zip.close();
4513
- }
4683
+ function listFileEntries(hapPath) {
4684
+ return withHapZip(
4685
+ hapPath,
4686
+ (zip) => Object.values(zip.entries()).filter((e) => !e.isDirectory).map((e) => e.name)
4687
+ );
4514
4688
  }
4515
4689
  async function diffHapAssets(opts) {
4516
4690
  const [installed, next] = await Promise.all([listFileEntries(opts.installedHap), listFileEntries(opts.newHap)]);
@@ -4518,8 +4692,8 @@ async function diffHapAssets(opts) {
4518
4692
  }
4519
4693
 
4520
4694
  // src/install/apply.ts
4521
- import * as fs33 from "fs";
4522
- import * as path32 from "path";
4695
+ import * as fs35 from "fs";
4696
+ import * as path34 from "path";
4523
4697
 
4524
4698
  // src/install/hapBundleName.ts
4525
4699
  function bundleNameFromHapConfig(text) {
@@ -4557,7 +4731,7 @@ async function pidOf(config, bundle, deviceSerial) {
4557
4731
  async function resolveHap(opts, logger) {
4558
4732
  let hap;
4559
4733
  if (opts.hapPath) {
4560
- 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;
4561
4735
  hap = existingHapOrUnsigned(hapPath);
4562
4736
  if (!hap) throw new OniroError(`HAP not found at ${hapPath}.`);
4563
4737
  } else {
@@ -4566,7 +4740,7 @@ async function resolveHap(opts, logger) {
4566
4740
  if (!picked) throw new OniroError(`No HAP found${opts.module ? ` for module ${opts.module}` : ""}. Build first.`);
4567
4741
  hap = { path: picked.path, fallback: !picked.signed };
4568
4742
  }
4569
- 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)}.`);
4570
4744
  return hap;
4571
4745
  }
4572
4746
  async function applyChanges(opts) {
@@ -4582,12 +4756,12 @@ async function applyChanges(opts) {
4582
4756
  });
4583
4757
  if (hapBundle && hapBundle !== bundle) {
4584
4758
  throw new OniroError(
4585
- `${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.`
4586
4760
  );
4587
4761
  }
4588
4762
  logger.info(`installing ${hapPath}`);
4589
4763
  let assetsChanged = false;
4590
- if (opts.installedHapPath && fs33.existsSync(opts.installedHapPath)) {
4764
+ if (opts.installedHapPath && fs35.existsSync(opts.installedHapPath)) {
4591
4765
  try {
4592
4766
  const diff = await diffHapAssets({ installedHap: opts.installedHapPath, newHap: hapPath });
4593
4767
  assetsChanged = diff.addedAssetPaths.length > 0 || diff.removedAssetPaths.length > 0;
@@ -4620,7 +4794,7 @@ async function applyChanges(opts) {
4620
4794
  }
4621
4795
  if (!await isBundleInstalled({ config, bundle, deviceSerial })) {
4622
4796
  throw new OniroError(
4623
- `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).`
4624
4798
  );
4625
4799
  }
4626
4800
  let postInstallPid = await pidOf(config, bundle, deviceSerial);
@@ -4697,10 +4871,10 @@ var AUTH_PATHS = {
4697
4871
  };
4698
4872
 
4699
4873
  // src/harmonyos/auth/tokenStore.ts
4700
- import * as fs34 from "fs";
4701
- import * as os11 from "os";
4702
- import * as path33 from "path";
4703
- 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";
4704
4878
  var ALGORITHM = "aes-256-gcm";
4705
4879
  var KEY_LENGTH = 32;
4706
4880
  var IV_LENGTH = 12;
@@ -4709,8 +4883,8 @@ var KEY_FILE = "kek.bin";
4709
4883
  var DIR_MODE = 448;
4710
4884
  var FILE_MODE = 384;
4711
4885
  function encrypt2(key, plaintext) {
4712
- const iv = crypto3.randomBytes(IV_LENGTH);
4713
- const cipher = crypto3.createCipheriv(ALGORITHM, key, iv);
4886
+ const iv = crypto4.randomBytes(IV_LENGTH);
4887
+ const cipher = crypto4.createCipheriv(ALGORITHM, key, iv);
4714
4888
  const ciphertext = Buffer.concat([cipher.update(plaintext), cipher.final()]);
4715
4889
  return {
4716
4890
  version: 1,
@@ -4721,7 +4895,7 @@ function encrypt2(key, plaintext) {
4721
4895
  };
4722
4896
  }
4723
4897
  function decrypt2(key, blob) {
4724
- const decipher = crypto3.createDecipheriv(ALGORITHM, key, Buffer.from(blob.iv, "base64"));
4898
+ const decipher = crypto4.createDecipheriv(ALGORITHM, key, Buffer.from(blob.iv, "base64"));
4725
4899
  decipher.setAuthTag(Buffer.from(blob.authTag, "base64"));
4726
4900
  return Buffer.concat([decipher.update(Buffer.from(blob.ciphertext, "base64")), decipher.final()]);
4727
4901
  }
@@ -4735,7 +4909,7 @@ function permissionHint(dir) {
4735
4909
  }
4736
4910
  function ensureDir(dir) {
4737
4911
  try {
4738
- fs34.mkdirSync(dir, { recursive: true, mode: DIR_MODE });
4912
+ fs36.mkdirSync(dir, { recursive: true, mode: DIR_MODE });
4739
4913
  } catch (err) {
4740
4914
  if (err.code === "EACCES") {
4741
4915
  throw new OniroError(permissionHint(dir), err);
@@ -4746,17 +4920,17 @@ function ensureDir(dir) {
4746
4920
  function createTokenStore(opts) {
4747
4921
  const logger = opts.logger ?? noopLogger;
4748
4922
  const authDir = opts.config.get("harmonyosAuthDir", defaultPaths.harmonyosAuthDir());
4749
- const tokenPath = path33.join(authDir, TOKEN_FILE);
4750
- 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);
4751
4925
  function loadOrCreateKey() {
4752
- if (fs34.existsSync(keyPath)) {
4753
- const existing = fs34.readFileSync(keyPath);
4926
+ if (fs36.existsSync(keyPath)) {
4927
+ const existing = fs36.readFileSync(keyPath);
4754
4928
  if (existing.length === KEY_LENGTH) return existing;
4755
4929
  logger.warn("[harmonyos] The token key file was malformed; regenerating (you will need to log in again).");
4756
4930
  }
4757
- ensureDir(path33.dirname(keyPath));
4758
- const key = crypto3.randomBytes(KEY_LENGTH);
4759
- 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 });
4760
4934
  return key;
4761
4935
  }
4762
4936
  return {
@@ -4766,12 +4940,12 @@ function createTokenStore(opts) {
4766
4940
  const payload = Buffer.from(JSON.stringify(session), "utf8");
4767
4941
  const blob = encrypt2(loadOrCreateKey(), payload);
4768
4942
  ensureDir(authDir);
4769
- fs34.writeFileSync(tokenPath, JSON.stringify(blob, null, 2), { mode: FILE_MODE });
4943
+ fs36.writeFileSync(tokenPath, JSON.stringify(blob, null, 2), { mode: FILE_MODE });
4770
4944
  },
4771
4945
  load() {
4772
- if (!fs34.existsSync(tokenPath)) return null;
4946
+ if (!fs36.existsSync(tokenPath)) return null;
4773
4947
  try {
4774
- const blob = JSON.parse(fs34.readFileSync(tokenPath, "utf8"));
4948
+ const blob = JSON.parse(fs36.readFileSync(tokenPath, "utf8"));
4775
4949
  if (!isEncryptedBlob(blob)) return null;
4776
4950
  const { jwtToken, siteId } = JSON.parse(decrypt2(loadOrCreateKey(), blob).toString("utf8"));
4777
4951
  return typeof jwtToken === "string" && jwtToken && typeof siteId === "string" ? { jwtToken, siteId } : null;
@@ -4783,7 +4957,7 @@ function createTokenStore(opts) {
4783
4957
  }
4784
4958
  logger.debug(`[harmonyos] Discarding unreadable token at ${tokenPath}.`);
4785
4959
  try {
4786
- fs34.rmSync(tokenPath, { force: true });
4960
+ fs36.rmSync(tokenPath, { force: true });
4787
4961
  } catch {
4788
4962
  }
4789
4963
  return null;
@@ -4791,7 +4965,7 @@ function createTokenStore(opts) {
4791
4965
  },
4792
4966
  clear() {
4793
4967
  try {
4794
- fs34.rmSync(tokenPath, { force: true });
4968
+ fs36.rmSync(tokenPath, { force: true });
4795
4969
  } catch (err) {
4796
4970
  throw new OniroError(`Failed to remove ${tokenPath}.`, err);
4797
4971
  }
@@ -4800,7 +4974,7 @@ function createTokenStore(opts) {
4800
4974
  }
4801
4975
 
4802
4976
  // src/harmonyos/auth/session.ts
4803
- import * as crypto5 from "crypto";
4977
+ import * as crypto6 from "crypto";
4804
4978
 
4805
4979
  // src/harmonyos/http.ts
4806
4980
  import { EnvHttpProxyAgent, fetch } from "undici";
@@ -4891,13 +5065,13 @@ function createHttpClient(opts = {}) {
4891
5065
 
4892
5066
  // src/harmonyos/auth/callbackServer.ts
4893
5067
  import * as http2 from "http";
4894
- import * as crypto4 from "crypto";
5068
+ import * as crypto5 from "crypto";
4895
5069
  var CALLBACK_PATH = "/callback";
4896
5070
  var MAX_BODY_BYTES = 65536;
4897
5071
  function codeMatches(code, secret) {
4898
5072
  const a = Buffer.from(code ?? "", "utf8");
4899
5073
  const b = Buffer.from(secret, "utf8");
4900
- return a.length === b.length && crypto4.timingSafeEqual(a, b);
5074
+ return a.length === b.length && crypto5.timingSafeEqual(a, b);
4901
5075
  }
4902
5076
  function isQuit(quit) {
4903
5077
  return quit === "true" || quit === "access_denied" || quit === "quit";
@@ -4905,8 +5079,8 @@ function isQuit(quit) {
4905
5079
  async function startCallbackServer(opts) {
4906
5080
  let resolveCallback;
4907
5081
  let rejectCallback;
4908
- const callback = new Promise((resolve7, reject) => {
4909
- resolveCallback = resolve7;
5082
+ const callback = new Promise((resolve9, reject) => {
5083
+ resolveCallback = resolve9;
4910
5084
  rejectCallback = reject;
4911
5085
  });
4912
5086
  callback.catch(() => {
@@ -4965,9 +5139,9 @@ async function startCallbackServer(opts) {
4965
5139
  }
4966
5140
  });
4967
5141
  server.keepAliveTimeout = 1;
4968
- await new Promise((resolve7, reject) => {
5142
+ await new Promise((resolve9, reject) => {
4969
5143
  server.once("error", (err) => reject(new OniroError("Failed to start the local login callback server.", err)));
4970
- server.listen(0, "127.0.0.1", resolve7);
5144
+ server.listen(0, "127.0.0.1", resolve9);
4971
5145
  });
4972
5146
  const port = server.address().port;
4973
5147
  return {
@@ -4983,12 +5157,12 @@ async function startCallbackServer(opts) {
4983
5157
  return Promise.race([callback, timeout]).finally(() => clearTimeout(timer));
4984
5158
  },
4985
5159
  stop() {
4986
- return new Promise((resolve7) => {
5160
+ return new Promise((resolve9) => {
4987
5161
  server.closeAllConnections?.();
4988
- const timer = setTimeout(resolve7, 100);
5162
+ const timer = setTimeout(resolve9, 100);
4989
5163
  server.close(() => {
4990
5164
  clearTimeout(timer);
4991
- resolve7();
5165
+ resolve9();
4992
5166
  });
4993
5167
  });
4994
5168
  }
@@ -5020,11 +5194,11 @@ function openBrowser(url) {
5020
5194
  windowsHide: true
5021
5195
  });
5022
5196
  child.unref();
5023
- return new Promise((resolve7, reject) => {
5197
+ return new Promise((resolve9, reject) => {
5024
5198
  child.on("error", (err) => reject(new OniroError(`Failed to open a browser (${command}).`, err)));
5025
5199
  child.on(
5026
5200
  "exit",
5027
- (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}.`))
5028
5202
  );
5029
5203
  });
5030
5204
  }
@@ -5106,7 +5280,7 @@ function createHarmonyOsSession(opts) {
5106
5280
  getDeveloperAgreement,
5107
5281
  async login(o = {}) {
5108
5282
  const start = HUAWEI_SITES.CN.loginUrl;
5109
- const clientSecret = crypto5.randomUUID().replace(/-/g, "");
5283
+ const clientSecret = crypto6.randomUUID().replace(/-/g, "");
5110
5284
  const server = await startCallbackServer({
5111
5285
  clientSecret,
5112
5286
  baseUrl: start,
@@ -5227,7 +5401,7 @@ var SIGNING_ERRORS = {
5227
5401
  DEVICE_ADD: "AppGallery Connect did not register the device.",
5228
5402
  DEVICE_LIMIT: "This team has reached its device limit. Remove an unused device in AppGallery Connect and retry.",
5229
5403
  DEVICE_NAME_REPEAT: "A device with that name is already registered. Retry.",
5230
- DEVICE_NONE: "No device is connected and none is registered to this team, but a debug profile must name at least one. Connect a device and retry.",
5404
+ DEVICE_NONE: "This team has no registered device and none could be registered from here, but a debug profile must name at least one. Connect a device that reports a UDID (`hdc shell bm get -u`), or register one in AppGallery Connect, and retry.",
5231
5405
  PROFILE_ADD: "AppGallery Connect did not issue the provisioning profile.",
5232
5406
  PROFILE_NAME_REPEAT: "A provisioning profile with that name already exists in AppGallery Connect. Retry.",
5233
5407
  PROVISION_LIMIT: "This team has reached its debug-profile limit. Delete unused profiles in AppGallery Connect and retry.",
@@ -5272,7 +5446,7 @@ function mapAgcError(operation, statusCode, reasonPhrase, body) {
5272
5446
  }
5273
5447
 
5274
5448
  // src/harmonyos/signing/profile.ts
5275
- import * as fs35 from "fs";
5449
+ import * as fs37 from "fs";
5276
5450
  import { X509Certificate } from "crypto";
5277
5451
  import forge from "node-forge";
5278
5452
  function extractProfileJson(buffer) {
@@ -5304,7 +5478,7 @@ function extractProfileJson(buffer) {
5304
5478
  function readProvisionProfile(profilePath) {
5305
5479
  let raw;
5306
5480
  try {
5307
- const json = extractProfileJson(fs35.readFileSync(profilePath));
5481
+ const json = extractProfileJson(fs37.readFileSync(profilePath));
5308
5482
  if (!json) return null;
5309
5483
  raw = JSON.parse(json);
5310
5484
  } catch {
@@ -5333,7 +5507,7 @@ function parseCertificate(pem) {
5333
5507
  }
5334
5508
  function keystoreHoldsKey(p12Path, keyAlias, keyPwd, certificate) {
5335
5509
  try {
5336
- const der = forge.util.createBuffer(fs35.readFileSync(p12Path).toString("binary"));
5510
+ const der = forge.util.createBuffer(fs37.readFileSync(p12Path).toString("binary"));
5337
5511
  const p12 = forge.pkcs12.pkcs12FromAsn1(forge.asn1.fromDer(der), keyPwd);
5338
5512
  const certBag = forge.pki.oids.certBag ?? "1.2.840.113549.1.12.10.1.3";
5339
5513
  const wanted = certificate.publicKey.export({ type: "spki", format: "der" });
@@ -5349,7 +5523,7 @@ function keystoreHoldsKey(p12Path, keyAlias, keyPwd, certificate) {
5349
5523
  }
5350
5524
  }
5351
5525
  function verifySigningMaterial(opts) {
5352
- 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);
5353
5527
  const chain = (pems ?? []).map(parseCertificate);
5354
5528
  const leaf = chain.find((c) => !c.ca) ?? chain.at(-1);
5355
5529
  if (!leaf) throw new AgcError(SIGNING_ERRORS.CERT_INVALID);
@@ -5367,15 +5541,15 @@ function verifySigningMaterial(opts) {
5367
5541
  }
5368
5542
 
5369
5543
  // src/harmonyos/signing/autoSign.ts
5370
- import * as crypto9 from "crypto";
5371
- import * as fs40 from "fs";
5372
- import * as path39 from "path";
5544
+ import * as crypto10 from "crypto";
5545
+ import * as fs42 from "fs";
5546
+ import * as path41 from "path";
5373
5547
  import JSON57 from "json5";
5374
5548
 
5375
5549
  // src/harmonyos/signing/agc.ts
5376
- import * as crypto6 from "crypto";
5377
- import * as fs36 from "fs";
5378
- import * as path34 from "path";
5550
+ import * as crypto7 from "crypto";
5551
+ import * as fs38 from "fs";
5552
+ import * as path36 from "path";
5379
5553
  var ENDPOINTS = {
5380
5554
  CERT_LIST: "/api/cps/harmony-cert-manage/v1/cert/list",
5381
5555
  CERT_ADD: "/api/cps/harmony-cert-manage/v1/cert/add",
@@ -5423,7 +5597,7 @@ function createAgcClient(http3, auth) {
5423
5597
  }
5424
5598
  },
5425
5599
  async addDevice(udid, deviceKind) {
5426
- const deviceName = `oniro_app_device_${crypto6.randomBytes(4).toString("hex")}`;
5600
+ const deviceName = `oniro_app_device_${crypto7.randomBytes(4).toString("hex")}`;
5427
5601
  await call("device", "post", ENDPOINTS.DEVICE_ADD, {
5428
5602
  body: { deviceName, udid, deviceType: String(DEVICE_TYPE_CODES[deviceKind] ?? DEVICE_TYPE_CODES.phone) }
5429
5603
  });
@@ -5457,12 +5631,12 @@ function createAgcClient(http3, auth) {
5457
5631
  const target = urlsInfo?.[0];
5458
5632
  if (!target) throw mapAgcError(operation, 200, "", "");
5459
5633
  const buffer = await http3.getBinary(target.newUrl, { timeoutMs: 6e4 });
5460
- const actual = crypto6.createHash("sha256").update(buffer).digest("hex");
5634
+ const actual = crypto7.createHash("sha256").update(buffer).digest("hex");
5461
5635
  if (target.sha256 && actual !== target.sha256.toLowerCase()) {
5462
5636
  throw new ChecksumMismatchError(target.sha256, actual);
5463
5637
  }
5464
- fs36.mkdirSync(path34.dirname(dest), { recursive: true, mode: 448 });
5465
- fs36.writeFileSync(dest, buffer, { mode: 384 });
5638
+ fs38.mkdirSync(path36.dirname(dest), { recursive: true, mode: 448 });
5639
+ fs38.writeFileSync(dest, buffer, { mode: 384 });
5466
5640
  }
5467
5641
  };
5468
5642
  }
@@ -5476,21 +5650,31 @@ function parseDeviceKind(stdout) {
5476
5650
  }
5477
5651
  async function collectLocalDevices(config, logger) {
5478
5652
  const devices = [];
5653
+ const skipped = [];
5479
5654
  for (const target of await listDevices(config, { logger, timeoutMs: 15e3 })) {
5480
- if (target.status !== "Connected") continue;
5655
+ const skip = (why) => {
5656
+ logger.warn(`[harmonyos] ${target.serial} ${why}`);
5657
+ skipped.push(target.serial);
5658
+ };
5659
+ if (!isUsableDevice(target)) {
5660
+ skip(
5661
+ target.status === "Unauthorized" ? "is unauthorized; accept the debugging prompt on the device to register it." : `is ${target.status.toLowerCase()}; skipping it.`
5662
+ );
5663
+ continue;
5664
+ }
5481
5665
  const shell2 = async (...args) => (await hdcExec({ config, deviceSerial: target.serial, args: ["shell", ...args], timeoutMs: 15e3, logger })).stdout;
5482
5666
  try {
5483
5667
  const udid = parseUdid(await shell2("bm", "get", "-u"));
5484
5668
  if (!udid) {
5485
- logger.warn(`[harmonyos] ${target.serial} did not report a UDID; skipping it.`);
5669
+ skip("did not report a UDID (`bm get -u`), so it cannot be registered.");
5486
5670
  continue;
5487
5671
  }
5488
5672
  devices.push({ serial: target.serial, udid, kind: parseDeviceKind(await shell2("getprop", "hw_sc.build.os.deviceType")) });
5489
5673
  } catch (err) {
5490
- logger.warn(`[harmonyos] Could not read ${target.serial}: ${err.message}`);
5674
+ skip(`could not be read: ${err.message}`);
5491
5675
  }
5492
5676
  }
5493
- return devices;
5677
+ return { devices, skipped };
5494
5678
  }
5495
5679
  async function registerDevices(agc, connected, logger) {
5496
5680
  let devices = await agc.listDevices();
@@ -5503,32 +5687,27 @@ async function registerDevices(agc, connected, logger) {
5503
5687
  if (unregistered.length > 0) devices = await agc.listDevices();
5504
5688
  const deviceIds = devices.map((d) => d.id);
5505
5689
  if (deviceIds.length === 0) throw new AgcError(SIGNING_ERRORS.DEVICE_NONE);
5506
- if (connected.length === 0) {
5507
- logger.warn(
5508
- `[harmonyos] No device is connected; the profile will name the ${deviceIds.length} device(s) already registered to this team.`
5509
- );
5510
- }
5511
5690
  return deviceIds;
5512
5691
  }
5513
5692
 
5514
5693
  // src/harmonyos/signing/keystore.ts
5515
- import * as crypto7 from "crypto";
5516
- import * as fs38 from "fs";
5517
- import * as path36 from "path";
5694
+ import * as crypto8 from "crypto";
5695
+ import * as fs40 from "fs";
5696
+ import * as path38 from "path";
5518
5697
 
5519
5698
  // src/harmonyos/java.ts
5520
- import * as fs37 from "fs";
5521
- import * as path35 from "path";
5699
+ import * as fs39 from "fs";
5700
+ import * as path37 from "path";
5522
5701
  var JAVA_BIN = process.platform === "win32" ? "java.exe" : "java";
5523
5702
  function findJava(sdk, env = process.env) {
5524
5703
  const candidates = [
5525
- env.JAVA_HOME && path35.join(env.JAVA_HOME, "bin", JAVA_BIN),
5526
- 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),
5527
5706
  // macOS: the JBR is itself a bundle inside Studio's Contents.
5528
- sdk && path35.join(sdk.installRoot, "jbr", "Contents", "Home", "bin", JAVA_BIN),
5529
- ...(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))
5530
5709
  ];
5531
- const found = candidates.find((c) => !!c && fs37.existsSync(c));
5710
+ const found = candidates.find((c) => !!c && fs39.existsSync(c));
5532
5711
  if (found) return found;
5533
5712
  throw new OniroError(
5534
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."
@@ -5541,49 +5720,77 @@ var SIGN_ALG = "SHA256withECDSA";
5541
5720
  function redactArgs(args) {
5542
5721
  return args.map((arg, i) => args[i - 1] === "-keyPwd" || args[i - 1] === "-keystorePwd" ? "******" : arg);
5543
5722
  }
5544
- async function generateKeystoreAndCsr(opts) {
5545
- const jar = path36.join(opts.sdk.sdkPath, "default", "openharmony", "toolchains", "lib", "hap-sign-tool.jar");
5546
- 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)) {
5547
5726
  throw new OniroError(`hap-sign-tool.jar not found at ${jar}; the HarmonyOS SDK looks incomplete.`);
5548
5727
  }
5728
+ return jar;
5729
+ }
5730
+ async function runHapSignTool(opts) {
5731
+ const jar = hapSignToolJar(opts.sdk);
5549
5732
  const java = findJava(opts.sdk);
5550
- const keyPwd = crypto7.randomBytes(12).toString("base64url").replaceAll(/[-_]/g, "");
5551
- fs38.mkdirSync(path36.dirname(opts.p12Path), { recursive: true, mode: 448 });
5552
- const run = async (what, args) => {
5553
- const argv = ["-jar", jar, ...args, "-keyAlias", KEY_ALIAS, "-keystoreFile", opts.p12Path, "-keystorePwd", keyPwd, "-keyPwd", keyPwd];
5554
- opts.logger.debug(`[harmonyos] ${java} ${redactArgs(argv).join(" ")}`);
5555
- const result = await runProcess({ command: java, args: argv, timeoutMs: 12e4, logger: opts.logger });
5556
- if (result.code !== 0) {
5557
- const detail = (result.stderr || result.stdout || "").trim();
5558
- 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 ? `
5559
5739
  ${detail}` : ""}`);
5560
- }
5561
- };
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 });
5562
5748
  opts.logger.info("[harmonyos] Generating the local keystore and certificate signing request...");
5563
5749
  await run("generate the key pair", ["generate-keypair", "-keyAlg", "ECC", "-keySize", "NIST-P-256"]);
5564
5750
  await run("generate the CSR", ["generate-csr", "-subject", "CN=DebugKey", "-signAlg", SIGN_ALG, "-outFile", opts.csrPath]);
5565
- 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
+ });
5566
5773
  }
5567
5774
 
5568
5775
  // src/harmonyos/signing/paths.ts
5569
- import * as crypto8 from "crypto";
5570
- import * as path37 from "path";
5776
+ import * as crypto9 from "crypto";
5777
+ import * as path39 from "path";
5571
5778
  function sanitizeNamePart(raw) {
5572
5779
  const safe = (raw?.trim() ?? "").replace(/[\\/:*?"<>|=.-]+/g, "_");
5573
5780
  return safe.slice(0, 64) || "default";
5574
5781
  }
5575
5782
  function resolveSigningMaterialPaths(config, productName, projectRoot) {
5576
- const hash = crypto8.createHash("sha256").update(projectRoot, "utf8").digest("base64url").replaceAll(/[-_]/g, "");
5577
- const base = path37.join(
5783
+ const hash = crypto9.createHash("sha256").update(projectRoot, "utf8").digest("base64url").replaceAll(/[-_]/g, "");
5784
+ const base = path39.join(
5578
5785
  config.get("harmonyosSigningDir", defaultPaths.harmonyosSigningDir()),
5579
- `${sanitizeNamePart(productName)}_${path37.basename(projectRoot)}_${hash}=`
5786
+ `${sanitizeNamePart(productName)}_${path39.basename(projectRoot)}_${hash}=`
5580
5787
  );
5581
5788
  return { p12Path: `${base}.p12`, csrPath: `${base}.csr`, cerPath: `${base}.cer`, profilePath: `${base}.p7b` };
5582
5789
  }
5583
5790
 
5584
5791
  // src/harmonyos/signing/project.ts
5585
- import * as fs39 from "fs";
5586
- import * as path38 from "path";
5792
+ import * as fs41 from "fs";
5793
+ import * as path40 from "path";
5587
5794
  import JSON56 from "json5";
5588
5795
 
5589
5796
  // src/harmonyos/signing/aclPermissionList.ts
@@ -5651,9 +5858,9 @@ var ACL_PERMISSIONS = /* @__PURE__ */ new Set([
5651
5858
 
5652
5859
  // src/harmonyos/signing/project.ts
5653
5860
  function readRequestedPermissions(moduleConfigPath) {
5654
- if (!fs39.existsSync(moduleConfigPath)) return [];
5861
+ if (!fs41.existsSync(moduleConfigPath)) return [];
5655
5862
  try {
5656
- const parsed = JSON56.parse(fs39.readFileSync(moduleConfigPath, "utf-8"));
5863
+ const parsed = JSON56.parse(fs41.readFileSync(moduleConfigPath, "utf-8"));
5657
5864
  return (parsed.module?.requestPermissions ?? []).map((p) => p?.name).filter((n) => typeof n === "string" && n.length > 0);
5658
5865
  } catch {
5659
5866
  return [];
@@ -5662,32 +5869,38 @@ function readRequestedPermissions(moduleConfigPath) {
5662
5869
  function collectAclPermissions(projectDir, logger = noopLogger) {
5663
5870
  const requested = /* @__PURE__ */ new Set();
5664
5871
  for (const module of listModules({ projectDir })) {
5665
- const moduleRoot = path38.resolve(projectDir, module.srcPath);
5872
+ const moduleRoot = path40.resolve(projectDir, module.srcPath);
5666
5873
  for (const sourceSet of ["main", "ohosTest"]) {
5667
5874
  for (const name of readRequestedPermissions(
5668
- path38.join(moduleRoot, "src", sourceSet, "module.json5")
5875
+ path40.join(moduleRoot, "src", sourceSet, "module.json5")
5669
5876
  )) {
5670
5877
  requested.add(name);
5671
5878
  }
5672
5879
  }
5673
5880
  }
5674
- const acls = [...requested].filter((p) => ACL_PERMISSIONS.has(p)).sort();
5881
+ const acls = aclPermissionsAmong(requested);
5675
5882
  if (acls.length > 0) {
5676
5883
  logger.info(`[harmonyos] Project requests ${acls.length} ACL permission(s): ${acls.join(", ")}`);
5677
5884
  }
5678
5885
  return acls;
5679
5886
  }
5887
+ function aclPermissionsAmong(names) {
5888
+ return [...new Set(names)].filter((p) => ACL_PERMISSIONS.has(p)).sort();
5889
+ }
5680
5890
 
5681
5891
  // src/harmonyos/signing/autoSign.ts
5682
5892
  function shouldRegenerate(input) {
5683
5893
  if (input.force) return "--force was passed";
5684
- const missing = Object.values(input.paths).find((file) => !fs40.existsSync(file));
5894
+ const missing = Object.values(input.paths).find((file) => !fs42.existsSync(file));
5685
5895
  if (missing) return `${missing} is missing`;
5686
5896
  const config = input.buildProfile.app?.signingConfigs?.find((c) => c.name === input.productName);
5687
5897
  if (config?.type !== "HarmonyOS" || config.material?.profile !== input.paths.profilePath) {
5688
5898
  return "build-profile.json5 has no signingConfig for this material";
5689
5899
  }
5690
- 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);
5691
5904
  if (!profile) return "the provisioning profile could not be read";
5692
5905
  if (profile.bundleName !== input.bundleName) return `the profile is for bundle '${profile.bundleName}'`;
5693
5906
  if (profile.developerId && profile.developerId !== input.teamId) {
@@ -5720,31 +5933,29 @@ function writeSigningConfig(opts) {
5720
5933
  for (const product of app.products ?? []) {
5721
5934
  if (product.name === opts.productName) product.signingConfig = opts.productName;
5722
5935
  }
5723
- fs40.writeFileSync(opts.buildProfilePath, JSON.stringify(opts.buildProfile, null, 2));
5936
+ fs42.writeFileSync(opts.buildProfilePath, JSON.stringify(opts.buildProfile, null, 2));
5724
5937
  }
5725
5938
  async function harmonyOsAutoSign(opts) {
5726
5939
  const logger = opts.logger ?? noopLogger;
5727
5940
  const productName = opts.productName ?? "default";
5728
- const projectDir = path39.resolve(opts.projectDir);
5729
- const buildProfilePath = path39.join(projectDir, "build-profile.json5");
5941
+ const projectDir = path41.resolve(opts.projectDir);
5942
+ const buildProfilePath = path41.join(projectDir, "build-profile.json5");
5730
5943
  let buildProfile;
5731
5944
  try {
5732
- buildProfile = JSON57.parse(fs40.readFileSync(buildProfilePath, "utf-8"));
5945
+ buildProfile = JSON57.parse(fs42.readFileSync(buildProfilePath, "utf-8"));
5733
5946
  } catch (err) {
5734
5947
  throw new OniroError(`Could not read ${buildProfilePath}: ${err.message}`, err);
5735
5948
  }
5736
5949
  const product = buildProfile.app?.products?.find((p) => p.name === productName);
5737
5950
  if (!product) throw new OniroError(`${buildProfilePath} declares no product named '${productName}'.`);
5738
5951
  const bundleName = product.bundleName || getBundleName(projectDir);
5739
- if (!isValidBundleName(bundleName) || bundleName.split(".").length < 3 || bundleName.length < 7 || bundleName.length > 128) {
5740
- throw new AgcError(SIGNING_ERRORS.BUNDLE_NAME_INVALID);
5741
- }
5952
+ assertAgcBundleName(bundleName);
5742
5953
  const sdk = requireHarmonyOsSdk({ config: opts.config, logger });
5743
5954
  const auth = await opts.session.resolveAgcAuth({ teamId: opts.teamId });
5744
5955
  const aclPermissions = collectAclPermissions(projectDir, logger);
5745
5956
  const paths = resolveSigningMaterialPaths(opts.config, productName, projectDir);
5746
5957
  logger.info(`[harmonyos] Signing ${bundleName} (product '${productName}', team ${auth.teamId}).`);
5747
- const connected = await collectLocalDevices(opts.config, logger);
5958
+ const local = await collectLocalDevices(opts.config, logger);
5748
5959
  const result = (regenerated) => ({
5749
5960
  bundleName,
5750
5961
  teamId: auth.teamId,
@@ -5758,7 +5969,7 @@ async function harmonyOsAutoSign(opts) {
5758
5969
  productName,
5759
5970
  bundleName,
5760
5971
  teamId: auth.teamId,
5761
- connectedUdids: connected.map((d) => d.udid),
5972
+ connectedUdids: local.devices.map((d) => d.udid),
5762
5973
  aclPermissions,
5763
5974
  force: opts.force
5764
5975
  });
@@ -5767,10 +5978,47 @@ async function harmonyOsAutoSign(opts) {
5767
5978
  return result(false);
5768
5979
  }
5769
5980
  logger.info(`[harmonyos] Regenerating signing material: ${reason}.`);
5770
- const agc = createAgcClient(opts.http ?? createHttpClient({ logger }), auth);
5771
- const deviceIds = await registerDevices(agc, connected, logger);
5772
- for (const file of Object.values(paths)) fs40.rmSync(file, { force: true });
5773
- const certName = `oniro_debug_${auth.teamId.replace(/[\\/.:]/g, "")}.cer`;
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;
6015
+ const deviceIds = await registerDevices(agc, local.devices, logger);
6016
+ if (local.devices.length === 0) {
6017
+ const cause = local.skipped.length > 0 ? "No connected device could be registered" : "No device is connected";
6018
+ logger.warn(`[harmonyos] ${cause}; the profile will name the ${deviceIds.length} device(s) already registered to this team.`);
6019
+ }
6020
+ for (const file of Object.values(paths)) fs42.rmSync(file, { force: true });
6021
+ const certName = `oniro_debug_${opts.teamId.replace(/[\\/.:]/g, "")}.cer`;
5774
6022
  const stale = (await agc.listCertificates()).find((c) => c.certName === certName);
5775
6023
  if (stale) {
5776
6024
  logger.info(`[harmonyos] Replacing the previous '${certName}' certificate in AppGallery Connect...`);
@@ -5784,11 +6032,11 @@ async function harmonyOsAutoSign(opts) {
5784
6032
  await agc.download("cert", cert.certObjectId, paths.cerPath);
5785
6033
  logger.info("[harmonyos] Issuing the debug provisioning profile...");
5786
6034
  const profile = await agc.addDebugProfile({
5787
- provisionName: `oniro_${crypto9.randomBytes(6).toString("hex")}`,
5788
- bundleName,
6035
+ provisionName: `oniro_${crypto10.randomBytes(6).toString("hex")}`,
6036
+ bundleName: opts.bundleName,
5789
6037
  certId: cert.id,
5790
6038
  deviceIds,
5791
- aclPermissions
6039
+ aclPermissions: opts.aclPermissions
5792
6040
  });
5793
6041
  try {
5794
6042
  await agc.download("profile", profile.provisionFileUrl, paths.profilePath);
@@ -5800,22 +6048,86 @@ async function harmonyOsAutoSign(opts) {
5800
6048
  try {
5801
6049
  verifySigningMaterial({ ...paths, keyAlias: KEY_ALIAS, keyPwd });
5802
6050
  } catch (err) {
5803
- fs40.rmSync(paths.profilePath, { force: true });
6051
+ fs42.rmSync(paths.profilePath, { force: true });
5804
6052
  throw err;
5805
6053
  }
5806
- 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) {
5807
6065
  try {
5808
- getKey(materialDir);
6066
+ return decryptPwd(fs43.readFileSync(paths.pwdPath, "utf8").trim(), materialDirFor(paths));
5809
6067
  } catch {
5810
- createMaterial(materialDir);
6068
+ return null;
5811
6069
  }
5812
- writeSigningConfig({ buildProfilePath, buildProfile, productName, paths, encryptedPassword: encryptPwd(keyPwd, materialDir) });
5813
- logger.info(`[harmonyos] Wrote signingConfigs['${productName}'] to ${buildProfilePath}.`);
5814
- 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 };
5815
6127
  }
5816
6128
 
5817
6129
  // src/index.ts
5818
- var VERSION = "0.15.0";
6130
+ var VERSION = "0.16.0";
5819
6131
  export {
5820
6132
  ALL_SDKS,
5821
6133
  APL_VALUES,
@@ -5832,6 +6144,7 @@ export {
5832
6144
  OHOS_DAILY_BUILDS_URL,
5833
6145
  OHOS_URL_BASE,
5834
6146
  OniroError,
6147
+ SIGNED_HAP_CHUNK_SIZE,
5835
6148
  SIGNING_ERRORS,
5836
6149
  SdkNotInstalledError,
5837
6150
  TMP_DIR_HINT,
@@ -5867,6 +6180,7 @@ export {
5867
6180
  dedupEntries,
5868
6181
  defaultContext,
5869
6182
  defaultPaths,
6183
+ defaultSignedHapPath,
5870
6184
  describeLayoutNode,
5871
6185
  detectAaStartFailure,
5872
6186
  detectCmdToolsFlavour,
@@ -5933,6 +6247,7 @@ export {
5933
6247
  getSupportedSdksForUi,
5934
6248
  harmonyOsAutoSign,
5935
6249
  harmonyOsSdkLabelForApi,
6250
+ harmonyOsSignHap,
5936
6251
  hdcClientFailure,
5937
6252
  hdcExec,
5938
6253
  hdcTargetArgs,
@@ -5950,6 +6265,7 @@ export {
5950
6265
  isOutOfSpaceError,
5951
6266
  isRamBackedPath,
5952
6267
  isUnsignedHap,
6268
+ isUsableDevice,
5953
6269
  isValidBundleName,
5954
6270
  isValidProjectName,
5955
6271
  killProcessTree,
@@ -5985,6 +6301,7 @@ export {
5985
6301
  prepareSigning,
5986
6302
  pruneLayout,
5987
6303
  readHapBundleName,
6304
+ readHapSigningInfo,
5988
6305
  readJson5File,
5989
6306
  readJsonFile,
5990
6307
  readProvisionProfile,
@@ -6012,6 +6329,7 @@ export {
6012
6329
  setHilogLevel,
6013
6330
  shell,
6014
6331
  shellChecked,
6332
+ signOpenHarmonyHap,
6015
6333
  spawnCompat,
6016
6334
  startEmulator,
6017
6335
  staticConfig,
@@ -6031,6 +6349,7 @@ export {
6031
6349
  waitForTarget,
6032
6350
  watchLog,
6033
6351
  waypointsToRawEvents,
6352
+ withHapZip,
6034
6353
  withoutWindow,
6035
6354
  writeJson5File,
6036
6355
  writeJsonFile