@oniroproject/core 0.14.0 → 0.15.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
@@ -180,6 +180,7 @@ var ALL_SDKS = [
180
180
  { version: "7.0", api: "26", tarballStrip: { linuxWindows: 1, darwin: 3 }, archiveSuffix: "_20260829" }
181
181
  ];
182
182
  var OHOS_URL_BASE = "https://repo.huaweicloud.com/openharmony/os";
183
+ var OHOS_DAILY_BUILDS_URL = "https://dcp.openharmony.cn/workbench/cicd/dailybuild/dailylist";
183
184
 
184
185
  // src/sdk/platform.ts
185
186
  import * as os2 from "os";
@@ -762,8 +763,17 @@ async function extractZipWithProgress(opts) {
762
763
  if (inc > 0) progress.report({ message: "Extracting: 100%", increment: inc });
763
764
  }
764
765
  }
765
- async function extractTarball(tarPath, dest, strip = 0) {
766
- await tar.x({ file: tarPath, cwd: dest, strip });
766
+ async function extractTarball(tarPath, dest, strip = 0, filter) {
767
+ await tar.x({ file: tarPath, cwd: dest, strip, ...filter ? { filter: (p) => filter(p) } : {} });
768
+ }
769
+ async function readZipEntry(zipPath, match) {
770
+ const zip = new StreamZip.async({ file: zipPath });
771
+ 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
+ } finally {
775
+ await zip.close();
776
+ }
767
777
  }
768
778
 
769
779
  // src/sdk/list.ts
@@ -869,34 +879,7 @@ async function downloadAndInstallSdk(opts) {
869
879
  `Expected folder '${osFolder}' not found in extracted SDK. Tarball layout may have changed.`
870
880
  );
871
881
  }
872
- const zipFiles = fs10.readdirSync(osContentPath).filter((n2) => n2.endsWith(".zip"));
873
- const componentsStart = 60;
874
- const componentsBudget = 35;
875
- const n = zipFiles.length;
876
- if (n === 0) {
877
- progress?.report({ message: "No SDK component ZIPs found.", increment: componentsBudget });
878
- } else {
879
- progress?.report({ message: "Extracting SDK components...", increment: 0 });
880
- }
881
- const base = n > 0 ? Math.floor(componentsBudget / n) : 0;
882
- let rem = n > 0 ? componentsBudget % n : 0;
883
- let cursor = componentsStart;
884
- for (const zipFile of zipFiles) {
885
- checkCancelled();
886
- logger.info(`Extracting component ${zipFile}`);
887
- const zipPath = path10.join(osContentPath, zipFile);
888
- const thisBudget = base + (rem > 0 ? 1 : 0);
889
- if (rem > 0) rem--;
890
- await extractZipWithProgress({ zipPath, dest: osContentPath, progress, start: cursor, range: thisBudget, logger });
891
- cursor += thisBudget;
892
- fs10.unlinkSync(zipPath);
893
- }
894
- progress?.report({ message: "Finalizing installation...", increment: 0 });
895
- if (fs10.existsSync(sdkInstallDir)) {
896
- fs10.rmSync(sdkInstallDir, { recursive: true, force: true });
897
- }
898
- movePath(osContentPath, sdkInstallDir);
899
- progress?.report({ message: "Finalizing installation...", increment: 3 });
882
+ await installComponents({ osContentPath, sdkInstallDir, progress, logger, start: 60, range: 35, checkCancelled });
900
883
  progress?.report({ message: "Cleaning up...", increment: 0 });
901
884
  removeTempWorkDir(tmpDir, tmpRoot);
902
885
  progress?.report({ message: "Cleaning up...", increment: 2 });
@@ -907,6 +890,115 @@ async function downloadAndInstallSdk(opts) {
907
890
  throw wrapped;
908
891
  }
909
892
  }
893
+ async function installComponents(opts) {
894
+ const { osContentPath, sdkInstallDir, progress, logger, checkCancelled } = opts;
895
+ const zipFiles = fs10.readdirSync(osContentPath).filter((n2) => n2.endsWith(".zip"));
896
+ const n = zipFiles.length;
897
+ if (n === 0) {
898
+ progress?.report({ message: "No SDK component ZIPs found.", increment: opts.range });
899
+ } else {
900
+ progress?.report({ message: "Extracting SDK components...", increment: 0 });
901
+ }
902
+ const base = n > 0 ? Math.floor(opts.range / n) : 0;
903
+ let rem = n > 0 ? opts.range % n : 0;
904
+ let cursor = opts.start;
905
+ for (const zipFile of zipFiles) {
906
+ checkCancelled();
907
+ logger.info(`Extracting component ${zipFile}`);
908
+ const zipPath = path10.join(osContentPath, zipFile);
909
+ const thisBudget = base + (rem > 0 ? 1 : 0);
910
+ if (rem > 0) rem--;
911
+ await extractZipWithProgress({ zipPath, dest: osContentPath, progress, start: cursor, range: thisBudget, logger });
912
+ cursor += thisBudget;
913
+ fs10.unlinkSync(zipPath);
914
+ }
915
+ progress?.report({ message: "Finalizing installation...", increment: 0 });
916
+ if (fs10.existsSync(sdkInstallDir)) {
917
+ fs10.rmSync(sdkInstallDir, { recursive: true, force: true });
918
+ }
919
+ movePath(osContentPath, sdkInstallDir);
920
+ progress?.report({ message: "Finalizing installation...", increment: 3 });
921
+ }
922
+ async function installSdkFromArchive(opts) {
923
+ const { config, archivePath, progress, abortSignal } = opts;
924
+ const logger = opts.logger ?? noopLogger;
925
+ if (!fs10.statSync(archivePath, { throwIfNoEntry: false })?.isFile()) {
926
+ throw new OniroError(`SDK archive not found: ${archivePath}`);
927
+ }
928
+ const osFolder = getOsFolder();
929
+ const what = `the SDK install from '${path10.basename(archivePath)}'`;
930
+ const sdkRoot = getSdkRootDir(config);
931
+ const { dir: tmpDir, root: tmpRoot } = createInstallTempDir({
932
+ config,
933
+ override: opts.tmpDir,
934
+ installRoot: sdkRoot,
935
+ prefix: "oniro-sdk-",
936
+ logger
937
+ });
938
+ const extractDir = path10.join(tmpDir, "extract");
939
+ fs10.mkdirSync(extractDir);
940
+ const checkCancelled = () => {
941
+ if (abortSignal?.aborted) throw new CancelledError();
942
+ };
943
+ try {
944
+ progress?.report({ message: "Extracting SDK archive (this may take a while)...", increment: 0 });
945
+ await extractTarball(archivePath, extractDir, 0, (p) => p.split("/").includes(osFolder));
946
+ progress?.report({ message: "Extracting SDK archive (this may take a while)...", increment: 40 });
947
+ checkCancelled();
948
+ const osContentPath = findSdkOsDir(extractDir, osFolder);
949
+ if (!osContentPath) {
950
+ throw new OniroError(
951
+ `No '${osFolder}' SDK components (a '${osFolder}' folder of ZIPs) found in ${archivePath}. Is it an OpenHarmony SDK archive for this OS?`
952
+ );
953
+ }
954
+ const { api, version } = await readSdkComponentsMeta(osContentPath);
955
+ const sdkInstallDir = path10.join(sdkRoot, osFolder, api);
956
+ if (fs10.existsSync(sdkInstallDir) && !opts.force) {
957
+ throw new OniroError(
958
+ `An SDK for api ${api} is already installed at ${sdkInstallDir}; pass --force to replace it with this archive, or set ONIRO_SDK_ROOT_DIR to install it in a separate SDK root.`
959
+ );
960
+ }
961
+ fs10.mkdirSync(path10.dirname(sdkInstallDir), { recursive: true });
962
+ await installComponents({ osContentPath, sdkInstallDir, progress, logger, start: 40, range: 55, checkCancelled });
963
+ progress?.report({ message: "Cleaning up...", increment: 0 });
964
+ removeTempWorkDir(tmpDir, tmpRoot);
965
+ progress?.report({ message: "Cleaning up...", increment: 2 });
966
+ return { api, version, dir: sdkInstallDir };
967
+ } catch (err) {
968
+ removeTempWorkDir(tmpDir, tmpRoot);
969
+ throw toSpaceError(err, tmpRoot, what);
970
+ }
971
+ }
972
+ function findSdkOsDir(root, osFolder, maxDepth = 4) {
973
+ let level = [root];
974
+ for (let depth = 0; depth < maxDepth && level.length > 0; depth++) {
975
+ const next = [];
976
+ for (const dir of level) {
977
+ for (const entry of fs10.readdirSync(dir, { withFileTypes: true })) {
978
+ if (!entry.isDirectory()) continue;
979
+ const p = path10.join(dir, entry.name);
980
+ if (entry.name === osFolder && fs10.readdirSync(p).some((n) => n.endsWith(".zip"))) return p;
981
+ next.push(p);
982
+ }
983
+ }
984
+ level = next;
985
+ }
986
+ return void 0;
987
+ }
988
+ async function readSdkComponentsMeta(dir) {
989
+ const zips = fs10.readdirSync(dir).filter((n) => n.endsWith(".zip")).sort((a, b) => Number(b.startsWith("ets-")) - Number(a.startsWith("ets-")));
990
+ for (const name of zips) {
991
+ const data = await readZipEntry(path10.join(dir, name), (e) => /^[^/]+\/oh-uni-package\.json$/.test(e));
992
+ if (!data) continue;
993
+ try {
994
+ const pkg = JSON.parse(data.toString("utf8"));
995
+ const api = String(pkg.apiVersion ?? "");
996
+ if (/^\d+$/.test(api)) return { api, version: String(pkg.version ?? "") };
997
+ } catch {
998
+ }
999
+ }
1000
+ throw new OniroError("No SDK component in the archive declares a numeric apiVersion (oh-uni-package.json).");
1001
+ }
910
1002
 
