@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.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);
@@ -1849,6 +1949,7 @@ var import_node_child_process4 = require("child_process");
1849
1949
 
1850
1950
  // src/hdc/exec.ts
1851
1951
  var fs15 = __toESM(require("fs"), 1);
1952
+ var import_node_string_decoder = require("string_decoder");
1852
1953
  function errorMessage(err) {
1853
1954
  return err instanceof Error ? err.message : String(err);
1854
1955
  }
@@ -1876,6 +1977,8 @@ function runProcess(opts) {
1876
1977
  let stderr = "";
1877
1978
  let settled = false;
1878
1979
  let timedOut = false;
1980
+ const stdoutDecoder = new import_node_string_decoder.StringDecoder("utf8");
1981
+ const stderrDecoder = new import_node_string_decoder.StringDecoder("utf8");
1879
1982
  const cleanup = () => {
1880
1983
  clearTimeout(timer);
1881
1984
  abortSignal?.removeEventListener("abort", onAbort);
@@ -1893,14 +1996,14 @@ function runProcess(opts) {
1893
1996
  }, timeoutMs);
1894
1997
  abortSignal?.addEventListener("abort", onAbort, { once: true });
1895
1998
  child.stdout?.on("data", (d) => {
1896
- const s = d.toString();
1999
+ const s = stdoutDecoder.write(d);
1897
2000
  stdout += s;
1898
- onOutput?.(s, "stdout");
2001
+ if (s) onOutput?.(s, "stdout");
1899
2002
  });
1900
2003
  child.stderr?.on("data", (d) => {
1901
- const s = d.toString();
2004
+ const s = stderrDecoder.write(d);
1902
2005
  stderr += s;
1903
- onOutput?.(s, "stderr");
2006
+ if (s) onOutput?.(s, "stderr");
1904
2007
  });
1905
2008
  child.on("error", (err) => {
1906
2009
  if (settled) return;
@@ -1912,6 +2015,12 @@ function runProcess(opts) {
1912
2015
  if (settled) return;
1913
2016
  settled = true;
1914
2017
  cleanup();
2018
+ const stdoutTail = stdoutDecoder.end();
2019
+ const stderrTail = stderrDecoder.end();
2020
+ stdout += stdoutTail;
2021
+ stderr += stderrTail;
2022
+ if (stdoutTail) onOutput?.(stdoutTail, "stdout");
2023
+ if (stderrTail) onOutput?.(stderrTail, "stderr");
1915
2024
  if (timedOut) {
1916
2025
  reject(
1917
2026
  new OniroError(
@@ -2199,6 +2308,7 @@ async function getEmulatorStatus(config, opts = {}) {
2199
2308
  // src/hdc/devices.ts
2200
2309
  var STATUS_TOKENS = /* @__PURE__ */ new Set([
2201
2310
  "Connected",
2311
+ "Ready",
2202
2312
  "Offline",
2203
2313
  "Unauthorized",
2204
2314
  "Authorizing",
@@ -2428,9 +2538,13 @@ function moduleOfHap(hapFileName) {
2428
2538
  const dash = hapFileName.indexOf("-");
2429
2539
  return dash > 0 ? hapFileName.slice(0, dash) : hapFileName.replace(/\.hap$/i, "");
2430
2540
  }
2541
+ function isAppProjectRoot(dir) {
2542
+ return fs17.existsSync(path18.join(dir, "AppScope", "app.json5"));
2543
+ }
2431
2544
  async function discoverHaps(opts) {
2432
2545
  const { projectDir } = opts;
2433
2546
  const maxDepth = opts.maxDepth ?? 8;
2547
+ const skipNestedProjects = isAppProjectRoot(projectDir);
2434
2548
  const result = {};
2435
2549
  const walk = async (dir, depth) => {
2436
2550
  if (depth > maxDepth) return;
@@ -2439,6 +2553,7 @@ async function discoverHaps(opts) {
2439
2553
  const full = path18.join(dir, e.name);
2440
2554
  if (e.isDirectory()) {
2441
2555
  if (e.name === "node_modules" || e.name === "oh_modules" || e.name === ".git") continue;
2556
+ if (skipNestedProjects && isAppProjectRoot(full)) continue;
2442
2557
  await walk(full, depth + 1);
2443
2558
  } else if (e.isFile() && e.name.endsWith(".hap")) {
2444
2559
  const moduleName = moduleOfHap(e.name);
@@ -3630,6 +3745,54 @@ async function resolveProcessId(config, bundle, deviceSerial) {
3630
3745
  const proc = await findRunningProcess({ config, bundle, deviceSerial });
3631
3746
  return proc?.pid;
3632
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
+ }
3633
3796
  function waitForLog(opts) {
3634
3797
  return new Promise((resolve7, reject) => {
3635
3798
  const deadline = Date.now() + opts.timeoutMs;
@@ -3637,6 +3800,8 @@ function waitForLog(opts) {
3637
3800
  let child = null;
3638
3801
  let reconnectTimer;
3639
3802
  let buf = "";
3803
+ let recent = { test: () => true, afterReconnect: async () => {
3804
+ } };
3640
3805
  const cleanup = () => {
3641
3806
  clearTimeout(timer);
3642
3807
  if (reconnectTimer) clearTimeout(reconnectTimer);
@@ -3677,7 +3842,7 @@ function waitForLog(opts) {
3677
3842
  buf = lines.pop() ?? "";
3678
3843
  for (const line of lines) {
3679
3844
  const entry = parseHilogLine(line);
3680
- if (entry && opts.pattern.test(entryText(entry))) {
3845
+ if (entry && recent.test(entry) && opts.pattern.test(entryText(entry))) {
3681
3846
  finish(entry);
3682
3847
  return;
3683
3848
  }
@@ -3687,13 +3852,21 @@ function waitForLog(opts) {
3687
3852
  if (settled || child !== stream) return;
3688
3853
  child = null;
3689
3854
  if (Date.now() < deadline) {
3690
- reconnectTimer = setTimeout(() => void spawnStream().catch(failWith), 1e3);
3855
+ reconnectTimer = setTimeout(() => void recent.afterReconnect().then(spawnStream).catch(failWith), 1e3);
3691
3856
  }
3692
3857
  };
3693
3858
  stream.once("close", onEnd);
3694
3859
  stream.once("error", onEnd);
3695
3860
  };
3696
- 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);
3697
3870
  });
3698
3871
  }
3699
3872
  function watchLog(opts) {
@@ -3702,6 +3875,8 @@ function watchLog(opts) {
3702
3875
  let settled = false;
3703
3876
  let child = null;
3704
3877
  let buf = "";
3878
+ let recent = { test: () => true, afterReconnect: async () => {
3879
+ } };
3705
3880
  const cleanup = () => {
3706
3881
  clearTimeout(timer);
3707
3882
  opts.abortSignal?.removeEventListener("abort", onAbort);
@@ -3725,6 +3900,13 @@ function watchLog(opts) {
3725
3900
  const timer = setTimeout(done, opts.durationMs);
3726
3901
  opts.abortSignal?.addEventListener("abort", onAbort, { once: true });
3727
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;
3728
3910
  const processId = await resolveProcessId(opts.config, opts.bundle, opts.deviceSerial);
3729
3911
  if (settled) return;
3730
3912
  const stream = streamHilog({ config: opts.config, processId, domain: opts.domain, deviceSerial: opts.deviceSerial });
@@ -3735,7 +3917,7 @@ function watchLog(opts) {
3735
3917
  buf = lines.pop() ?? "";
3736
3918
  for (const line of lines) {
3737
3919
  const entry = parseHilogLine(line);
3738
- if (entry && opts.pattern.test(entryText(entry))) matches.push(entry);
3920
+ if (entry && recent.test(entry) && opts.pattern.test(entryText(entry))) matches.push(entry);
3739
3921
  }
3740
3922
  });
3741
3923
  })().catch((err) => {
@@ -4020,6 +4202,8 @@ async function ensureOhModules(opts) {
4020
4202
  }
4021
4203
 
4022
4204
  // src/build/codelinter.ts
4205
+ var fs26 = __toESM(require("fs"), 1);
4206
+ var path26 = __toESM(require("path"), 1);
4023
4207
  var FINDING_RE = /^(.*?):(\d+):(?:(\d+):)?\s*(error|warning|warn|suggestion|info)\b[:\s-]*(.*)$/i;
4024
4208
  var TRAILING_CODE_RE = /[([]([\w@/.\-]+)[)\]]\s*$/;
4025
4209
  var FILE_HEADER_RE = /^(.+\.\w+)\((\d+)\)$/;
@@ -4106,6 +4290,16 @@ function parseCodelinterReport(output) {
4106
4290
  function parseCodelinterFindings(output) {
4107
4291
  return parseCodelinterReport(output).findings;
4108
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
+ }
4109
4303
  async function runCodelinter(opts) {
4110
4304
  const res = await runProcess({
4111
4305
  command: getCmdToolsBin(opts.config, "codelinter"),
@@ -4123,7 +4317,7 @@ async function runCodelinter(opts) {
4123
4317
  var DIAGNOSIS_OUTPUT_LIMIT = 2e6;
4124
4318
  function diagnoseBuildFailure(output) {
4125
4319
  if (/is not exported from Kit|has no exported member .*@systemapi/i.test(output)) {
4126
- 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\`).`;
4127
4321
  }
4128
4322
  if (/00308018|SDK path does not exist|SDK license agreement is not accepted/i.test(output)) {
4129
4323
  return "hvigor cannot find the SDK the project compiles against: check `oniro-app sdk list` against compileSdkVersion.";
@@ -4195,21 +4389,21 @@ function isValidBundleName(bundleName) {
4195
4389
  }
4196
4390
 
4197
4391
  // src/project/templates.ts
4198
- var fs26 = __toESM(require("fs"), 1);
4199
- var path26 = __toESM(require("path"), 1);
4392
+ var fs27 = __toESM(require("fs"), 1);
4393
+ var path27 = __toESM(require("path"), 1);
4200
4394
  function toHumanTemplateName(folderName) {
4201
4395
  return folderName.replace(/([a-z])([A-Z])/g, "$1 $2");
4202
4396
  }
4203
4397
  function listTemplates(templateRoot) {
4204
- if (!fs26.existsSync(templateRoot)) return [];
4205
- const entries = fs26.readdirSync(templateRoot, { withFileTypes: true });
4398
+ if (!fs27.existsSync(templateRoot)) return [];
4399
+ const entries = fs27.readdirSync(templateRoot, { withFileTypes: true });
4206
4400
  return entries.filter((e) => e.isDirectory()).map((e) => {
4207
- const dir = path26.join(templateRoot, e.name);
4208
- const metaPath = path26.join(dir, "template.json");
4401
+ const dir = path27.join(templateRoot, e.name);
4402
+ const metaPath = path27.join(dir, "template.json");
4209
4403
  let meta = {};
4210
- if (fs26.existsSync(metaPath)) {
4404
+ if (fs27.existsSync(metaPath)) {
4211
4405
  try {
4212
- meta = JSON.parse(fs26.readFileSync(metaPath, "utf8"));
4406
+ meta = JSON.parse(fs27.readFileSync(metaPath, "utf8"));
4213
4407
  } catch {
4214
4408
  }
4215
4409
  }
@@ -4224,57 +4418,57 @@ function listTemplates(templateRoot) {
4224
4418
  function validateTemplateLayout(templateDir, defaultModuleName) {
4225
4419
  const required = [
4226
4420
  "build-profile.json5",
4227
- path26.join("AppScope", "app.json5"),
4228
- path26.join(defaultModuleName, "src", "main", "module.json5"),
4229
- 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"),
4230
4424
  "hvigorfile.ts"
4231
4425
  ];
4232
- return required.filter((rel) => !fs26.existsSync(path26.join(templateDir, rel)));
4426
+ return required.filter((rel) => !fs27.existsSync(path27.join(templateDir, rel)));
4233
4427
  }
4234
4428
 
4235
4429
  // src/project/createScaffold.ts
4236
- var fs28 = __toESM(require("fs"), 1);
4237
- var path27 = __toESM(require("path"), 1);
4430
+ var fs29 = __toESM(require("fs"), 1);
4431
+ var path28 = __toESM(require("path"), 1);
4238
4432
 
4239
4433
  // src/project/jsonHelpers.ts
4240
- var fs27 = __toESM(require("fs"), 1);
4434
+ var fs28 = __toESM(require("fs"), 1);
4241
4435
  var import_json55 = __toESM(require("json5"), 1);
4242
4436
  function readJson5File(filePath) {
4243
- return import_json55.default.parse(fs27.readFileSync(filePath, "utf8"));
4437
+ return import_json55.default.parse(fs28.readFileSync(filePath, "utf8"));
4244
4438
  }
4245
4439
  function writeJson5File(filePath, value) {
4246
- fs27.writeFileSync(filePath, JSON.stringify(value, null, 2) + "\n", "utf8");
4440
+ fs28.writeFileSync(filePath, JSON.stringify(value, null, 2) + "\n", "utf8");
4247
4441
  }
4248
4442
  function readJsonFile(filePath) {
4249
- return JSON.parse(fs27.readFileSync(filePath, "utf8"));
4443
+ return JSON.parse(fs28.readFileSync(filePath, "utf8"));
4250
4444
  }
4251
4445
  function writeJsonFile(filePath, value) {
4252
- fs27.writeFileSync(filePath, JSON.stringify(value, null, 2) + "\n", "utf8");
4446
+ fs28.writeFileSync(filePath, JSON.stringify(value, null, 2) + "\n", "utf8");
4253
4447
  }
4254
4448
 
4255
4449
  // src/project/createScaffold.ts
4256
4450
  var IGNORED_DIRS = /* @__PURE__ */ new Set(["oh_modules", "node_modules", "build", ".hvigor"]);
4257
4451
  async function pathExists(p) {
4258
4452
  try {
4259
- await fs28.promises.access(p);
4453
+ await fs29.promises.access(p);
4260
4454
  return true;
4261
4455
  } catch {
4262
4456
  return false;
4263
4457
  }
4264
4458
  }
4265
4459
  async function copyDirRecursive(srcDir, destDir) {
4266
- await fs28.promises.mkdir(destDir, { recursive: true });
4267
- 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 });
4268
4462
  for (const entry of entries) {
4269
- const src = path27.join(srcDir, entry.name);
4270
- const dest = path27.join(destDir, entry.name);
4463
+ const src = path28.join(srcDir, entry.name);
4464
+ const dest = path28.join(destDir, entry.name);
4271
4465
  if (entry.isDirectory()) {
4272
4466
  await copyDirRecursive(src, dest);
4273
4467
  } else if (entry.isSymbolicLink()) {
4274
4468
  continue;
4275
4469
  } else if (entry.isFile()) {
4276
4470
  if (entry.name === "template.json") continue;
4277
- await fs28.promises.copyFile(src, dest);
4471
+ await fs29.promises.copyFile(src, dest);
4278
4472
  }
4279
4473
  }
4280
4474
  }
@@ -4282,9 +4476,9 @@ async function normalizeJson5ToJson(projectDir, logger) {
4282
4476
  const stack = [projectDir];
4283
4477
  while (stack.length > 0) {
4284
4478
  const currentDir = stack.pop();
4285
- const entries = await fs28.promises.readdir(currentDir, { withFileTypes: true });
4479
+ const entries = await fs29.promises.readdir(currentDir, { withFileTypes: true });
4286
4480
  for (const entry of entries) {
4287
- const fullPath = path27.join(currentDir, entry.name);
4481
+ const fullPath = path28.join(currentDir, entry.name);
4288
4482
  if (entry.isDirectory()) {
4289
4483
  if (IGNORED_DIRS.has(entry.name)) continue;
4290
4484
  stack.push(fullPath);
@@ -4306,23 +4500,23 @@ function toJavaPropertiesPath(p) {
4306
4500
  function createOrUpdateLocalProperties(projectDir, sdkDir) {
4307
4501
  const content = `sdk.dir=${toJavaPropertiesPath(sdkDir)}
4308
4502
  `;
4309
- fs28.writeFileSync(path27.join(projectDir, "local.properties"), content, "utf8");
4503
+ fs29.writeFileSync(path28.join(projectDir, "local.properties"), content, "utf8");
4310
4504
  }
4311
4505
  function renameIfExists(fromPath, toPath) {
4312
- if (fs28.existsSync(fromPath) && fromPath !== toPath) {
4313
- fs28.renameSync(fromPath, toPath);
4506
+ if (fs29.existsSync(fromPath) && fromPath !== toPath) {
4507
+ fs29.renameSync(fromPath, toPath);
4314
4508
  }
4315
4509
  }
4316
4510
  function updateTemplateConfigs(projectDir, args, sdkBaseDir) {
4317
- const appJsonPath = path27.join(projectDir, "AppScope", "app.json5");
4318
- if (fs28.existsSync(appJsonPath)) {
4511
+ const appJsonPath = path28.join(projectDir, "AppScope", "app.json5");
4512
+ if (fs29.existsSync(appJsonPath)) {
4319
4513
  const appJson = readJson5File(appJsonPath);
4320
4514
  appJson.app = appJson.app ?? {};
4321
4515
  appJson.app.bundleName = args.bundleName;
4322
4516
  writeJson5File(appJsonPath, appJson);
4323
4517
  }
4324
- const stringsPath = path27.join(projectDir, "AppScope", "resources", "base", "element", "string.json");
4325
- if (fs28.existsSync(stringsPath)) {
4518
+ const stringsPath = path28.join(projectDir, "AppScope", "resources", "base", "element", "string.json");
4519
+ if (fs29.existsSync(stringsPath)) {
4326
4520
  const strings = readJsonFile(stringsPath);
4327
4521
  if (Array.isArray(strings.string)) {
4328
4522
  const appName = strings.string.find((s) => s.name === "app_name");
@@ -4330,8 +4524,8 @@ function updateTemplateConfigs(projectDir, args, sdkBaseDir) {
4330
4524
  }
4331
4525
  writeJsonFile(stringsPath, strings);
4332
4526
  }
4333
- const buildProfilePath = path27.join(projectDir, "build-profile.json5");
4334
- if (fs28.existsSync(buildProfilePath)) {
4527
+ const buildProfilePath = path28.join(projectDir, "build-profile.json5");
4528
+ if (fs29.existsSync(buildProfilePath)) {
4335
4529
  const buildProfile = readJson5File(buildProfilePath);
4336
4530
  buildProfile.app = buildProfile.app ?? {};
4337
4531
  if (Array.isArray(buildProfile.app.products) && buildProfile.app.products.length > 0) {
@@ -4344,23 +4538,23 @@ function updateTemplateConfigs(projectDir, args, sdkBaseDir) {
4344
4538
  }
4345
4539
  writeJson5File(buildProfilePath, buildProfile);
4346
4540
  }
4347
- const moduleJsonPath = path27.join(projectDir, args.moduleName, "src", "main", "module.json5");
4348
- if (fs28.existsSync(moduleJsonPath)) {
4541
+ const moduleJsonPath = path28.join(projectDir, args.moduleName, "src", "main", "module.json5");
4542
+ if (fs29.existsSync(moduleJsonPath)) {
4349
4543
  const moduleJson = readJson5File(moduleJsonPath);
4350
4544
  moduleJson.module = moduleJson.module ?? {};
4351
4545
  moduleJson.module.name = args.moduleName;
4352
4546
  writeJson5File(moduleJsonPath, moduleJson);
4353
4547
  }
4354
- const moduleOhPackagePath = path27.join(projectDir, args.moduleName, "oh-package.json5");
4355
- if (fs28.existsSync(moduleOhPackagePath)) {
4548
+ const moduleOhPackagePath = path28.join(projectDir, args.moduleName, "oh-package.json5");
4549
+ if (fs29.existsSync(moduleOhPackagePath)) {
4356
4550
  const pkg = readJson5File(moduleOhPackagePath);
4357
4551
  pkg.name = args.moduleName;
4358
4552
  writeJson5File(moduleOhPackagePath, pkg);
4359
4553
  }
4360
- const vscodeDir = path27.join(projectDir, ".vscode");
4361
- fs28.mkdirSync(vscodeDir, { recursive: true });
4554
+ const vscodeDir = path28.join(projectDir, ".vscode");
4555
+ fs29.mkdirSync(vscodeDir, { recursive: true });
4362
4556
  const hapPath = `${args.moduleName}/build/default/outputs/default/${args.moduleName}-default-signed.hap`;
4363
- writeJsonFile(path27.join(vscodeDir, "settings.json"), {
4557
+ writeJsonFile(path28.join(vscodeDir, "settings.json"), {
4364
4558
  "oniro.hapPath": hapPath,
4365
4559
  "files.associations": { "*.json5": "jsonc" }
4366
4560
  });
@@ -4377,7 +4571,7 @@ async function createScaffold(opts) {
4377
4571
  if (!opts.location || !await pathExists(opts.location)) {
4378
4572
  throw new OniroError(`Location does not exist: ${opts.location}`);
4379
4573
  }
4380
- const templateDir = path27.join(opts.templateRoot, opts.templateId);
4574
+ const templateDir = path28.join(opts.templateRoot, opts.templateId);
4381
4575
  if (!await pathExists(templateDir)) {
4382
4576
  throw new OniroError(`Template not found: ${templateDir}`);
4383
4577
  }
@@ -4412,17 +4606,17 @@ async function createScaffold(opts) {
4412
4606
  }
4413
4607
  }
4414
4608
  }
4415
- const projectDir = path27.join(opts.location, opts.projectName);
4609
+ const projectDir = path28.join(opts.location, opts.projectName);
4416
4610
  if (await pathExists(projectDir)) {
4417
4611
  if (!opts.overwrite) {
4418
4612
  throw new OniroError(`Destination already exists: ${projectDir}. Pass overwrite: true to replace it.`);
4419
4613
  }
4420
- await fs28.promises.rm(projectDir, { recursive: true, force: true });
4614
+ await fs29.promises.rm(projectDir, { recursive: true, force: true });
4421
4615
  }
4422
4616
  logger.info(`[create] Scaffolding ${opts.templateId} at ${projectDir}`);
4423
4617
  await copyDirRecursive(templateDir, projectDir);
4424
4618
  if (moduleName !== defaultModuleName) {
4425
- renameIfExists(path27.join(projectDir, defaultModuleName), path27.join(projectDir, moduleName));
4619
+ renameIfExists(path28.join(projectDir, defaultModuleName), path28.join(projectDir, moduleName));
4426
4620
  }
4427
4621
  updateTemplateConfigs(
4428
4622
  projectDir,
@@ -4439,11 +4633,11 @@ async function createScaffold(opts) {
4439
4633
  }
4440
4634
 
4441
4635
  // src/project/listModules.ts
4442
- var fs29 = __toESM(require("fs"), 1);
4443
- var path28 = __toESM(require("path"), 1);
4636
+ var fs30 = __toESM(require("fs"), 1);
4637
+ var path29 = __toESM(require("path"), 1);
4444
4638
  function listModules(opts) {
4445
- const buildProfile = path28.join(opts.projectDir, "build-profile.json5");
4446
- if (!fs29.existsSync(buildProfile)) {
4639
+ const buildProfile = path29.join(opts.projectDir, "build-profile.json5");
4640
+ if (!fs30.existsSync(buildProfile)) {
4447
4641
  throw new OniroError(`build-profile.json5 not found at ${buildProfile}`);
4448
4642
  }
4449
4643
  const parsed = readJson5File(buildProfile);
@@ -4455,12 +4649,12 @@ function listModules(opts) {
4455
4649
  }
4456
4650
 
4457
4651
  // src/project/abilities.ts
4458
- var fs30 = __toESM(require("fs"), 1);
4459
- var path29 = __toESM(require("path"), 1);
4652
+ var fs31 = __toESM(require("fs"), 1);
4653
+ var path30 = __toESM(require("path"), 1);
4460
4654
  function listAbilities(opts) {
4461
4655
  const moduleName = opts.moduleName ?? "entry";
4462
- const moduleJsonPath = path29.join(opts.projectDir, moduleName, "src", "main", "module.json5");
4463
- if (!fs30.existsSync(moduleJsonPath)) {
4656
+ const moduleJsonPath = path30.join(opts.projectDir, moduleName, "src", "main", "module.json5");
4657
+ if (!fs31.existsSync(moduleJsonPath)) {
4464
4658
  throw new OniroError(`Could not find module.json5 at ${moduleJsonPath}`);
4465
4659
  }
4466
4660
  const parsed = readJson5File(moduleJsonPath);
@@ -4468,12 +4662,12 @@ function listAbilities(opts) {
4468
4662
  }
4469
4663
 
4470
4664
  // src/project/prepareSigning.ts
4471
- var fs31 = __toESM(require("fs"), 1);
4472
- var path30 = __toESM(require("path"), 1);
4665
+ var fs32 = __toESM(require("fs"), 1);
4666
+ var path31 = __toESM(require("path"), 1);
4473
4667
  function hasSigningArtifacts(dir) {
4474
- if (!fs31.existsSync(path30.join(dir, "signatures"))) return false;
4475
- const buildProfile = path30.join(dir, "build-profile.json5");
4476
- 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;
4477
4671
  try {
4478
4672
  const parsed = readJson5File(buildProfile);
4479
4673
  return Array.isArray(parsed.app?.signingConfigs) && parsed.app.signingConfigs.length > 0;
@@ -4576,8 +4770,25 @@ async function diffHapAssets(opts) {
4576
4770
  }
4577
4771
 
4578
4772
  // src/install/apply.ts
4579
- var fs32 = __toESM(require("fs"), 1);
4580
- 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
4581
4792
  function decideInstallMethod(s) {
4582
4793
  if (!s.signInfoInconsistent) return "replace";
4583
4794
  if (s.isSystemBundle && !s.allowUninstall) return "refuse";
@@ -4598,7 +4809,7 @@ async function pidOf(config, bundle, deviceSerial) {
4598
4809
  async function resolveHap(opts, logger) {
4599
4810
  let hap;
4600
4811
  if (opts.hapPath) {
4601
- 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;
4602
4813
  hap = existingHapOrUnsigned(hapPath);
4603
4814
  if (!hap) throw new OniroError(`HAP not found at ${hapPath}.`);
4604
4815
  } else {
@@ -4607,7 +4818,7 @@ async function resolveHap(opts, logger) {
4607
4818
  if (!picked) throw new OniroError(`No HAP found${opts.module ? ` for module ${opts.module}` : ""}. Build first.`);
4608
4819
  hap = { path: picked.path, fallback: !picked.signed };
4609
4820
  }
4610
- 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)}.`);
4611
4822
  return hap;
4612
4823
  }
4613
4824
  async function applyChanges(opts) {
@@ -4617,9 +4828,18 @@ async function applyChanges(opts) {
4617
4828
  const allowUninstall = opts.allowUninstall ?? false;
4618
4829
  const hap = await resolveHap(opts, logger);
4619
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
+ }
4620
4840
  logger.info(`installing ${hapPath}`);
4621
4841
  let assetsChanged = false;
4622
- if (opts.installedHapPath && fs32.existsSync(opts.installedHapPath)) {
4842
+ if (opts.installedHapPath && fs33.existsSync(opts.installedHapPath)) {
4623
4843
  try {
4624
4844
  const diff = await diffHapAssets({ installedHap: opts.installedHapPath, newHap: hapPath });
4625
4845
  assetsChanged = diff.addedAssetPaths.length > 0 || diff.removedAssetPaths.length > 0;
@@ -4652,7 +4872,7 @@ async function applyChanges(opts) {
4652
4872
  }
4653
4873
  if (!await isBundleInstalled({ config, bundle, deviceSerial })) {
4654
4874
  throw new OniroError(
4655
- `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).`
4656
4876
  );
4657
4877
  }
4658
4878
  let postInstallPid = await pidOf(config, bundle, deviceSerial);
@@ -4729,9 +4949,9 @@ var AUTH_PATHS = {
4729
4949
  };
4730
4950
 
4731
4951
  // src/harmonyos/auth/tokenStore.ts
4732
- var fs33 = __toESM(require("fs"), 1);
4952
+ var fs34 = __toESM(require("fs"), 1);
4733
4953
  var os11 = __toESM(require("os"), 1);
4734
- var path32 = __toESM(require("path"), 1);
4954
+ var path33 = __toESM(require("path"), 1);
4735
4955
  var crypto3 = __toESM(require("crypto"), 1);
4736
4956
  var ALGORITHM = "aes-256-gcm";
4737
4957
  var KEY_LENGTH = 32;
@@ -4767,7 +4987,7 @@ function permissionHint(dir) {
4767
4987
  }
4768
4988
  function ensureDir(dir) {
4769
4989
  try {
4770
- fs33.mkdirSync(dir, { recursive: true, mode: DIR_MODE });
4990
+ fs34.mkdirSync(dir, { recursive: true, mode: DIR_MODE });
4771
4991
  } catch (err) {
4772
4992
  if (err.code === "EACCES") {
4773
4993
  throw new OniroError(permissionHint(dir), err);
@@ -4778,17 +4998,17 @@ function ensureDir(dir) {
4778
4998
  function createTokenStore(opts) {
4779
4999
  const logger = opts.logger ?? noopLogger;
4780
5000
  const authDir = opts.config.get("harmonyosAuthDir", defaultPaths.harmonyosAuthDir());
4781
- const tokenPath = path32.join(authDir, TOKEN_FILE);
4782
- 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);
4783
5003
  function loadOrCreateKey() {
4784
- if (fs33.existsSync(keyPath)) {
4785
- const existing = fs33.readFileSync(keyPath);
5004
+ if (fs34.existsSync(keyPath)) {
5005
+ const existing = fs34.readFileSync(keyPath);
4786
5006
  if (existing.length === KEY_LENGTH) return existing;
4787
5007
  logger.warn("[harmonyos] The token key file was malformed; regenerating (you will need to log in again).");
4788
5008
  }
4789
- ensureDir(path32.dirname(keyPath));
5009
+ ensureDir(path33.dirname(keyPath));
4790
5010
  const key = crypto3.randomBytes(KEY_LENGTH);
4791
- fs33.writeFileSync(keyPath, key, { mode: FILE_MODE });
5011
+ fs34.writeFileSync(keyPath, key, { mode: FILE_MODE });
4792
5012
  return key;
4793
5013
  }
4794
5014
  return {
@@ -4798,12 +5018,12 @@ function createTokenStore(opts) {
4798
5018
  const payload = Buffer.from(JSON.stringify(session), "utf8");
4799
5019
  const blob = encrypt2(loadOrCreateKey(), payload);
4800
5020
  ensureDir(authDir);
4801
- fs33.writeFileSync(tokenPath, JSON.stringify(blob, null, 2), { mode: FILE_MODE });
5021
+ fs34.writeFileSync(tokenPath, JSON.stringify(blob, null, 2), { mode: FILE_MODE });
4802
5022
  },
4803
5023
  load() {
4804
- if (!fs33.existsSync(tokenPath)) return null;
5024
+ if (!fs34.existsSync(tokenPath)) return null;
4805
5025
  try {
4806
- const blob = JSON.parse(fs33.readFileSync(tokenPath, "utf8"));
5026
+ const blob = JSON.parse(fs34.readFileSync(tokenPath, "utf8"));
4807
5027
  if (!isEncryptedBlob(blob)) return null;
4808
5028
  const { jwtToken, siteId } = JSON.parse(decrypt2(loadOrCreateKey(), blob).toString("utf8"));
4809
5029
  return typeof jwtToken === "string" && jwtToken && typeof siteId === "string" ? { jwtToken, siteId } : null;
@@ -4815,7 +5035,7 @@ function createTokenStore(opts) {
4815
5035
  }
4816
5036
  logger.debug(`[harmonyos] Discarding unreadable token at ${tokenPath}.`);
4817
5037
  try {
4818
- fs33.rmSync(tokenPath, { force: true });
5038
+ fs34.rmSync(tokenPath, { force: true });
4819
5039
  } catch {
4820
5040
  }
4821
5041
  return null;
@@ -4823,7 +5043,7 @@ function createTokenStore(opts) {
4823
5043
  },
4824
5044
  clear() {
4825
5045
  try {
4826
- fs33.rmSync(tokenPath, { force: true });
5046
+ fs34.rmSync(tokenPath, { force: true });
4827
5047
  } catch (err) {
4828
5048
  throw new OniroError(`Failed to remove ${tokenPath}.`, err);
4829
5049
  }
@@ -5304,7 +5524,7 @@ function mapAgcError(operation, statusCode, reasonPhrase, body) {
5304
5524
  }
5305
5525
 
5306
5526
  // src/harmonyos/signing/profile.ts
5307
- var fs34 = __toESM(require("fs"), 1);
5527
+ var fs35 = __toESM(require("fs"), 1);
5308
5528
  var import_node_crypto3 = require("crypto");
5309
5529
  var import_node_forge = __toESM(require("node-forge"), 1);
5310
5530
  function extractProfileJson(buffer) {
@@ -5336,7 +5556,7 @@ function extractProfileJson(buffer) {
5336
5556
  function readProvisionProfile(profilePath) {
5337
5557
  let raw;
5338
5558
  try {
5339
- const json = extractProfileJson(fs34.readFileSync(profilePath));
5559
+ const json = extractProfileJson(fs35.readFileSync(profilePath));
5340
5560
  if (!json) return null;
5341
5561
  raw = JSON.parse(json);
5342
5562
  } catch {
@@ -5365,7 +5585,7 @@ function parseCertificate(pem) {
5365
5585
  }
5366
5586
  function keystoreHoldsKey(p12Path, keyAlias, keyPwd, certificate) {
5367
5587
  try {
5368
- 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"));
5369
5589
  const p12 = import_node_forge.default.pkcs12.pkcs12FromAsn1(import_node_forge.default.asn1.fromDer(der), keyPwd);
5370
5590
  const certBag = import_node_forge.default.pki.oids.certBag ?? "1.2.840.113549.1.12.10.1.3";
5371
5591
  const wanted = certificate.publicKey.export({ type: "spki", format: "der" });
@@ -5381,7 +5601,7 @@ function keystoreHoldsKey(p12Path, keyAlias, keyPwd, certificate) {
5381
5601
  }
5382
5602
  }
5383
5603
  function verifySigningMaterial(opts) {
5384
- 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);
5385
5605
  const chain = (pems ?? []).map(parseCertificate);
5386
5606
  const leaf = chain.find((c) => !c.ca) ?? chain.at(-1);
5387
5607
  if (!leaf) throw new AgcError(SIGNING_ERRORS.CERT_INVALID);
@@ -5400,14 +5620,14 @@ function verifySigningMaterial(opts) {
5400
5620
 
5401
5621
  // src/harmonyos/signing/autoSign.ts
5402
5622
  var crypto9 = __toESM(require("crypto"), 1);
5403
- var fs39 = __toESM(require("fs"), 1);
5404
- var path38 = __toESM(require("path"), 1);
5623
+ var fs40 = __toESM(require("fs"), 1);
5624
+ var path39 = __toESM(require("path"), 1);
5405
5625
  var import_json57 = __toESM(require("json5"), 1);
5406
5626
 
5407
5627
  // src/harmonyos/signing/agc.ts
5408
5628
  var crypto6 = __toESM(require("crypto"), 1);
5409
- var fs35 = __toESM(require("fs"), 1);
5410
- var path33 = __toESM(require("path"), 1);
5629
+ var fs36 = __toESM(require("fs"), 1);
5630
+ var path34 = __toESM(require("path"), 1);
5411
5631
  var ENDPOINTS = {
5412
5632
  CERT_LIST: "/api/cps/harmony-cert-manage/v1/cert/list",
5413
5633
  CERT_ADD: "/api/cps/harmony-cert-manage/v1/cert/add",
@@ -5493,8 +5713,8 @@ function createAgcClient(http3, auth) {
5493
5713
  if (target.sha256 && actual !== target.sha256.toLowerCase()) {
5494
5714
  throw new ChecksumMismatchError(target.sha256, actual);
5495
5715
  }
5496
- fs35.mkdirSync(path33.dirname(dest), { recursive: true, mode: 448 });
5497
- fs35.writeFileSync(dest, buffer, { mode: 384 });
5716
+ fs36.mkdirSync(path34.dirname(dest), { recursive: true, mode: 448 });
5717
+ fs36.writeFileSync(dest, buffer, { mode: 384 });
5498
5718
  }
5499
5719
  };
5500
5720
  }
@@ -5545,22 +5765,22 @@ async function registerDevices(agc, connected, logger) {
5545
5765
 
5546
5766
  // src/harmonyos/signing/keystore.ts
5547
5767
  var crypto7 = __toESM(require("crypto"), 1);
5548
- var fs37 = __toESM(require("fs"), 1);
5549
- var path35 = __toESM(require("path"), 1);
5768
+ var fs38 = __toESM(require("fs"), 1);
5769
+ var path36 = __toESM(require("path"), 1);
5550
5770
 
5551
5771
  // src/harmonyos/java.ts
5552
- var fs36 = __toESM(require("fs"), 1);
5553
- var path34 = __toESM(require("path"), 1);
5772
+ var fs37 = __toESM(require("fs"), 1);
5773
+ var path35 = __toESM(require("path"), 1);
5554
5774
  var JAVA_BIN = process.platform === "win32" ? "java.exe" : "java";
5555
5775
  function findJava(sdk, env = process.env) {
5556
5776
  const candidates = [
5557
- env.JAVA_HOME && path34.join(env.JAVA_HOME, "bin", JAVA_BIN),
5558
- 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),
5559
5779
  // macOS: the JBR is itself a bundle inside Studio's Contents.
5560
- sdk && path34.join(sdk.installRoot, "jbr", "Contents", "Home", "bin", JAVA_BIN),
5561
- ...(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))
5562
5782
  ];
5563
- const found = candidates.find((c) => !!c && fs36.existsSync(c));
5783
+ const found = candidates.find((c) => !!c && fs37.existsSync(c));
5564
5784
  if (found) return found;
5565
5785
  throw new OniroError(
5566
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."
@@ -5574,13 +5794,13 @@ function redactArgs(args) {
5574
5794
  return args.map((arg, i) => args[i - 1] === "-keyPwd" || args[i - 1] === "-keystorePwd" ? "******" : arg);
5575
5795
  }
5576
5796
  async function generateKeystoreAndCsr(opts) {
5577
- const jar = path35.join(opts.sdk.sdkPath, "default", "openharmony", "toolchains", "lib", "hap-sign-tool.jar");
5578
- 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)) {
5579
5799
  throw new OniroError(`hap-sign-tool.jar not found at ${jar}; the HarmonyOS SDK looks incomplete.`);
5580
5800
  }
5581
5801
  const java = findJava(opts.sdk);
5582
5802
  const keyPwd = crypto7.randomBytes(12).toString("base64url").replaceAll(/[-_]/g, "");
5583
- fs37.mkdirSync(path35.dirname(opts.p12Path), { recursive: true, mode: 448 });
5803
+ fs38.mkdirSync(path36.dirname(opts.p12Path), { recursive: true, mode: 448 });
5584
5804
  const run = async (what, args) => {
5585
5805
  const argv = ["-jar", jar, ...args, "-keyAlias", KEY_ALIAS, "-keystoreFile", opts.p12Path, "-keystorePwd", keyPwd, "-keyPwd", keyPwd];
5586
5806
  opts.logger.debug(`[harmonyos] ${java} ${redactArgs(argv).join(" ")}`);
@@ -5594,28 +5814,28 @@ ${detail}` : ""}`);
5594
5814
  opts.logger.info("[harmonyos] Generating the local keystore and certificate signing request...");
5595
5815
  await run("generate the key pair", ["generate-keypair", "-keyAlg", "ECC", "-keySize", "NIST-P-256"]);
5596
5816
  await run("generate the CSR", ["generate-csr", "-subject", "CN=DebugKey", "-signAlg", SIGN_ALG, "-outFile", opts.csrPath]);
5597
- return { keyPwd, csr: fs37.readFileSync(opts.csrPath, "utf8") };
5817
+ return { keyPwd, csr: fs38.readFileSync(opts.csrPath, "utf8") };
5598
5818
  }
5599
5819
 
5600
5820
  // src/harmonyos/signing/paths.ts
5601
5821
  var crypto8 = __toESM(require("crypto"), 1);
5602
- var path36 = __toESM(require("path"), 1);
5822
+ var path37 = __toESM(require("path"), 1);
5603
5823
  function sanitizeNamePart(raw) {
5604
5824
  const safe = (raw?.trim() ?? "").replace(/[\\/:*?"<>|=.-]+/g, "_");
5605
5825
  return safe.slice(0, 64) || "default";
5606
5826
  }
5607
5827
  function resolveSigningMaterialPaths(config, productName, projectRoot) {
5608
5828
  const hash = crypto8.createHash("sha256").update(projectRoot, "utf8").digest("base64url").replaceAll(/[-_]/g, "");
5609
- const base = path36.join(
5829
+ const base = path37.join(
5610
5830
  config.get("harmonyosSigningDir", defaultPaths.harmonyosSigningDir()),
5611
- `${sanitizeNamePart(productName)}_${path36.basename(projectRoot)}_${hash}=`
5831
+ `${sanitizeNamePart(productName)}_${path37.basename(projectRoot)}_${hash}=`
5612
5832
  );
5613
5833
  return { p12Path: `${base}.p12`, csrPath: `${base}.csr`, cerPath: `${base}.cer`, profilePath: `${base}.p7b` };
5614
5834
  }
5615
5835
 
5616
5836
  // src/harmonyos/signing/project.ts
5617
- var fs38 = __toESM(require("fs"), 1);
5618
- var path37 = __toESM(require("path"), 1);
5837
+ var fs39 = __toESM(require("fs"), 1);
5838
+ var path38 = __toESM(require("path"), 1);
5619
5839
  var import_json56 = __toESM(require("json5"), 1);
5620
5840
 
5621
5841
  // src/harmonyos/signing/aclPermissionList.ts
@@ -5683,9 +5903,9 @@ var ACL_PERMISSIONS = /* @__PURE__ */ new Set([
5683
5903
 
5684
5904
  // src/harmonyos/signing/project.ts
5685
5905
  function readRequestedPermissions(moduleConfigPath) {
5686
- if (!fs38.existsSync(moduleConfigPath)) return [];
5906
+ if (!fs39.existsSync(moduleConfigPath)) return [];
5687
5907
  try {
5688
- const parsed = import_json56.default.parse(fs38.readFileSync(moduleConfigPath, "utf-8"));
5908
+ const parsed = import_json56.default.parse(fs39.readFileSync(moduleConfigPath, "utf-8"));
5689
5909
  return (parsed.module?.requestPermissions ?? []).map((p) => p?.name).filter((n) => typeof n === "string" && n.length > 0);
5690
5910
  } catch {
5691
5911
  return [];
@@ -5694,10 +5914,10 @@ function readRequestedPermissions(moduleConfigPath) {
5694
5914
  function collectAclPermissions(projectDir, logger = noopLogger) {
5695
5915
  const requested = /* @__PURE__ */ new Set();
5696
5916
  for (const module2 of listModules({ projectDir })) {
5697
- const moduleRoot = path37.resolve(projectDir, module2.srcPath);
5917
+ const moduleRoot = path38.resolve(projectDir, module2.srcPath);
5698
5918
  for (const sourceSet of ["main", "ohosTest"]) {
5699
5919
  for (const name of readRequestedPermissions(
5700
- path37.join(moduleRoot, "src", sourceSet, "module.json5")
5920
+ path38.join(moduleRoot, "src", sourceSet, "module.json5")
5701
5921
  )) {
5702
5922
  requested.add(name);
5703
5923
  }
@@ -5713,7 +5933,7 @@ function collectAclPermissions(projectDir, logger = noopLogger) {
5713
5933
  // src/harmonyos/signing/autoSign.ts
5714
5934
  function shouldRegenerate(input) {
5715
5935
  if (input.force) return "--force was passed";
5716
- const missing = Object.values(input.paths).find((file) => !fs39.existsSync(file));
5936
+ const missing = Object.values(input.paths).find((file) => !fs40.existsSync(file));
5717
5937
  if (missing) return `${missing} is missing`;
5718
5938
  const config = input.buildProfile.app?.signingConfigs?.find((c) => c.name === input.productName);
5719
5939
  if (config?.type !== "HarmonyOS" || config.material?.profile !== input.paths.profilePath) {
@@ -5752,16 +5972,16 @@ function writeSigningConfig(opts) {
5752
5972
  for (const product of app.products ?? []) {
5753
5973
  if (product.name === opts.productName) product.signingConfig = opts.productName;
5754
5974
  }
5755
- fs39.writeFileSync(opts.buildProfilePath, JSON.stringify(opts.buildProfile, null, 2));
5975
+ fs40.writeFileSync(opts.buildProfilePath, JSON.stringify(opts.buildProfile, null, 2));
5756
5976
  }
5757
5977
  async function harmonyOsAutoSign(opts) {
5758
5978
  const logger = opts.logger ?? noopLogger;
5759
5979
  const productName = opts.productName ?? "default";
5760
- const projectDir = path38.resolve(opts.projectDir);
5761
- const buildProfilePath = path38.join(projectDir, "build-profile.json5");
5980
+ const projectDir = path39.resolve(opts.projectDir);
5981
+ const buildProfilePath = path39.join(projectDir, "build-profile.json5");
5762
5982
  let buildProfile;
5763
5983
  try {
5764
- buildProfile = import_json57.default.parse(fs39.readFileSync(buildProfilePath, "utf-8"));
5984
+ buildProfile = import_json57.default.parse(fs40.readFileSync(buildProfilePath, "utf-8"));
5765
5985
  } catch (err) {
5766
5986
  throw new OniroError(`Could not read ${buildProfilePath}: ${err.message}`, err);
5767
5987
  }
@@ -5801,7 +6021,7 @@ async function harmonyOsAutoSign(opts) {
5801
6021
  logger.info(`[harmonyos] Regenerating signing material: ${reason}.`);
5802
6022
  const agc = createAgcClient(opts.http ?? createHttpClient({ logger }), auth);
5803
6023
  const deviceIds = await registerDevices(agc, connected, logger);
5804
- 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 });
5805
6025
  const certName = `oniro_debug_${auth.teamId.replace(/[\\/.:]/g, "")}.cer`;
5806
6026
  const stale = (await agc.listCertificates()).find((c) => c.certName === certName);
5807
6027
  if (stale) {
@@ -5832,10 +6052,10 @@ async function harmonyOsAutoSign(opts) {
5832
6052
  try {
5833
6053
  verifySigningMaterial({ ...paths, keyAlias: KEY_ALIAS, keyPwd });
5834
6054
  } catch (err) {
5835
- fs39.rmSync(paths.profilePath, { force: true });
6055
+ fs40.rmSync(paths.profilePath, { force: true });
5836
6056
  throw err;
5837
6057
  }
5838
- const materialDir = path38.join(path38.dirname(paths.p12Path), "material");
6058
+ const materialDir = path39.join(path39.dirname(paths.p12Path), "material");
5839
6059
  try {
5840
6060
  getKey(materialDir);
5841
6061
  } catch {
@@ -5847,7 +6067,7 @@ async function harmonyOsAutoSign(opts) {
5847
6067
  }
5848
6068
 
5849
6069
  // src/index.ts
5850
- var VERSION = "0.14.0";
6070
+ var VERSION = "0.15.0";
5851
6071
  // Annotate the CommonJS export names for ESM import in node:
5852
6072
  0 && (module.exports = {
5853
6073
  ALL_SDKS,
@@ -5862,6 +6082,7 @@ var VERSION = "0.14.0";
5862
6082
  HUAWEI_SITES,
5863
6083
  INSTALL_TMP_DIRNAME,
5864
6084
  InsufficientSpaceError,
6085
+ OHOS_DAILY_BUILDS_URL,
5865
6086
  OHOS_URL_BASE,
5866
6087
  OniroError,
5867
6088
  SIGNING_ERRORS,
@@ -5882,6 +6103,7 @@ var VERSION = "0.14.0";
5882
6103
  buildHvigorwArgs,
5883
6104
  buildInputCommand,
5884
6105
  buildRawTouchCommand,
6106
+ bundleNameFromHapConfig,
5885
6107
  captureBurst,
5886
6108
  captureLog,
5887
6109
  cleanCodelinterOutput,
@@ -5935,6 +6157,7 @@ var VERSION = "0.14.0";
5935
6157
  findLayoutNodes,
5936
6158
  findMissingProjectSdk,
5937
6159
  findRunningProcess,
6160
+ findSdkOsDir,
5938
6161
  flattenLayout,
5939
6162
  forceStop,
5940
6163
  formatBytes,
@@ -5968,10 +6191,12 @@ var VERSION = "0.14.0";
5968
6191
  hdcTargetArgs,
5969
6192
  hdcTargetArgsFor,
5970
6193
  hideKeyboard,
6194
+ hilogTimeMs,
5971
6195
  installApp,
5972
6196
  installCmdTools,
5973
6197
  installEmulator,
5974
6198
  installFailureReason,
6199
+ installSdkFromArchive,
5975
6200
  isBundleInstalled,
5976
6201
  isCmdToolsInstalled,
5977
6202
  isEmulatorInstalled,
@@ -5988,6 +6213,7 @@ var VERSION = "0.14.0";
5988
6213
  listRunningProcesses,
5989
6214
  listTemplates,
5990
6215
  locateTarget,
6216
+ missingProjectRootFiles,
5991
6217
  needsCmdWrapper,
5992
6218
  nonInteractivePrompter,
5993
6219
  noopLogger,
@@ -6011,9 +6237,11 @@ var VERSION = "0.14.0";
6011
6237
  pickTarget,
6012
6238
  prepareSigning,
6013
6239
  pruneLayout,
6240
+ readHapBundleName,
6014
6241
  readJson5File,
6015
6242
  readJsonFile,
6016
6243
  readProvisionProfile,
6244
+ readZipEntry,
6017
6245
  reboot,
6018
6246
  recvFile,
6019
6247
  removeCmdTools,