@oniroproject/core 0.14.1 → 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.cjs CHANGED
@@ -42,6 +42,7 @@ __export(index_exports, {
42
42
  HUAWEI_SITES: () => HUAWEI_SITES,
43
43
  INSTALL_TMP_DIRNAME: () => INSTALL_TMP_DIRNAME,
44
44
  InsufficientSpaceError: () => InsufficientSpaceError,
45
+ OHOS_DAILY_BUILDS_URL: () => OHOS_DAILY_BUILDS_URL,
45
46
  OHOS_URL_BASE: () => OHOS_URL_BASE,
46
47
  OniroError: () => OniroError,
47
48
  SIGNING_ERRORS: () => SIGNING_ERRORS,
@@ -62,6 +63,7 @@ __export(index_exports, {
62
63
  buildHvigorwArgs: () => buildHvigorwArgs,
63
64
  buildInputCommand: () => buildInputCommand,
64
65
  buildRawTouchCommand: () => buildRawTouchCommand,
66
+ bundleNameFromHapConfig: () => bundleNameFromHapConfig,
65
67
  captureBurst: () => captureBurst,
66
68
  captureLog: () => captureLog,
67
69
  cleanCodelinterOutput: () => cleanCodelinterOutput,
@@ -115,6 +117,7 @@ __export(index_exports, {
115
117
  findLayoutNodes: () => findLayoutNodes,
116
118
  findMissingProjectSdk: () => findMissingProjectSdk,
117
119
  findRunningProcess: () => findRunningProcess,
120
+ findSdkOsDir: () => findSdkOsDir,
118
121
  flattenLayout: () => flattenLayout,
119
122
  forceStop: () => forceStop,
120
123
  formatBytes: () => formatBytes,
@@ -148,10 +151,12 @@ __export(index_exports, {
148
151
  hdcTargetArgs: () => hdcTargetArgs,
149
152
  hdcTargetArgsFor: () => hdcTargetArgsFor,
150
153
  hideKeyboard: () => hideKeyboard,
154
+ hilogTimeMs: () => hilogTimeMs,
151
155
  installApp: () => installApp,
152
156
  installCmdTools: () => installCmdTools,
153
157
  installEmulator: () => installEmulator,
154
158
  installFailureReason: () => installFailureReason,
159
+ installSdkFromArchive: () => installSdkFromArchive,
155
160
  isBundleInstalled: () => isBundleInstalled,
156
161
  isCmdToolsInstalled: () => isCmdToolsInstalled,
157
162
  isEmulatorInstalled: () => isEmulatorInstalled,
@@ -168,6 +173,7 @@ __export(index_exports, {
168
173
  listRunningProcesses: () => listRunningProcesses,
169
174
  listTemplates: () => listTemplates,
170
175
  locateTarget: () => locateTarget,
176
+ missingProjectRootFiles: () => missingProjectRootFiles,
171
177
  needsCmdWrapper: () => needsCmdWrapper,
172
178
  nonInteractivePrompter: () => nonInteractivePrompter,
173
179
  noopLogger: () => noopLogger,
@@ -191,9 +197,11 @@ __export(index_exports, {
191
197
  pickTarget: () => pickTarget,
192
198
  prepareSigning: () => prepareSigning,
193
199
  pruneLayout: () => pruneLayout,
200
+ readHapBundleName: () => readHapBundleName,
194
201
  readJson5File: () => readJson5File,
195
202
  readJsonFile: () => readJsonFile,
196
203
  readProvisionProfile: () => readProvisionProfile,
204
+ readZipEntry: () => readZipEntry,
197
205
  reboot: () => reboot,
198
206
  recvFile: () => recvFile,
199
207
  removeCmdTools: () => removeCmdTools,
@@ -424,6 +432,7 @@ var ALL_SDKS = [
424
432
  { version: "7.0", api: "26", tarballStrip: { linuxWindows: 1, darwin: 3 }, archiveSuffix: "_20260829" }
425
433
  ];
426
434
  var OHOS_URL_BASE = "https://repo.huaweicloud.com/openharmony/os";
435
+ var OHOS_DAILY_BUILDS_URL = "https://dcp.openharmony.cn/workbench/cicd/dailybuild/dailylist";
427
436
 
428
437
  // src/sdk/platform.ts
429
438
  var os2 = __toESM(require("os"), 1);
@@ -1006,8 +1015,17 @@ async function extractZipWithProgress(opts) {
1006
1015
  if (inc > 0) progress.report({ message: "Extracting: 100%", increment: inc });
1007
1016
  }
1008
1017
  }
1009
- async function extractTarball(tarPath, dest, strip = 0) {
1010
- await tar.x({ file: tarPath, cwd: dest, strip });
1018
+ async function extractTarball(tarPath, dest, strip = 0, filter) {
1019
+ await tar.x({ file: tarPath, cwd: dest, strip, ...filter ? { filter: (p) => filter(p) } : {} });
1020
+ }
1021
+ async function readZipEntry(zipPath, match) {
1022
+ const zip = new import_node_stream_zip.default.async({ file: zipPath });
1023
+ try {
1024
+ const entry = Object.values(await zip.entries()).find((e) => !e.isDirectory && match(e.name));
1025
+ return entry ? await zip.entryData(entry) : void 0;
1026
+ } finally {
1027
+ await zip.close();
1028
+ }
1011
1029
  }
1012
1030
 
1013
1031
  // src/sdk/list.ts
@@ -1113,34 +1131,7 @@ async function downloadAndInstallSdk(opts) {
1113
1131
  `Expected folder '${osFolder}' not found in extracted SDK. Tarball layout may have changed.`
1114
1132
  );
1115
1133
  }
1116
- const zipFiles = fs10.readdirSync(osContentPath).filter((n2) => n2.endsWith(".zip"));
1117
- const componentsStart = 60;
1118
- const componentsBudget = 35;
1119
- const n = zipFiles.length;
1120
- if (n === 0) {
1121
- progress?.report({ message: "No SDK component ZIPs found.", increment: componentsBudget });
1122
- } else {
1123
- progress?.report({ message: "Extracting SDK components...", increment: 0 });
1124
- }
1125
- const base = n > 0 ? Math.floor(componentsBudget / n) : 0;
1126
- let rem = n > 0 ? componentsBudget % n : 0;
1127
- let cursor = componentsStart;
1128
- for (const zipFile of zipFiles) {
1129
- checkCancelled();
1130
- logger.info(`Extracting component ${zipFile}`);
1131
- const zipPath = path10.join(osContentPath, zipFile);
1132
- const thisBudget = base + (rem > 0 ? 1 : 0);
1133
- if (rem > 0) rem--;
1134
- await extractZipWithProgress({ zipPath, dest: osContentPath, progress, start: cursor, range: thisBudget, logger });
1135
- cursor += thisBudget;
1136
- fs10.unlinkSync(zipPath);
1137
- }
1138
- progress?.report({ message: "Finalizing installation...", increment: 0 });
1139
- if (fs10.existsSync(sdkInstallDir)) {
1140
- fs10.rmSync(sdkInstallDir, { recursive: true, force: true });
1141
- }
1142
- movePath(osContentPath, sdkInstallDir);
1143
- progress?.report({ message: "Finalizing installation...", increment: 3 });
1134
+ await installComponents({ osContentPath, sdkInstallDir, progress, logger, start: 60, range: 35, checkCancelled });
1144
1135
  progress?.report({ message: "Cleaning up...", increment: 0 });
1145
1136
  removeTempWorkDir(tmpDir, tmpRoot);
1146
1137
  progress?.report({ message: "Cleaning up...", increment: 2 });
@@ -1151,6 +1142,115 @@ async function downloadAndInstallSdk(opts) {
1151
1142
  throw wrapped;
1152
1143
  }
1153
1144
  }
1145
+ async function installComponents(opts) {
1146
+ const { osContentPath, sdkInstallDir, progress, logger, checkCancelled } = opts;
1147
+ const zipFiles = fs10.readdirSync(osContentPath).filter((n2) => n2.endsWith(".zip"));
1148
+ const n = zipFiles.length;
1149
+ if (n === 0) {
1150
+ progress?.report({ message: "No SDK component ZIPs found.", increment: opts.range });
1151
+ } else {
1152
+ progress?.report({ message: "Extracting SDK components...", increment: 0 });
1153
+ }
1154
+ const base = n > 0 ? Math.floor(opts.range / n) : 0;
1155
+ let rem = n > 0 ? opts.range % n : 0;
1156
+ let cursor = opts.start;
1157
+ for (const zipFile of zipFiles) {
1158
+ checkCancelled();
1159
+ logger.info(`Extracting component ${zipFile}`);
1160
+ const zipPath = path10.join(osContentPath, zipFile);
1161
+ const thisBudget = base + (rem > 0 ? 1 : 0);
1162
+ if (rem > 0) rem--;
1163
+ await extractZipWithProgress({ zipPath, dest: osContentPath, progress, start: cursor, range: thisBudget, logger });
1164
+ cursor += thisBudget;
1165
+ fs10.unlinkSync(zipPath);
1166
+ }
1167
+ progress?.report({ message: "Finalizing installation...", increment: 0 });
1168
+ if (fs10.existsSync(sdkInstallDir)) {
1169
+ fs10.rmSync(sdkInstallDir, { recursive: true, force: true });
1170
+ }
1171
+ movePath(osContentPath, sdkInstallDir);
1172
+ progress?.report({ message: "Finalizing installation...", increment: 3 });
1173
+ }
1174
+ async function installSdkFromArchive(opts) {
1175
+ const { config, archivePath, progress, abortSignal } = opts;
1176
+ const logger = opts.logger ?? noopLogger;
1177
+ if (!fs10.statSync(archivePath, { throwIfNoEntry: false })?.isFile()) {
1178
+ throw new OniroError(`SDK archive not found: ${archivePath}`);
1179
+ }
1180
+ const osFolder = getOsFolder();
1181
+ const what = `the SDK install from '${path10.basename(archivePath)}'`;
1182
+ const sdkRoot = getSdkRootDir(config);
1183
+ const { dir: tmpDir, root: tmpRoot } = createInstallTempDir({
1184
+ config,
1185
+ override: opts.tmpDir,
1186
+ installRoot: sdkRoot,
1187
+ prefix: "oniro-sdk-",
1188
+ logger
1189
+ });
1190
+ const extractDir = path10.join(tmpDir, "extract");
1191
+ fs10.mkdirSync(extractDir);
1192
+ const checkCancelled = () => {
1193
+ if (abortSignal?.aborted) throw new CancelledError();
1194
+ };
1195
+ try {
1196
+ progress?.report({ message: "Extracting SDK archive (this may take a while)...", increment: 0 });
1197
+ await extractTarball(archivePath, extractDir, 0, (p) => p.split("/").includes(osFolder));
1198
+ progress?.report({ message: "Extracting SDK archive (this may take a while)...", increment: 40 });
1199
+ checkCancelled();
1200
+ const osContentPath = findSdkOsDir(extractDir, osFolder);
1201
+ if (!osContentPath) {
1202
+ throw new OniroError(
1203
+ `No '${osFolder}' SDK components (a '${osFolder}' folder of ZIPs) found in ${archivePath}. Is it an OpenHarmony SDK archive for this OS?`
1204
+ );
1205
+ }
1206
+ const { api, version } = await readSdkComponentsMeta(osContentPath);
1207
+ const sdkInstallDir = path10.join(sdkRoot, osFolder, api);
1208
+ if (fs10.existsSync(sdkInstallDir) && !opts.force) {
1209
+ throw new OniroError(
1210
+ `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.`
1211
+ );
1212
+ }
1213
+ fs10.mkdirSync(path10.dirname(sdkInstallDir), { recursive: true });
1214
+ await installComponents({ osContentPath, sdkInstallDir, progress, logger, start: 40, range: 55, checkCancelled });
1215
+ progress?.report({ message: "Cleaning up...", increment: 0 });
1216
+ removeTempWorkDir(tmpDir, tmpRoot);
1217
+ progress?.report({ message: "Cleaning up...", increment: 2 });
1218
+ return { api, version, dir: sdkInstallDir };
1219
+ } catch (err) {
1220
+ removeTempWorkDir(tmpDir, tmpRoot);
1221
+ throw toSpaceError(err, tmpRoot, what);
1222
+ }
1223
+ }
1224
+ function findSdkOsDir(root, osFolder, maxDepth = 4) {
1225
+ let level = [root];
1226
+ for (let depth = 0; depth < maxDepth && level.length > 0; depth++) {
1227
+ const next = [];
1228
+ for (const dir of level) {
1229
+ for (const entry of fs10.readdirSync(dir, { withFileTypes: true })) {
1230
+ if (!entry.isDirectory()) continue;
1231
+ const p = path10.join(dir, entry.name);
1232
+ if (entry.name === osFolder && fs10.readdirSync(p).some((n) => n.endsWith(".zip"))) return p;
1233
+ next.push(p);
1234
+ }
1235
+ }
1236
+ level = next;
1237
+ }
1238
+ return void 0;
1239
+ }
1240
+ async function readSdkComponentsMeta(dir) {
1241
+ const zips = fs10.readdirSync(dir).filter((n) => n.endsWith(".zip")).sort((a, b) => Number(b.startsWith("ets-")) - Number(a.startsWith("ets-")));
1242
+ for (const name of zips) {
1243
+ const data = await readZipEntry(path10.join(dir, name), (e) => /^[^/]+\/oh-uni-package\.json$/.test(e));
1244
+ if (!data) continue;
1245
+ try {
1246
+ const pkg = JSON.parse(data.toString("utf8"));
1247
+ const api = String(pkg.apiVersion ?? "");
1248
+ if (/^\d+$/.test(api)) return { api, version: String(pkg.version ?? "") };
1249
+ } catch {
1250
+ }
1251
+ }
1252
+ throw new OniroError("No SDK component in the archive declares a numeric apiVersion (oh-uni-package.json).");
1253
+ }
1154
1254
 
1155
1255
  // src/cmdTools/install.ts
1156
1256
  var fs11 = __toESM(require("fs"), 1);
@@ -2438,9 +2538,13 @@ function moduleOfHap(hapFileName) {
2438
2538
  const dash = hapFileName.indexOf("-");
2439
2539
  return dash > 0 ? hapFileName.slice(0, dash) : hapFileName.replace(/\.hap$/i, "");
2440
2540
  }
2541
+ function isAppProjectRoot(dir) {
2542
+ return fs17.existsSync(path18.join(dir, "AppScope", "app.json5"));
2543
+ }
2441
2544
  async function discoverHaps(opts) {
2442
2545
  const { projectDir } = opts;
2443
2546
  const maxDepth = opts.maxDepth ?? 8;
2547
+ const skipNestedProjects = isAppProjectRoot(projectDir);
2444
2548
  const result = {};
2445
2549
  const walk = async (dir, depth) => {
2446
2550
  if (depth > maxDepth) return;
@@ -2449,6 +2553,7 @@ async function discoverHaps(opts) {
2449
2553
  const full = path18.join(dir, e.name);
2450
2554
  if (e.isDirectory()) {
2451
2555
  if (e.name === "node_modules" || e.name === "oh_modules" || e.name === ".git") continue;
2556
+ if (skipNestedProjects && isAppProjectRoot(full)) continue;
2452
2557
  await walk(full, depth + 1);
2453
2558
  } else if (e.isFile() && e.name.endsWith(".hap")) {
2454
2559
  const moduleName = moduleOfHap(e.name);
@@ -3640,6 +3745,54 @@ async function resolveProcessId(config, bundle, deviceSerial) {
3640
3745
  const proc = await findRunningProcess({ config, bundle, deviceSerial });
3641
3746
  return proc?.pid;
3642
3747
  }
3748
+ async function readDeviceClock(config, deviceSerial) {
3749
+ try {
3750
+ const res = await shell({ config, command: "date +%Y-%m-%dT%H:%M:%S", deviceSerial, timeoutMs: 1e4 });
3751
+ const m = res.code === 0 ? /(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})/.exec(res.stdout) : null;
3752
+ if (!m) return null;
3753
+ const [year, month, day, hour, minute, second] = m.slice(1).map(Number);
3754
+ return Date.UTC(year, month - 1, day, hour, minute, second);
3755
+ } catch {
3756
+ return null;
3757
+ }
3758
+ }
3759
+ function hilogTimeMs(time, referenceMs) {
3760
+ const m = /^(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})\.(\d{3})$/.exec(time);
3761
+ if (!m) return null;
3762
+ const [month, day, hour, minute, second, ms] = m.slice(1).map(Number);
3763
+ const year = new Date(referenceMs).getUTCFullYear();
3764
+ let best = null;
3765
+ for (const y of [year - 1, year, year + 1]) {
3766
+ const t = Date.UTC(y, month - 1, day, hour, minute, second, ms);
3767
+ if (best === null || Math.abs(t - referenceMs) < Math.abs(best - referenceMs)) best = t;
3768
+ }
3769
+ return best;
3770
+ }
3771
+ async function recentEntryFilter(opts) {
3772
+ const passAll = { test: () => true, afterReconnect: async () => {
3773
+ } };
3774
+ const lookbackMs = opts.lookbackMs ?? 0;
3775
+ if (lookbackMs === Infinity) return passAll;
3776
+ const startedAt = Date.now();
3777
+ const now = await readDeviceClock(opts.config, opts.deviceSerial);
3778
+ if (now === null) {
3779
+ opts.logger.warn("Could not read the device clock, so lines already in the log buffer can match too.");
3780
+ return passAll;
3781
+ }
3782
+ let since = now - (Date.now() - startedAt) - lookbackMs;
3783
+ return {
3784
+ test: (entry) => {
3785
+ if (since === null) return true;
3786
+ const t = hilogTimeMs(entry.time, now);
3787
+ return t === null || t >= since;
3788
+ },
3789
+ afterReconnect: async () => {
3790
+ if (since === null) return;
3791
+ const later = await readDeviceClock(opts.config, opts.deviceSerial);
3792
+ if (later !== null && later < since) since = null;
3793
+ }
3794
+ };
3795
+ }
3643
3796
  function waitForLog(opts) {
3644
3797
  return new Promise((resolve7, reject) => {
3645
3798
  const deadline = Date.now() + opts.timeoutMs;
@@ -3647,6 +3800,8 @@ function waitForLog(opts) {
3647
3800
  let child = null;
3648
3801
  let reconnectTimer;
3649
3802
  let buf = "";
3803
+ let recent = { test: () => true, afterReconnect: async () => {
3804
+ } };
3650
3805
  const cleanup = () => {
3651
3806
  clearTimeout(timer);
3652
3807
  if (reconnectTimer) clearTimeout(reconnectTimer);
@@ -3687,7 +3842,7 @@ function waitForLog(opts) {
3687
3842
  buf = lines.pop() ?? "";
3688
3843
  for (const line of lines) {
3689
3844
  const entry = parseHilogLine(line);
3690
- if (entry && opts.pattern.test(entryText(entry))) {
3845
+ if (entry && recent.test(entry) && opts.pattern.test(entryText(entry))) {
3691
3846
  finish(entry);
3692
3847
  return;
3693
3848
  }
@@ -3697,13 +3852,21 @@ function waitForLog(opts) {
3697
3852
  if (settled || child !== stream) return;
3698
3853
  child = null;
3699
3854
  if (Date.now() < deadline) {
3700
- reconnectTimer = setTimeout(() => void spawnStream().catch(failWith), 1e3);
3855
+ reconnectTimer = setTimeout(() => void recent.afterReconnect().then(spawnStream).catch(failWith), 1e3);
3701
3856
  }
3702
3857
  };
3703
3858
  stream.once("close", onEnd);
3704
3859
  stream.once("error", onEnd);
3705
3860
  };
3706
- void spawnStream().catch(failWith);
3861
+ void (async () => {
3862
+ recent = await recentEntryFilter({
3863
+ config: opts.config,
3864
+ deviceSerial: opts.deviceSerial,
3865
+ lookbackMs: opts.lookbackMs,
3866
+ logger: opts.logger ?? noopLogger
3867
+ });
3868
+ await spawnStream();
3869
+ })().catch(failWith);
3707
3870
  });
3708
3871
  }
3709
3872
  function watchLog(opts) {
@@ -3712,6 +3875,8 @@ function watchLog(opts) {
3712
3875
  let settled = false;
3713
3876
  let child = null;
3714
3877
  let buf = "";
3878
+ let recent = { test: () => true, afterReconnect: async () => {
3879
+ } };
3715
3880
  const cleanup = () => {
3716
3881
  clearTimeout(timer);
3717
3882
  opts.abortSignal?.removeEventListener("abort", onAbort);
@@ -3735,6 +3900,13 @@ function watchLog(opts) {
3735
3900
  const timer = setTimeout(done, opts.durationMs);
3736
3901
  opts.abortSignal?.addEventListener("abort", onAbort, { once: true });
3737
3902
  void (async () => {
3903
+ recent = await recentEntryFilter({
3904
+ config: opts.config,
3905
+ deviceSerial: opts.deviceSerial,
3906
+ lookbackMs: opts.lookbackMs,
3907
+ logger: opts.logger ?? noopLogger
3908
+ });
3909
+ if (settled) return;
3738
3910
  const processId = await resolveProcessId(opts.config, opts.bundle, opts.deviceSerial);
3739
3911
  if (settled) return;
3740
3912
  const stream = streamHilog({ config: opts.config, processId, domain: opts.domain, deviceSerial: opts.deviceSerial });
@@ -3745,7 +3917,7 @@ function watchLog(opts) {
3745
3917
  buf = lines.pop() ?? "";
3746
3918
  for (const line of lines) {
3747
3919
  const entry = parseHilogLine(line);
3748
- if (entry && opts.pattern.test(entryText(entry))) matches.push(entry);
3920
+ if (entry && recent.test(entry) && opts.pattern.test(entryText(entry))) matches.push(entry);
3749
3921
  }
3750
3922
  });
3751
3923
  })().catch((err) => {
@@ -4030,6 +4202,8 @@ async function ensureOhModules(opts) {
4030
4202
  }
4031
4203
 
4032
4204
  // src/build/codelinter.ts
4205
+ var fs26 = __toESM(require("fs"), 1);
4206
+ var path26 = __toESM(require("path"), 1);
4033
4207
  var FINDING_RE = /^(.*?):(\d+):(?:(\d+):)?\s*(error|warning|warn|suggestion|info)\b[:\s-]*(.*)$/i;
4034
4208
  var TRAILING_CODE_RE = /[([]([\w@/.\-]+)[)\]]\s*$/;
4035
4209
  var FILE_HEADER_RE = /^(.+\.\w+)\((\d+)\)$/;
@@ -4116,6 +4290,16 @@ function parseCodelinterReport(output) {
4116
4290
  function parseCodelinterFindings(output) {
4117
4291
  return parseCodelinterReport(output).findings;
4118
4292
  }
4293
+ var PROJECT_ROOT_FILES = [
4294
+ ["build-profile.json5"],
4295
+ ["hvigorfile.ts", "hvigorfile.js"],
4296
+ [path26.join("hvigor", "hvigor-config.json5")]
4297
+ ];
4298
+ function missingProjectRootFiles(projectDir) {
4299
+ return PROJECT_ROOT_FILES.filter((names) => !names.some((f) => fs26.existsSync(path26.join(projectDir, f)))).map(
4300
+ (names) => names[0]
4301
+ );
4302
+ }
4119
4303
  async function runCodelinter(opts) {
4120
4304
  const res = await runProcess({
4121
4305
  command: getCmdToolsBin(opts.config, "codelinter"),
@@ -4133,7 +4317,7 @@ async function runCodelinter(opts) {
4133
4317
  var DIAGNOSIS_OUTPUT_LIMIT = 2e6;
4134
4318
  function diagnoseBuildFailure(output) {
4135
4319
  if (/is not exported from Kit|has no exported member .*@systemapi/i.test(output)) {
4136
- 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`).";
4320
+ 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\`).`;
4137
4321
  }
4138
4322
  if (/00308018|SDK path does not exist|SDK license agreement is not accepted/i.test(output)) {
4139
4323
  return "hvigor cannot find the SDK the project compiles against: check `oniro-app sdk list` against compileSdkVersion.";
@@ -4205,21 +4389,21 @@ function isValidBundleName(bundleName) {
4205
4389
  }
4206
4390
 
4207
4391
  // src/project/templates.ts
4208
- var fs26 = __toESM(require("fs"), 1);
4209
- var path26 = __toESM(require("path"), 1);
4392
+ var fs27 = __toESM(require("fs"), 1);
4393
+ var path27 = __toESM(require("path"), 1);
4210
4394
  function toHumanTemplateName(folderName) {
4211
4395
  return folderName.replace(/([a-z])([A-Z])/g, "$1 $2");
4212
4396
  }
4213
4397
  function listTemplates(templateRoot) {
4214
- if (!fs26.existsSync(templateRoot)) return [];
4215
- const entries = fs26.readdirSync(templateRoot, { withFileTypes: true });
4398
+ if (!fs27.existsSync(templateRoot)) return [];
4399
+ const entries = fs27.readdirSync(templateRoot, { withFileTypes: true });
4216
4400
  return entries.filter((e) => e.isDirectory()).map((e) => {
4217
- const dir = path26.join(templateRoot, e.name);
4218
- const metaPath = path26.join(dir, "template.json");
4401
+ const dir = path27.join(templateRoot, e.name);
4402
+ const metaPath = path27.join(dir, "template.json");
4219
4403
  let meta = {};
4220
- if (fs26.existsSync(metaPath)) {
4404
+ if (fs27.existsSync(metaPath)) {
4221
4405
  try {
4222
- meta = JSON.parse(fs26.readFileSync(metaPath, "utf8"));
4406
+ meta = JSON.parse(fs27.readFileSync(metaPath, "utf8"));
4223
4407
  } catch {
4224
4408
  }
4225
4409
  }
@@ -4234,57 +4418,57 @@ function listTemplates(templateRoot) {
4234
4418
  function validateTemplateLayout(templateDir, defaultModuleName) {
4235
4419
  const required = [
4236
4420
  "build-profile.json5",
4237
- path26.join("AppScope", "app.json5"),
4238
- path26.join(defaultModuleName, "src", "main", "module.json5"),
4239
- path26.join(defaultModuleName, "oh-package.json5"),
4421
+ path27.join("AppScope", "app.json5"),
4422
+ path27.join(defaultModuleName, "src", "main", "module.json5"),
4423
+ path27.join(defaultModuleName, "oh-package.json5"),
4240
4424
  "hvigorfile.ts"
4241
4425
  ];
4242
- return required.filter((rel) => !fs26.existsSync(path26.join(templateDir, rel)));
4426
+ return required.filter((rel) => !fs27.existsSync(path27.join(templateDir, rel)));
4243
4427
  }
4244
4428
 
4245
4429
  // src/project/createScaffold.ts
4246
- var fs28 = __toESM(require("fs"), 1);
4247
- var path27 = __toESM(require("path"), 1);
4430
+ var fs29 = __toESM(require("fs"), 1);
4431
+ var path28 = __toESM(require("path"), 1);
4248
4432
 
4249
4433
  // src/project/jsonHelpers.ts
4250
- var fs27 = __toESM(require("fs"), 1);
4434
+ var fs28 = __toESM(require("fs"), 1);
4251
4435
  var import_json55 = __toESM(require("json5"), 1);
4252
4436
  function readJson5File(filePath) {
4253
- return import_json55.default.parse(fs27.readFileSync(filePath, "utf8"));
4437
+ return import_json55.default.parse(fs28.readFileSync(filePath, "utf8"));
4254
4438
  }
4255
4439
  function writeJson5File(filePath, value) {
4256
- fs27.writeFileSync(filePath, JSON.stringify(value, null, 2) + "\n", "utf8");
4440
+ fs28.writeFileSync(filePath, JSON.stringify(value, null, 2) + "\n", "utf8");
4257
4441
  }
4258
4442
  function readJsonFile(filePath) {
4259
- return JSON.parse(fs27.readFileSync(filePath, "utf8"));
4443
+ return JSON.parse(fs28.readFileSync(filePath, "utf8"));
4260
4444
  }
4261
4445
  function writeJsonFile(filePath, value) {
4262
- fs27.writeFileSync(filePath, JSON.stringify(value, null, 2) + "\n", "utf8");
4446
+ fs28.writeFileSync(filePath, JSON.stringify(value, null, 2) + "\n", "utf8");
4263
4447
  }
4264
4448
 
4265
4449
  // src/project/createScaffold.ts
4266
4450
  var IGNORED_DIRS = /* @__PURE__ */ new Set(["oh_modules", "node_modules", "build", ".hvigor"]);
4267
4451
  async function pathExists(p) {
4268
4452
  try {
4269
- await fs28.promises.access(p);
4453
+ await fs29.promises.access(p);
4270
4454
  return true;
4271
4455
  } catch {
4272
4456
  return false;
4273
4457
  }
4274
4458
  }
4275
4459
  async function copyDirRecursive(srcDir, destDir) {
4276
- await fs28.promises.mkdir(destDir, { recursive: true });
4277
- const entries = await fs28.promises.readdir(srcDir, { withFileTypes: true });
4460
+ await fs29.promises.mkdir(destDir, { recursive: true });
4461
+ const entries = await fs29.promises.readdir(srcDir, { withFileTypes: true });
4278
4462
  for (const entry of entries) {
4279
- const src = path27.join(srcDir, entry.name);
4280
- const dest = path27.join(destDir, entry.name);
4463
+ const src = path28.join(srcDir, entry.name);
4464
+ const dest = path28.join(destDir, entry.name);
4281
4465
  if (entry.isDirectory()) {
4282
4466
  await copyDirRecursive(src, dest);
4283
4467
  } else if (entry.isSymbolicLink()) {
4284
4468
  continue;
4285
4469
  } else if (entry.isFile()) {
4286
4470
  if (entry.name === "template.json") continue;
4287
- await fs28.promises.copyFile(src, dest);
4471
+ await fs29.promises.copyFile(src, dest);
4288
4472
  }
4289
4473
  }
4290
4474
  }
@@ -4292,9 +4476,9 @@ async function normalizeJson5ToJson(projectDir, logger) {
4292
4476
  const stack = [projectDir];
4293
4477
  while (stack.length > 0) {
4294
4478
  const currentDir = stack.pop();
4295
- const entries = await fs28.promises.readdir(currentDir, { withFileTypes: true });
4479
+ const entries = await fs29.promises.readdir(currentDir, { withFileTypes: true });
4296
4480
  for (const entry of entries) {
4297
- const fullPath = path27.join(currentDir, entry.name);
4481
+ const fullPath = path28.join(currentDir, entry.name);
4298
4482
  if (entry.isDirectory()) {
4299
4483
  if (IGNORED_DIRS.has(entry.name)) continue;
4300
4484
  stack.push(fullPath);
@@ -4316,23 +4500,23 @@ function toJavaPropertiesPath(p) {
4316
4500
  function createOrUpdateLocalProperties(projectDir, sdkDir) {
4317
4501
  const content = `sdk.dir=${toJavaPropertiesPath(sdkDir)}
4318
4502
  `;
4319
- fs28.writeFileSync(path27.join(projectDir, "local.properties"), content, "utf8");
4503
+ fs29.writeFileSync(path28.join(projectDir, "local.properties"), content, "utf8");
4320
4504
  }
4321
4505
  function renameIfExists(fromPath, toPath) {
4322
- if (fs28.existsSync(fromPath) && fromPath !== toPath) {
4323
- fs28.renameSync(fromPath, toPath);
4506
+ if (fs29.existsSync(fromPath) && fromPath !== toPath) {
4507
+ fs29.renameSync(fromPath, toPath);
4324
4508
  }
4325
4509
  }
4326
4510
  function updateTemplateConfigs(projectDir, args, sdkBaseDir) {
4327
- const appJsonPath = path27.join(projectDir, "AppScope", "app.json5");
4328
- if (fs28.existsSync(appJsonPath)) {
4511
+ const appJsonPath = path28.join(projectDir, "AppScope", "app.json5");
4512
+ if (fs29.existsSync(appJsonPath)) {
4329
4513
  const appJson = readJson5File(appJsonPath);
4330
4514
  appJson.app = appJson.app ?? {};
4331
4515
  appJson.app.bundleName = args.bundleName;
4332
4516
  writeJson5File(appJsonPath, appJson);
4333
4517
  }
4334
- const stringsPath = path27.join(projectDir, "AppScope", "resources", "base", "element", "string.json");
4335
- if (fs28.existsSync(stringsPath)) {
4518
+ const stringsPath = path28.join(projectDir, "AppScope", "resources", "base", "element", "string.json");
4519
+ if (fs29.existsSync(stringsPath)) {
4336
4520
  const strings = readJsonFile(stringsPath);
4337
4521
  if (Array.isArray(strings.string)) {
4338
4522
  const appName = strings.string.find((s) => s.name === "app_name");
@@ -4340,8 +4524,8 @@ function updateTemplateConfigs(projectDir, args, sdkBaseDir) {
4340
4524
  }
4341
4525
  writeJsonFile(stringsPath, strings);
4342
4526
  }
4343
- const buildProfilePath = path27.join(projectDir, "build-profile.json5");
4344
- if (fs28.existsSync(buildProfilePath)) {
4527
+ const buildProfilePath = path28.join(projectDir, "build-profile.json5");
4528
+ if (fs29.existsSync(buildProfilePath)) {
4345
4529
  const buildProfile = readJson5File(buildProfilePath);
4346
4530
  buildProfile.app = buildProfile.app ?? {};
4347
4531
  if (Array.isArray(buildProfile.app.products) && buildProfile.app.products.length > 0) {
@@ -4354,23 +4538,23 @@ function updateTemplateConfigs(projectDir, args, sdkBaseDir) {
4354
4538
  }
4355
4539
  writeJson5File(buildProfilePath, buildProfile);
4356
4540
  }
4357
- const moduleJsonPath = path27.join(projectDir, args.moduleName, "src", "main", "module.json5");
4358
- if (fs28.existsSync(moduleJsonPath)) {
4541
+ const moduleJsonPath = path28.join(projectDir, args.moduleName, "src", "main", "module.json5");
4542
+ if (fs29.existsSync(moduleJsonPath)) {
4359
4543
  const moduleJson = readJson5File(moduleJsonPath);
4360
4544
  moduleJson.module = moduleJson.module ?? {};
4361
4545
  moduleJson.module.name = args.moduleName;
4362
4546
  writeJson5File(moduleJsonPath, moduleJson);
4363
4547
  }
4364
- const moduleOhPackagePath = path27.join(projectDir, args.moduleName, "oh-package.json5");
4365
- if (fs28.existsSync(moduleOhPackagePath)) {
4548
+ const moduleOhPackagePath = path28.join(projectDir, args.moduleName, "oh-package.json5");
4549
+ if (fs29.existsSync(moduleOhPackagePath)) {
4366
4550
  const pkg = readJson5File(moduleOhPackagePath);
4367
4551
  pkg.name = args.moduleName;
4368
4552
  writeJson5File(moduleOhPackagePath, pkg);
4369
4553
  }
4370
- const vscodeDir = path27.join(projectDir, ".vscode");
4371
- fs28.mkdirSync(vscodeDir, { recursive: true });
4554
+ const vscodeDir = path28.join(projectDir, ".vscode");
4555
+ fs29.mkdirSync(vscodeDir, { recursive: true });
4372
4556
  const hapPath = `${args.moduleName}/build/default/outputs/default/${args.moduleName}-default-signed.hap`;
4373
- writeJsonFile(path27.join(vscodeDir, "settings.json"), {
4557
+ writeJsonFile(path28.join(vscodeDir, "settings.json"), {
4374
4558
  "oniro.hapPath": hapPath,
4375
4559
  "files.associations": { "*.json5": "jsonc" }
4376
4560
  });
@@ -4387,7 +4571,7 @@ async function createScaffold(opts) {
4387
4571
  if (!opts.location || !await pathExists(opts.location)) {
4388
4572
  throw new OniroError(`Location does not exist: ${opts.location}`);
4389
4573
  }
4390
- const templateDir = path27.join(opts.templateRoot, opts.templateId);
4574
+ const templateDir = path28.join(opts.templateRoot, opts.templateId);
4391
4575
  if (!await pathExists(templateDir)) {
4392
4576
  throw new OniroError(`Template not found: ${templateDir}`);
4393
4577
  }
@@ -4422,17 +4606,17 @@ async function createScaffold(opts) {
4422
4606
  }
4423
4607
  }
4424
4608
  }
4425
- const projectDir = path27.join(opts.location, opts.projectName);
4609
+ const projectDir = path28.join(opts.location, opts.projectName);
4426
4610
  if (await pathExists(projectDir)) {
4427
4611
  if (!opts.overwrite) {
4428
4612
  throw new OniroError(`Destination already exists: ${projectDir}. Pass overwrite: true to replace it.`);
4429
4613
  }
4430
- await fs28.promises.rm(projectDir, { recursive: true, force: true });
4614
+ await fs29.promises.rm(projectDir, { recursive: true, force: true });
4431
4615
  }
4432
4616
  logger.info(`[create] Scaffolding ${opts.templateId} at ${projectDir}`);
4433
4617
  await copyDirRecursive(templateDir, projectDir);
4434
4618
  if (moduleName !== defaultModuleName) {
4435
- renameIfExists(path27.join(projectDir, defaultModuleName), path27.join(projectDir, moduleName));
4619
+ renameIfExists(path28.join(projectDir, defaultModuleName), path28.join(projectDir, moduleName));
4436
4620
  }
4437
4621
  updateTemplateConfigs(
4438
4622
  projectDir,
@@ -4449,11 +4633,11 @@ async function createScaffold(opts) {
4449
4633
  }
4450
4634
 
4451
4635
  // src/project/listModules.ts
4452
- var fs29 = __toESM(require("fs"), 1);
4453
- var path28 = __toESM(require("path"), 1);
4636
+ var fs30 = __toESM(require("fs"), 1);
4637
+ var path29 = __toESM(require("path"), 1);
4454
4638
  function listModules(opts) {
4455
- const buildProfile = path28.join(opts.projectDir, "build-profile.json5");
4456
- if (!fs29.existsSync(buildProfile)) {
4639
+ const buildProfile = path29.join(opts.projectDir, "build-profile.json5");
4640
+ if (!fs30.existsSync(buildProfile)) {
4457
4641
  throw new OniroError(`build-profile.json5 not found at ${buildProfile}`);
4458
4642
  }
4459
4643
  const parsed = readJson5File(buildProfile);
@@ -4465,12 +4649,12 @@ function listModules(opts) {
4465
4649
  }
4466
4650
 
4467
4651
  // src/project/abilities.ts
4468
- var fs30 = __toESM(require("fs"), 1);
4469
- var path29 = __toESM(require("path"), 1);
4652
+ var fs31 = __toESM(require("fs"), 1);
4653
+ var path30 = __toESM(require("path"), 1);
4470
4654
  function listAbilities(opts) {
4471
4655
  const moduleName = opts.moduleName ?? "entry";
4472
- const moduleJsonPath = path29.join(opts.projectDir, moduleName, "src", "main", "module.json5");
4473
- if (!fs30.existsSync(moduleJsonPath)) {
4656
+ const moduleJsonPath = path30.join(opts.projectDir, moduleName, "src", "main", "module.json5");
4657
+ if (!fs31.existsSync(moduleJsonPath)) {
4474
4658
  throw new OniroError(`Could not find module.json5 at ${moduleJsonPath}`);
4475
4659
  }
4476
4660
  const parsed = readJson5File(moduleJsonPath);
@@ -4478,12 +4662,12 @@ function listAbilities(opts) {
4478
4662
  }
4479
4663
 
4480
4664
  // src/project/prepareSigning.ts
4481
- var fs31 = __toESM(require("fs"), 1);
4482
- var path30 = __toESM(require("path"), 1);
4665
+ var fs32 = __toESM(require("fs"), 1);
4666
+ var path31 = __toESM(require("path"), 1);
4483
4667
  function hasSigningArtifacts(dir) {
4484
- if (!fs31.existsSync(path30.join(dir, "signatures"))) return false;
4485
- const buildProfile = path30.join(dir, "build-profile.json5");
4486
- if (!fs31.existsSync(buildProfile)) return false;
4668
+ if (!fs32.existsSync(path31.join(dir, "signatures"))) return false;
4669
+ const buildProfile = path31.join(dir, "build-profile.json5");
4670
+ if (!fs32.existsSync(buildProfile)) return false;
4487
4671
  try {
4488
4672
  const parsed = readJson5File(buildProfile);
4489
4673
  return Array.isArray(parsed.app?.signingConfigs) && parsed.app.signingConfigs.length > 0;
@@ -4586,8 +4770,25 @@ async function diffHapAssets(opts) {
4586
4770
  }
4587
4771
 
4588
4772
  // src/install/apply.ts
4589
- var fs32 = __toESM(require("fs"), 1);
4590
- var path31 = __toESM(require("path"), 1);
4773
+ var fs33 = __toESM(require("fs"), 1);
4774
+ var path32 = __toESM(require("path"), 1);
4775
+
4776
+ // src/install/hapBundleName.ts
4777
+ function bundleNameFromHapConfig(text) {
4778
+ try {
4779
+ const parsed = JSON.parse(text.replace(/^/, ""));
4780
+ const name = parsed.app?.bundleName;
4781
+ return typeof name === "string" && name ? name : null;
4782
+ } catch {
4783
+ return null;
4784
+ }
4785
+ }
4786
+ async function readHapBundleName(hapPath) {
4787
+ const config = await readZipEntry(hapPath, (n) => n === "module.json") ?? await readZipEntry(hapPath, (n) => n === "config.json");
4788
+ return config ? bundleNameFromHapConfig(config.toString("utf8")) : null;
4789
+ }
4790
+
4791
+ // src/install/apply.ts
4591
4792
  function decideInstallMethod(s) {
4592
4793
  if (!s.signInfoInconsistent) return "replace";
4593
4794
  if (s.isSystemBundle && !s.allowUninstall) return "refuse";
@@ -4608,7 +4809,7 @@ async function pidOf(config, bundle, deviceSerial) {
4608
4809
  async function resolveHap(opts, logger) {
4609
4810
  let hap;
4610
4811
  if (opts.hapPath) {
4611
- const hapPath = !path31.isAbsolute(opts.hapPath) && opts.projectDir ? path31.join(opts.projectDir, opts.hapPath) : opts.hapPath;
4812
+ const hapPath = !path32.isAbsolute(opts.hapPath) && opts.projectDir ? path32.join(opts.projectDir, opts.hapPath) : opts.hapPath;
4612
4813
  hap = existingHapOrUnsigned(hapPath);
4613
4814
  if (!hap) throw new OniroError(`HAP not found at ${hapPath}.`);
4614
4815
  } else {
@@ -4617,7 +4818,7 @@ async function resolveHap(opts, logger) {
4617
4818
  if (!picked) throw new OniroError(`No HAP found${opts.module ? ` for module ${opts.module}` : ""}. Build first.`);
4618
4819
  hap = { path: picked.path, fallback: !picked.signed };
4619
4820
  }
4620
- if (hap.fallback) logger.warn(`No signed HAP found; installing the unsigned ${path31.basename(hap.path)}.`);
4821
+ if (hap.fallback) logger.warn(`No signed HAP found; installing the unsigned ${path32.basename(hap.path)}.`);
4621
4822
  return hap;
4622
4823
  }
4623
4824
  async function applyChanges(opts) {
@@ -4627,9 +4828,18 @@ async function applyChanges(opts) {
4627
4828
  const allowUninstall = opts.allowUninstall ?? false;
4628
4829
  const hap = await resolveHap(opts, logger);
4629
4830
  const hapPath = hap.path;
4831
+ const hapBundle = await readHapBundleName(hapPath).catch((err) => {
4832
+ logger.debug(`bundle name check skipped: ${err.message}`);
4833
+ return null;
4834
+ });
4835
+ if (hapBundle && hapBundle !== bundle) {
4836
+ throw new OniroError(
4837
+ `${path32.basename(hapPath)} (${hapPath}) is bundle ${hapBundle}, not ${bundle}, so installing it would not update ${bundle}. Choose the HAP explicitly, or name ${hapBundle} as the bundle if it is the app to update.`
4838
+ );
4839
+ }
4630
4840
  logger.info(`installing ${hapPath}`);
4631
4841
  let assetsChanged = false;
4632
- if (opts.installedHapPath && fs32.existsSync(opts.installedHapPath)) {
4842
+ if (opts.installedHapPath && fs33.existsSync(opts.installedHapPath)) {
4633
4843
  try {
4634
4844
  const diff = await diffHapAssets({ installedHap: opts.installedHapPath, newHap: hapPath });
4635
4845
  assetsChanged = diff.addedAssetPaths.length > 0 || diff.removedAssetPaths.length > 0;
@@ -4662,7 +4872,7 @@ async function applyChanges(opts) {
4662
4872
  }
4663
4873
  if (!await isBundleInstalled({ config, bundle, deviceSerial })) {
4664
4874
  throw new OniroError(
4665
- `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).`
4875
+ `hdc reported ${path32.basename(hapPath)} as installed, but bm dump does not list ${bundle}. Check that the HAP was built from this project (its bundleName must match).`
4666
4876
  );
4667
4877
  }
4668
4878
  let postInstallPid = await pidOf(config, bundle, deviceSerial);
@@ -4739,9 +4949,9 @@ var AUTH_PATHS = {
4739
4949
  };
4740
4950
 
4741
4951
  // src/harmonyos/auth/tokenStore.ts
4742
- var fs33 = __toESM(require("fs"), 1);
4952
+ var fs34 = __toESM(require("fs"), 1);
4743
4953
  var os11 = __toESM(require("os"), 1);
4744
- var path32 = __toESM(require("path"), 1);
4954
+ var path33 = __toESM(require("path"), 1);
4745
4955
  var crypto3 = __toESM(require("crypto"), 1);
4746
4956
  var ALGORITHM = "aes-256-gcm";
4747
4957
  var KEY_LENGTH = 32;
@@ -4777,7 +4987,7 @@ function permissionHint(dir) {
4777
4987
  }
4778
4988
  function ensureDir(dir) {
4779
4989
  try {
4780
- fs33.mkdirSync(dir, { recursive: true, mode: DIR_MODE });
4990
+ fs34.mkdirSync(dir, { recursive: true, mode: DIR_MODE });
4781
4991
  } catch (err) {
4782
4992
  if (err.code === "EACCES") {
4783
4993
  throw new OniroError(permissionHint(dir), err);
@@ -4788,17 +4998,17 @@ function ensureDir(dir) {
4788
4998
  function createTokenStore(opts) {
4789
4999
  const logger = opts.logger ?? noopLogger;
4790
5000
  const authDir = opts.config.get("harmonyosAuthDir", defaultPaths.harmonyosAuthDir());
4791
- const tokenPath = path32.join(authDir, TOKEN_FILE);
4792
- const keyPath = path32.join(os11.homedir(), ".local", "share", "oniro-app", "keys", KEY_FILE);
5001
+ const tokenPath = path33.join(authDir, TOKEN_FILE);
5002
+ const keyPath = path33.join(os11.homedir(), ".local", "share", "oniro-app", "keys", KEY_FILE);
4793
5003
  function loadOrCreateKey() {
4794
- if (fs33.existsSync(keyPath)) {
4795
- const existing = fs33.readFileSync(keyPath);
5004
+ if (fs34.existsSync(keyPath)) {
5005
+ const existing = fs34.readFileSync(keyPath);
4796
5006
  if (existing.length === KEY_LENGTH) return existing;
4797
5007
  logger.warn("[harmonyos] The token key file was malformed; regenerating (you will need to log in again).");
4798
5008
  }
4799
- ensureDir(path32.dirname(keyPath));
5009
+ ensureDir(path33.dirname(keyPath));
4800
5010
  const key = crypto3.randomBytes(KEY_LENGTH);
4801
- fs33.writeFileSync(keyPath, key, { mode: FILE_MODE });
5011
+ fs34.writeFileSync(keyPath, key, { mode: FILE_MODE });
4802
5012
  return key;
4803
5013
  }
4804
5014
  return {
@@ -4808,12 +5018,12 @@ function createTokenStore(opts) {
4808
5018
  const payload = Buffer.from(JSON.stringify(session), "utf8");
4809
5019
  const blob = encrypt2(loadOrCreateKey(), payload);
4810
5020
  ensureDir(authDir);
4811
- fs33.writeFileSync(tokenPath, JSON.stringify(blob, null, 2), { mode: FILE_MODE });
5021
+ fs34.writeFileSync(tokenPath, JSON.stringify(blob, null, 2), { mode: FILE_MODE });
4812
5022
  },
4813
5023
  load() {
4814
- if (!fs33.existsSync(tokenPath)) return null;
5024
+ if (!fs34.existsSync(tokenPath)) return null;
4815
5025
  try {
4816
- const blob = JSON.parse(fs33.readFileSync(tokenPath, "utf8"));
5026
+ const blob = JSON.parse(fs34.readFileSync(tokenPath, "utf8"));
4817
5027
  if (!isEncryptedBlob(blob)) return null;
4818
5028
  const { jwtToken, siteId } = JSON.parse(decrypt2(loadOrCreateKey(), blob).toString("utf8"));
4819
5029
  return typeof jwtToken === "string" && jwtToken && typeof siteId === "string" ? { jwtToken, siteId } : null;
@@ -4825,7 +5035,7 @@ function createTokenStore(opts) {
4825
5035
  }
4826
5036
  logger.debug(`[harmonyos] Discarding unreadable token at ${tokenPath}.`);
4827
5037
  try {
4828
- fs33.rmSync(tokenPath, { force: true });
5038
+ fs34.rmSync(tokenPath, { force: true });
4829
5039
  } catch {
4830
5040
  }
4831
5041
  return null;
@@ -4833,7 +5043,7 @@ function createTokenStore(opts) {
4833
5043
  },
4834
5044
  clear() {
4835
5045
  try {
4836
- fs33.rmSync(tokenPath, { force: true });
5046
+ fs34.rmSync(tokenPath, { force: true });
4837
5047
  } catch (err) {
4838
5048
  throw new OniroError(`Failed to remove ${tokenPath}.`, err);
4839
5049
  }
@@ -5314,7 +5524,7 @@ function mapAgcError(operation, statusCode, reasonPhrase, body) {
5314
5524
  }
5315
5525
 
5316
5526
  // src/harmonyos/signing/profile.ts
5317
- var fs34 = __toESM(require("fs"), 1);
5527
+ var fs35 = __toESM(require("fs"), 1);
5318
5528
  var import_node_crypto3 = require("crypto");
5319
5529
  var import_node_forge = __toESM(require("node-forge"), 1);
5320
5530
  function extractProfileJson(buffer) {
@@ -5346,7 +5556,7 @@ function extractProfileJson(buffer) {
5346
5556
  function readProvisionProfile(profilePath) {
5347
5557
  let raw;
5348
5558
  try {
5349
- const json = extractProfileJson(fs34.readFileSync(profilePath));
5559
+ const json = extractProfileJson(fs35.readFileSync(profilePath));
5350
5560
  if (!json) return null;
5351
5561
  raw = JSON.parse(json);
5352
5562
  } catch {
@@ -5375,7 +5585,7 @@ function parseCertificate(pem) {
5375
5585
  }
5376
5586
  function keystoreHoldsKey(p12Path, keyAlias, keyPwd, certificate) {
5377
5587
  try {
5378
- const der = import_node_forge.default.util.createBuffer(fs34.readFileSync(p12Path).toString("binary"));
5588
+ const der = import_node_forge.default.util.createBuffer(fs35.readFileSync(p12Path).toString("binary"));
5379
5589
  const p12 = import_node_forge.default.pkcs12.pkcs12FromAsn1(import_node_forge.default.asn1.fromDer(der), keyPwd);
5380
5590
  const certBag = import_node_forge.default.pki.oids.certBag ?? "1.2.840.113549.1.12.10.1.3";
5381
5591
  const wanted = certificate.publicKey.export({ type: "spki", format: "der" });
@@ -5391,7 +5601,7 @@ function keystoreHoldsKey(p12Path, keyAlias, keyPwd, certificate) {
5391
5601
  }
5392
5602
  }
5393
5603
  function verifySigningMaterial(opts) {
5394
- const pems = fs34.readFileSync(opts.cerPath, "utf8").match(/-----BEGIN CERTIFICATE-----[\s\S]*?-----END CERTIFICATE-----/g);
5604
+ const pems = fs35.readFileSync(opts.cerPath, "utf8").match(/-----BEGIN CERTIFICATE-----[\s\S]*?-----END CERTIFICATE-----/g);
5395
5605
  const chain = (pems ?? []).map(parseCertificate);
5396
5606
  const leaf = chain.find((c) => !c.ca) ?? chain.at(-1);
5397
5607
  if (!leaf) throw new AgcError(SIGNING_ERRORS.CERT_INVALID);
@@ -5410,14 +5620,14 @@ function verifySigningMaterial(opts) {
5410
5620
 
5411
5621
  // src/harmonyos/signing/autoSign.ts
5412
5622
  var crypto9 = __toESM(require("crypto"), 1);
5413
- var fs39 = __toESM(require("fs"), 1);
5414
- var path38 = __toESM(require("path"), 1);
5623
+ var fs40 = __toESM(require("fs"), 1);
5624
+ var path39 = __toESM(require("path"), 1);
5415
5625
  var import_json57 = __toESM(require("json5"), 1);
5416
5626
 
5417
5627
  // src/harmonyos/signing/agc.ts
5418
5628
  var crypto6 = __toESM(require("crypto"), 1);
5419
- var fs35 = __toESM(require("fs"), 1);
5420
- var path33 = __toESM(require("path"), 1);
5629
+ var fs36 = __toESM(require("fs"), 1);
5630
+ var path34 = __toESM(require("path"), 1);
5421
5631
  var ENDPOINTS = {
5422
5632
  CERT_LIST: "/api/cps/harmony-cert-manage/v1/cert/list",
5423
5633
  CERT_ADD: "/api/cps/harmony-cert-manage/v1/cert/add",
@@ -5503,8 +5713,8 @@ function createAgcClient(http3, auth) {
5503
5713
  if (target.sha256 && actual !== target.sha256.toLowerCase()) {
5504
5714
  throw new ChecksumMismatchError(target.sha256, actual);
5505
5715
  }
5506
- fs35.mkdirSync(path33.dirname(dest), { recursive: true, mode: 448 });
5507
- fs35.writeFileSync(dest, buffer, { mode: 384 });
5716
+ fs36.mkdirSync(path34.dirname(dest), { recursive: true, mode: 448 });
5717
+ fs36.writeFileSync(dest, buffer, { mode: 384 });
5508
5718
  }
5509
5719
  };
5510
5720
  }
@@ -5555,22 +5765,22 @@ async function registerDevices(agc, connected, logger) {
5555
5765
 
5556
5766
  // src/harmonyos/signing/keystore.ts
5557
5767
  var crypto7 = __toESM(require("crypto"), 1);
5558
- var fs37 = __toESM(require("fs"), 1);
5559
- var path35 = __toESM(require("path"), 1);
5768
+ var fs38 = __toESM(require("fs"), 1);
5769
+ var path36 = __toESM(require("path"), 1);
5560
5770
 
5561
5771
  // src/harmonyos/java.ts
5562
- var fs36 = __toESM(require("fs"), 1);
5563
- var path34 = __toESM(require("path"), 1);
5772
+ var fs37 = __toESM(require("fs"), 1);
5773
+ var path35 = __toESM(require("path"), 1);
5564
5774
  var JAVA_BIN = process.platform === "win32" ? "java.exe" : "java";
5565
5775
  function findJava(sdk, env = process.env) {
5566
5776
  const candidates = [
5567
- env.JAVA_HOME && path34.join(env.JAVA_HOME, "bin", JAVA_BIN),
5568
- sdk && path34.join(sdk.installRoot, "jbr", "bin", JAVA_BIN),
5777
+ env.JAVA_HOME && path35.join(env.JAVA_HOME, "bin", JAVA_BIN),
5778
+ sdk && path35.join(sdk.installRoot, "jbr", "bin", JAVA_BIN),
5569
5779
  // macOS: the JBR is itself a bundle inside Studio's Contents.
5570
- sdk && path34.join(sdk.installRoot, "jbr", "Contents", "Home", "bin", JAVA_BIN),
5571
- ...(env.PATH ?? "").split(path34.delimiter).map((dir) => dir && path34.join(dir, JAVA_BIN))
5780
+ sdk && path35.join(sdk.installRoot, "jbr", "Contents", "Home", "bin", JAVA_BIN),
5781
+ ...(env.PATH ?? "").split(path35.delimiter).map((dir) => dir && path35.join(dir, JAVA_BIN))
5572
5782
  ];
5573
- const found = candidates.find((c) => !!c && fs36.existsSync(c));
5783
+ const found = candidates.find((c) => !!c && fs37.existsSync(c));
5574
5784
  if (found) return found;
5575
5785
  throw new OniroError(
5576
5786
  "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."
@@ -5584,13 +5794,13 @@ function redactArgs(args) {
5584
5794
  return args.map((arg, i) => args[i - 1] === "-keyPwd" || args[i - 1] === "-keystorePwd" ? "******" : arg);
5585
5795
  }
5586
5796
  async function generateKeystoreAndCsr(opts) {
5587
- const jar = path35.join(opts.sdk.sdkPath, "default", "openharmony", "toolchains", "lib", "hap-sign-tool.jar");
5588
- if (!fs37.existsSync(jar)) {
5797
+ const jar = path36.join(opts.sdk.sdkPath, "default", "openharmony", "toolchains", "lib", "hap-sign-tool.jar");
5798
+ if (!fs38.existsSync(jar)) {
5589
5799
  throw new OniroError(`hap-sign-tool.jar not found at ${jar}; the HarmonyOS SDK looks incomplete.`);
5590
5800
  }
5591
5801
  const java = findJava(opts.sdk);
5592
5802
  const keyPwd = crypto7.randomBytes(12).toString("base64url").replaceAll(/[-_]/g, "");
5593
- fs37.mkdirSync(path35.dirname(opts.p12Path), { recursive: true, mode: 448 });
5803
+ fs38.mkdirSync(path36.dirname(opts.p12Path), { recursive: true, mode: 448 });
5594
5804
  const run = async (what, args) => {
5595
5805
  const argv = ["-jar", jar, ...args, "-keyAlias", KEY_ALIAS, "-keystoreFile", opts.p12Path, "-keystorePwd", keyPwd, "-keyPwd", keyPwd];
5596
5806
  opts.logger.debug(`[harmonyos] ${java} ${redactArgs(argv).join(" ")}`);
@@ -5604,28 +5814,28 @@ ${detail}` : ""}`);
5604
5814
  opts.logger.info("[harmonyos] Generating the local keystore and certificate signing request...");
5605
5815
  await run("generate the key pair", ["generate-keypair", "-keyAlg", "ECC", "-keySize", "NIST-P-256"]);
5606
5816
  await run("generate the CSR", ["generate-csr", "-subject", "CN=DebugKey", "-signAlg", SIGN_ALG, "-outFile", opts.csrPath]);
5607
- return { keyPwd, csr: fs37.readFileSync(opts.csrPath, "utf8") };
5817
+ return { keyPwd, csr: fs38.readFileSync(opts.csrPath, "utf8") };
5608
5818
  }
5609
5819
 
5610
5820
  // src/harmonyos/signing/paths.ts
5611
5821
  var crypto8 = __toESM(require("crypto"), 1);
5612
- var path36 = __toESM(require("path"), 1);
5822
+ var path37 = __toESM(require("path"), 1);
5613
5823
  function sanitizeNamePart(raw) {
5614
5824
  const safe = (raw?.trim() ?? "").replace(/[\\/:*?"<>|=.-]+/g, "_");
5615
5825
  return safe.slice(0, 64) || "default";
5616
5826
  }
5617
5827
  function resolveSigningMaterialPaths(config, productName, projectRoot) {
5618
5828
  const hash = crypto8.createHash("sha256").update(projectRoot, "utf8").digest("base64url").replaceAll(/[-_]/g, "");
5619
- const base = path36.join(
5829
+ const base = path37.join(
5620
5830
  config.get("harmonyosSigningDir", defaultPaths.harmonyosSigningDir()),
5621
- `${sanitizeNamePart(productName)}_${path36.basename(projectRoot)}_${hash}=`
5831
+ `${sanitizeNamePart(productName)}_${path37.basename(projectRoot)}_${hash}=`
5622
5832
  );
5623
5833
  return { p12Path: `${base}.p12`, csrPath: `${base}.csr`, cerPath: `${base}.cer`, profilePath: `${base}.p7b` };
5624
5834
  }
5625
5835
 
5626
5836
  // src/harmonyos/signing/project.ts
5627
- var fs38 = __toESM(require("fs"), 1);
5628
- var path37 = __toESM(require("path"), 1);
5837
+ var fs39 = __toESM(require("fs"), 1);
5838
+ var path38 = __toESM(require("path"), 1);
5629
5839
  var import_json56 = __toESM(require("json5"), 1);
5630
5840
 
5631
5841
  // src/harmonyos/signing/aclPermissionList.ts
@@ -5693,9 +5903,9 @@ var ACL_PERMISSIONS = /* @__PURE__ */ new Set([
5693
5903
 
5694
5904
  // src/harmonyos/signing/project.ts
5695
5905
  function readRequestedPermissions(moduleConfigPath) {
5696
- if (!fs38.existsSync(moduleConfigPath)) return [];
5906
+ if (!fs39.existsSync(moduleConfigPath)) return [];
5697
5907
  try {
5698
- const parsed = import_json56.default.parse(fs38.readFileSync(moduleConfigPath, "utf-8"));
5908
+ const parsed = import_json56.default.parse(fs39.readFileSync(moduleConfigPath, "utf-8"));
5699
5909
  return (parsed.module?.requestPermissions ?? []).map((p) => p?.name).filter((n) => typeof n === "string" && n.length > 0);
5700
5910
  } catch {
5701
5911
  return [];
@@ -5704,10 +5914,10 @@ function readRequestedPermissions(moduleConfigPath) {
5704
5914
  function collectAclPermissions(projectDir, logger = noopLogger) {
5705
5915
  const requested = /* @__PURE__ */ new Set();
5706
5916
  for (const module2 of listModules({ projectDir })) {
5707
- const moduleRoot = path37.resolve(projectDir, module2.srcPath);
5917
+ const moduleRoot = path38.resolve(projectDir, module2.srcPath);
5708
5918
  for (const sourceSet of ["main", "ohosTest"]) {
5709
5919
  for (const name of readRequestedPermissions(
5710
- path37.join(moduleRoot, "src", sourceSet, "module.json5")
5920
+ path38.join(moduleRoot, "src", sourceSet, "module.json5")
5711
5921
  )) {
5712
5922
  requested.add(name);
5713
5923
  }
@@ -5723,7 +5933,7 @@ function collectAclPermissions(projectDir, logger = noopLogger) {
5723
5933
  // src/harmonyos/signing/autoSign.ts
5724
5934
  function shouldRegenerate(input) {
5725
5935
  if (input.force) return "--force was passed";
5726
- const missing = Object.values(input.paths).find((file) => !fs39.existsSync(file));
5936
+ const missing = Object.values(input.paths).find((file) => !fs40.existsSync(file));
5727
5937
  if (missing) return `${missing} is missing`;
5728
5938
  const config = input.buildProfile.app?.signingConfigs?.find((c) => c.name === input.productName);
5729
5939
  if (config?.type !== "HarmonyOS" || config.material?.profile !== input.paths.profilePath) {
@@ -5762,16 +5972,16 @@ function writeSigningConfig(opts) {
5762
5972
  for (const product of app.products ?? []) {
5763
5973
  if (product.name === opts.productName) product.signingConfig = opts.productName;
5764
5974
  }
5765
- fs39.writeFileSync(opts.buildProfilePath, JSON.stringify(opts.buildProfile, null, 2));
5975
+ fs40.writeFileSync(opts.buildProfilePath, JSON.stringify(opts.buildProfile, null, 2));
5766
5976
  }
5767
5977
  async function harmonyOsAutoSign(opts) {
5768
5978
  const logger = opts.logger ?? noopLogger;
5769
5979
  const productName = opts.productName ?? "default";
5770
- const projectDir = path38.resolve(opts.projectDir);
5771
- const buildProfilePath = path38.join(projectDir, "build-profile.json5");
5980
+ const projectDir = path39.resolve(opts.projectDir);
5981
+ const buildProfilePath = path39.join(projectDir, "build-profile.json5");
5772
5982
  let buildProfile;
5773
5983
  try {
5774
- buildProfile = import_json57.default.parse(fs39.readFileSync(buildProfilePath, "utf-8"));
5984
+ buildProfile = import_json57.default.parse(fs40.readFileSync(buildProfilePath, "utf-8"));
5775
5985
  } catch (err) {
5776
5986
  throw new OniroError(`Could not read ${buildProfilePath}: ${err.message}`, err);
5777
5987
  }
@@ -5811,7 +6021,7 @@ async function harmonyOsAutoSign(opts) {
5811
6021
  logger.info(`[harmonyos] Regenerating signing material: ${reason}.`);
5812
6022
  const agc = createAgcClient(opts.http ?? createHttpClient({ logger }), auth);
5813
6023
  const deviceIds = await registerDevices(agc, connected, logger);
5814
- for (const file of Object.values(paths)) fs39.rmSync(file, { force: true });
6024
+ for (const file of Object.values(paths)) fs40.rmSync(file, { force: true });
5815
6025
  const certName = `oniro_debug_${auth.teamId.replace(/[\\/.:]/g, "")}.cer`;
5816
6026
  const stale = (await agc.listCertificates()).find((c) => c.certName === certName);
5817
6027
  if (stale) {
@@ -5842,10 +6052,10 @@ async function harmonyOsAutoSign(opts) {
5842
6052
  try {
5843
6053
  verifySigningMaterial({ ...paths, keyAlias: KEY_ALIAS, keyPwd });
5844
6054
  } catch (err) {
5845
- fs39.rmSync(paths.profilePath, { force: true });
6055
+ fs40.rmSync(paths.profilePath, { force: true });
5846
6056
  throw err;
5847
6057
  }
5848
- const materialDir = path38.join(path38.dirname(paths.p12Path), "material");
6058
+ const materialDir = path39.join(path39.dirname(paths.p12Path), "material");
5849
6059
  try {
5850
6060
  getKey(materialDir);
5851
6061
  } catch {
@@ -5857,7 +6067,7 @@ async function harmonyOsAutoSign(opts) {
5857
6067
  }
5858
6068
 
5859
6069
  // src/index.ts
5860
- var VERSION = "0.14.1";
6070
+ var VERSION = "0.15.0";
5861
6071
  // Annotate the CommonJS export names for ESM import in node:
5862
6072
  0 && (module.exports = {
5863
6073
  ALL_SDKS,
@@ -5872,6 +6082,7 @@ var VERSION = "0.14.1";
5872
6082
  HUAWEI_SITES,
5873
6083
  INSTALL_TMP_DIRNAME,
5874
6084
  InsufficientSpaceError,
6085
+ OHOS_DAILY_BUILDS_URL,
5875
6086
  OHOS_URL_BASE,
5876
6087
  OniroError,
5877
6088
  SIGNING_ERRORS,
@@ -5892,6 +6103,7 @@ var VERSION = "0.14.1";
5892
6103
  buildHvigorwArgs,
5893
6104
  buildInputCommand,
5894
6105
  buildRawTouchCommand,
6106
+ bundleNameFromHapConfig,
5895
6107
  captureBurst,
5896
6108
  captureLog,
5897
6109
  cleanCodelinterOutput,
@@ -5945,6 +6157,7 @@ var VERSION = "0.14.1";
5945
6157
  findLayoutNodes,
5946
6158
  findMissingProjectSdk,
5947
6159
  findRunningProcess,
6160
+ findSdkOsDir,
5948
6161
  flattenLayout,
5949
6162
  forceStop,
5950
6163
  formatBytes,
@@ -5978,10 +6191,12 @@ var VERSION = "0.14.1";
5978
6191
  hdcTargetArgs,
5979
6192
  hdcTargetArgsFor,
5980
6193
  hideKeyboard,
6194
+ hilogTimeMs,
5981
6195
  installApp,
5982
6196
  installCmdTools,
5983
6197
  installEmulator,
5984
6198
  installFailureReason,
6199
+ installSdkFromArchive,
5985
6200
  isBundleInstalled,
5986
6201
  isCmdToolsInstalled,
5987
6202
  isEmulatorInstalled,
@@ -5998,6 +6213,7 @@ var VERSION = "0.14.1";
5998
6213
  listRunningProcesses,
5999
6214
  listTemplates,
6000
6215
  locateTarget,
6216
+ missingProjectRootFiles,
6001
6217
  needsCmdWrapper,
6002
6218
  nonInteractivePrompter,
6003
6219
  noopLogger,
@@ -6021,9 +6237,11 @@ var VERSION = "0.14.1";
6021
6237
  pickTarget,
6022
6238
  prepareSigning,
6023
6239
  pruneLayout,
6240
+ readHapBundleName,
6024
6241
  readJson5File,
6025
6242
  readJsonFile,
6026
6243
  readProvisionProfile,
6244
+ readZipEntry,
6027
6245
  reboot,
6028
6246
  recvFile,
6029
6247
  removeCmdTools,