911
1003
  // src/cmdTools/install.ts
912
1004
  import * as fs11 from "fs";
@@ -1605,6 +1697,7 @@ import { spawn as spawn2 } from "child_process";
1605
1697
 
1606
1698
  // src/hdc/exec.ts
1607
1699
  import * as fs15 from "fs";
1700
+ import { StringDecoder } from "string_decoder";
1608
1701
  function errorMessage(err) {
1609
1702
  return err instanceof Error ? err.message : String(err);
1610
1703
  }
@@ -1632,6 +1725,8 @@ function runProcess(opts) {
1632
1725
  let stderr = "";
1633
1726
  let settled = false;
1634
1727
  let timedOut = false;
1728
+ const stdoutDecoder = new StringDecoder("utf8");
1729
+ const stderrDecoder = new StringDecoder("utf8");
1635
1730
  const cleanup = () => {
1636
1731
  clearTimeout(timer);
1637
1732
  abortSignal?.removeEventListener("abort", onAbort);
@@ -1649,14 +1744,14 @@ function runProcess(opts) {
1649
1744
  }, timeoutMs);
1650
1745
  abortSignal?.addEventListener("abort", onAbort, { once: true });
1651
1746
  child.stdout?.on("data", (d) => {
1652
- const s = d.toString();
1747
+ const s = stdoutDecoder.write(d);
1653
1748
  stdout += s;
1654
- onOutput?.(s, "stdout");
1749
+ if (s) onOutput?.(s, "stdout");
1655
1750
  });
1656
1751
  child.stderr?.on("data", (d) => {
1657
- const s = d.toString();
1752
+ const s = stderrDecoder.write(d);
1658
1753
  stderr += s;
1659
- onOutput?.(s, "stderr");
1754
+ if (s) onOutput?.(s, "stderr");
1660
1755
  });
1661
1756
  child.on("error", (err) => {
1662
1757
  if (settled) return;
@@ -1668,6 +1763,12 @@ function runProcess(opts) {
1668
1763
  if (settled) return;
1669
1764
  settled = true;
1670
1765
  cleanup();
1766
+ const stdoutTail = stdoutDecoder.end();
1767
+ const stderrTail = stderrDecoder.end();
1768
+ stdout += stdoutTail;
1769
+ stderr += stderrTail;
1770
+ if (stdoutTail) onOutput?.(stdoutTail, "stdout");
1771
+ if (stderrTail) onOutput?.(stderrTail, "stderr");
1671
1772
  if (timedOut) {
1672
1773
  reject(
1673
1774
  new OniroError(
@@ -1955,6 +2056,7 @@ async function getEmulatorStatus(config, opts = {}) {
1955
2056
  // src/hdc/devices.ts
1956
2057
  var STATUS_TOKENS = /* @__PURE__ */ new Set([
1957
2058
  "Connected",
2059
+ "Ready",
1958
2060
  "Offline",
1959
2061
  "Unauthorized",
1960
2062
  "Authorizing",
@@ -2184,9 +2286,13 @@ function moduleOfHap(hapFileName) {
2184
2286
  const dash = hapFileName.indexOf("-");
2185
2287
  return dash > 0 ? hapFileName.slice(0, dash) : hapFileName.replace(/\.hap$/i, "");
2186
2288
  }
2289
+ function isAppProjectRoot(dir) {
2290
+ return fs17.existsSync(path18.join(dir, "AppScope", "app.json5"));
2291
+ }
2187
2292
  async function discoverHaps(opts) {
2188
2293
  const { projectDir } = opts;
2189
2294
  const maxDepth = opts.maxDepth ?? 8;
2295
+ const skipNestedProjects = isAppProjectRoot(projectDir);
2190
2296
  const result = {};
2191
2297
  const walk = async (dir, depth) => {
2192
2298
  if (depth > maxDepth) return;
@@ -2195,6 +2301,7 @@ async function discoverHaps(opts) {
2195
2301
  const full = path18.join(dir, e.name);
2196
2302
  if (e.isDirectory()) {
2197
2303
  if (e.name === "node_modules" || e.name === "oh_modules" || e.name === ".git") continue;
2304
+ if (skipNestedProjects && isAppProjectRoot(full)) continue;
2198
2305
  await walk(full, depth + 1);
2199
2306
  } else if (e.isFile() && e.name.endsWith(".hap")) {
2200
2307
  const moduleName = moduleOfHap(e.name);
@@ -3386,6 +3493,54 @@ async function resolveProcessId(config, bundle, deviceSerial) {
3386
3493
  const proc = await findRunningProcess({ config, bundle, deviceSerial });
3387
3494
  return proc?.pid;
3388
3495
  }
3496
+ async function readDeviceClock(config, deviceSerial) {
3497
+ try {
3498
+ const res = await shell({ config, command: "date +%Y-%m-%dT%H:%M:%S", deviceSerial, timeoutMs: 1e4 });
3499
+ const m = res.code === 0 ? /(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})/.exec(res.stdout) : null;
3500
+ if (!m) return null;
3501
+ const [year, month, day, hour, minute, second] = m.slice(1).map(Number);
3502
+ return Date.UTC(year, month - 1, day, hour, minute, second);
3503
+ } catch {
3504
+ return null;
3505
+ }
3506
+ }
3507
+ function hilogTimeMs(time, referenceMs) {
3508
+ const m = /^(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})\.(\d{3})$/.exec(time);
3509
+ if (!m) return null;
3510
+ const [month, day, hour, minute, second, ms] = m.slice(1).map(Number);
3511
+ const year = new Date(referenceMs).getUTCFullYear();
3512
+ let best = null;
3513
+ for (const y of [year - 1, year, year + 1]) {
3514
+ const t = Date.UTC(y, month - 1, day, hour, minute, second, ms);
3515
+ if (best === null || Math.abs(t - referenceMs) < Math.abs(best - referenceMs)) best = t;
3516
+ }
3517
+ return best;
3518
+ }
3519
+ async function recentEntryFilter(opts) {
3520
+ const passAll = { test: () => true, afterReconnect: async () => {
3521
+ } };
3522
+ const lookbackMs = opts.lookbackMs ?? 0;
3523
+ if (lookbackMs === Infinity) return passAll;
3524
+ const startedAt = Date.now();
3525
+ const now = await readDeviceClock(opts.config, opts.deviceSerial);
3526
+ if (now === null) {
3527
+ opts.logger.warn("Could not read the device clock, so lines already in the log buffer can match too.");
3528
+ return passAll;
3529
+ }
3530
+ let since = now - (Date.now() - startedAt) - lookbackMs;
3531
+ return {
3532
+ test: (entry) => {
3533
+ if (since === null) return true;
3534
+ const t = hilogTimeMs(entry.time, now);
3535
+ return t === null || t >= since;
3536
+ },
3537
+ afterReconnect: async () => {
3538
+ if (since === null) return;
3539
+ const later = await readDeviceClock(opts.config, opts.deviceSerial);
3540
+ if (later !== null && later < since) since = null;
3541
+ }
3542
+ };
3543
+ }
3389
3544
  function waitForLog(opts) {
3390
3545
  return new Promise((resolve7, reject) => {
3391
3546
  const deadline = Date.now() + opts.timeoutMs;
@@ -3393,6 +3548,8 @@ function waitForLog(opts) {
3393
3548
  let child = null;
3394
3549
  let reconnectTimer;
3395
3550
  let buf = "";
3551
+ let recent = { test: () => true, afterReconnect: async () => {
3552
+ } };
3396
3553
  const cleanup = () => {
3397
3554
  clearTimeout(timer);
3398
3555
  if (reconnectTimer) clearTimeout(reconnectTimer);
@@ -3433,7 +3590,7 @@ function waitForLog(opts) {
3433
3590
  buf = lines.pop() ?? "";
3434
3591
  for (const line of lines) {
3435
3592
  const entry = parseHilogLine(line);
3436
- if (entry && opts.pattern.test(entryText(entry))) {
3593
+ if (entry && recent.test(entry) && opts.pattern.test(entryText(entry))) {
3437
3594
  finish(entry);
3438
3595
  return;
3439
3596
  }
@@ -3443,13 +3600,21 @@ function waitForLog(opts) {
3443
3600
  if (settled || child !== stream) return;
3444
3601
  child = null;
3445
3602
  if (Date.now() < deadline) {
3446
- reconnectTimer = setTimeout(() => void spawnStream().catch(failWith), 1e3);
3603
+ reconnectTimer = setTimeout(() => void recent.afterReconnect().then(spawnStream).catch(failWith), 1e3);
3447
3604
  }
3448
3605
  };
3449
3606
  stream.once("close", onEnd);
3450
3607
  stream.once("error", onEnd);
3451
3608
  };
3452
- void spawnStream().catch(failWith);
3609
+ void (async () => {
3610
+ recent = await recentEntryFilter({
3611
+ config: opts.config,
3612
+ deviceSerial: opts.deviceSerial,
3613
+ lookbackMs: opts.lookbackMs,
3614
+ logger: opts.logger ?? noopLogger
3615
+ });
3616
+ await spawnStream();
3617
+ })().catch(failWith);
3453
3618
  });
3454
3619
  }
3455
3620
  function watchLog(opts) {
@@ -3458,6 +3623,8 @@ function watchLog(opts) {
3458
3623
  let settled = false;
3459
3624
  let child = null;
3460
3625
  let buf = "";
3626
+ let recent = { test: () => true, afterReconnect: async () => {
3627
+ } };
3461
3628
  const cleanup = () => {
3462
3629
  clearTimeout(timer);
3463
3630
  opts.abortSignal?.removeEventListener("abort", onAbort);
@@ -3481,6 +3648,13 @@ function watchLog(opts) {
3481
3648
  const timer = setTimeout(done, opts.durationMs);
3482
3649
  opts.abortSignal?.addEventListener("abort", onAbort, { once: true });
3483
3650
  void (async () => {
3651
+ recent = await recentEntryFilter({
3652
+ config: opts.config,
3653
+ deviceSerial: opts.deviceSerial,
3654
+ lookbackMs: opts.lookbackMs,
3655
+ logger: opts.logger ?? noopLogger
3656
+ });
3657
+ if (settled) return;
3484
3658
  const processId = await resolveProcessId(opts.config, opts.bundle, opts.deviceSerial);
3485
3659
  if (settled) return;
3486
3660
  const stream = streamHilog({ config: opts.config, processId, domain: opts.domain, deviceSerial: opts.deviceSerial });
@@ -3491,7 +3665,7 @@ function watchLog(opts) {
3491
3665
  buf = lines.pop() ?? "";
3492
3666
  for (const line of lines) {
3493
3667
  const entry = parseHilogLine(line);
3494
- if (entry && opts.pattern.test(entryText(entry))) matches.push(entry);
3668
+ if (entry && recent.test(entry) && opts.pattern.test(entryText(entry))) matches.push(entry);
3495
3669
  }
3496
3670
  });
3497
3671
  })().catch((err) => {
@@ -3776,6 +3950,8 @@ async function ensureOhModules(opts) {
3776
3950
  }
3777
3951
 
3778
3952
  // src/build/codelinter.ts
3953
+ import * as fs26 from "fs";
3954
+ import * as path26 from "path";
3779
3955
  var FINDING_RE = /^(.*?):(\d+):(?:(\d+):)?\s*(error|warning|warn|suggestion|info)\b[:\s-]*(.*)$/i;
3780
3956
  var TRAILING_CODE_RE = /[([]([\w@/.\-]+)[)\]]\s*$/;
3781
3957
  var FILE_HEADER_RE = /^(.+\.\w+)\((\d+)\)$/;
@@ -3862,6 +4038,16 @@ function parseCodelinterReport(output) {
3862
4038
  function parseCodelinterFindings(output) {
3863
4039
  return parseCodelinterReport(output).findings;
3864
4040
  }
4041
+ var PROJECT_ROOT_FILES = [
4042
+ ["build-profile.json5"],
4043
+ ["hvigorfile.ts", "hvigorfile.js"],
4044
+ [path26.join("hvigor", "hvigor-config.json5")]
4045
+ ];
4046
+ function missingProjectRootFiles(projectDir) {
4047
+ return PROJECT_ROOT_FILES.filter((names) => !names.some((f) => fs26.existsSync(path26.join(projectDir, f)))).map(
4048
+ (names) => names[0]
4049
+ );
4050
+ }
3865
4051
  async function runCodelinter(opts) {
3866
4052
  const res = await runProcess({
3867
4053
  command: getCmdToolsBin(opts.config, "codelinter"),
@@ -3879,7 +4065,7 @@ async function runCodelinter(opts) {
3879
4065
  var DIAGNOSIS_OUTPUT_LIMIT = 2e6;
3880
4066
  function diagnoseBuildFailure(output) {
3881
4067
  if (/is not exported from Kit|has no exported member .*@systemapi/i.test(output)) {
3882
- return "The project calls system APIs (`@systemapi`), which the public SDK leaves out. Build against the full OpenHarmony SDK: unpack its components into <root>/<os>/<api> of a separate SDK root and set ONIRO_SDK_ROOT_DIR to that root. Installing such an app also needs privileged signing (`oniro-app sign --apl system_core --acls \u2026 --app-privilege-capabilities \u2026`).";
4068
+ return `The project calls system APIs (\`@systemapi\`), which the public SDK leaves out. Build against the full OpenHarmony SDK: download an \`ohos-sdk-full\` archive from ${OHOS_DAILY_BUILDS_URL} and install it with \`oniro-app sdk install --from-archive <archive> --force\` (this replaces the public SDK of that API level; set ONIRO_SDK_ROOT_DIR to keep the two apart). Installing such an app also needs privileged signing (\`oniro-app sign --apl system_core --acls \u2026 --app-privilege-capabilities \u2026\`).`;
3883
4069
  }
3884
4070
  if (/00308018|SDK path does not exist|SDK license agreement is not accepted/i.test(output)) {
3885
4071
  return "hvigor cannot find the SDK the project compiles against: check `oniro-app sdk list` against compileSdkVersion.";
@@ -3951,21 +4137,21 @@ function isValidBundleName(bundleName) {
3951
4137
  }
3952
4138
 
3953
4139
  // src/project/templates.ts
3954
- import * as fs26 from "fs";
3955
- import * as path26 from "path";
4140
+ import * as fs27 from "fs";
4141
+ import * as path27 from "path";
3956
4142
  function toHumanTemplateName(folderName) {
3957
4143
  return folderName.replace(/([a-z])([A-Z])/g, "$1 $2");
3958
4144
  }
3959
4145
  function listTemplates(templateRoot) {
3960
- if (!fs26.existsSync(templateRoot)) return [];
3961
- const entries = fs26.readdirSync(templateRoot, { withFileTypes: true });
4146
+ if (!fs27.existsSync(templateRoot)) return [];
4147
+ const entries = fs27.readdirSync(templateRoot, { withFileTypes: true });
3962
4148
  return entries.filter((e) => e.isDirectory()).map((e) => {
3963
- const dir = path26.join(templateRoot, e.name);
3964
- const metaPath = path26.join(dir, "template.json");
4149
+ const dir = path27.join(templateRoot, e.name);
4150
+ const metaPath = path27.join(dir, "template.json");
3965
4151
  let meta = {};
3966
- if (fs26.existsSync(metaPath)) {
4152
+ if (fs27.existsSync(metaPath)) {
3967
4153
  try {
3968
- meta = JSON.parse(fs26.readFileSync(metaPath, "utf8"));
4154
+ meta = JSON.parse(fs27.readFileSync(metaPath, "utf8"));
3969
4155
  } catch {
3970
4156
  }
3971
4157
  }
@@ -3980,57 +4166,57 @@ function listTemplates(templateRoot) {
3980
4166
  function validateTemplateLayout(templateDir, defaultModuleName) {
3981
4167
  const required = [
3982
4168
  "build-profile.json5",
3983
- path26.join("AppScope", "app.json5"),
3984
- path26.join(defaultModuleName, "src", "main", "module.json5"),
3985
- path26.join(defaultModuleName, "oh-package.json5"),
4169
+ path27.join("AppScope", "app.json5"),
4170
+ path27.join(defaultModuleName, "src", "main", "module.json5"),
4171
+ path27.join(defaultModuleName, "oh-package.json5"),
3986
4172
  "hvigorfile.ts"
3987
4173
  ];
3988
- return required.filter((rel) => !fs26.existsSync(path26.join(templateDir, rel)));
4174
+ return required.filter((rel) => !fs27.existsSync(path27.join(templateDir, rel)));
3989
4175
  }
3990
4176
 
3991
4177
  // src/project/createScaffold.ts
3992
- import * as fs28 from "fs";
3993
- import * as path27 from "path";
4178
+ import * as fs29 from "fs";
4179
+ import * as path28 from "path";
3994
4180
 
3995
4181
  // src/project/jsonHelpers.ts
3996
- import * as fs27 from "fs";
4182
+ import * as fs28 from "fs";
3997
4183
  import JSON55 from "json5";
3998
4184
  function readJson5File(filePath) {
3999
- return JSON55.parse(fs27.readFileSync(filePath, "utf8"));
4185
+ return JSON55.parse(fs28.readFileSync(filePath, "utf8"));
4000
4186
  }
4001
4187
  function writeJson5File(filePath, value) {
4002
- fs27.writeFileSync(filePath, JSON.stringify(value, null, 2) + "\n", "utf8");
4188
+ fs28.writeFileSync(filePath, JSON.stringify(value, null, 2) + "\n", "utf8");
4003
4189
  }
4004
4190
  function readJsonFile(filePath) {
4005
- return JSON.parse(fs27.readFileSync(filePath, "utf8"));
4191
+ return JSON.parse(fs28.readFileSync(filePath, "utf8"));
4006
4192
  }
4007
4193
  function writeJsonFile(filePath, value) {
4008
- fs27.writeFileSync(filePath, JSON.stringify(value, null, 2) + "\n", "utf8");
4194
+ fs28.writeFileSync(filePath, JSON.stringify(value, null, 2) + "\n", "utf8");
4009
4195
  }
4010
4196
 
4011
4197
  // src/project/createScaffold.ts
4012
4198
  var IGNORED_DIRS = /* @__PURE__ */ new Set(["oh_modules", "node_modules", "build", ".hvigor"]);
4013
4199
  async function pathExists(p) {
4014
4200
  try {
4015
- await fs28.promises.access(p);
4201
+ await fs29.promises.access(p);
4016
4202
  return true;
4017
4203
  } catch {
4018
4204
  return false;
4019
4205
  }
4020
4206
  }
4021
4207
  async function copyDirRecursive(srcDir, destDir) {
4022
- await fs28.promises.mkdir(destDir, { recursive: true });
4023
- const entries = await fs28.promises.readdir(srcDir, { withFileTypes: true });
4208
+ await fs29.promises.mkdir(destDir, { recursive: true });
4209
+ const entries = await fs29.promises.readdir(srcDir, { withFileTypes: true });
4024
4210
  for (const entry of entries) {
4025
- const src = path27.join(srcDir, entry.name);
4026
- const dest = path27.join(destDir, entry.name);
4211
+ const src = path28.join(srcDir, entry.name);
4212
+ const dest = path28.join(destDir, entry.name);
4027
4213
  if (entry.isDirectory()) {
4028
4214
  await copyDirRecursive(src, dest);
4029
4215
  } else if (entry.isSymbolicLink()) {
4030
4216
  continue;
4031
4217
  } else if (entry.isFile()) {
4032
4218
  if (entry.name === "template.json") continue;
4033
- await fs28.promises.copyFile(src, dest);
4219
+ await fs29.promises.copyFile(src, dest);
4034
4220
  }
4035
4221
  }
4036
4222
  }
@@ -4038,9 +4224,9 @@ async function normalizeJson5ToJson(projectDir, logger) {
4038
4224
  const stack = [projectDir];
4039
4225
  while (stack.length > 0) {
4040
4226
  const currentDir = stack.pop();
4041
- const entries = await fs28.promises.readdir(currentDir, { withFileTypes: true });
4227
+ const entries = await fs29.promises.readdir(currentDir, { withFileTypes: true });
4042
4228
  for (const entry of entries) {
4043
- const fullPath = path27.join(currentDir, entry.name);
4229
+ const fullPath = path28.join(currentDir, entry.name);
4044
4230
  if (entry.isDirectory()) {
4045
4231
  if (IGNORED_DIRS.has(entry.name)) continue;
4046
4232
  stack.push(fullPath);
@@ -4062,23 +4248,23 @@ function toJavaPropertiesPath(p) {
4062
4248
  function createOrUpdateLocalProperties(projectDir, sdkDir) {
4063
4249
  const content = `sdk.dir=${toJavaPropertiesPath(sdkDir)}
4064
4250
  `;
4065
- fs28.writeFileSync(path27.join(projectDir, "local.properties"), content, "utf8");
4251
+ fs29.writeFileSync(path28.join(projectDir, "local.properties"), content, "utf8");
4066
4252
  }
4067
4253
  function renameIfExists(fromPath, toPath) {
4068
- if (fs28.existsSync(fromPath) && fromPath !== toPath) {
4069
- fs28.renameSync(fromPath, toPath);
4254
+ if (fs29.existsSync(fromPath) && fromPath !== toPath) {
4255
+ fs29.renameSync(fromPath, toPath);
4070
4256
  }
4071
4257
  }
4072
4258
  function updateTemplateConfigs(projectDir, args, sdkBaseDir) {
4073
- const appJsonPath = path27.join(projectDir, "AppScope", "app.json5");
4074
- if (fs28.existsSync(appJsonPath)) {
4259
+ const appJsonPath = path28.join(projectDir, "AppScope", "app.json5");
4260
+ if (fs29.existsSync(appJsonPath)) {
4075
4261
  const appJson = readJson5File(appJsonPath);
4076
4262
  appJson.app = appJson.app ?? {};
4077
4263
  appJson.app.bundleName = args.bundleName;
4078
4264
  writeJson5File(appJsonPath, appJson);
4079
4265
  }
4080
- const stringsPath = path27.join(projectDir, "AppScope", "resources", "base", "element", "string.json");
4081
- if (fs28.existsSync(stringsPath)) {
4266
+ const stringsPath = path28.join(projectDir, "AppScope", "resources", "base", "element", "string.json");
4267
+ if (fs29.existsSync(stringsPath)) {
4082
4268
  const strings = readJsonFile(stringsPath);
4083
4269
  if (Array.isArray(strings.string)) {
4084
4270
  const appName = strings.string.find((s) => s.name === "app_name");
@@ -4086,8 +4272,8 @@ function updateTemplateConfigs(projectDir, args, sdkBaseDir) {
4086
4272
  }
4087
4273
  writeJsonFile(stringsPath, strings);
4088
4274
  }
4089
- const buildProfilePath = path27.join(projectDir, "build-profile.json5");
4090
- if (fs28.existsSync(buildProfilePath)) {
4275
+ const buildProfilePath = path28.join(projectDir, "build-profile.json5");
4276
+ if (fs29.existsSync(buildProfilePath)) {
4091
4277
  const buildProfile = readJson5File(buildProfilePath);
4092
4278
  buildProfile.app = buildProfile.app ?? {};
4093
4279
  if (Array.isArray(buildProfile.app.products) && buildProfile.app.products.length > 0) {
@@ -4100,23 +4286,23 @@ function updateTemplateConfigs(projectDir, args, sdkBaseDir) {
4100
4286
  }
4101
4287
  writeJson5File(buildProfilePath, buildProfile);
4102
4288
  }
4103
- const moduleJsonPath = path27.join(projectDir, args.moduleName, "src", "main", "module.json5");
4104
- if (fs28.existsSync(moduleJsonPath)) {
4289
+ const moduleJsonPath = path28.join(projectDir, args.moduleName, "src", "main", "module.json5");
4290
+ if (fs29.existsSync(moduleJsonPath)) {
4105
4291
  const moduleJson = readJson5File(moduleJsonPath);
4106
4292
  moduleJson.module = moduleJson.module ?? {};
4107
4293
  moduleJson.module.name = args.moduleName;
4108
4294
  writeJson5File(moduleJsonPath, moduleJson);
4109
4295
  }
4110
- const moduleOhPackagePath = path27.join(projectDir, args.moduleName, "oh-package.json5");
4111
- if (fs28.existsSync(moduleOhPackagePath)) {
4296
+ const moduleOhPackagePath = path28.join(projectDir, args.moduleName, "oh-package.json5");
4297
+ if (fs29.existsSync(moduleOhPackagePath)) {
4112
4298
  const pkg = readJson5File(moduleOhPackagePath);
4113
4299
  pkg.name = args.moduleName;
4114
4300
  writeJson5File(moduleOhPackagePath, pkg);
4115
4301
  }
4116
- const vscodeDir = path27.join(projectDir, ".vscode");
4117
- fs28.mkdirSync(vscodeDir, { recursive: true });
4302
+ const vscodeDir = path28.join(projectDir, ".vscode");
4303
+ fs29.mkdirSync(vscodeDir, { recursive: true });
4118
4304
  const hapPath = `${args.moduleName}/build/default/outputs/default/${args.moduleName}-default-signed.hap`;
4119
- writeJsonFile(path27.join(vscodeDir, "settings.json"), {
4305
+ writeJsonFile(path28.join(vscodeDir, "settings.json"), {
4120
4306
  "oniro.hapPath": hapPath,
4121
4307
  "files.associations": { "*.json5": "jsonc" }
4122
4308
  });
@@ -4133,7 +4319,7 @@ async function createScaffold(opts) {
4133
4319
  if (!opts.location || !await pathExists(opts.location)) {
4134
4320
  throw new OniroError(`Location does not exist: ${opts.location}`);
4135
4321
  }
4136
- const templateDir = path27.join(opts.templateRoot, opts.templateId);
4322
+ const templateDir = path28.join(opts.templateRoot, opts.templateId);
4137
4323
  if (!await pathExists(templateDir)) {
4138
4324
  throw new OniroError(`Template not found: ${templateDir}`);
4139
4325
  }
@@ -4168,17 +4354,17 @@ async function createScaffold(opts) {
4168
4354
  }
4169
4355
  }
4170
4356
  }
4171
- const projectDir = path27.join(opts.location, opts.projectName);
4357
+ const projectDir = path28.join(opts.location, opts.projectName);
4172
4358
  if (await pathExists(projectDir)) {
4173
4359
  if (!opts.overwrite) {
4174
4360
  throw new OniroError(`Destination already exists: ${projectDir}. Pass overwrite: true to replace it.`);
4175
4361
  }
4176
- await fs28.promises.rm(projectDir, { recursive: true, force: true });
4362
+ await fs29.promises.rm(projectDir, { recursive: true, force: true });
4177
4363
  }
4178
4364
  logger.info(`[create] Scaffolding ${opts.templateId} at ${projectDir}`);
4179
4365
  await copyDirRecursive(templateDir, projectDir);
4180
4366
  if (moduleName !== defaultModuleName) {
4181
- renameIfExists(path27.join(projectDir, defaultModuleName), path27.join(projectDir, moduleName));
4367
+ renameIfExists(path28.join(projectDir, defaultModuleName), path28.join(projectDir, moduleName));
4182
4368
  }
4183
4369
  updateTemplateConfigs(
4184
4370
  projectDir,
@@ -4195,11 +4381,11 @@ async function createScaffold(opts) {
4195
4381
  }
4196
4382
 
4197
4383
  // src/project/listModules.ts
4198
- import * as fs29 from "fs";
4199
- import * as path28 from "path";
4384
+ import * as fs30 from "fs";
4385
+ import * as path29 from "path";
4200
4386
  function listModules(opts) {
4201
- const buildProfile = path28.join(opts.projectDir, "build-profile.json5");
4202
- if (!fs29.existsSync(buildProfile)) {
4387
+ const buildProfile = path29.join(opts.projectDir, "build-profile.json5");
4388
+ if (!fs30.existsSync(buildProfile)) {
4203
4389
  throw new OniroError(`build-profile.json5 not found at ${buildProfile}`);
4204
4390
  }
4205
4391
  const parsed = readJson5File(buildProfile);
@@ -4211,12 +4397,12 @@ function listModules(opts) {
4211
4397
  }
4212
4398
 
4213
4399
  // src/project/abilities.ts
4214
- import * as fs30 from "fs";
4215
- import * as path29 from "path";
4400
+ import * as fs31 from "fs";
4401
+ import * as path30 from "path";
4216
4402
  function listAbilities(opts) {
4217
4403
  const moduleName = opts.moduleName ?? "entry";
4218
- const moduleJsonPath = path29.join(opts.projectDir, moduleName, "src", "main", "module.json5");
4219
- if (!fs30.existsSync(moduleJsonPath)) {
4404
+ const moduleJsonPath = path30.join(opts.projectDir, moduleName, "src", "main", "module.json5");
4405
+ if (!fs31.existsSync(moduleJsonPath)) {
4220
4406
  throw new OniroError(`Could not find module.json5 at ${moduleJsonPath}`);
4221
4407
  }
4222
4408
  const parsed = readJson5File(moduleJsonPath);
@@ -4224,12 +4410,12 @@ function listAbilities(opts) {
4224
4410
  }
4225
4411
 
4226
4412
  // src/project/prepareSigning.ts
4227
- import * as fs31 from "fs";
4228
- import * as path30 from "path";
4413
+ import * as fs32 from "fs";
4414
+ import * as path31 from "path";
4229
4415
  function hasSigningArtifacts(dir) {
4230
- if (!fs31.existsSync(path30.join(dir, "signatures"))) return false;
4231
- const buildProfile = path30.join(dir, "build-profile.json5");
4232
- if (!fs31.existsSync(buildProfile)) return false;
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;
4233
4419
  try {
4234
4420
  const parsed = readJson5File(buildProfile);
4235
4421
  return Array.isArray(parsed.app?.signingConfigs) && parsed.app.signingConfigs.length > 0;
@@ -4332,8 +4518,25 @@ async function diffHapAssets(opts) {
4332
4518
  }
4333
4519
 
4334
4520
  // src/install/apply.ts
4335
- import * as fs32 from "fs";
4336
- import * as path31 from "path";
4521
+ import * as fs33 from "fs";
4522
+ import * as path32 from "path";
4523
+
4524
+ // src/install/hapBundleName.ts
4525
+ function bundleNameFromHapConfig(text) {
4526
+ try {
4527
+ const parsed = JSON.parse(text.replace(/^/, ""));
4528
+ const name = parsed.app?.bundleName;
4529
+ return typeof name === "string" && name ? name : null;
4530
+ } catch {
4531
+ return null;
4532
+ }
4533
+ }
4534
+ async function readHapBundleName(hapPath) {
4535
+ const config = await readZipEntry(hapPath, (n) => n === "module.json") ?? await readZipEntry(hapPath, (n) => n === "config.json");
4536
+ return config ? bundleNameFromHapConfig(config.toString("utf8")) : null;
4537
+ }
4538
+
4539
+ // src/install/apply.ts
4337
4540
  function decideInstallMethod(s) {
4338
4541
  if (!s.signInfoInconsistent) return "replace";
4339
4542
  if (s.isSystemBundle && !s.allowUninstall) return "refuse";
@@ -4354,7 +4557,7 @@ async function pidOf(config, bundle, deviceSerial) {
4354
4557
  async function resolveHap(opts, logger) {
4355
4558
  let hap;
4356
4559
  if (opts.hapPath) {
4357
- const hapPath = !path31.isAbsolute(opts.hapPath) && opts.projectDir ? path31.join(opts.projectDir, opts.hapPath) : opts.hapPath;
4560
+ const hapPath = !path32.isAbsolute(opts.hapPath) && opts.projectDir ? path32.join(opts.projectDir, opts.hapPath) : opts.hapPath;
4358
4561
  hap = existingHapOrUnsigned(hapPath);
4359
4562
  if (!hap) throw new OniroError(`HAP not found at ${hapPath}.`);
4360
4563
  } else {
@@ -4363,7 +4566,7 @@ async function resolveHap(opts, logger) {
4363
4566
  if (!picked) throw new OniroError(`No HAP found${opts.module ? ` for module ${opts.module}` : ""}. Build first.`);
4364
4567
  hap = { path: picked.path, fallback: !picked.signed };
4365
4568
  }
4366
- if (hap.fallback) logger.warn(`No signed HAP found; installing the unsigned ${path31.basename(hap.path)}.`);
4569
+ if (hap.fallback) logger.warn(`No signed HAP found; installing the unsigned ${path32.basename(hap.path)}.`);
4367
4570
  return hap;
4368
4571
  }
4369
4572
  async function applyChanges(opts) {
@@ -4373,9 +4576,18 @@ async function applyChanges(opts) {
4373
4576
  const allowUninstall = opts.allowUninstall ?? false;
4374
4577
  const hap = await resolveHap(opts, logger);
4375
4578
  const hapPath = hap.path;
4579
+ const hapBundle = await readHapBundleName(hapPath).catch((err) => {
4580
+ logger.debug(`bundle name check skipped: ${err.message}`);
4581
+ return null;
4582
+ });
4583
+ if (hapBundle && hapBundle !== bundle) {
4584
+ 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.`
4586
+ );
4587
+ }
4376
4588
  logger.info(`installing ${hapPath}`);
4377
4589
  let assetsChanged = false;
4378
- if (opts.installedHapPath && fs32.existsSync(opts.installedHapPath)) {
4590
+ if (opts.installedHapPath && fs33.existsSync(opts.installedHapPath)) {
4379
4591
  try {
4380
4592
  const diff = await diffHapAssets({ installedHap: opts.installedHapPath, newHap: hapPath });
4381
4593
  assetsChanged = diff.addedAssetPaths.length > 0 || diff.removedAssetPaths.length > 0;
@@ -4408,7 +4620,7 @@ async function applyChanges(opts) {
4408
4620
  }
4409
4621
  if (!await isBundleInstalled({ config, bundle, deviceSerial })) {
4410
4622
  throw new OniroError(
4411
- `hdc reported ${path31.basename(hapPath)} as installed, but bm dump does not list ${bundle}. Check that the HAP was built from this project (its bundleName must match).`
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).`
4412
4624
  );
4413
4625
  }
4414
4626
  let postInstallPid = await pidOf(config, bundle, deviceSerial);
@@ -4485,9 +4697,9 @@ var AUTH_PATHS = {
4485
4697
  };
4486
4698
 
4487
4699
  // src/harmonyos/auth/tokenStore.ts
4488
- import * as fs33 from "fs";
4700
+ import * as fs34 from "fs";
4489
4701
  import * as os11 from "os";
4490
- import * as path32 from "path";
4702
+ import * as path33 from "path";
4491
4703
  import * as crypto3 from "crypto";
4492
4704
  var ALGORITHM = "aes-256-gcm";
4493
4705
  var KEY_LENGTH = 32;
@@ -4523,7 +4735,7 @@ function permissionHint(dir) {
4523
4735
  }
4524
4736
  function ensureDir(dir) {
4525
4737
  try {
4526
- fs33.mkdirSync(dir, { recursive: true, mode: DIR_MODE });
4738
+ fs34.mkdirSync(dir, { recursive: true, mode: DIR_MODE });
4527
4739
  } catch (err) {
4528
4740
  if (err.code === "EACCES") {
4529
4741
  throw new OniroError(permissionHint(dir), err);
@@ -4534,17 +4746,17 @@ function ensureDir(dir) {
4534
4746
  function createTokenStore(opts) {
4535
4747
  const logger = opts.logger ?? noopLogger;
4536
4748
  const authDir = opts.config.get("harmonyosAuthDir", defaultPaths.harmonyosAuthDir());
4537
- const tokenPath = path32.join(authDir, TOKEN_FILE);
4538
- const keyPath = path32.join(os11.homedir(), ".local", "share", "oniro-app", "keys", KEY_FILE);
4749
+ const tokenPath = path33.join(authDir, TOKEN_FILE);
4750
+ const keyPath = path33.join(os11.homedir(), ".local", "share", "oniro-app", "keys", KEY_FILE);
4539
4751
  function loadOrCreateKey() {
4540
- if (fs33.existsSync(keyPath)) {
4541
- const existing = fs33.readFileSync(keyPath);
4752
+ if (fs34.existsSync(keyPath)) {
4753
+ const existing = fs34.readFileSync(keyPath);
4542
4754
  if (existing.length === KEY_LENGTH) return existing;
4543
4755
  logger.warn("[harmonyos] The token key file was malformed; regenerating (you will need to log in again).");
4544
4756
  }
4545
- ensureDir(path32.dirname(keyPath));
4757
+ ensureDir(path33.dirname(keyPath));
4546
4758
  const key = crypto3.randomBytes(KEY_LENGTH);
4547
- fs33.writeFileSync(keyPath, key, { mode: FILE_MODE });
4759
+ fs34.writeFileSync(keyPath, key, { mode: FILE_MODE });
4548
4760
  return key;
4549
4761
  }
4550
4762
  return {
@@ -4554,12 +4766,12 @@ function createTokenStore(opts) {
4554
4766
  const payload = Buffer.from(JSON.stringify(session), "utf8");
4555
4767
  const blob = encrypt2(loadOrCreateKey(), payload);
4556
4768
  ensureDir(authDir);
4557
- fs33.writeFileSync(tokenPath, JSON.stringify(blob, null, 2), { mode: FILE_MODE });
4769
+ fs34.writeFileSync(tokenPath, JSON.stringify(blob, null, 2), { mode: FILE_MODE });
4558
4770
  },
4559
4771
  load() {
4560
- if (!fs33.existsSync(tokenPath)) return null;
4772
+ if (!fs34.existsSync(tokenPath)) return null;
4561
4773
  try {
4562
- const blob = JSON.parse(fs33.readFileSync(tokenPath, "utf8"));
4774
+ const blob = JSON.parse(fs34.readFileSync(tokenPath, "utf8"));
4563
4775
  if (!isEncryptedBlob(blob)) return null;
4564
4776
  const { jwtToken, siteId } = JSON.parse(decrypt2(loadOrCreateKey(), blob).toString("utf8"));
4565
4777
  return typeof jwtToken === "string" && jwtToken && typeof siteId === "string" ? { jwtToken, siteId } : null;
@@ -4571,7 +4783,7 @@ function createTokenStore(opts) {
4571
4783
  }
4572
4784
  logger.debug(`[harmonyos] Discarding unreadable token at ${tokenPath}.`);
4573
4785
  try {
4574
- fs33.rmSync(tokenPath, { force: true });
4786
+ fs34.rmSync(tokenPath, { force: true });
4575
4787
  } catch {
4576
4788
  }
4577
4789
  return null;
@@ -4579,7 +4791,7 @@ function createTokenStore(opts) {
4579
4791
  },
4580
4792
  clear() {
4581
4793
  try {
4582
- fs33.rmSync(tokenPath, { force: true });
4794
+ fs34.rmSync(tokenPath, { force: true });
4583
4795
  } catch (err) {
4584
4796
  throw new OniroError(`Failed to remove ${tokenPath}.`, err);
4585
4797
  }
@@ -5060,7 +5272,7 @@ function mapAgcError(operation, statusCode, reasonPhrase, body) {
5060
5272
  }
5061
5273
 
5062
5274
  // src/harmonyos/signing/profile.ts
5063
- import * as fs34 from "fs";
5275
+ import * as fs35 from "fs";
5064
5276
  import { X509Certificate } from "crypto";
5065
5277
  import forge from "node-forge";
5066
5278
  function extractProfileJson(buffer) {
@@ -5092,7 +5304,7 @@ function extractProfileJson(buffer) {
5092
5304
  function readProvisionProfile(profilePath) {
5093
5305
  let raw;
5094
5306
  try {
5095
- const json = extractProfileJson(fs34.readFileSync(profilePath));
5307
+ const json = extractProfileJson(fs35.readFileSync(profilePath));
5096
5308
  if (!json) return null;
5097
5309
  raw = JSON.parse(json);
5098
5310
  } catch {
@@ -5121,7 +5333,7 @@ function parseCertificate(pem) {
5121
5333
  }
5122
5334
  function keystoreHoldsKey(p12Path, keyAlias, keyPwd, certificate) {
5123
5335
  try {
5124
- const der = forge.util.createBuffer(fs34.readFileSync(p12Path).toString("binary"));
5336
+ const der = forge.util.createBuffer(fs35.readFileSync(p12Path).toString("binary"));
5125
5337
  const p12 = forge.pkcs12.pkcs12FromAsn1(forge.asn1.fromDer(der), keyPwd);
5126
5338
  const certBag = forge.pki.oids.certBag ?? "1.2.840.113549.1.12.10.1.3";
5127
5339
  const wanted = certificate.publicKey.export({ type: "spki", format: "der" });
@@ -5137,7 +5349,7 @@ function keystoreHoldsKey(p12Path, keyAlias, keyPwd, certificate) {
5137
5349
  }
5138
5350
  }
5139
5351
  function verifySigningMaterial(opts) {
5140
- const pems = fs34.readFileSync(opts.cerPath, "utf8").match(/-----BEGIN CERTIFICATE-----[\s\S]*?-----END CERTIFICATE-----/g);
5352
+ const pems = fs35.readFileSync(opts.cerPath, "utf8").match(/-----BEGIN CERTIFICATE-----[\s\S]*?-----END CERTIFICATE-----/g);
5141
5353
  const chain = (pems ?? []).map(parseCertificate);
5142
5354
  const leaf = chain.find((c) => !c.ca) ?? chain.at(-1);
5143
5355
  if (!leaf) throw new AgcError(SIGNING_ERRORS.CERT_INVALID);
@@ -5156,14 +5368,14 @@ function verifySigningMaterial(opts) {
5156
5368
 
5157
5369
  // src/harmonyos/signing/autoSign.ts
5158
5370
  import * as crypto9 from "crypto";
5159
- import * as fs39 from "fs";
5160
- import * as path38 from "path";
5371
+ import * as fs40 from "fs";
5372
+ import * as path39 from "path";
5161
5373
  import JSON57 from "json5";
5162
5374
 
5163
5375
  // src/harmonyos/signing/agc.ts
5164
5376
  import * as crypto6 from "crypto";
5165
- import * as fs35 from "fs";
5166
- import * as path33 from "path";
5377
+ import * as fs36 from "fs";
5378
+ import * as path34 from "path";
5167
5379
  var ENDPOINTS = {
5168
5380
  CERT_LIST: "/api/cps/harmony-cert-manage/v1/cert/list",
5169
5381
  CERT_ADD: "/api/cps/harmony-cert-manage/v1/cert/add",
@@ -5249,8 +5461,8 @@ function createAgcClient(http3, auth) {
5249
5461
  if (target.sha256 && actual !== target.sha256.toLowerCase()) {
5250
5462
  throw new ChecksumMismatchError(target.sha256, actual);
5251
5463
  }
5252
- fs35.mkdirSync(path33.dirname(dest), { recursive: true, mode: 448 });
5253
- fs35.writeFileSync(dest, buffer, { mode: 384 });
5464
+ fs36.mkdirSync(path34.dirname(dest), { recursive: true, mode: 448 });
5465
+ fs36.writeFileSync(dest, buffer, { mode: 384 });
5254
5466
  }
5255
5467
  };
5256
5468
  }
@@ -5301,22 +5513,22 @@ async function registerDevices(agc, connected, logger) {
5301
5513
 
5302
5514
  // src/harmonyos/signing/keystore.ts
5303
5515
  import * as crypto7 from "crypto";
5304
- import * as fs37 from "fs";
5305
- import * as path35 from "path";
5516
+ import * as fs38 from "fs";
5517
+ import * as path36 from "path";
5306
5518
 
5307
5519
  // src/harmonyos/java.ts
5308
- import * as fs36 from "fs";
5309
- import * as path34 from "path";
5520
+ import * as fs37 from "fs";
5521
+ import * as path35 from "path";
5310
5522
  var JAVA_BIN = process.platform === "win32" ? "java.exe" : "java";
5311
5523
  function findJava(sdk, env = process.env) {
5312
5524
  const candidates = [
5313
- env.JAVA_HOME && path34.join(env.JAVA_HOME, "bin", JAVA_BIN),
5314
- sdk && path34.join(sdk.installRoot, "jbr", "bin", JAVA_BIN),
5525
+ env.JAVA_HOME && path35.join(env.JAVA_HOME, "bin", JAVA_BIN),
5526
+ sdk && path35.join(sdk.installRoot, "jbr", "bin", JAVA_BIN),
5315
5527
  // macOS: the JBR is itself a bundle inside Studio's Contents.
5316
- sdk && path34.join(sdk.installRoot, "jbr", "Contents", "Home", "bin", JAVA_BIN),
5317
- ...(env.PATH ?? "").split(path34.delimiter).map((dir) => dir && path34.join(dir, JAVA_BIN))
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))
5318
5530
  ];
5319
- const found = candidates.find((c) => !!c && fs36.existsSync(c));
5531
+ const found = candidates.find((c) => !!c && fs37.existsSync(c));
5320
5532
  if (found) return found;
5321
5533
  throw new OniroError(
5322
5534
  "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."
@@ -5330,13 +5542,13 @@ function redactArgs(args) {
5330
5542
  return args.map((arg, i) => args[i - 1] === "-keyPwd" || args[i - 1] === "-keystorePwd" ? "******" : arg);
5331
5543
  }
5332
5544
  async function generateKeystoreAndCsr(opts) {
5333
- const jar = path35.join(opts.sdk.sdkPath, "default", "openharmony", "toolchains", "lib", "hap-sign-tool.jar");
5334
- if (!fs37.existsSync(jar)) {
5545
+ const jar = path36.join(opts.sdk.sdkPath, "default", "openharmony", "toolchains", "lib", "hap-sign-tool.jar");
5546
+ if (!fs38.existsSync(jar)) {
5335
5547
  throw new OniroError(`hap-sign-tool.jar not found at ${jar}; the HarmonyOS SDK looks incomplete.`);
5336
5548
  }
5337
5549
  const java = findJava(opts.sdk);
5338
5550
  const keyPwd = crypto7.randomBytes(12).toString("base64url").replaceAll(/[-_]/g, "");
5339
- fs37.mkdirSync(path35.dirname(opts.p12Path), { recursive: true, mode: 448 });
5551
+ fs38.mkdirSync(path36.dirname(opts.p12Path), { recursive: true, mode: 448 });
5340
5552
  const run = async (what, args) => {
5341
5553
  const argv = ["-jar", jar, ...args, "-keyAlias", KEY_ALIAS, "-keystoreFile", opts.p12Path, "-keystorePwd", keyPwd, "-keyPwd", keyPwd];
5342
5554
  opts.logger.debug(`[harmonyos] ${java} ${redactArgs(argv).join(" ")}`);
@@ -5350,28 +5562,28 @@ ${detail}` : ""}`);
5350
5562
  opts.logger.info("[harmonyos] Generating the local keystore and certificate signing request...");
5351
5563
  await run("generate the key pair", ["generate-keypair", "-keyAlg", "ECC", "-keySize", "NIST-P-256"]);
5352
5564
  await run("generate the CSR", ["generate-csr", "-subject", "CN=DebugKey", "-signAlg", SIGN_ALG, "-outFile", opts.csrPath]);
5353
- return { keyPwd, csr: fs37.readFileSync(opts.csrPath, "utf8") };
5565
+ return { keyPwd, csr: fs38.readFileSync(opts.csrPath, "utf8") };
5354
5566
  }
5355
5567
 
5356
5568
  // src/harmonyos/signing/paths.ts
5357
5569
  import * as crypto8 from "crypto";
5358
- import * as path36 from "path";
5570
+ import * as path37 from "path";
5359
5571
  function sanitizeNamePart(raw) {
5360
5572
  const safe = (raw?.trim() ?? "").replace(/[\\/:*?"<>|=.-]+/g, "_");
5361
5573
  return safe.slice(0, 64) || "default";
5362
5574
  }
5363
5575
  function resolveSigningMaterialPaths(config, productName, projectRoot) {
5364
5576
  const hash = crypto8.createHash("sha256").update(projectRoot, "utf8").digest("base64url").replaceAll(/[-_]/g, "");
5365
- const base = path36.join(
5577
+ const base = path37.join(
5366
5578
  config.get("harmonyosSigningDir", defaultPaths.harmonyosSigningDir()),
5367
- `${sanitizeNamePart(productName)}_${path36.basename(projectRoot)}_${hash}=`
5579
+ `${sanitizeNamePart(productName)}_${path37.basename(projectRoot)}_${hash}=`
5368
5580
  );
5369
5581
  return { p12Path: `${base}.p12`, csrPath: `${base}.csr`, cerPath: `${base}.cer`, profilePath: `${base}.p7b` };
5370
5582
  }
5371
5583
 
5372
5584
  // src/harmonyos/signing/project.ts
5373
- import * as fs38 from "fs";
5374
- import * as path37 from "path";
5585
+ import * as fs39 from "fs";
5586
+ import * as path38 from "path";
5375
5587
  import JSON56 from "json5";
5376
5588
 
5377
5589
  // src/harmonyos/signing/aclPermissionList.ts
@@ -5439,9 +5651,9 @@ var ACL_PERMISSIONS = /* @__PURE__ */ new Set([
5439
5651
 
5440
5652
  // src/harmonyos/signing/project.ts
5441
5653
  function readRequestedPermissions(moduleConfigPath) {
5442
- if (!fs38.existsSync(moduleConfigPath)) return [];
5654
+ if (!fs39.existsSync(moduleConfigPath)) return [];
5443
5655
  try {
5444
- const parsed = JSON56.parse(fs38.readFileSync(moduleConfigPath, "utf-8"));
5656
+ const parsed = JSON56.parse(fs39.readFileSync(moduleConfigPath, "utf-8"));
5445
5657
  return (parsed.module?.requestPermissions ?? []).map((p) => p?.name).filter((n) => typeof n === "string" && n.length > 0);
5446
5658
  } catch {
5447
5659
  return [];
@@ -5450,10 +5662,10 @@ function readRequestedPermissions(moduleConfigPath) {
5450
5662
  function collectAclPermissions(projectDir, logger = noopLogger) {
5451
5663
  const requested = /* @__PURE__ */ new Set();
5452
5664
  for (const module of listModules({ projectDir })) {
5453
- const moduleRoot = path37.resolve(projectDir, module.srcPath);
5665
+ const moduleRoot = path38.resolve(projectDir, module.srcPath);
5454
5666
  for (const sourceSet of ["main", "ohosTest"]) {
5455
5667
  for (const name of readRequestedPermissions(
5456
- path37.join(moduleRoot, "src", sourceSet, "module.json5")
5668
+ path38.join(moduleRoot, "src", sourceSet, "module.json5")
5457
5669
  )) {
5458
5670
  requested.add(name);
5459
5671
  }
@@ -5469,7 +5681,7 @@ function collectAclPermissions(projectDir, logger = noopLogger) {
5469
5681
  // src/harmonyos/signing/autoSign.ts
5470
5682
  function shouldRegenerate(input) {
5471
5683
  if (input.force) return "--force was passed";
5472
- const missing = Object.values(input.paths).find((file) => !fs39.existsSync(file));
5684
+ const missing = Object.values(input.paths).find((file) => !fs40.existsSync(file));
5473
5685
  if (missing) return `${missing} is missing`;
5474
5686
  const config = input.buildProfile.app?.signingConfigs?.find((c) => c.name === input.productName);
5475
5687
  if (config?.type !== "HarmonyOS" || config.material?.profile !== input.paths.profilePath) {
@@ -5508,16 +5720,16 @@ function writeSigningConfig(opts) {
5508
5720
  for (const product of app.products ?? []) {
5509
5721
  if (product.name === opts.productName) product.signingConfig = opts.productName;
5510
5722
  }
5511
- fs39.writeFileSync(opts.buildProfilePath, JSON.stringify(opts.buildProfile, null, 2));
5723
+ fs40.writeFileSync(opts.buildProfilePath, JSON.stringify(opts.buildProfile, null, 2));
5512
5724
  }
5513
5725
  async function harmonyOsAutoSign(opts) {
5514
5726
  const logger = opts.logger ?? noopLogger;
5515
5727
  const productName = opts.productName ?? "default";
5516
- const projectDir = path38.resolve(opts.projectDir);
5517
- const buildProfilePath = path38.join(projectDir, "build-profile.json5");
5728
+ const projectDir = path39.resolve(opts.projectDir);
5729
+ const buildProfilePath = path39.join(projectDir, "build-profile.json5");
5518
5730
  let buildProfile;
5519
5731
  try {
5520
- buildProfile = JSON57.parse(fs39.readFileSync(buildProfilePath, "utf-8"));
5732
+ buildProfile = JSON57.parse(fs40.readFileSync(buildProfilePath, "utf-8"));
5521
5733
  } catch (err) {
5522
5734
  throw new OniroError(`Could not read ${buildProfilePath}: ${err.message}`, err);
5523
5735
  }
@@ -5557,7 +5769,7 @@ async function harmonyOsAutoSign(opts) {
5557
5769
  logger.info(`[harmonyos] Regenerating signing material: ${reason}.`);
5558
5770
  const agc = createAgcClient(opts.http ?? createHttpClient({ logger }), auth);
5559
5771
  const deviceIds = await registerDevices(agc, connected, logger);
5560
- for (const file of Object.values(paths)) fs39.rmSync(file, { force: true });
5772
+ for (const file of Object.values(paths)) fs40.rmSync(file, { force: true });
5561
5773
  const certName = `oniro_debug_${auth.teamId.replace(/[\\/.:]/g, "")}.cer`;
5562
5774
  const stale = (await agc.listCertificates()).find((c) => c.certName === certName);
5563
5775
  if (stale) {
@@ -5588,10 +5800,10 @@ async function harmonyOsAutoSign(opts) {
5588
5800
  try {
5589
5801
  verifySigningMaterial({ ...paths, keyAlias: KEY_ALIAS, keyPwd });
5590
5802
  } catch (err) {
5591
- fs39.rmSync(paths.profilePath, { force: true });
5803
+ fs40.rmSync(paths.profilePath, { force: true });
5592
5804
  throw err;
5593
5805
  }
5594
- const materialDir = path38.join(path38.dirname(paths.p12Path), "material");
5806
+ const materialDir = path39.join(path39.dirname(paths.p12Path), "material");
5595
5807
  try {
5596
5808
  getKey(materialDir);
5597
5809
  } catch {
@@ -5603,7 +5815,7 @@ async function harmonyOsAutoSign(opts) {
5603
5815
  }
5604
5816
 
5605
5817
  // src/index.ts
5606
- var VERSION = "0.14.0";
5818
+ var VERSION = "0.15.0";
5607
5819
  export {
5608
5820
  ALL_SDKS,
5609
5821
  APL_VALUES,
@@ -5617,6 +5829,7 @@ export {
5617
5829
  HUAWEI_SITES,
5618
5830
  INSTALL_TMP_DIRNAME,
5619
5831
  InsufficientSpaceError,
5832
+ OHOS_DAILY_BUILDS_URL,
5620
5833
  OHOS_URL_BASE,
5621
5834
  OniroError,
5622
5835
  SIGNING_ERRORS,
@@ -5637,6 +5850,7 @@ export {
5637
5850
  buildHvigorwArgs,
5638
5851
  buildInputCommand,
5639
5852
  buildRawTouchCommand,
5853
+ bundleNameFromHapConfig,
5640
5854
  captureBurst,
5641
5855
  captureLog,
5642
5856
  cleanCodelinterOutput,
@@ -5690,6 +5904,7 @@ export {
5690
5904
  findLayoutNodes,
5691
5905
  findMissingProjectSdk,
5692
5906
  findRunningProcess,
5907
+ findSdkOsDir,
5693
5908
  flattenLayout,
5694
5909
  forceStop,
5695
5910
  formatBytes,
@@ -5723,10 +5938,12 @@ export {
5723
5938
  hdcTargetArgs,
5724
5939
  hdcTargetArgsFor,
5725
5940
  hideKeyboard,
5941
+ hilogTimeMs,
5726
5942
  installApp,
5727
5943
  installCmdTools,
5728
5944
  installEmulator,
5729
5945
  installFailureReason,
5946
+ installSdkFromArchive,
5730
5947
  isBundleInstalled,
5731
5948
  isCmdToolsInstalled,
5732
5949
  isEmulatorInstalled,
@@ -5743,6 +5960,7 @@ export {
5743
5960
  listRunningProcesses,
5744
5961
  listTemplates,
5745
5962
  locateTarget,
5963
+ missingProjectRootFiles,
5746
5964
  needsCmdWrapper,
5747
5965
  nonInteractivePrompter,
5748
5966
  noopLogger,
@@ -5766,9 +5984,11 @@ export {
5766
5984
  pickTarget,
5767
5985
  prepareSigning,
5768
5986
  pruneLayout,
5987
+ readHapBundleName,
5769
5988
  readJson5File,
5770
5989
  readJsonFile,
5771
5990
  readProvisionProfile,
5991
+ readZipEntry,
5772
5992
  reboot,
5773
5993
  recvFile,
5774
5994
  removeCmdTools,