@oniroproject/core 0.13.0 → 0.14.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
@@ -50,7 +50,9 @@ __export(index_exports, {
50
50
  TargetNotFoundError: () => TargetNotFoundError,
51
51
  UnsupportedPlatformError: () => UnsupportedPlatformError,
52
52
  VERSION: () => VERSION,
53
+ appSandboxDir: () => appSandboxDir,
53
54
  applyChanges: () => applyChanges,
55
+ assertDevicePath: () => assertDevicePath,
54
56
  assertLayoutSelector: () => assertLayoutSelector,
55
57
  attemptHdcConnection: () => attemptHdcConnection,
56
58
  buildCmdWrapper: () => buildCmdWrapper,
@@ -61,6 +63,8 @@ __export(index_exports, {
61
63
  buildInputCommand: () => buildInputCommand,
62
64
  buildRawTouchCommand: () => buildRawTouchCommand,
63
65
  captureBurst: () => captureBurst,
66
+ captureLog: () => captureLog,
67
+ cleanCodelinterOutput: () => cleanCodelinterOutput,
64
68
  consoleLogger: () => consoleLogger,
65
69
  createHarmonyOsSession: () => createHarmonyOsSession,
66
70
  createInstallTempDir: () => createInstallTempDir,
@@ -76,9 +80,12 @@ __export(index_exports, {
76
80
  defaultPaths: () => defaultPaths,
77
81
  describeLayoutNode: () => describeLayoutNode,
78
82
  detectAaStartFailure: () => detectAaStartFailure,
83
+ detectCmdToolsFlavour: () => detectCmdToolsFlavour,
84
+ detectInstallFailure: () => detectInstallFailure,
79
85
  detectProjectSdkVersion: () => detectProjectSdkVersion,
80
86
  detectRuntimeOs: () => detectRuntimeOs,
81
87
  detectSigningConfigNames: () => detectSigningConfigNames,
88
+ diagnoseBuildFailure: () => diagnoseBuildFailure,
82
89
  diffEntryNames: () => diffEntryNames,
83
90
  diffHapAssets: () => diffHapAssets,
84
91
  discoverHaps: () => discoverHaps,
@@ -91,11 +98,13 @@ __export(index_exports, {
91
98
  dumpWindow: () => dumpWindow,
92
99
  encryptPwd: () => encryptPwd,
93
100
  ensureFreeSpace: () => ensureFreeSpace,
101
+ ensureInstallOk: () => ensureInstallOk,
94
102
  ensureOhModules: () => ensureOhModules,
95
103
  ensureOk: () => ensureOk,
96
104
  ensureRemoteOk: () => ensureRemoteOk,
97
105
  escapeCmdArgument: () => escapeCmdArgument,
98
106
  escapeCmdCommand: () => escapeCmdCommand,
107
+ existingHapOrUnsigned: () => existingHapOrUnsigned,
99
108
  extractTarball: () => extractTarball,
100
109
  extractZipWithProgress: () => extractZipWithProgress,
101
110
  findAppProcessId: () => findAppProcessId,
@@ -104,6 +113,7 @@ __export(index_exports, {
104
113
  findHarmonyOsSdk: () => findHarmonyOsSdk,
105
114
  findKeyboardWindow: () => findKeyboardWindow,
106
115
  findLayoutNodes: () => findLayoutNodes,
116
+ findMissingProjectSdk: () => findMissingProjectSdk,
107
117
  findRunningProcess: () => findRunningProcess,
108
118
  flattenLayout: () => flattenLayout,
109
119
  forceStop: () => forceStop,
@@ -117,6 +127,7 @@ __export(index_exports, {
117
127
  getDeviceInfo: () => getDeviceInfo,
118
128
  getDisplaySize: () => getDisplaySize,
119
129
  getEmulatorDir: () => getEmulatorDir,
130
+ getEmulatorStatus: () => getEmulatorStatus,
120
131
  getFreeSpaceBytes: () => getFreeSpaceBytes,
121
132
  getHdcPath: () => getHdcPath,
122
133
  getHvigorwPath: () => getHvigorwPath,
@@ -132,15 +143,21 @@ __export(index_exports, {
132
143
  getSupportedSdksForUi: () => getSupportedSdksForUi,
133
144
  harmonyOsAutoSign: () => harmonyOsAutoSign,
134
145
  harmonyOsSdkLabelForApi: () => harmonyOsSdkLabelForApi,
146
+ hdcClientFailure: () => hdcClientFailure,
135
147
  hdcExec: () => hdcExec,
148
+ hdcTargetArgs: () => hdcTargetArgs,
149
+ hdcTargetArgsFor: () => hdcTargetArgsFor,
136
150
  hideKeyboard: () => hideKeyboard,
137
151
  installApp: () => installApp,
138
152
  installCmdTools: () => installCmdTools,
139
153
  installEmulator: () => installEmulator,
154
+ installFailureReason: () => installFailureReason,
155
+ isBundleInstalled: () => isBundleInstalled,
140
156
  isCmdToolsInstalled: () => isCmdToolsInstalled,
141
157
  isEmulatorInstalled: () => isEmulatorInstalled,
142
158
  isOutOfSpaceError: () => isOutOfSpaceError,
143
159
  isRamBackedPath: () => isRamBackedPath,
160
+ isUnsignedHap: () => isUnsignedHap,
144
161
  isValidBundleName: () => isValidBundleName,
145
162
  isValidProjectName: () => isValidProjectName,
146
163
  killProcessTree: () => killProcessTree,
@@ -155,16 +172,21 @@ __export(index_exports, {
155
172
  nonInteractivePrompter: () => nonInteractivePrompter,
156
173
  noopLogger: () => noopLogger,
157
174
  noopProgress: () => noopProgress,
175
+ normaliseLocalPath: () => normaliseLocalPath,
158
176
  paramGet: () => paramGet,
177
+ paramGetMany: () => paramGetMany,
159
178
  paramSet: () => paramSet,
160
179
  parseBounds: () => parseBounds,
161
180
  parseCodelinterFindings: () => parseCodelinterFindings,
181
+ parseCodelinterReport: () => parseCodelinterReport,
162
182
  parseDeviceList: () => parseDeviceList,
163
183
  parseHilogLine: () => parseHilogLine,
164
184
  parseImeBundles: () => parseImeBundles,
165
185
  parseMainAbilityFromBmDump: () => parseMainAbilityFromBmDump,
186
+ parseParamValues: () => parseParamValues,
166
187
  parseRemoteExit: () => parseRemoteExit,
167
188
  performInput: () => performInput,
189
+ pickHap: () => pickHap,
168
190
  pickSigningKind: () => pickSigningKind,
169
191
  pickTarget: () => pickTarget,
170
192
  prepareSigning: () => prepareSigning,
@@ -179,6 +201,7 @@ __export(index_exports, {
179
201
  removeSdk: () => removeSdk,
180
202
  removeTempWorkDir: () => removeTempWorkDir,
181
203
  requireHarmonyOsSdk: () => requireHarmonyOsSdk,
204
+ resolveDeviceSerial: () => resolveDeviceSerial,
182
205
  resolveKeyEvent: () => resolveKeyEvent,
183
206
  resolveTmpRoot: () => resolveTmpRoot,
184
207
  runCodelinter: () => runCodelinter,
@@ -203,6 +226,7 @@ __export(index_exports, {
203
226
  toLongPath: () => toLongPath,
204
227
  toSpaceError: () => toSpaceError,
205
228
  uninstallApp: () => uninstallApp,
229
+ unsignedHapRejection: () => unsignedHapRejection,
206
230
  validateTemplateLayout: () => validateTemplateLayout,
207
231
  verifySha256: () => verifySha256,
208
232
  waitForBoot: () => waitForBoot,
@@ -309,14 +333,19 @@ var OniroError = class extends Error {
309
333
  cause;
310
334
  };
311
335
  var SdkNotInstalledError = class extends OniroError {
312
- constructor(api, expectedPath) {
313
- super(`SDK API ${api} is not installed at ${expectedPath}.`);
336
+ /** `version` is the release that ships `api` (e.g. `6.0` for API 20), when one is known. */
337
+ constructor(api, expectedPath, version) {
338
+ super(
339
+ `SDK API ${api} is not installed at ${expectedPath}.` + (version ? ` Install it with \`oniro-app sdk install ${version}\`.` : "")
340
+ );
314
341
  this.api = api;
315
342
  this.expectedPath = expectedPath;
343
+ this.version = version;
316
344
  this.name = "SdkNotInstalledError";
317
345
  }
318
346
  api;
319
347
  expectedPath;
348
+ version;
320
349
  };
321
350
  var CmdToolsNotInstalledError = class extends OniroError {
322
351
  constructor(expectedPath) {
@@ -592,6 +621,24 @@ function isProjectLocalHvigorwWorking(projectDir) {
592
621
  var fs3 = __toESM(require("fs"), 1);
593
622
  var path4 = __toESM(require("path"), 1);
594
623
  var import_json5 = __toESM(require("json5"), 1);
624
+ function localPropertiesSdkDir(projectRoot) {
625
+ try {
626
+ const content = fs3.readFileSync(path4.join(projectRoot, "local.properties"), "utf-8");
627
+ const m = /^\s*sdk\.dir\s*[=:]\s*(.+?)\s*$/m.exec(content);
628
+ return m ? m[1].replace(/\\(.)/g, "$1") : void 0;
629
+ } catch {
630
+ return void 0;
631
+ }
632
+ }
633
+ function findMissingProjectSdk(config, projectRoot) {
634
+ const api = detectProjectSdkVersion(projectRoot);
635
+ if (api === void 0) return null;
636
+ const expected = path4.join(getOhosBaseSdkHome(config), String(api));
637
+ if (fs3.existsSync(expected)) return null;
638
+ const localSdkDir = localPropertiesSdkDir(projectRoot);
639
+ if (localSdkDir && fs3.existsSync(path4.join(localSdkDir, String(api)))) return null;
640
+ return new SdkNotInstalledError(String(api), expected, ALL_SDKS.find((s) => s.api === String(api))?.version);
641
+ }
595
642
  function detectProjectSdkVersion(projectRoot, logger = noopLogger) {
596
643
  const buildProfilePath = path4.join(projectRoot, "build-profile.json5");
597
644
  if (!fs3.existsSync(buildProfilePath)) {
@@ -777,14 +824,14 @@ var pipelineAsync = (0, import_node_util.promisify)(import_node_stream.pipeline)
777
824
  async function downloadFile(opts) {
778
825
  const { url, dest, progress, abortSignal } = opts;
779
826
  const proto = url.startsWith("https") ? https : http;
780
- return new Promise((resolve6, reject) => {
827
+ return new Promise((resolve7, reject) => {
781
828
  const s = Math.max(0, Math.min(100, opts.start ?? 0));
782
829
  const r = Math.max(0, Math.min(100 - s, opts.range ?? 100));
783
830
  let settled = false;
784
831
  const done = (err) => {
785
832
  if (settled) return;
786
833
  settled = true;
787
- err ? reject(err) : resolve6();
834
+ err ? reject(err) : resolve7();
788
835
  };
789
836
  if (abortSignal?.aborted) {
790
837
  done(new CancelledError("Download cancelled before start."));
@@ -1271,21 +1318,27 @@ async function installCmdTools(opts) {
1271
1318
  removeTempWorkDir(tmpDir, tmpRoot);
1272
1319
  }
1273
1320
  }
1321
+ function detectCmdToolsFlavour(cmdToolsPath) {
1322
+ return fs11.existsSync(path12.join(cmdToolsPath, "sdk", "default", "hms")) ? "HarmonyOS" : "OpenHarmony";
1323
+ }
1274
1324
  function getCmdToolsStatus(config) {
1275
1325
  const cmdPath = getCmdToolsPath(config);
1326
+ const hdcPath = getHdcPath(config);
1327
+ const base = { path: cmdPath, hdc: { path: hdcPath, exists: fs11.existsSync(hdcPath) } };
1276
1328
  const binDir = path12.join(cmdPath, "bin");
1277
1329
  const candidates = os7.platform() === "win32" ? ["ohpm.exe", "ohpm.cmd", "ohpm.bat", "ohpm"] : ["ohpm"];
1278
1330
  const bin = candidates.map((c) => path12.join(binDir, c)).find((p) => fs11.existsSync(p));
1279
1331
  if (!bin) {
1280
- return { installed: false, status: "Not installed" };
1332
+ return { installed: false, status: "Not installed", ...base };
1281
1333
  }
1334
+ const installed = { installed: true, ...base, flavour: detectCmdToolsFlavour(cmdPath) };
1282
1335
  try {
1283
1336
  const versionFile = path12.join(cmdPath, "version.txt");
1284
1337
  if (fs11.existsSync(versionFile)) {
1285
1338
  const content = fs11.readFileSync(versionFile, "utf8");
1286
1339
  for (const line of content.split(/\r?\n/)) {
1287
1340
  const m = line.match(/^\s*#\s*Version:\s*(.+)$/);
1288
- if (m?.[1]) return { installed: true, status: `Installed (${m[1].trim()})` };
1341
+ if (m?.[1]) return { ...installed, status: `Installed (${m[1].trim()})` };
1289
1342
  }
1290
1343
  }
1291
1344
  } catch {
@@ -1299,9 +1352,9 @@ function getCmdToolsStatus(config) {
1299
1352
  });
1300
1353
  if (res.error || res.status !== 0) throw res.error ?? new Error(`exit ${String(res.status)}`);
1301
1354
  const version = res.stdout.trim();
1302
- return { installed: true, status: `Installed (${version})` };
1355
+ return { ...installed, status: `Installed (${version})` };
1303
1356
  } catch {
1304
- return { installed: true, status: "Installed (version unknown)" };
1357
+ return { ...installed, status: "Installed (version unknown)" };
1305
1358
  }
1306
1359
  }
1307
1360
  function isCmdToolsInstalled(config) {
@@ -1591,6 +1644,9 @@ function modifyProfileTemplate(projectDir, apl, appFeature, logger, options = {}
1591
1644
  profileTemplate.acls = profileTemplate.acls ?? {};
1592
1645
  profileTemplate.acls["allowed-acls"] = options.acls;
1593
1646
  }
1647
+ if (options.appPrivilegeCapabilities !== void 0) {
1648
+ profileTemplate["app-privilege-capabilities"] = options.appPrivilegeCapabilities;
1649
+ }
1594
1650
  fs13.writeFileSync(profileTemplatePath, JSON.stringify(profileTemplate, null, 2));
1595
1651
  }
1596
1652
  function generateP7bFile(projectDir, paths, logger, passwords) {
@@ -1680,6 +1736,12 @@ function updateBuildProfile(projectDir, kind, logger, passwords) {
1680
1736
  storeFile: "./signatures/OpenHarmony.p12"
1681
1737
  }
1682
1738
  }));
1739
+ for (const product of buildProfile.app.products ?? []) {
1740
+ if (typeof product.signingConfig !== "string" || product.signingConfig === "") {
1741
+ product.signingConfig = configNames[0];
1742
+ logger.info(`[sign] Pointing a product with no signingConfig at "${configNames[0]}".`);
1743
+ }
1744
+ }
1683
1745
  fs13.writeFileSync(buildProfilePath, JSON.stringify(buildProfile, null, 2));
1684
1746
  }
1685
1747
  function prepareMaterialDirectory(projectDir, logger) {
@@ -1711,7 +1773,7 @@ function generateSigningConfigs(options) {
1711
1773
  }
1712
1774
  const sdkPath = path14.join(sdkHome, String(sdkVersion));
1713
1775
  if (!fs13.existsSync(sdkPath)) {
1714
- throw new OniroError(`SDK path does not exist: ${sdkPath}`);
1776
+ throw new SdkNotInstalledError(String(sdkVersion), sdkPath, ALL_SDKS.find((s) => s.api === String(sdkVersion))?.version);
1715
1777
  }
1716
1778
  const paths = {
1717
1779
  signTool: path14.join(sdkPath, "toolchains/lib/hap-sign-tool.jar"),
@@ -1723,7 +1785,11 @@ function generateSigningConfigs(options) {
1723
1785
  const passwords = options.passwords ?? DEFAULT_PASSWORDS;
1724
1786
  logger.info("[sign] Starting signing configuration generation...");
1725
1787
  copyFilesToProject(projectDir, paths, kind, logger, options.applicationCertPath);
1726
- modifyProfileTemplate(projectDir, apl, appFeature, logger, { kind, acls: options.acls });
1788
+ modifyProfileTemplate(projectDir, apl, appFeature, logger, {
1789
+ kind,
1790
+ acls: options.acls,
1791
+ appPrivilegeCapabilities: options.appPrivilegeCapabilities
1792
+ });
1727
1793
  generateP7bFile(projectDir, paths, logger, passwords);
1728
1794
  prepareMaterialDirectory(projectDir, logger);
1729
1795
  updateBuildProfile(projectDir, kind, logger, passwords);
@@ -1790,7 +1856,7 @@ function runProcess(opts) {
1790
1856
  const { command, args, cwd, env, onOutput } = opts;
1791
1857
  const timeoutMs = opts.timeoutMs ?? 6e4;
1792
1858
  const { abortSignal } = opts;
1793
- return new Promise((resolve6, reject) => {
1859
+ return new Promise((resolve7, reject) => {
1794
1860
  if (abortSignal?.aborted) {
1795
1861
  reject(new CancelledError(`Command aborted before start: ${command}`));
1796
1862
  return;
@@ -1855,21 +1921,41 @@ ${stderr.trim()}` : "")
1855
1921
  );
1856
1922
  return;
1857
1923
  }
1858
- resolve6({ code: code ?? -1, stdout, stderr });
1924
+ resolve7({ code: code ?? -1, stdout, stderr });
1859
1925
  });
1860
1926
  });
1861
1927
  }
1862
- function hdcExec(opts) {
1928
+ function resolveDeviceSerial(explicit, env = process.env) {
1929
+ if (explicit && explicit !== "auto") return explicit;
1930
+ const fromEnv = env.ONIRO_DEVICE_SERIAL || env.DEVICE_SERIAL;
1931
+ return fromEnv && fromEnv !== "auto" ? fromEnv : void 0;
1932
+ }
1933
+ function hdcTargetArgs(explicit) {
1934
+ const serial = resolveDeviceSerial(explicit);
1935
+ return serial ? ["-t", serial] : [];
1936
+ }
1937
+ var HOST_COMMANDS = /* @__PURE__ */ new Set(["list", "tconn", "start", "kill", "version", "checkserver"]);
1938
+ function hdcTargetArgsFor(args, explicit) {
1939
+ if (HOST_COMMANDS.has(args[0] ?? "")) return explicit ? ["-t", explicit] : [];
1940
+ return hdcTargetArgs(explicit);
1941
+ }
1942
+ function hdcClientFailure(output) {
1943
+ const first = output.split(/\r?\n/).find((l) => l.trim() !== "")?.trim() ?? "";
1944
+ return first.startsWith("[Fail]") ? first : null;
1945
+ }
1946
+ async function hdcExec(opts) {
1863
1947
  const hdc = getHdcPath(opts.config);
1864
- const argv = opts.deviceSerial ? ["-t", opts.deviceSerial, ...opts.args] : [...opts.args];
1865
- return runProcess({
1948
+ const res = await runProcess({
1866
1949
  command: hdc,
1867
- args: argv,
1950
+ args: [...hdcTargetArgsFor(opts.args, opts.deviceSerial), ...opts.args],
1868
1951
  timeoutMs: opts.timeoutMs,
1869
1952
  abortSignal: opts.abortSignal,
1870
1953
  onOutput: opts.onOutput,
1871
1954
  logger: opts.logger
1872
1955
  });
1956
+ if (res.code === 0 && hdcClientFailure(`${res.stdout}
1957
+ ${res.stderr}`)) return { ...res, code: 1 };
1958
+ return res;
1873
1959
  }
1874
1960
  function shell(opts) {
1875
1961
  const { command, ...rest } = opts;
@@ -1877,10 +1963,15 @@ function shell(opts) {
1877
1963
  }
1878
1964
  function ensureOk(result, command) {
1879
1965
  if (result.code !== 0) {
1880
- throw new CommandFailedError(command, result.code, result.stderr.trim());
1966
+ throw new CommandFailedError(command, result.code, withTargetHint(result.stderr.trim() || result.stdout.trim()));
1881
1967
  }
1882
1968
  return result;
1883
1969
  }
1970
+ function withTargetHint(detail) {
1971
+ if (!/need connect-key|Not match target/i.test(detail)) return detail;
1972
+ return `${detail}
1973
+ More than one target is connected, or the serial is unknown: pass --device <serial> or set ONIRO_DEVICE_SERIAL to one listed by \`oniro-app devices\`.`;
1974
+ }
1884
1975
  var REMOTE_RC_RE = /__ONIRO_RC=(-?\d+)__/g;
1885
1976
  function parseRemoteExit(stdout) {
1886
1977
  let code = null;
@@ -1897,7 +1988,7 @@ async function shellChecked(opts) {
1897
1988
  }
1898
1989
  function ensureRemoteOk(result, command) {
1899
1990
  if (result.code !== 0) {
1900
- throw new CommandFailedError(command, result.code, result.stderr.trim() || result.stdout.trim());
1991
+ throw new CommandFailedError(command, result.code, withTargetHint(result.stderr.trim() || result.stdout.trim()));
1901
1992
  }
1902
1993
  return result;
1903
1994
  }
@@ -1983,11 +2074,11 @@ async function startEmulator(opts) {
1983
2074
  fs16.writeFileSync(PID_FILE, String(child.pid));
1984
2075
  child.unref();
1985
2076
  logger.info(`Emulator launched (PID ${child.pid}). Logs: ${logPath}`);
1986
- const earlyExit = new Promise((resolve6) => {
1987
- child.once("exit", (code) => resolve6(code));
1988
- child.once("error", () => resolve6(-1));
2077
+ const earlyExit = new Promise((resolve7) => {
2078
+ child.once("exit", (code) => resolve7(code));
2079
+ child.once("error", () => resolve7(-1));
1989
2080
  });
1990
- const liveness = new Promise((resolve6) => setTimeout(() => resolve6("alive"), 2e3));
2081
+ const liveness = new Promise((resolve7) => setTimeout(() => resolve7("alive"), 2e3));
1991
2082
  const outcome = await Promise.race([earlyExit, liveness]);
1992
2083
  if (outcome !== "alive") {
1993
2084
  const tail = readTail(logPath, 4e3);
@@ -2067,6 +2158,43 @@ async function stopEmulator(logger = noopLogger) {
2067
2158
  if (err) logger.warn(`Could not remove PID file: ${err.message}`);
2068
2159
  });
2069
2160
  }
2161
+ async function isQemuRunning(logger) {
2162
+ if (os8.platform() !== "win32") {
2163
+ try {
2164
+ const pid = Number(fs16.readFileSync(PID_FILE, "utf8").trim());
2165
+ if (Number.isFinite(pid) && pid > 0) {
2166
+ process.kill(pid, 0);
2167
+ return true;
2168
+ }
2169
+ } catch {
2170
+ }
2171
+ }
2172
+ const [cmd, ...args] = os8.platform() === "win32" ? ["tasklist", "/FI", "IMAGENAME eq qemu-system-x86_64.exe", "/NH"] : ["pgrep", "-f", "qemu-system-x86_64"];
2173
+ try {
2174
+ const res = await runProcess({ command: cmd, args, timeoutMs: 1e4, logger });
2175
+ return os8.platform() === "win32" ? /qemu-system-x86_64/i.test(res.stdout) : res.code === 0;
2176
+ } catch {
2177
+ return false;
2178
+ }
2179
+ }
2180
+ async function getEmulatorStatus(config, opts = {}) {
2181
+ const logger = opts.logger ?? noopLogger;
2182
+ const address = opts.address ?? "127.0.0.1:55555";
2183
+ const dir = getEmulatorDir(config);
2184
+ let connected = false;
2185
+ try {
2186
+ const targets = await hdcExec({ config, args: ["list", "targets", "-v"], timeoutMs: 1e4, logger });
2187
+ connected = targets.stdout.split("\n").some((l) => l.trim().startsWith(address) && /\bConnected\b/.test(l));
2188
+ } catch {
2189
+ }
2190
+ return {
2191
+ installed: isEmulatorInstalled(config),
2192
+ dir,
2193
+ running: await isQemuRunning(logger),
2194
+ address,
2195
+ connected
2196
+ };
2197
+ }
2070
2198
 
2071
2199
  // src/hdc/devices.ts
2072
2200
  var STATUS_TOKENS = /* @__PURE__ */ new Set([
@@ -2105,9 +2233,8 @@ async function listDevices(config, opts = {}) {
2105
2233
  return parseDeviceList(res.stdout);
2106
2234
  }
2107
2235
  async function selectDevice(config, serial, opts = {}) {
2108
- if (serial && serial !== "auto") return serial;
2109
- const envSerial = process.env.ONIRO_DEVICE_SERIAL || process.env.DEVICE_SERIAL;
2110
- if (envSerial && envSerial !== "auto") return envSerial;
2236
+ const resolved = resolveDeviceSerial(serial);
2237
+ if (resolved) return resolved;
2111
2238
  const usable = (await listDevices(config, opts)).filter(
2112
2239
  (d) => d.status === "Connected" || d.status === "Unknown"
2113
2240
  );
@@ -2121,6 +2248,7 @@ async function selectDevice(config, serial, opts = {}) {
2121
2248
  }
2122
2249
 
2123
2250
  // src/hdc/param.ts
2251
+ var PARAM_MISSING_RE = /^Get parameter ".*" fail!/;
2124
2252
  async function paramGet(config, key, opts = {}) {
2125
2253
  const res = await shell({
2126
2254
  config,
@@ -2130,7 +2258,30 @@ async function paramGet(config, key, opts = {}) {
2130
2258
  logger: opts.logger
2131
2259
  });
2132
2260
  ensureOk(res, `hdc shell param get ${key}`);
2133
- return res.stdout.trim();
2261
+ const value = res.stdout.trim();
2262
+ if (PARAM_MISSING_RE.test(value)) throw new OniroError(`System parameter ${key} is not set: ${value}`);
2263
+ return value;
2264
+ }
2265
+ var PARAM_SEPARATOR = "__ONIRO_PARAM__";
2266
+ function parseParamValues(keys, stdout) {
2267
+ const parts = stdout.split(PARAM_SEPARATOR);
2268
+ const values = {};
2269
+ keys.forEach((key, i) => {
2270
+ const value = parts[i]?.trim();
2271
+ values[key] = value && !PARAM_MISSING_RE.test(value) ? value : void 0;
2272
+ });
2273
+ return values;
2274
+ }
2275
+ async function paramGetMany(config, keys, opts = {}) {
2276
+ const res = await shell({
2277
+ config,
2278
+ command: keys.map((k) => `param get ${k}; echo ${PARAM_SEPARATOR}`).join("; "),
2279
+ deviceSerial: opts.deviceSerial,
2280
+ timeoutMs: opts.timeoutMs ?? 1e4,
2281
+ logger: opts.logger
2282
+ });
2283
+ ensureOk(res, "hdc shell param get \u2026");
2284
+ return parseParamValues(keys, res.stdout);
2134
2285
  }
2135
2286
  async function paramSet(config, key, value, opts = {}) {
2136
2287
  const res = await shell({
@@ -2144,29 +2295,66 @@ async function paramSet(config, key, value, opts = {}) {
2144
2295
  }
2145
2296
 
2146
2297
  // src/hdc/files.ts
2298
+ var path17 = __toESM(require("path"), 1);
2299
+ function normaliseLocalPath(local) {
2300
+ const abs = path17.resolve(local);
2301
+ const long = toLongPath(abs);
2302
+ return long !== abs ? long : path17.join(toLongPath(path17.dirname(abs)), path17.basename(abs));
2303
+ }
2304
+ function assertDevicePath(remote) {
2305
+ if (/^[A-Za-z]:[\\/]/.test(remote)) {
2306
+ throw new OniroError(
2307
+ `The device path ${remote} is a Windows path. Git Bash rewrites arguments that start with /: run the command with MSYS_NO_PATHCONV=1, or from PowerShell or cmd.`
2308
+ );
2309
+ }
2310
+ }
2311
+ function appSandboxDir(bundle) {
2312
+ return `/data/app/el2/100/base/${bundle}`;
2313
+ }
2314
+ function shQuote(s) {
2315
+ return `'${s.replace(/'/g, `'\\''`)}'`;
2316
+ }
2147
2317
  async function sendFile(opts) {
2318
+ assertDevicePath(opts.remote);
2319
+ const local = normaliseLocalPath(opts.local);
2320
+ const sandbox = opts.bundle ? appSandboxDir(opts.bundle) : void 0;
2321
+ const remote = sandbox && !opts.remote.startsWith("/") ? path17.posix.join(sandbox, opts.remote) : opts.remote;
2322
+ if (sandbox && !remote.startsWith(`${sandbox}/`)) {
2323
+ throw new OniroError(`${remote} is outside the sandbox of ${opts.bundle} (${sandbox}).`);
2324
+ }
2325
+ const device = { config: opts.config, deviceSerial: opts.deviceSerial, logger: opts.logger };
2326
+ if (sandbox) {
2327
+ const mkdir = `mkdir -p ${shQuote(path17.posix.dirname(remote))}`;
2328
+ ensureRemoteOk(await shellChecked({ ...device, command: mkdir }), `hdc shell ${mkdir}`);
2329
+ }
2148
2330
  const res = await hdcExec({
2149
- config: opts.config,
2150
- args: ["file", "send", opts.local, opts.remote],
2151
- deviceSerial: opts.deviceSerial,
2331
+ ...device,
2332
+ args: ["file", "send", local, remote],
2152
2333
  timeoutMs: opts.timeoutMs ?? 6e5,
2153
2334
  abortSignal: opts.abortSignal,
2154
- onOutput: opts.onOutput,
2155
- logger: opts.logger
2335
+ onOutput: opts.onOutput
2156
2336
  });
2157
- ensureOk(res, `hdc file send ${opts.local} ${opts.remote}`);
2337
+ ensureOk(res, `hdc file send ${local} ${remote}`);
2338
+ if (sandbox) {
2339
+ const top = path17.posix.join(sandbox, path17.posix.relative(sandbox, remote).split("/")[0]);
2340
+ const chown = `chown -R "$(stat -c %u:%g ${shQuote(sandbox)})" ${shQuote(top)}`;
2341
+ ensureRemoteOk(await shellChecked({ ...device, command: chown }), `hdc shell ${chown}`);
2342
+ }
2343
+ return remote;
2158
2344
  }
2159
2345
  async function recvFile(opts) {
2346
+ assertDevicePath(opts.remote);
2347
+ const local = normaliseLocalPath(opts.local);
2160
2348
  const res = await hdcExec({
2161
2349
  config: opts.config,
2162
- args: ["file", "recv", opts.remote, opts.local],
2350
+ args: ["file", "recv", opts.remote, local],
2163
2351
  deviceSerial: opts.deviceSerial,
2164
2352
  timeoutMs: opts.timeoutMs ?? 6e5,
2165
2353
  abortSignal: opts.abortSignal,
2166
2354
  onOutput: opts.onOutput,
2167
2355
  logger: opts.logger
2168
2356
  });
2169
- ensureOk(res, `hdc file recv ${opts.remote} ${opts.local}`);
2357
+ ensureOk(res, `hdc file recv ${opts.remote} ${local}`);
2170
2358
  }
2171
2359
 
2172
2360
  // src/hdc/display.ts
@@ -2186,7 +2374,7 @@ async function getDisplaySize(config, opts = {}) {
2186
2374
 
2187
2375
  // src/hdc/wait.ts
2188
2376
  function delay(ms, signal) {
2189
- return new Promise((resolve6, reject) => {
2377
+ return new Promise((resolve7, reject) => {
2190
2378
  if (signal?.aborted) {
2191
2379
  reject(new CancelledError("Wait cancelled."));
2192
2380
  return;
@@ -2201,7 +2389,7 @@ function delay(ms, signal) {
2201
2389
  };
2202
2390
  const timer = setTimeout(() => {
2203
2391
  cleanup();
2204
- resolve6();
2392
+ resolve7();
2205
2393
  }, ms);
2206
2394
  signal?.addEventListener("abort", onAbort, { once: true });
2207
2395
  });
@@ -2230,20 +2418,73 @@ async function waitForCondition(opts) {
2230
2418
  }
2231
2419
 
2232
2420
  // src/hdc/app.ts
2233
- var fs18 = __toESM(require("fs"), 1);
2421
+ var fs19 = __toESM(require("fs"), 1);
2422
+ var path20 = __toESM(require("path"), 1);
2423
+
2424
+ // src/build/discoverHaps.ts
2425
+ var fs17 = __toESM(require("fs"), 1);
2234
2426
  var path18 = __toESM(require("path"), 1);
2427
+ function moduleOfHap(hapFileName) {
2428
+ const dash = hapFileName.indexOf("-");
2429
+ return dash > 0 ? hapFileName.slice(0, dash) : hapFileName.replace(/\.hap$/i, "");
2430
+ }
2431
+ async function discoverHaps(opts) {
2432
+ const { projectDir } = opts;
2433
+ const maxDepth = opts.maxDepth ?? 8;
2434
+ const result = {};
2435
+ const walk = async (dir, depth) => {
2436
+ if (depth > maxDepth) return;
2437
+ const entries = await fs17.promises.readdir(dir, { withFileTypes: true }).catch(() => []);
2438
+ for (const e of entries) {
2439
+ const full = path18.join(dir, e.name);
2440
+ if (e.isDirectory()) {
2441
+ if (e.name === "node_modules" || e.name === "oh_modules" || e.name === ".git") continue;
2442
+ await walk(full, depth + 1);
2443
+ } else if (e.isFile() && e.name.endsWith(".hap")) {
2444
+ const moduleName = moduleOfHap(e.name);
2445
+ const bucket = result[moduleName] ??= { signed: [], unsigned: [] };
2446
+ if (e.name.includes("unsigned")) bucket.unsigned.push(full);
2447
+ else if (e.name.includes("signed")) bucket.signed.push(full);
2448
+ }
2449
+ }
2450
+ };
2451
+ await walk(projectDir, 0);
2452
+ const mtime = (p) => fs17.statSync(p).mtimeMs;
2453
+ for (const bucket of Object.values(result)) {
2454
+ bucket.signed.sort((a, b) => mtime(b) - mtime(a));
2455
+ bucket.unsigned.sort((a, b) => mtime(b) - mtime(a));
2456
+ }
2457
+ return result;
2458
+ }
2459
+ function pickHap(haps, module2) {
2460
+ const pick = (name) => {
2461
+ const signed = haps[name]?.signed[0];
2462
+ if (signed) return { module: name, path: signed, signed: true };
2463
+ const unsigned = haps[name]?.unsigned[0];
2464
+ return unsigned ? { module: name, path: unsigned, signed: false } : null;
2465
+ };
2466
+ if (module2) return pick(module2);
2467
+ const names = Object.keys(haps);
2468
+ const withSigned = names.filter((n) => haps[n].signed.length > 0);
2469
+ const candidates = withSigned.length > 0 ? withSigned : names.filter((n) => haps[n].unsigned.length > 0);
2470
+ if (candidates.length === 0) return null;
2471
+ if (candidates.length > 1) {
2472
+ throw new OniroError(`Multiple modules have built HAPs (${candidates.join(", ")}). Pass a module to choose.`);
2473
+ }
2474
+ return pick(candidates[0]);
2475
+ }
2235
2476
 
2236
2477
  // src/hdc/project.ts
2237
- var fs17 = __toESM(require("fs"), 1);
2238
- var path17 = __toESM(require("path"), 1);
2478
+ var fs18 = __toESM(require("fs"), 1);
2479
+ var path19 = __toESM(require("path"), 1);
2239
2480
  var import_json53 = __toESM(require("json5"), 1);
2240
2481
  function readJson5(filePath) {
2241
- const content = fs17.readFileSync(filePath, "utf-8");
2482
+ const content = fs18.readFileSync(filePath, "utf-8");
2242
2483
  return import_json53.default.parse(content);
2243
2484
  }
2244
2485
  function getBundleName(projectDir) {
2245
- const appJsonPath = path17.join(projectDir, "AppScope", "app.json5");
2246
- if (!fs17.existsSync(appJsonPath)) {
2486
+ const appJsonPath = path19.join(projectDir, "AppScope", "app.json5");
2487
+ if (!fs18.existsSync(appJsonPath)) {
2247
2488
  throw new OniroError(`Could not find app.json5 at ${appJsonPath}`);
2248
2489
  }
2249
2490
  const appJson = readJson5(appJsonPath);
@@ -2253,8 +2494,8 @@ function getBundleName(projectDir) {
2253
2494
  return appJson.app.bundleName;
2254
2495
  }
2255
2496
  function getMainAbility(projectDir, moduleName = "entry", abilityName) {
2256
- const moduleJsonPath = path17.join(projectDir, moduleName, "src", "main", "module.json5");
2257
- if (!fs17.existsSync(moduleJsonPath)) {
2497
+ const moduleJsonPath = path19.join(projectDir, moduleName, "src", "main", "module.json5");
2498
+ if (!fs18.existsSync(moduleJsonPath)) {
2258
2499
  throw new OniroError(`Could not find module.json5 at ${moduleJsonPath}`);
2259
2500
  }
2260
2501
  const moduleJson = readJson5(moduleJsonPath);
@@ -2274,13 +2515,67 @@ function getMainAbility(projectDir, moduleName = "entry", abilityName) {
2274
2515
  }
2275
2516
 
2276
2517
  // src/hdc/app.ts
2518
+ function detectInstallFailure(output) {
2519
+ return /\[Fail\]|msg:\s*error|error:\s*failed to install|code:\s*95\d{5}/i.test(output) ? output.trim() : null;
2520
+ }
2521
+ function isUnsignedHap(hapPath) {
2522
+ return path20.basename(hapPath).includes("unsigned");
2523
+ }
2524
+ function installFailureReason(output) {
2525
+ const code = /code:\s*(\d+)/i.exec(output);
2526
+ if (!code) return output.trim().split(/\r?\n/)[0] ?? "";
2527
+ const message = /error:\s*([^\r\n]+)/i.exec(output.slice(code.index + code[0].length))?.[1]?.trim().replace(/\.$/, "");
2528
+ return message ? `code ${code[1]}: ${message}` : `code ${code[1]}`;
2529
+ }
2530
+ function unsignedHapRejection(output, hap) {
2531
+ const name = path20.basename(hap.path);
2532
+ const reason = installFailureReason(output);
2533
+ const what = hap.fallback ? `No signed HAP was found, so the unsigned ${name} was tried, and the target rejected it (${reason}).` : `The target rejected the unsigned ${name} (${reason}).`;
2534
+ if (!/9568320|signature|pkcs7/i.test(reason)) return what;
2535
+ return `${what} This target installs only signed HAPs. Sign the project for it, build again, and install the signed HAP: \`oniro-app sign\` for an OpenHarmony device or the Oniro emulator, \`oniro-app sign --harmonyos\` for a HarmonyOS device.`;
2536
+ }
2537
+ function ensureInstallOk(result, command, hap) {
2538
+ const output = `${result.stdout}
2539
+ ${result.stderr}`;
2540
+ if (hap && isUnsignedHap(hap.path) && /code:\s*95\d{5}/i.test(output)) {
2541
+ throw new OniroError(unsignedHapRejection(output, hap), new CommandFailedError(command, result.code || 1, output.trim()));
2542
+ }
2543
+ ensureOk(result, command);
2544
+ const failure = detectInstallFailure(output);
2545
+ if (failure) throw new CommandFailedError(command, 1, failure);
2546
+ return result;
2547
+ }
2548
+ function existingHapOrUnsigned(hapPath) {
2549
+ if (fs19.existsSync(hapPath)) return { path: hapPath };
2550
+ const sibling = hapPath.replace(/-signed\.hap$/i, "-unsigned.hap");
2551
+ return sibling !== hapPath && fs19.existsSync(sibling) ? { path: sibling, fallback: true } : void 0;
2552
+ }
2553
+ async function resolveInstallHap(opts, logger) {
2554
+ const warnIfFallback = (hap) => {
2555
+ if (hap.fallback) logger.warn(`No signed HAP found; installing the unsigned ${path20.basename(hap.path)}.`);
2556
+ return hap;
2557
+ };
2558
+ const configured = opts.hapPath ?? opts.config.get("hapPath", "");
2559
+ const relative3 = configured || defaultPaths.hapPath;
2560
+ const hapPath = path20.isAbsolute(relative3) ? relative3 : path20.join(opts.projectDir, relative3);
2561
+ if (configured) {
2562
+ const hap = existingHapOrUnsigned(hapPath);
2563
+ if (!hap) throw new OniroError(`HAP file not found at: ${hapPath}. Build the app first.`);
2564
+ return warnIfFallback(hap);
2565
+ }
2566
+ if (fs19.existsSync(hapPath)) return { path: hapPath };
2567
+ const haps = await discoverHaps({ projectDir: opts.projectDir });
2568
+ const anySigned = Object.values(haps).some((m) => m.signed.length > 0);
2569
+ const entryUnsigned = anySigned ? void 0 : existingHapOrUnsigned(hapPath);
2570
+ if (entryUnsigned) return warnIfFallback(entryUnsigned);
2571
+ const picked = pickHap(haps);
2572
+ if (!picked) throw new OniroError(`No HAP found under ${opts.projectDir}. Build the app first (\`oniro-app build\`).`);
2573
+ return warnIfFallback({ path: picked.path, fallback: !picked.signed });
2574
+ }
2277
2575
  async function installApp(opts) {
2278
2576
  const logger = scopedLogger(opts.logger ?? noopLogger, "hdc");
2279
- const relativeHapPath = opts.hapPath ?? opts.config.get("hapPath", defaultPaths.hapPath);
2280
- const hapPath = path18.isAbsolute(relativeHapPath) ? relativeHapPath : path18.join(opts.projectDir, relativeHapPath);
2281
- if (!fs18.existsSync(hapPath)) {
2282
- throw new OniroError(`HAP file not found at: ${hapPath}. Build and sign the app first.`);
2283
- }
2577
+ const hap = await resolveInstallHap(opts, logger);
2578
+ const hapPath = hap.path;
2284
2579
  const result = await hdcExec({
2285
2580
  config: opts.config,
2286
2581
  args: ["install", hapPath],
@@ -2289,7 +2584,7 @@ async function installApp(opts) {
2289
2584
  });
2290
2585
  if (result.stdout.trim()) logger.info(result.stdout.trim());
2291
2586
  if (result.stderr.trim()) logger.warn(result.stderr.trim());
2292
- ensureOk(result, `hdc install ${hapPath}`);
2587
+ ensureInstallOk(result, `hdc install ${hapPath}`, hap);
2293
2588
  let bundleName = "";
2294
2589
  try {
2295
2590
  bundleName = getBundleName(opts.projectDir);
@@ -2297,6 +2592,18 @@ async function installApp(opts) {
2297
2592
  }
2298
2593
  return { installed: true, bundleName, hapPath, output: `${result.stdout}${result.stderr}`.trim() };
2299
2594
  }
2595
+ async function isBundleInstalled(opts) {
2596
+ const safe = opts.bundle.replace(/'/g, `'\\''`);
2597
+ const res = await shell({
2598
+ config: opts.config,
2599
+ command: `bm dump -n '${safe}'`,
2600
+ deviceSerial: opts.deviceSerial,
2601
+ timeoutMs: opts.timeoutMs ?? 15e3,
2602
+ logger: opts.logger
2603
+ });
2604
+ ensureOk(res, `hdc shell bm dump -n ${opts.bundle}`);
2605
+ return res.stdout.includes("{") && !/failed to get information/i.test(res.stdout);
2606
+ }
2300
2607
  function detectAaStartFailure(output) {
2301
2608
  return /failed to start ability|Error Code:\s*\d|Error Message:/i.test(output) ? output.trim() : null;
2302
2609
  }
@@ -2367,10 +2674,10 @@ function listRunningProcesses(config, options = {}) {
2367
2674
  const logger = options.logger ?? noopLogger;
2368
2675
  const timeoutMs = options.timeoutMs ?? 1e3;
2369
2676
  const hdc = getHdcPath(config);
2370
- return new Promise((resolve6, reject) => {
2677
+ return new Promise((resolve7, reject) => {
2371
2678
  let proc;
2372
2679
  try {
2373
- proc = spawnCompat(hdc, ["track-jpid"]);
2680
+ proc = spawnCompat(hdc, [...hdcTargetArgs(options.deviceSerial), "track-jpid"]);
2374
2681
  } catch (err) {
2375
2682
  reject(new OniroError(`Failed to spawn ${hdc}: ${err instanceof Error ? err.message : String(err)}`, err));
2376
2683
  return;
@@ -2387,7 +2694,7 @@ function listRunningProcesses(config, options = {}) {
2387
2694
  if (options.targetProcessName && name === options.targetProcessName && !resolved) {
2388
2695
  resolved = true;
2389
2696
  killProcessTree(proc);
2390
- resolve6(pid);
2697
+ resolve7(pid);
2391
2698
  return;
2392
2699
  }
2393
2700
  }
@@ -2408,7 +2715,7 @@ function listRunningProcesses(config, options = {}) {
2408
2715
  if (options.targetProcessName) {
2409
2716
  reject(new OniroError(`Could not find process for bundle: ${options.targetProcessName}`));
2410
2717
  } else {
2411
- resolve6(processes);
2718
+ resolve7(processes);
2412
2719
  }
2413
2720
  }, timeoutMs);
2414
2721
  proc.on("close", () => {
@@ -2418,7 +2725,7 @@ function listRunningProcesses(config, options = {}) {
2418
2725
  if (options.targetProcessName) {
2419
2726
  reject(new OniroError(`Could not find process for bundle: ${options.targetProcessName}`));
2420
2727
  } else {
2421
- resolve6(processes);
2728
+ resolve7(processes);
2422
2729
  }
2423
2730
  });
2424
2731
  });
@@ -2440,7 +2747,11 @@ async function findRunningProcess(opts) {
2440
2747
  const pid = res.stdout.trim().split(/\s+/).filter(Boolean)[0];
2441
2748
  if (res.code === 0 && pid && /^\d+$/.test(pid)) return { pid, name: opts.bundle };
2442
2749
  try {
2443
- const list = await listRunningProcesses(opts.config, { timeoutMs: 1200, logger: opts.logger });
2750
+ const list = await listRunningProcesses(opts.config, {
2751
+ timeoutMs: 1200,
2752
+ deviceSerial: opts.deviceSerial,
2753
+ logger: opts.logger
2754
+ });
2444
2755
  if (Array.isArray(list)) {
2445
2756
  const mine = (name) => name === opts.bundle || name.startsWith(`${opts.bundle}/`) || name.startsWith(`${opts.bundle}:`);
2446
2757
  const match = list.find((p) => p.name === opts.bundle) ?? list.find((p) => mine(p.name));
@@ -2583,12 +2894,12 @@ async function reboot(opts) {
2583
2894
  }
2584
2895
 
2585
2896
  // src/hdc/screen.ts
2586
- var fs19 = __toESM(require("fs"), 1);
2897
+ var fs20 = __toESM(require("fs"), 1);
2587
2898
  var os9 = __toESM(require("os"), 1);
2588
- var path19 = __toESM(require("path"), 1);
2899
+ var path21 = __toESM(require("path"), 1);
2589
2900
  var import_node_crypto = require("crypto");
2590
2901
  function tmpName(ext) {
2591
- return path19.join(os9.tmpdir(), `oniro_screen_${(0, import_node_crypto.randomBytes)(6).toString("hex")}.${ext}`);
2902
+ return path21.join(os9.tmpdir(), `oniro_screen_${(0, import_node_crypto.randomBytes)(6).toString("hex")}.${ext}`);
2592
2903
  }
2593
2904
  async function takeScreenshot(opts) {
2594
2905
  const remote = "/data/local/tmp/oniro_screenshot.jpeg";
@@ -2598,11 +2909,11 @@ async function takeScreenshot(opts) {
2598
2909
  const local = tmpName("jpeg");
2599
2910
  try {
2600
2911
  await recvFile({ config: opts.config, remote, local, deviceSerial: opts.deviceSerial, timeoutMs });
2601
- const pixels = await fs19.promises.readFile(local);
2912
+ const pixels = await fs20.promises.readFile(local);
2602
2913
  const size = await getDisplaySize(opts.config, { deviceSerial: opts.deviceSerial }) ?? { width: 0, height: 0 };
2603
2914
  return { pixels, width: size.width, height: size.height };
2604
2915
  } finally {
2605
- fs19.promises.unlink(local).catch(() => {
2916
+ fs20.promises.unlink(local).catch(() => {
2606
2917
  });
2607
2918
  void shell({ config: opts.config, command: `rm -f ${remote}`, deviceSerial: opts.deviceSerial }).catch(() => {
2608
2919
  });
@@ -2621,9 +2932,9 @@ async function captureBurst(opts) {
2621
2932
  const local = tmpName(`burst_${i}.jpeg`);
2622
2933
  try {
2623
2934
  await recvFile({ config: opts.config, remote: `${remoteDir}/f${i}.jpeg`, local, deviceSerial: opts.deviceSerial });
2624
- frames.push(await fs19.promises.readFile(local));
2935
+ frames.push(await fs20.promises.readFile(local));
2625
2936
  } finally {
2626
- fs19.promises.unlink(local).catch(() => {
2937
+ fs20.promises.unlink(local).catch(() => {
2627
2938
  });
2628
2939
  }
2629
2940
  }
@@ -2798,9 +3109,9 @@ async function sendRawTouch(opts) {
2798
3109
  }
2799
3110
 
2800
3111
  // src/hdc/dump.ts
2801
- var fs20 = __toESM(require("fs"), 1);
3112
+ var fs21 = __toESM(require("fs"), 1);
2802
3113
  var os10 = __toESM(require("os"), 1);
2803
- var path20 = __toESM(require("path"), 1);
3114
+ var path22 = __toESM(require("path"), 1);
2804
3115
  var import_node_crypto2 = require("crypto");
2805
3116
 
2806
3117
  // src/hdc/ime.ts
@@ -3063,10 +3374,10 @@ async function dumpLayout(opts) {
3063
3374
  if (!m) throw new OniroError(`Could not parse dumpLayout output:
3064
3375
  ${dump.stdout}`);
3065
3376
  const remote = m[1];
3066
- const local = path20.join(os10.tmpdir(), `oniro_layout_${(0, import_node_crypto2.randomBytes)(6).toString("hex")}.json`);
3377
+ const local = path22.join(os10.tmpdir(), `oniro_layout_${(0, import_node_crypto2.randomBytes)(6).toString("hex")}.json`);
3067
3378
  try {
3068
3379
  await recvFile({ config: opts.config, remote, local, deviceSerial: opts.deviceSerial, timeoutMs });
3069
- const parsed = JSON.parse(await fs20.promises.readFile(local, "utf8"));
3380
+ const parsed = JSON.parse(await fs21.promises.readFile(local, "utf8"));
3070
3381
  const display = await getDisplaySize(opts.config, { deviceSerial: opts.deviceSerial });
3071
3382
  if (opts.prune === false) {
3072
3383
  return { display, tree: parsed };
@@ -3079,7 +3390,7 @@ ${dump.stdout}`);
3079
3390
  const keyboard = kb ? { shown: true, bundle: kb.bundle, ...kb.b ? { b: kb.b } : {} } : { shown: false };
3080
3391
  return { display, tree, keyboard };
3081
3392
  } finally {
3082
- fs20.promises.unlink(local).catch(() => {
3393
+ fs21.promises.unlink(local).catch(() => {
3083
3394
  });
3084
3395
  void shell({ config: opts.config, command: `rm -f ${remote}`, deviceSerial: opts.deviceSerial }).catch(() => {
3085
3396
  });
@@ -3089,7 +3400,7 @@ ${dump.stdout}`);
3089
3400
  // src/hdc/uiActions.ts
3090
3401
  var FOCUS_SETTLE_MS = 400;
3091
3402
  var KEYBOARD_SETTLE_MS = 400;
3092
- var sleep = (ms) => new Promise((resolve6) => setTimeout(resolve6, ms));
3403
+ var sleep = (ms) => new Promise((resolve7) => setTimeout(resolve7, ms));
3093
3404
  function targetTree(layout) {
3094
3405
  const kb = layout.keyboard;
3095
3406
  return kb?.shown && kb.bundle ? withoutWindow(layout.tree, kb.bundle) : layout.tree;
@@ -3242,13 +3553,14 @@ async function waitForTarget(opts) {
3242
3553
  }
3243
3554
 
3244
3555
  // src/hdc/hilog.ts
3556
+ var fs22 = __toESM(require("fs"), 1);
3245
3557
  function setHilogLevel(opts) {
3246
3558
  const logger = opts.logger ?? noopLogger;
3247
3559
  const hdc = getHdcPath(opts.config);
3248
- return new Promise((resolve6, reject) => {
3560
+ return new Promise((resolve7, reject) => {
3249
3561
  let child;
3250
3562
  try {
3251
- child = spawnCompat(hdc, ["shell", "hilog", "-b", opts.level]);
3563
+ child = spawnCompat(hdc, [...hdcTargetArgs(opts.deviceSerial), "shell", "hilog", "-b", opts.level]);
3252
3564
  } catch (err) {
3253
3565
  const msg = err instanceof Error ? err.message : String(err);
3254
3566
  logger.error(`[hilog] failed to spawn hdc: ${msg}`);
@@ -3265,7 +3577,7 @@ function setHilogLevel(opts) {
3265
3577
  });
3266
3578
  child.once("close", (code) => {
3267
3579
  if (code === 0) {
3268
- resolve6();
3580
+ resolve7();
3269
3581
  } else {
3270
3582
  const msg = stderr.trim() || `hdc shell hilog -b ${opts.level} exited with code ${code}`;
3271
3583
  logger.error(`[hilog] ${msg}`);
@@ -3276,8 +3588,7 @@ function setHilogLevel(opts) {
3276
3588
  }
3277
3589
  function streamHilog(opts) {
3278
3590
  const hdc = getHdcPath(opts.config);
3279
- const args = [];
3280
- if (opts.deviceSerial) args.push("-t", opts.deviceSerial);
3591
+ const args = [...hdcTargetArgs(opts.deviceSerial)];
3281
3592
  args.push("shell", "hilog");
3282
3593
  if (opts.processId && opts.processId.trim() !== "") {
3283
3594
  args.push("-P", opts.processId.trim());
@@ -3320,7 +3631,7 @@ async function resolveProcessId(config, bundle, deviceSerial) {
3320
3631
  return proc?.pid;
3321
3632
  }
3322
3633
  function waitForLog(opts) {
3323
- return new Promise((resolve6, reject) => {
3634
+ return new Promise((resolve7, reject) => {
3324
3635
  const deadline = Date.now() + opts.timeoutMs;
3325
3636
  let settled = false;
3326
3637
  let child = null;
@@ -3339,7 +3650,7 @@ function waitForLog(opts) {
3339
3650
  if (settled) return;
3340
3651
  settled = true;
3341
3652
  cleanup();
3342
- resolve6(entry);
3653
+ resolve7(entry);
3343
3654
  };
3344
3655
  const fail = (err) => {
3345
3656
  if (settled) return;
@@ -3386,7 +3697,7 @@ function waitForLog(opts) {
3386
3697
  });
3387
3698
  }
3388
3699
  function watchLog(opts) {
3389
- return new Promise((resolve6, reject) => {
3700
+ return new Promise((resolve7, reject) => {
3390
3701
  const matches = [];
3391
3702
  let settled = false;
3392
3703
  let child = null;
@@ -3403,7 +3714,7 @@ function watchLog(opts) {
3403
3714
  if (settled) return;
3404
3715
  settled = true;
3405
3716
  cleanup();
3406
- resolve6(opts.dedup === false ? matches : dedupEntries(matches));
3717
+ resolve7(opts.dedup === false ? matches : dedupEntries(matches));
3407
3718
  };
3408
3719
  const onAbort = () => {
3409
3720
  if (settled) return;
@@ -3435,6 +3746,94 @@ function watchLog(opts) {
3435
3746
  });
3436
3747
  });
3437
3748
  }
3749
+ async function captureLog(opts) {
3750
+ const logger = opts.logger ?? noopLogger;
3751
+ if (opts.clear) {
3752
+ const res = await shell({ config: opts.config, command: "hilog -r", deviceSerial: opts.deviceSerial, timeoutMs: 1e4, logger });
3753
+ ensureOk(res, "hdc shell hilog -r");
3754
+ }
3755
+ const fd = fs22.openSync(opts.file, "a");
3756
+ let lines = 0;
3757
+ let reconnects = 0;
3758
+ try {
3759
+ await new Promise((resolve7, reject) => {
3760
+ const deadline = opts.durationMs !== void 0 ? Date.now() + opts.durationMs : Infinity;
3761
+ let settled = false;
3762
+ let child = null;
3763
+ let reconnectTimer;
3764
+ let buf = "";
3765
+ let open = true;
3766
+ const write = (line) => {
3767
+ if (!open || opts.pattern && !opts.pattern.test(line)) return;
3768
+ fs22.writeSync(fd, `${line}
3769
+ `);
3770
+ lines++;
3771
+ };
3772
+ const cleanup = () => {
3773
+ if (timer) clearTimeout(timer);
3774
+ if (reconnectTimer) clearTimeout(reconnectTimer);
3775
+ opts.abortSignal?.removeEventListener("abort", finish);
3776
+ if (buf.trim()) write(buf.trimEnd());
3777
+ buf = "";
3778
+ open = false;
3779
+ if (child) {
3780
+ child.stdout.removeAllListeners("data");
3781
+ killProcessTree(child);
3782
+ child = null;
3783
+ }
3784
+ };
3785
+ function finish() {
3786
+ if (settled) return;
3787
+ settled = true;
3788
+ cleanup();
3789
+ resolve7();
3790
+ }
3791
+ const fail = (err) => {
3792
+ if (settled) return;
3793
+ settled = true;
3794
+ cleanup();
3795
+ reject(err instanceof Error ? err : new Error(String(err)));
3796
+ };
3797
+ const timer = opts.durationMs !== void 0 ? setTimeout(finish, opts.durationMs) : void 0;
3798
+ if (opts.abortSignal?.aborted) {
3799
+ finish();
3800
+ return;
3801
+ }
3802
+ opts.abortSignal?.addEventListener("abort", finish, { once: true });
3803
+ const spawnStream = () => {
3804
+ if (settled) return;
3805
+ const stream = streamHilog({ config: opts.config, domain: opts.domain, deviceSerial: opts.deviceSerial });
3806
+ child = stream;
3807
+ stream.stdout.on("data", (d) => {
3808
+ buf += d.toString();
3809
+ const parts = buf.split(/\r?\n/);
3810
+ buf = parts.pop() ?? "";
3811
+ for (const line of parts) write(line);
3812
+ });
3813
+ const onEnd = () => {
3814
+ if (settled || child !== stream) return;
3815
+ child = null;
3816
+ if (Date.now() >= deadline) return;
3817
+ reconnects++;
3818
+ logger.warn(`[hilog] stream ended; reconnecting (${reconnects}).`);
3819
+ reconnectTimer = setTimeout(() => {
3820
+ try {
3821
+ spawnStream();
3822
+ } catch (err) {
3823
+ fail(err);
3824
+ }
3825
+ }, 2e3);
3826
+ };
3827
+ stream.once("close", onEnd);
3828
+ stream.once("error", onEnd);
3829
+ };
3830
+ spawnStream();
3831
+ });
3832
+ } finally {
3833
+ fs22.closeSync(fd);
3834
+ }
3835
+ return { file: opts.file, lines, reconnects };
3836
+ }
3438
3837
  async function dumpLog(opts) {
3439
3838
  let pipe = "hilog -x";
3440
3839
  if (opts.domain !== void 0 && `${opts.domain}` !== "") pipe += ` -D ${opts.domain}`;
@@ -3459,17 +3858,17 @@ async function dumpLog(opts) {
3459
3858
  }
3460
3859
 
3461
3860
  // src/build/runHvigorw.ts
3462
- var fs22 = __toESM(require("fs"), 1);
3463
- var path22 = __toESM(require("path"), 1);
3861
+ var fs24 = __toESM(require("fs"), 1);
3862
+ var path24 = __toESM(require("path"), 1);
3464
3863
 
3465
3864
  // src/harmonyos/target.ts
3466
- var fs21 = __toESM(require("fs"), 1);
3467
- var path21 = __toESM(require("path"), 1);
3865
+ var fs23 = __toESM(require("fs"), 1);
3866
+ var path23 = __toESM(require("path"), 1);
3468
3867
  var import_json54 = __toESM(require("json5"), 1);
3469
3868
  function detectRuntimeOs(projectDir, productName) {
3470
3869
  let products = [];
3471
3870
  try {
3472
- const parsed = import_json54.default.parse(fs21.readFileSync(path21.join(projectDir, "build-profile.json5"), "utf-8"));
3871
+ const parsed = import_json54.default.parse(fs23.readFileSync(path23.join(projectDir, "build-profile.json5"), "utf-8"));
3473
3872
  if (Array.isArray(parsed.app?.products)) products = parsed.app.products;
3474
3873
  } catch {
3475
3874
  return "OpenHarmony";
@@ -3510,7 +3909,7 @@ function runHvigorw(opts) {
3510
3909
  const { config } = opts;
3511
3910
  const projectDir = toLongPath(opts.projectDir);
3512
3911
  const logger = opts.logger ?? noopLogger;
3513
- if (!fs22.existsSync(path22.join(projectDir, "build-profile.json5"))) {
3912
+ if (!fs24.existsSync(path24.join(projectDir, "build-profile.json5"))) {
3514
3913
  throw new OniroError(`Not an OpenHarmony project: ${projectDir} (build-profile.json5 not found).`);
3515
3914
  }
3516
3915
  let hvigorw;
@@ -3522,24 +3921,24 @@ function runHvigorw(opts) {
3522
3921
  hvigorw = resolved.startsWith(projectDir) ? resolved : findHarmonyOsTool(sdk, "hvigorw") ?? resolved;
3523
3922
  logger.info(`[build] Building for HarmonyOS against the SDK at ${sdk.sdkPath}.`);
3524
3923
  } else {
3525
- if (!fs22.existsSync(getCmdToolsPath(config))) {
3924
+ if (!fs24.existsSync(getCmdToolsPath(config))) {
3526
3925
  throw new CmdToolsNotInstalledError(getCmdToolsPath(config));
3527
3926
  }
3528
3927
  sdkEnv = { OHOS_BASE_SDK_HOME: getOhosBaseSdkHome(config) };
3529
3928
  hvigorw = getHvigorwPath(config, projectDir);
3530
3929
  }
3531
- if (!fs22.existsSync(hvigorw)) {
3930
+ if (!fs24.existsSync(hvigorw)) {
3532
3931
  throw new OniroError(`hvigorw wrapper not found at ${hvigorw}.`);
3533
3932
  }
3534
3933
  const args = buildHvigorwArgs(opts);
3535
3934
  if (process.platform !== "win32") {
3536
3935
  try {
3537
- fs22.chmodSync(hvigorw, 493);
3936
+ fs24.chmodSync(hvigorw, 493);
3538
3937
  } catch {
3539
3938
  }
3540
3939
  }
3541
3940
  const env = { ...process.env, ...sdkEnv };
3542
- return new Promise((resolve6, reject) => {
3941
+ return new Promise((resolve7, reject) => {
3543
3942
  if (opts.abortSignal?.aborted) {
3544
3943
  reject(new CancelledError("hvigorw build cancelled."));
3545
3944
  return;
@@ -3583,46 +3982,15 @@ function runHvigorw(opts) {
3583
3982
  settled = true;
3584
3983
  cleanup();
3585
3984
  const exitCode = code ?? 1;
3586
- if (exitCode === 0) resolve6({ exitCode });
3985
+ if (exitCode === 0) resolve7({ exitCode });
3587
3986
  else reject(new OniroError(`hvigorw exited with code ${exitCode}`));
3588
3987
  });
3589
3988
  });
3590
3989
  }
3591
3990
 
3592
- // src/build/discoverHaps.ts
3593
- var fs23 = __toESM(require("fs"), 1);
3594
- var path23 = __toESM(require("path"), 1);
3595
- function moduleOfHap(hapFileName) {
3596
- const dash = hapFileName.indexOf("-");
3597
- return dash > 0 ? hapFileName.slice(0, dash) : hapFileName.replace(/\.hap$/i, "");
3598
- }
3599
- async function discoverHaps(opts) {
3600
- const { projectDir } = opts;
3601
- const maxDepth = opts.maxDepth ?? 8;
3602
- const result = {};
3603
- const walk = async (dir, depth) => {
3604
- if (depth > maxDepth) return;
3605
- const entries = await fs23.promises.readdir(dir, { withFileTypes: true }).catch(() => []);
3606
- for (const e of entries) {
3607
- const full = path23.join(dir, e.name);
3608
- if (e.isDirectory()) {
3609
- if (e.name === "node_modules" || e.name === "oh_modules" || e.name === ".git") continue;
3610
- await walk(full, depth + 1);
3611
- } else if (e.isFile() && e.name.endsWith(".hap")) {
3612
- const moduleName = moduleOfHap(e.name);
3613
- const bucket = result[moduleName] ??= { signed: [], unsigned: [] };
3614
- if (e.name.includes("unsigned")) bucket.unsigned.push(full);
3615
- else if (e.name.includes("signed")) bucket.signed.push(full);
3616
- }
3617
- }
3618
- };
3619
- await walk(projectDir, 0);
3620
- return result;
3621
- }
3622
-
3623
3991
  // src/build/ohpm.ts
3624
- var fs24 = __toESM(require("fs"), 1);
3625
- var path24 = __toESM(require("path"), 1);
3992
+ var fs25 = __toESM(require("fs"), 1);
3993
+ var path25 = __toESM(require("path"), 1);
3626
3994
  function runOhpm(opts) {
3627
3995
  return runProcess({
3628
3996
  command: opts.ohpmPath ?? getOhpmPath(opts.config),
@@ -3635,7 +4003,7 @@ function runOhpm(opts) {
3635
4003
  });
3636
4004
  }
3637
4005
  async function ensureOhModules(opts) {
3638
- if (fs24.existsSync(path24.join(opts.projectDir, "oh_modules"))) {
4006
+ if (fs25.existsSync(path25.join(opts.projectDir, "oh_modules"))) {
3639
4007
  return { installed: false };
3640
4008
  }
3641
4009
  const res = await runOhpm({
@@ -3652,23 +4020,91 @@ async function ensureOhModules(opts) {
3652
4020
  }
3653
4021
 
3654
4022
  // src/build/codelinter.ts
3655
- var FINDING_RE = /^(.*?):(\d+):(?:\d+:)?\s*(error|warning|warn|info)\b[:\s-]*(.*)$/i;
4023
+ var FINDING_RE = /^(.*?):(\d+):(?:(\d+):)?\s*(error|warning|warn|suggestion|info)\b[:\s-]*(.*)$/i;
3656
4024
  var TRAILING_CODE_RE = /[([]([\w@/.\-]+)[)\]]\s*$/;
3657
- function parseCodelinterFindings(output) {
4025
+ var FILE_HEADER_RE = /^(.+\.\w+)\((\d+)\)$/;
4026
+ var ROW_RE = /^(\d+):(\d+)\s+(error|warning|warn|suggestion|info)\s+(.*)$/i;
4027
+ var RULE_RE = /^@?[\w.-]+(?:\/[\w.-]+)+$/;
4028
+ var SUMMARY_RE = /Defects:\s*(\d+);\s*Errors:\s*(\d+);\s*Warns:\s*(\d+);\s*Suggestions:\s*(\d+)/i;
4029
+ var NOTICE_RE = /does not match that of the project|error occurred during linting|incomplete report/i;
4030
+ var ANSI_RE = /\x1b\[[0-9;]*[A-Za-z]/g;
4031
+ function cleanCodelinterOutput(output) {
4032
+ return output.replace(ANSI_RE, "").split("\n").map((line) => (line.split("\r").filter((s) => s.trim() !== "").pop() ?? "").trimEnd()).join("\n");
4033
+ }
4034
+ function normaliseSeverity(s) {
4035
+ const lower = s.toLowerCase();
4036
+ return lower === "warn" ? "warning" : lower;
4037
+ }
4038
+ function parseCodelinterReport(output) {
3658
4039
  const findings = [];
3659
- for (const raw of output.split("\n")) {
3660
- const m = FINDING_RE.exec(raw.trim());
3661
- if (!m) continue;
3662
- let message = m[4].trim();
3663
- let code = "";
3664
- const codeMatch = TRAILING_CODE_RE.exec(message);
3665
- if (codeMatch) {
3666
- code = codeMatch[1];
3667
- message = message.slice(0, codeMatch.index).trim();
4040
+ const notices = [];
4041
+ let summary = null;
4042
+ let file = "";
4043
+ for (const rawLine of cleanCodelinterOutput(output).split("\n")) {
4044
+ const line = rawLine.trim();
4045
+ if (!line) continue;
4046
+ const row = ROW_RE.exec(line);
4047
+ if (row) {
4048
+ if (!file) continue;
4049
+ const columns = row[4].trim().split(/\s{2,}/);
4050
+ const last = columns[columns.length - 1];
4051
+ const code = columns.length > 1 && RULE_RE.test(last) ? last : "";
4052
+ const message = (code ? columns.slice(0, -1) : columns).join(" ").trim();
4053
+ findings.push({
4054
+ file,
4055
+ line: Number(row[1]),
4056
+ column: Number(row[2]),
4057
+ severity: normaliseSeverity(row[3]),
4058
+ code,
4059
+ message
4060
+ });
4061
+ continue;
4062
+ }
4063
+ const header = FILE_HEADER_RE.exec(line);
4064
+ if (header) {
4065
+ file = header[1];
4066
+ continue;
4067
+ }
4068
+ const legacy = FINDING_RE.exec(line);
4069
+ if (legacy) {
4070
+ let message = legacy[5].trim();
4071
+ let code = "";
4072
+ const codeMatch = TRAILING_CODE_RE.exec(message);
4073
+ if (codeMatch) {
4074
+ code = codeMatch[1];
4075
+ message = message.slice(0, codeMatch.index).trim();
4076
+ }
4077
+ const finding = {
4078
+ file: legacy[1].trim(),
4079
+ line: Number(legacy[2]),
4080
+ severity: normaliseSeverity(legacy[4]),
4081
+ code,
4082
+ message
4083
+ };
4084
+ if (legacy[3]) finding.column = Number(legacy[3]);
4085
+ findings.push(finding);
4086
+ continue;
3668
4087
  }
3669
- findings.push({ file: m[1].trim(), line: Number(m[2]), severity: m[3].toLowerCase(), code, message });
4088
+ const totals = SUMMARY_RE.exec(line);
4089
+ if (totals) {
4090
+ summary = {
4091
+ defects: Number(totals[1]),
4092
+ errors: Number(totals[2]),
4093
+ warnings: Number(totals[3]),
4094
+ suggestions: Number(totals[4])
4095
+ };
4096
+ continue;
4097
+ }
4098
+ if (/no defects found/i.test(line)) {
4099
+ summary = { defects: 0, errors: 0, warnings: 0, suggestions: 0 };
4100
+ continue;
4101
+ }
4102
+ if (NOTICE_RE.test(line)) notices.push(line);
3670
4103
  }
3671
- return findings;
4104
+ return { findings, summary, notices, incomplete: notices.length > 0 };
4105
+ }
4106
+ function parseCodelinterFindings(output) {
4107
+ return parseCodelinterReport(output).findings;
3672
4108
  }
3673
4109
  async function runCodelinter(opts) {
3674
4110
  const res = await runProcess({
@@ -3680,16 +4116,28 @@ async function runCodelinter(opts) {
3680
4116
  logger: opts.logger
3681
4117
  });
3682
4118
  const raw = `${res.stdout}${res.stderr}`;
3683
- return { code: res.code, findings: parseCodelinterFindings(raw), raw };
4119
+ return { code: res.code, ...parseCodelinterReport(raw), raw: cleanCodelinterOutput(raw) };
3684
4120
  }
3685
4121
 
3686
4122
  // src/build/buildHap.ts
4123
+ var DIAGNOSIS_OUTPUT_LIMIT = 2e6;
4124
+ function diagnoseBuildFailure(output) {
4125
+ 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`).";
4127
+ }
4128
+ if (/00308018|SDK path does not exist|SDK license agreement is not accepted/i.test(output)) {
4129
+ return "hvigor cannot find the SDK the project compiles against: check `oniro-app sdk list` against compileSdkVersion.";
4130
+ }
4131
+ return void 0;
4132
+ }
3687
4133
  async function buildHap(opts) {
3688
4134
  const logger = scopedLogger(opts.logger ?? noopLogger, "build");
3689
4135
  const warnings = [];
3690
4136
  const start = Date.now();
3691
4137
  const projectDir = toLongPath(opts.projectDir);
3692
4138
  const isHarmonyOs = detectRuntimeOs(projectDir, opts.product) === "HarmonyOS";
4139
+ const missingSdk = isHarmonyOs ? null : findMissingProjectSdk(opts.config, projectDir);
4140
+ if (missingSdk) throw missingSdk;
3693
4141
  if (opts.autoInstallDeps !== false) {
3694
4142
  const harmonySdk = isHarmonyOs ? findHarmonyOsSdk({ config: opts.config, logger: opts.logger }) : null;
3695
4143
  const r = await ensureOhModules({
@@ -3702,23 +4150,35 @@ async function buildHap(opts) {
3702
4150
  });
3703
4151
  if (r.installed) logger.info("ran ohpm install --all (oh_modules was missing)");
3704
4152
  }
3705
- const { exitCode } = await runHvigorw({
3706
- config: opts.config,
3707
- projectDir,
3708
- product: opts.product,
3709
- module: opts.module,
3710
- buildMode: opts.mode,
3711
- task: opts.task,
3712
- parallel: opts.parallel,
3713
- abortSignal: opts.abortSignal,
3714
- onOutput: opts.onOutput,
3715
- logger: opts.logger
3716
- });
4153
+ let output = "";
4154
+ let exitCode;
4155
+ try {
4156
+ ({ exitCode } = await runHvigorw({
4157
+ config: opts.config,
4158
+ projectDir,
4159
+ product: opts.product,
4160
+ module: opts.module,
4161
+ buildMode: opts.mode,
4162
+ task: opts.task,
4163
+ parallel: opts.parallel,
4164
+ abortSignal: opts.abortSignal,
4165
+ onOutput: (chunk, stream) => {
4166
+ output = (output + chunk).slice(-DIAGNOSIS_OUTPUT_LIMIT);
4167
+ opts.onOutput?.(chunk, stream);
4168
+ },
4169
+ logger: opts.logger
4170
+ }));
4171
+ } catch (err) {
4172
+ const hint = diagnoseBuildFailure(output);
4173
+ if (hint && err instanceof OniroError) throw new OniroError(`${err.message}
4174
+ ${hint}`, err);
4175
+ throw err;
4176
+ }
3717
4177
  const discoveredHaps = await discoverHaps({ projectDir });
3718
4178
  const anySigned = Object.values(discoveredHaps).some((m) => m.signed.length > 0);
3719
4179
  if (!anySigned) {
3720
4180
  warnings.push(
3721
- isHarmonyOs ? "No signed HAPs found \u2014 run `oniro-app sign --harmonyos` to have AppGallery Connect issue a certificate and profile." : "No signed HAPs found \u2014 configure signingConfigs in build-profile.json5 (unsigned HAPs are rejected by most devices)."
4181
+ isHarmonyOs ? "No signed HAPs found. The HarmonyOS emulator installs the unsigned HAP as it is; for a device, run `oniro-app sign --harmonyos` to have AppGallery Connect issue a certificate and profile." : "No signed HAPs found: most OpenHarmony targets reject unsigned HAPs. Run `oniro-app sign` to generate local signing material, then build again."
3722
4182
  );
3723
4183
  }
3724
4184
  return { exitCode, durationMs: Date.now() - start, discoveredHaps, warnings };
@@ -3735,21 +4195,21 @@ function isValidBundleName(bundleName) {
3735
4195
  }
3736
4196
 
3737
4197
  // src/project/templates.ts
3738
- var fs25 = __toESM(require("fs"), 1);
3739
- var path25 = __toESM(require("path"), 1);
4198
+ var fs26 = __toESM(require("fs"), 1);
4199
+ var path26 = __toESM(require("path"), 1);
3740
4200
  function toHumanTemplateName(folderName) {
3741
4201
  return folderName.replace(/([a-z])([A-Z])/g, "$1 $2");
3742
4202
  }
3743
4203
  function listTemplates(templateRoot) {
3744
- if (!fs25.existsSync(templateRoot)) return [];
3745
- const entries = fs25.readdirSync(templateRoot, { withFileTypes: true });
4204
+ if (!fs26.existsSync(templateRoot)) return [];
4205
+ const entries = fs26.readdirSync(templateRoot, { withFileTypes: true });
3746
4206
  return entries.filter((e) => e.isDirectory()).map((e) => {
3747
- const dir = path25.join(templateRoot, e.name);
3748
- const metaPath = path25.join(dir, "template.json");
4207
+ const dir = path26.join(templateRoot, e.name);
4208
+ const metaPath = path26.join(dir, "template.json");
3749
4209
  let meta = {};
3750
- if (fs25.existsSync(metaPath)) {
4210
+ if (fs26.existsSync(metaPath)) {
3751
4211
  try {
3752
- meta = JSON.parse(fs25.readFileSync(metaPath, "utf8"));
4212
+ meta = JSON.parse(fs26.readFileSync(metaPath, "utf8"));
3753
4213
  } catch {
3754
4214
  }
3755
4215
  }
@@ -3764,57 +4224,57 @@ function listTemplates(templateRoot) {
3764
4224
  function validateTemplateLayout(templateDir, defaultModuleName) {
3765
4225
  const required = [
3766
4226
  "build-profile.json5",
3767
- path25.join("AppScope", "app.json5"),
3768
- path25.join(defaultModuleName, "src", "main", "module.json5"),
3769
- path25.join(defaultModuleName, "oh-package.json5"),
4227
+ path26.join("AppScope", "app.json5"),
4228
+ path26.join(defaultModuleName, "src", "main", "module.json5"),
4229
+ path26.join(defaultModuleName, "oh-package.json5"),
3770
4230
  "hvigorfile.ts"
3771
4231
  ];
3772
- return required.filter((rel) => !fs25.existsSync(path25.join(templateDir, rel)));
4232
+ return required.filter((rel) => !fs26.existsSync(path26.join(templateDir, rel)));
3773
4233
  }
3774
4234
 
3775
4235
  // src/project/createScaffold.ts
3776
- var fs27 = __toESM(require("fs"), 1);
3777
- var path26 = __toESM(require("path"), 1);
4236
+ var fs28 = __toESM(require("fs"), 1);
4237
+ var path27 = __toESM(require("path"), 1);
3778
4238
 
3779
4239
  // src/project/jsonHelpers.ts
3780
- var fs26 = __toESM(require("fs"), 1);
4240
+ var fs27 = __toESM(require("fs"), 1);
3781
4241
  var import_json55 = __toESM(require("json5"), 1);
3782
4242
  function readJson5File(filePath) {
3783
- return import_json55.default.parse(fs26.readFileSync(filePath, "utf8"));
4243
+ return import_json55.default.parse(fs27.readFileSync(filePath, "utf8"));
3784
4244
  }
3785
4245
  function writeJson5File(filePath, value) {
3786
- fs26.writeFileSync(filePath, JSON.stringify(value, null, 2) + "\n", "utf8");
4246
+ fs27.writeFileSync(filePath, JSON.stringify(value, null, 2) + "\n", "utf8");
3787
4247
  }
3788
4248
  function readJsonFile(filePath) {
3789
- return JSON.parse(fs26.readFileSync(filePath, "utf8"));
4249
+ return JSON.parse(fs27.readFileSync(filePath, "utf8"));
3790
4250
  }
3791
4251
  function writeJsonFile(filePath, value) {
3792
- fs26.writeFileSync(filePath, JSON.stringify(value, null, 2) + "\n", "utf8");
4252
+ fs27.writeFileSync(filePath, JSON.stringify(value, null, 2) + "\n", "utf8");
3793
4253
  }
3794
4254
 
3795
4255
  // src/project/createScaffold.ts
3796
4256
  var IGNORED_DIRS = /* @__PURE__ */ new Set(["oh_modules", "node_modules", "build", ".hvigor"]);
3797
4257
  async function pathExists(p) {
3798
4258
  try {
3799
- await fs27.promises.access(p);
4259
+ await fs28.promises.access(p);
3800
4260
  return true;
3801
4261
  } catch {
3802
4262
  return false;
3803
4263
  }
3804
4264
  }
3805
4265
  async function copyDirRecursive(srcDir, destDir) {
3806
- await fs27.promises.mkdir(destDir, { recursive: true });
3807
- const entries = await fs27.promises.readdir(srcDir, { withFileTypes: true });
4266
+ await fs28.promises.mkdir(destDir, { recursive: true });
4267
+ const entries = await fs28.promises.readdir(srcDir, { withFileTypes: true });
3808
4268
  for (const entry of entries) {
3809
- const src = path26.join(srcDir, entry.name);
3810
- const dest = path26.join(destDir, entry.name);
4269
+ const src = path27.join(srcDir, entry.name);
4270
+ const dest = path27.join(destDir, entry.name);
3811
4271
  if (entry.isDirectory()) {
3812
4272
  await copyDirRecursive(src, dest);
3813
4273
  } else if (entry.isSymbolicLink()) {
3814
4274
  continue;
3815
4275
  } else if (entry.isFile()) {
3816
4276
  if (entry.name === "template.json") continue;
3817
- await fs27.promises.copyFile(src, dest);
4277
+ await fs28.promises.copyFile(src, dest);
3818
4278
  }
3819
4279
  }
3820
4280
  }
@@ -3822,9 +4282,9 @@ async function normalizeJson5ToJson(projectDir, logger) {
3822
4282
  const stack = [projectDir];
3823
4283
  while (stack.length > 0) {
3824
4284
  const currentDir = stack.pop();
3825
- const entries = await fs27.promises.readdir(currentDir, { withFileTypes: true });
4285
+ const entries = await fs28.promises.readdir(currentDir, { withFileTypes: true });
3826
4286
  for (const entry of entries) {
3827
- const fullPath = path26.join(currentDir, entry.name);
4287
+ const fullPath = path27.join(currentDir, entry.name);
3828
4288
  if (entry.isDirectory()) {
3829
4289
  if (IGNORED_DIRS.has(entry.name)) continue;
3830
4290
  stack.push(fullPath);
@@ -3846,23 +4306,23 @@ function toJavaPropertiesPath(p) {
3846
4306
  function createOrUpdateLocalProperties(projectDir, sdkDir) {
3847
4307
  const content = `sdk.dir=${toJavaPropertiesPath(sdkDir)}
3848
4308
  `;
3849
- fs27.writeFileSync(path26.join(projectDir, "local.properties"), content, "utf8");
4309
+ fs28.writeFileSync(path27.join(projectDir, "local.properties"), content, "utf8");
3850
4310
  }
3851
4311
  function renameIfExists(fromPath, toPath) {
3852
- if (fs27.existsSync(fromPath) && fromPath !== toPath) {
3853
- fs27.renameSync(fromPath, toPath);
4312
+ if (fs28.existsSync(fromPath) && fromPath !== toPath) {
4313
+ fs28.renameSync(fromPath, toPath);
3854
4314
  }
3855
4315
  }
3856
4316
  function updateTemplateConfigs(projectDir, args, sdkBaseDir) {
3857
- const appJsonPath = path26.join(projectDir, "AppScope", "app.json5");
3858
- if (fs27.existsSync(appJsonPath)) {
4317
+ const appJsonPath = path27.join(projectDir, "AppScope", "app.json5");
4318
+ if (fs28.existsSync(appJsonPath)) {
3859
4319
  const appJson = readJson5File(appJsonPath);
3860
4320
  appJson.app = appJson.app ?? {};
3861
4321
  appJson.app.bundleName = args.bundleName;
3862
4322
  writeJson5File(appJsonPath, appJson);
3863
4323
  }
3864
- const stringsPath = path26.join(projectDir, "AppScope", "resources", "base", "element", "string.json");
3865
- if (fs27.existsSync(stringsPath)) {
4324
+ const stringsPath = path27.join(projectDir, "AppScope", "resources", "base", "element", "string.json");
4325
+ if (fs28.existsSync(stringsPath)) {
3866
4326
  const strings = readJsonFile(stringsPath);
3867
4327
  if (Array.isArray(strings.string)) {
3868
4328
  const appName = strings.string.find((s) => s.name === "app_name");
@@ -3870,8 +4330,8 @@ function updateTemplateConfigs(projectDir, args, sdkBaseDir) {
3870
4330
  }
3871
4331
  writeJsonFile(stringsPath, strings);
3872
4332
  }
3873
- const buildProfilePath = path26.join(projectDir, "build-profile.json5");
3874
- if (fs27.existsSync(buildProfilePath)) {
4333
+ const buildProfilePath = path27.join(projectDir, "build-profile.json5");
4334
+ if (fs28.existsSync(buildProfilePath)) {
3875
4335
  const buildProfile = readJson5File(buildProfilePath);
3876
4336
  buildProfile.app = buildProfile.app ?? {};
3877
4337
  if (Array.isArray(buildProfile.app.products) && buildProfile.app.products.length > 0) {
@@ -3884,23 +4344,23 @@ function updateTemplateConfigs(projectDir, args, sdkBaseDir) {
3884
4344
  }
3885
4345
  writeJson5File(buildProfilePath, buildProfile);
3886
4346
  }
3887
- const moduleJsonPath = path26.join(projectDir, args.moduleName, "src", "main", "module.json5");
3888
- if (fs27.existsSync(moduleJsonPath)) {
4347
+ const moduleJsonPath = path27.join(projectDir, args.moduleName, "src", "main", "module.json5");
4348
+ if (fs28.existsSync(moduleJsonPath)) {
3889
4349
  const moduleJson = readJson5File(moduleJsonPath);
3890
4350
  moduleJson.module = moduleJson.module ?? {};
3891
4351
  moduleJson.module.name = args.moduleName;
3892
4352
  writeJson5File(moduleJsonPath, moduleJson);
3893
4353
  }
3894
- const moduleOhPackagePath = path26.join(projectDir, args.moduleName, "oh-package.json5");
3895
- if (fs27.existsSync(moduleOhPackagePath)) {
4354
+ const moduleOhPackagePath = path27.join(projectDir, args.moduleName, "oh-package.json5");
4355
+ if (fs28.existsSync(moduleOhPackagePath)) {
3896
4356
  const pkg = readJson5File(moduleOhPackagePath);
3897
4357
  pkg.name = args.moduleName;
3898
4358
  writeJson5File(moduleOhPackagePath, pkg);
3899
4359
  }
3900
- const vscodeDir = path26.join(projectDir, ".vscode");
3901
- fs27.mkdirSync(vscodeDir, { recursive: true });
4360
+ const vscodeDir = path27.join(projectDir, ".vscode");
4361
+ fs28.mkdirSync(vscodeDir, { recursive: true });
3902
4362
  const hapPath = `${args.moduleName}/build/default/outputs/default/${args.moduleName}-default-signed.hap`;
3903
- writeJsonFile(path26.join(vscodeDir, "settings.json"), {
4363
+ writeJsonFile(path27.join(vscodeDir, "settings.json"), {
3904
4364
  "oniro.hapPath": hapPath,
3905
4365
  "files.associations": { "*.json5": "jsonc" }
3906
4366
  });
@@ -3917,7 +4377,7 @@ async function createScaffold(opts) {
3917
4377
  if (!opts.location || !await pathExists(opts.location)) {
3918
4378
  throw new OniroError(`Location does not exist: ${opts.location}`);
3919
4379
  }
3920
- const templateDir = path26.join(opts.templateRoot, opts.templateId);
4380
+ const templateDir = path27.join(opts.templateRoot, opts.templateId);
3921
4381
  if (!await pathExists(templateDir)) {
3922
4382
  throw new OniroError(`Template not found: ${templateDir}`);
3923
4383
  }
@@ -3952,17 +4412,17 @@ async function createScaffold(opts) {
3952
4412
  }
3953
4413
  }
3954
4414
  }
3955
- const projectDir = path26.join(opts.location, opts.projectName);
4415
+ const projectDir = path27.join(opts.location, opts.projectName);
3956
4416
  if (await pathExists(projectDir)) {
3957
4417
  if (!opts.overwrite) {
3958
4418
  throw new OniroError(`Destination already exists: ${projectDir}. Pass overwrite: true to replace it.`);
3959
4419
  }
3960
- await fs27.promises.rm(projectDir, { recursive: true, force: true });
4420
+ await fs28.promises.rm(projectDir, { recursive: true, force: true });
3961
4421
  }
3962
4422
  logger.info(`[create] Scaffolding ${opts.templateId} at ${projectDir}`);
3963
4423
  await copyDirRecursive(templateDir, projectDir);
3964
4424
  if (moduleName !== defaultModuleName) {
3965
- renameIfExists(path26.join(projectDir, defaultModuleName), path26.join(projectDir, moduleName));
4425
+ renameIfExists(path27.join(projectDir, defaultModuleName), path27.join(projectDir, moduleName));
3966
4426
  }
3967
4427
  updateTemplateConfigs(
3968
4428
  projectDir,
@@ -3979,11 +4439,11 @@ async function createScaffold(opts) {
3979
4439
  }
3980
4440
 
3981
4441
  // src/project/listModules.ts
3982
- var fs28 = __toESM(require("fs"), 1);
3983
- var path27 = __toESM(require("path"), 1);
4442
+ var fs29 = __toESM(require("fs"), 1);
4443
+ var path28 = __toESM(require("path"), 1);
3984
4444
  function listModules(opts) {
3985
- const buildProfile = path27.join(opts.projectDir, "build-profile.json5");
3986
- if (!fs28.existsSync(buildProfile)) {
4445
+ const buildProfile = path28.join(opts.projectDir, "build-profile.json5");
4446
+ if (!fs29.existsSync(buildProfile)) {
3987
4447
  throw new OniroError(`build-profile.json5 not found at ${buildProfile}`);
3988
4448
  }
3989
4449
  const parsed = readJson5File(buildProfile);
@@ -3995,12 +4455,12 @@ function listModules(opts) {
3995
4455
  }
3996
4456
 
3997
4457
  // src/project/abilities.ts
3998
- var fs29 = __toESM(require("fs"), 1);
3999
- var path28 = __toESM(require("path"), 1);
4458
+ var fs30 = __toESM(require("fs"), 1);
4459
+ var path29 = __toESM(require("path"), 1);
4000
4460
  function listAbilities(opts) {
4001
4461
  const moduleName = opts.moduleName ?? "entry";
4002
- const moduleJsonPath = path28.join(opts.projectDir, moduleName, "src", "main", "module.json5");
4003
- if (!fs29.existsSync(moduleJsonPath)) {
4462
+ const moduleJsonPath = path29.join(opts.projectDir, moduleName, "src", "main", "module.json5");
4463
+ if (!fs30.existsSync(moduleJsonPath)) {
4004
4464
  throw new OniroError(`Could not find module.json5 at ${moduleJsonPath}`);
4005
4465
  }
4006
4466
  const parsed = readJson5File(moduleJsonPath);
@@ -4008,12 +4468,12 @@ function listAbilities(opts) {
4008
4468
  }
4009
4469
 
4010
4470
  // src/project/prepareSigning.ts
4011
- var fs30 = __toESM(require("fs"), 1);
4012
- var path29 = __toESM(require("path"), 1);
4471
+ var fs31 = __toESM(require("fs"), 1);
4472
+ var path30 = __toESM(require("path"), 1);
4013
4473
  function hasSigningArtifacts(dir) {
4014
- if (!fs30.existsSync(path29.join(dir, "signatures"))) return false;
4015
- const buildProfile = path29.join(dir, "build-profile.json5");
4016
- if (!fs30.existsSync(buildProfile)) return false;
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;
4017
4477
  try {
4018
4478
  const parsed = readJson5File(buildProfile);
4019
4479
  return Array.isArray(parsed.app?.signingConfigs) && parsed.app.signingConfigs.length > 0;
@@ -4031,6 +4491,7 @@ function prepareSigning(opts) {
4031
4491
  apl: opts.apl,
4032
4492
  appFeature: opts.appFeature,
4033
4493
  acls: opts.acls,
4494
+ appPrivilegeCapabilities: opts.appPrivilegeCapabilities,
4034
4495
  passwords: opts.passwords,
4035
4496
  logger: opts.logger
4036
4497
  });
@@ -4059,19 +4520,31 @@ var PROPS = [
4059
4520
  ["model", "const.product.model"],
4060
4521
  ["manufacturer", "const.product.manufacturer"],
4061
4522
  ["brand", "const.product.brand"],
4523
+ ["osDistName", "const.product.os.dist.name"],
4524
+ ["osDistVersion", "const.product.os.dist.version"],
4062
4525
  ["osFullName", "const.ohos.fullname"],
4063
4526
  ["osReleaseType", "const.ohos.releasetype"],
4064
4527
  ["sdkApiVersion", "const.ohos.apiversion"],
4065
- ["buildVersion", "const.product.software.version"]
4528
+ ["buildVersion", "const.product.software.version"],
4529
+ ["abi", "const.product.cpu.abilist"],
4530
+ ["deviceType", "const.product.devicetype"]
4066
4531
  ];
4067
4532
  async function getDeviceInfo(opts) {
4068
4533
  const info = { serial: opts.deviceSerial ?? "(default)", display: null };
4069
- for (const [field, prop] of PROPS) {
4070
- try {
4071
- const value = await paramGet(opts.config, prop, { deviceSerial: opts.deviceSerial, timeoutMs: opts.timeoutMs ?? 1e4, logger: opts.logger });
4534
+ try {
4535
+ const values = await paramGetMany(
4536
+ opts.config,
4537
+ PROPS.map(([, prop]) => prop),
4538
+ { deviceSerial: opts.deviceSerial, timeoutMs: opts.timeoutMs ?? 1e4, logger: opts.logger }
4539
+ );
4540
+ for (const [field, prop] of PROPS) {
4541
+ const value = values[prop];
4072
4542
  if (value) info[field] = value;
4073
- } catch {
4074
4543
  }
4544
+ } catch {
4545
+ }
4546
+ if (info.osDistName || info.osFullName) {
4547
+ info.osFamily = /harmonyos/i.test(info.osDistName ?? "") ? "HarmonyOS" : "OpenHarmony";
4075
4548
  }
4076
4549
  const screen = await dumpScreen({ config: opts.config, deviceSerial: opts.deviceSerial, logger: opts.logger });
4077
4550
  if (screen) info.display = { width: screen.width, height: screen.height };
@@ -4103,8 +4576,8 @@ async function diffHapAssets(opts) {
4103
4576
  }
4104
4577
 
4105
4578
  // src/install/apply.ts
4106
- var fs31 = __toESM(require("fs"), 1);
4107
- var path30 = __toESM(require("path"), 1);
4579
+ var fs32 = __toESM(require("fs"), 1);
4580
+ var path31 = __toESM(require("path"), 1);
4108
4581
  function decideInstallMethod(s) {
4109
4582
  if (!s.signInfoInconsistent) return "replace";
4110
4583
  if (s.isSystemBundle && !s.allowUninstall) return "refuse";
@@ -4122,39 +4595,31 @@ async function pidOf(config, bundle, deviceSerial) {
4122
4595
  const proc = await findRunningProcess({ config, bundle, deviceSerial });
4123
4596
  return proc ? Number(proc.pid) : null;
4124
4597
  }
4125
- async function resolveHapPath(opts) {
4126
- let hapPath = opts.hapPath;
4127
- if (hapPath) {
4128
- if (!path30.isAbsolute(hapPath) && opts.projectDir) hapPath = path30.join(opts.projectDir, hapPath);
4598
+ async function resolveHap(opts, logger) {
4599
+ let hap;
4600
+ if (opts.hapPath) {
4601
+ const hapPath = !path31.isAbsolute(opts.hapPath) && opts.projectDir ? path31.join(opts.projectDir, opts.hapPath) : opts.hapPath;
4602
+ hap = existingHapOrUnsigned(hapPath);
4603
+ if (!hap) throw new OniroError(`HAP not found at ${hapPath}.`);
4129
4604
  } else {
4130
4605
  if (!opts.projectDir) throw new OniroError("applyChanges requires hapPath or projectDir.");
4131
- const haps = await discoverHaps({ projectDir: opts.projectDir });
4132
- if (opts.module) {
4133
- const signed = haps[opts.module]?.signed ?? [];
4134
- if (signed.length === 0) throw new OniroError(`No signed HAP for module ${opts.module}. Build first.`);
4135
- hapPath = signed[0];
4136
- } else {
4137
- const withSigned = Object.entries(haps).filter(([, m]) => m.signed.length > 0);
4138
- if (withSigned.length === 0) throw new OniroError("No signed HAP found. Build first.");
4139
- if (withSigned.length > 1) {
4140
- throw new OniroError(
4141
- `Multiple modules have signed HAPs (${withSigned.map(([k]) => k).join(", ")}). Pass module to choose.`
4142
- );
4143
- }
4144
- hapPath = withSigned[0][1].signed[0];
4145
- }
4606
+ const picked = pickHap(await discoverHaps({ projectDir: opts.projectDir }), opts.module);
4607
+ if (!picked) throw new OniroError(`No HAP found${opts.module ? ` for module ${opts.module}` : ""}. Build first.`);
4608
+ hap = { path: picked.path, fallback: !picked.signed };
4146
4609
  }
4147
- if (!fs31.existsSync(hapPath)) throw new OniroError(`HAP not found at ${hapPath}.`);
4148
- return hapPath;
4610
+ if (hap.fallback) logger.warn(`No signed HAP found; installing the unsigned ${path31.basename(hap.path)}.`);
4611
+ return hap;
4149
4612
  }
4150
4613
  async function applyChanges(opts) {
4151
4614
  const logger = scopedLogger(opts.logger ?? noopLogger, "apply");
4152
4615
  const { config, bundle, deviceSerial } = opts;
4153
4616
  const isSystemBundle = opts.isSystemBundle ?? false;
4154
4617
  const allowUninstall = opts.allowUninstall ?? false;
4155
- const hapPath = await resolveHapPath(opts);
4618
+ const hap = await resolveHap(opts, logger);
4619
+ const hapPath = hap.path;
4620
+ logger.info(`installing ${hapPath}`);
4156
4621
  let assetsChanged = false;
4157
- if (opts.installedHapPath && fs31.existsSync(opts.installedHapPath)) {
4622
+ if (opts.installedHapPath && fs32.existsSync(opts.installedHapPath)) {
4158
4623
  try {
4159
4624
  const diff = await diffHapAssets({ installedHap: opts.installedHapPath, newHap: hapPath });
4160
4625
  assetsChanged = diff.addedAssetPaths.length > 0 || diff.removedAssetPaths.length > 0;
@@ -4175,12 +4640,21 @@ async function applyChanges(opts) {
4175
4640
  if (decision === "uninstall-install") {
4176
4641
  logger.warn(`sign-info inconsistent for ${bundle}; uninstalling then installing fresh.`);
4177
4642
  await uninstallApp({ config, bundle, deviceSerial });
4178
- ensureOk(await hdcExec({ config, args: ["install", hapPath], deviceSerial, timeoutMs: 3e5 }), `hdc install ${hapPath}`);
4643
+ ensureInstallOk(
4644
+ await hdcExec({ config, args: ["install", hapPath], deviceSerial, timeoutMs: 3e5 }),
4645
+ `hdc install ${hapPath}`,
4646
+ hap
4647
+ );
4179
4648
  installMethod = "uninstall-install";
4180
4649
  } else {
4181
- ensureOk(replaceRes, `hdc install -r ${hapPath}`);
4650
+ ensureInstallOk(replaceRes, `hdc install -r ${hapPath}`, hap);
4182
4651
  installMethod = "replace";
4183
4652
  }
4653
+ if (!await isBundleInstalled({ config, bundle, deviceSerial })) {
4654
+ 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).`
4656
+ );
4657
+ }
4184
4658
  let postInstallPid = await pidOf(config, bundle, deviceSerial);
4185
4659
  const rebootDecision = decideReboot({ assetsChanged, isSystemBundle, preInstallPid, postInstallPid });
4186
4660
  let method = installMethod;
@@ -4255,9 +4729,9 @@ var AUTH_PATHS = {
4255
4729
  };
4256
4730
 
4257
4731
  // src/harmonyos/auth/tokenStore.ts
4258
- var fs32 = __toESM(require("fs"), 1);
4732
+ var fs33 = __toESM(require("fs"), 1);
4259
4733
  var os11 = __toESM(require("os"), 1);
4260
- var path31 = __toESM(require("path"), 1);
4734
+ var path32 = __toESM(require("path"), 1);
4261
4735
  var crypto3 = __toESM(require("crypto"), 1);
4262
4736
  var ALGORITHM = "aes-256-gcm";
4263
4737
  var KEY_LENGTH = 32;
@@ -4293,7 +4767,7 @@ function permissionHint(dir) {
4293
4767
  }
4294
4768
  function ensureDir(dir) {
4295
4769
  try {
4296
- fs32.mkdirSync(dir, { recursive: true, mode: DIR_MODE });
4770
+ fs33.mkdirSync(dir, { recursive: true, mode: DIR_MODE });
4297
4771
  } catch (err) {
4298
4772
  if (err.code === "EACCES") {
4299
4773
  throw new OniroError(permissionHint(dir), err);
@@ -4304,17 +4778,17 @@ function ensureDir(dir) {
4304
4778
  function createTokenStore(opts) {
4305
4779
  const logger = opts.logger ?? noopLogger;
4306
4780
  const authDir = opts.config.get("harmonyosAuthDir", defaultPaths.harmonyosAuthDir());
4307
- const tokenPath = path31.join(authDir, TOKEN_FILE);
4308
- const keyPath = path31.join(os11.homedir(), ".local", "share", "oniro-app", "keys", KEY_FILE);
4781
+ const tokenPath = path32.join(authDir, TOKEN_FILE);
4782
+ const keyPath = path32.join(os11.homedir(), ".local", "share", "oniro-app", "keys", KEY_FILE);
4309
4783
  function loadOrCreateKey() {
4310
- if (fs32.existsSync(keyPath)) {
4311
- const existing = fs32.readFileSync(keyPath);
4784
+ if (fs33.existsSync(keyPath)) {
4785
+ const existing = fs33.readFileSync(keyPath);
4312
4786
  if (existing.length === KEY_LENGTH) return existing;
4313
4787
  logger.warn("[harmonyos] The token key file was malformed; regenerating (you will need to log in again).");
4314
4788
  }
4315
- ensureDir(path31.dirname(keyPath));
4789
+ ensureDir(path32.dirname(keyPath));
4316
4790
  const key = crypto3.randomBytes(KEY_LENGTH);
4317
- fs32.writeFileSync(keyPath, key, { mode: FILE_MODE });
4791
+ fs33.writeFileSync(keyPath, key, { mode: FILE_MODE });
4318
4792
  return key;
4319
4793
  }
4320
4794
  return {
@@ -4324,12 +4798,12 @@ function createTokenStore(opts) {
4324
4798
  const payload = Buffer.from(JSON.stringify(session), "utf8");
4325
4799
  const blob = encrypt2(loadOrCreateKey(), payload);
4326
4800
  ensureDir(authDir);
4327
- fs32.writeFileSync(tokenPath, JSON.stringify(blob, null, 2), { mode: FILE_MODE });
4801
+ fs33.writeFileSync(tokenPath, JSON.stringify(blob, null, 2), { mode: FILE_MODE });
4328
4802
  },
4329
4803
  load() {
4330
- if (!fs32.existsSync(tokenPath)) return null;
4804
+ if (!fs33.existsSync(tokenPath)) return null;
4331
4805
  try {
4332
- const blob = JSON.parse(fs32.readFileSync(tokenPath, "utf8"));
4806
+ const blob = JSON.parse(fs33.readFileSync(tokenPath, "utf8"));
4333
4807
  if (!isEncryptedBlob(blob)) return null;
4334
4808
  const { jwtToken, siteId } = JSON.parse(decrypt2(loadOrCreateKey(), blob).toString("utf8"));
4335
4809
  return typeof jwtToken === "string" && jwtToken && typeof siteId === "string" ? { jwtToken, siteId } : null;
@@ -4341,7 +4815,7 @@ function createTokenStore(opts) {
4341
4815
  }
4342
4816
  logger.debug(`[harmonyos] Discarding unreadable token at ${tokenPath}.`);
4343
4817
  try {
4344
- fs32.rmSync(tokenPath, { force: true });
4818
+ fs33.rmSync(tokenPath, { force: true });
4345
4819
  } catch {
4346
4820
  }
4347
4821
  return null;
@@ -4349,7 +4823,7 @@ function createTokenStore(opts) {
4349
4823
  },
4350
4824
  clear() {
4351
4825
  try {
4352
- fs32.rmSync(tokenPath, { force: true });
4826
+ fs33.rmSync(tokenPath, { force: true });
4353
4827
  } catch (err) {
4354
4828
  throw new OniroError(`Failed to remove ${tokenPath}.`, err);
4355
4829
  }
@@ -4463,8 +4937,8 @@ function isQuit(quit) {
4463
4937
  async function startCallbackServer(opts) {
4464
4938
  let resolveCallback;
4465
4939
  let rejectCallback;
4466
- const callback = new Promise((resolve6, reject) => {
4467
- resolveCallback = resolve6;
4940
+ const callback = new Promise((resolve7, reject) => {
4941
+ resolveCallback = resolve7;
4468
4942
  rejectCallback = reject;
4469
4943
  });
4470
4944
  callback.catch(() => {
@@ -4523,9 +4997,9 @@ async function startCallbackServer(opts) {
4523
4997
  }
4524
4998
  });
4525
4999
  server.keepAliveTimeout = 1;
4526
- await new Promise((resolve6, reject) => {
5000
+ await new Promise((resolve7, reject) => {
4527
5001
  server.once("error", (err) => reject(new OniroError("Failed to start the local login callback server.", err)));
4528
- server.listen(0, "127.0.0.1", resolve6);
5002
+ server.listen(0, "127.0.0.1", resolve7);
4529
5003
  });
4530
5004
  const port = server.address().port;
4531
5005
  return {
@@ -4541,12 +5015,12 @@ async function startCallbackServer(opts) {
4541
5015
  return Promise.race([callback, timeout]).finally(() => clearTimeout(timer));
4542
5016
  },
4543
5017
  stop() {
4544
- return new Promise((resolve6) => {
5018
+ return new Promise((resolve7) => {
4545
5019
  server.closeAllConnections?.();
4546
- const timer = setTimeout(resolve6, 100);
5020
+ const timer = setTimeout(resolve7, 100);
4547
5021
  server.close(() => {
4548
5022
  clearTimeout(timer);
4549
- resolve6();
5023
+ resolve7();
4550
5024
  });
4551
5025
  });
4552
5026
  }
@@ -4578,11 +5052,11 @@ function openBrowser(url) {
4578
5052
  windowsHide: true
4579
5053
  });
4580
5054
  child.unref();
4581
- return new Promise((resolve6, reject) => {
5055
+ return new Promise((resolve7, reject) => {
4582
5056
  child.on("error", (err) => reject(new OniroError(`Failed to open a browser (${command}).`, err)));
4583
5057
  child.on(
4584
5058
  "exit",
4585
- (code) => code === 0 ? resolve6() : reject(new OniroError(`Browser launcher '${command}' exited with code ${code}.`))
5059
+ (code) => code === 0 ? resolve7() : reject(new OniroError(`Browser launcher '${command}' exited with code ${code}.`))
4586
5060
  );
4587
5061
  });
4588
5062
  }
@@ -4830,7 +5304,7 @@ function mapAgcError(operation, statusCode, reasonPhrase, body) {
4830
5304
  }
4831
5305
 
4832
5306
  // src/harmonyos/signing/profile.ts
4833
- var fs33 = __toESM(require("fs"), 1);
5307
+ var fs34 = __toESM(require("fs"), 1);
4834
5308
  var import_node_crypto3 = require("crypto");
4835
5309
  var import_node_forge = __toESM(require("node-forge"), 1);
4836
5310
  function extractProfileJson(buffer) {
@@ -4862,7 +5336,7 @@ function extractProfileJson(buffer) {
4862
5336
  function readProvisionProfile(profilePath) {
4863
5337
  let raw;
4864
5338
  try {
4865
- const json = extractProfileJson(fs33.readFileSync(profilePath));
5339
+ const json = extractProfileJson(fs34.readFileSync(profilePath));
4866
5340
  if (!json) return null;
4867
5341
  raw = JSON.parse(json);
4868
5342
  } catch {
@@ -4891,7 +5365,7 @@ function parseCertificate(pem) {
4891
5365
  }
4892
5366
  function keystoreHoldsKey(p12Path, keyAlias, keyPwd, certificate) {
4893
5367
  try {
4894
- const der = import_node_forge.default.util.createBuffer(fs33.readFileSync(p12Path).toString("binary"));
5368
+ const der = import_node_forge.default.util.createBuffer(fs34.readFileSync(p12Path).toString("binary"));
4895
5369
  const p12 = import_node_forge.default.pkcs12.pkcs12FromAsn1(import_node_forge.default.asn1.fromDer(der), keyPwd);
4896
5370
  const certBag = import_node_forge.default.pki.oids.certBag ?? "1.2.840.113549.1.12.10.1.3";
4897
5371
  const wanted = certificate.publicKey.export({ type: "spki", format: "der" });
@@ -4907,7 +5381,7 @@ function keystoreHoldsKey(p12Path, keyAlias, keyPwd, certificate) {
4907
5381
  }
4908
5382
  }
4909
5383
  function verifySigningMaterial(opts) {
4910
- const pems = fs33.readFileSync(opts.cerPath, "utf8").match(/-----BEGIN CERTIFICATE-----[\s\S]*?-----END CERTIFICATE-----/g);
5384
+ const pems = fs34.readFileSync(opts.cerPath, "utf8").match(/-----BEGIN CERTIFICATE-----[\s\S]*?-----END CERTIFICATE-----/g);
4911
5385
  const chain = (pems ?? []).map(parseCertificate);
4912
5386
  const leaf = chain.find((c) => !c.ca) ?? chain.at(-1);
4913
5387
  if (!leaf) throw new AgcError(SIGNING_ERRORS.CERT_INVALID);
@@ -4926,14 +5400,14 @@ function verifySigningMaterial(opts) {
4926
5400
 
4927
5401
  // src/harmonyos/signing/autoSign.ts
4928
5402
  var crypto9 = __toESM(require("crypto"), 1);
4929
- var fs38 = __toESM(require("fs"), 1);
4930
- var path37 = __toESM(require("path"), 1);
5403
+ var fs39 = __toESM(require("fs"), 1);
5404
+ var path38 = __toESM(require("path"), 1);
4931
5405
  var import_json57 = __toESM(require("json5"), 1);
4932
5406
 
4933
5407
  // src/harmonyos/signing/agc.ts
4934
5408
  var crypto6 = __toESM(require("crypto"), 1);
4935
- var fs34 = __toESM(require("fs"), 1);
4936
- var path32 = __toESM(require("path"), 1);
5409
+ var fs35 = __toESM(require("fs"), 1);
5410
+ var path33 = __toESM(require("path"), 1);
4937
5411
  var ENDPOINTS = {
4938
5412
  CERT_LIST: "/api/cps/harmony-cert-manage/v1/cert/list",
4939
5413
  CERT_ADD: "/api/cps/harmony-cert-manage/v1/cert/add",
@@ -5019,8 +5493,8 @@ function createAgcClient(http3, auth) {
5019
5493
  if (target.sha256 && actual !== target.sha256.toLowerCase()) {
5020
5494
  throw new ChecksumMismatchError(target.sha256, actual);
5021
5495
  }
5022
- fs34.mkdirSync(path32.dirname(dest), { recursive: true, mode: 448 });
5023
- fs34.writeFileSync(dest, buffer, { mode: 384 });
5496
+ fs35.mkdirSync(path33.dirname(dest), { recursive: true, mode: 448 });
5497
+ fs35.writeFileSync(dest, buffer, { mode: 384 });
5024
5498
  }
5025
5499
  };
5026
5500
  }
@@ -5071,22 +5545,22 @@ async function registerDevices(agc, connected, logger) {
5071
5545
 
5072
5546
  // src/harmonyos/signing/keystore.ts
5073
5547
  var crypto7 = __toESM(require("crypto"), 1);
5074
- var fs36 = __toESM(require("fs"), 1);
5075
- var path34 = __toESM(require("path"), 1);
5548
+ var fs37 = __toESM(require("fs"), 1);
5549
+ var path35 = __toESM(require("path"), 1);
5076
5550
 
5077
5551
  // src/harmonyos/java.ts
5078
- var fs35 = __toESM(require("fs"), 1);
5079
- var path33 = __toESM(require("path"), 1);
5552
+ var fs36 = __toESM(require("fs"), 1);
5553
+ var path34 = __toESM(require("path"), 1);
5080
5554
  var JAVA_BIN = process.platform === "win32" ? "java.exe" : "java";
5081
5555
  function findJava(sdk, env = process.env) {
5082
5556
  const candidates = [
5083
- env.JAVA_HOME && path33.join(env.JAVA_HOME, "bin", JAVA_BIN),
5084
- sdk && path33.join(sdk.installRoot, "jbr", "bin", JAVA_BIN),
5557
+ env.JAVA_HOME && path34.join(env.JAVA_HOME, "bin", JAVA_BIN),
5558
+ sdk && path34.join(sdk.installRoot, "jbr", "bin", JAVA_BIN),
5085
5559
  // macOS: the JBR is itself a bundle inside Studio's Contents.
5086
- sdk && path33.join(sdk.installRoot, "jbr", "Contents", "Home", "bin", JAVA_BIN),
5087
- ...(env.PATH ?? "").split(path33.delimiter).map((dir) => dir && path33.join(dir, JAVA_BIN))
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))
5088
5562
  ];
5089
- const found = candidates.find((c) => !!c && fs35.existsSync(c));
5563
+ const found = candidates.find((c) => !!c && fs36.existsSync(c));
5090
5564
  if (found) return found;
5091
5565
  throw new OniroError(
5092
5566
  "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."
@@ -5100,13 +5574,13 @@ function redactArgs(args) {
5100
5574
  return args.map((arg, i) => args[i - 1] === "-keyPwd" || args[i - 1] === "-keystorePwd" ? "******" : arg);
5101
5575
  }
5102
5576
  async function generateKeystoreAndCsr(opts) {
5103
- const jar = path34.join(opts.sdk.sdkPath, "default", "openharmony", "toolchains", "lib", "hap-sign-tool.jar");
5104
- if (!fs36.existsSync(jar)) {
5577
+ const jar = path35.join(opts.sdk.sdkPath, "default", "openharmony", "toolchains", "lib", "hap-sign-tool.jar");
5578
+ if (!fs37.existsSync(jar)) {
5105
5579
  throw new OniroError(`hap-sign-tool.jar not found at ${jar}; the HarmonyOS SDK looks incomplete.`);
5106
5580
  }
5107
5581
  const java = findJava(opts.sdk);
5108
5582
  const keyPwd = crypto7.randomBytes(12).toString("base64url").replaceAll(/[-_]/g, "");
5109
- fs36.mkdirSync(path34.dirname(opts.p12Path), { recursive: true, mode: 448 });
5583
+ fs37.mkdirSync(path35.dirname(opts.p12Path), { recursive: true, mode: 448 });
5110
5584
  const run = async (what, args) => {
5111
5585
  const argv = ["-jar", jar, ...args, "-keyAlias", KEY_ALIAS, "-keystoreFile", opts.p12Path, "-keystorePwd", keyPwd, "-keyPwd", keyPwd];
5112
5586
  opts.logger.debug(`[harmonyos] ${java} ${redactArgs(argv).join(" ")}`);
@@ -5120,28 +5594,28 @@ ${detail}` : ""}`);
5120
5594
  opts.logger.info("[harmonyos] Generating the local keystore and certificate signing request...");
5121
5595
  await run("generate the key pair", ["generate-keypair", "-keyAlg", "ECC", "-keySize", "NIST-P-256"]);
5122
5596
  await run("generate the CSR", ["generate-csr", "-subject", "CN=DebugKey", "-signAlg", SIGN_ALG, "-outFile", opts.csrPath]);
5123
- return { keyPwd, csr: fs36.readFileSync(opts.csrPath, "utf8") };
5597
+ return { keyPwd, csr: fs37.readFileSync(opts.csrPath, "utf8") };
5124
5598
  }
5125
5599
 
5126
5600
  // src/harmonyos/signing/paths.ts
5127
5601
  var crypto8 = __toESM(require("crypto"), 1);
5128
- var path35 = __toESM(require("path"), 1);
5602
+ var path36 = __toESM(require("path"), 1);
5129
5603
  function sanitizeNamePart(raw) {
5130
5604
  const safe = (raw?.trim() ?? "").replace(/[\\/:*?"<>|=.-]+/g, "_");
5131
5605
  return safe.slice(0, 64) || "default";
5132
5606
  }
5133
5607
  function resolveSigningMaterialPaths(config, productName, projectRoot) {
5134
5608
  const hash = crypto8.createHash("sha256").update(projectRoot, "utf8").digest("base64url").replaceAll(/[-_]/g, "");
5135
- const base = path35.join(
5609
+ const base = path36.join(
5136
5610
  config.get("harmonyosSigningDir", defaultPaths.harmonyosSigningDir()),
5137
- `${sanitizeNamePart(productName)}_${path35.basename(projectRoot)}_${hash}=`
5611
+ `${sanitizeNamePart(productName)}_${path36.basename(projectRoot)}_${hash}=`
5138
5612
  );
5139
5613
  return { p12Path: `${base}.p12`, csrPath: `${base}.csr`, cerPath: `${base}.cer`, profilePath: `${base}.p7b` };
5140
5614
  }
5141
5615
 
5142
5616
  // src/harmonyos/signing/project.ts
5143
- var fs37 = __toESM(require("fs"), 1);
5144
- var path36 = __toESM(require("path"), 1);
5617
+ var fs38 = __toESM(require("fs"), 1);
5618
+ var path37 = __toESM(require("path"), 1);
5145
5619
  var import_json56 = __toESM(require("json5"), 1);
5146
5620
 
5147
5621
  // src/harmonyos/signing/aclPermissionList.ts
@@ -5209,9 +5683,9 @@ var ACL_PERMISSIONS = /* @__PURE__ */ new Set([
5209
5683
 
5210
5684
  // src/harmonyos/signing/project.ts
5211
5685
  function readRequestedPermissions(moduleConfigPath) {
5212
- if (!fs37.existsSync(moduleConfigPath)) return [];
5686
+ if (!fs38.existsSync(moduleConfigPath)) return [];
5213
5687
  try {
5214
- const parsed = import_json56.default.parse(fs37.readFileSync(moduleConfigPath, "utf-8"));
5688
+ const parsed = import_json56.default.parse(fs38.readFileSync(moduleConfigPath, "utf-8"));
5215
5689
  return (parsed.module?.requestPermissions ?? []).map((p) => p?.name).filter((n) => typeof n === "string" && n.length > 0);
5216
5690
  } catch {
5217
5691
  return [];
@@ -5220,10 +5694,10 @@ function readRequestedPermissions(moduleConfigPath) {
5220
5694
  function collectAclPermissions(projectDir, logger = noopLogger) {
5221
5695
  const requested = /* @__PURE__ */ new Set();
5222
5696
  for (const module2 of listModules({ projectDir })) {
5223
- const moduleRoot = path36.resolve(projectDir, module2.srcPath);
5697
+ const moduleRoot = path37.resolve(projectDir, module2.srcPath);
5224
5698
  for (const sourceSet of ["main", "ohosTest"]) {
5225
5699
  for (const name of readRequestedPermissions(
5226
- path36.join(moduleRoot, "src", sourceSet, "module.json5")
5700
+ path37.join(moduleRoot, "src", sourceSet, "module.json5")
5227
5701
  )) {
5228
5702
  requested.add(name);
5229
5703
  }
@@ -5239,7 +5713,7 @@ function collectAclPermissions(projectDir, logger = noopLogger) {
5239
5713
  // src/harmonyos/signing/autoSign.ts
5240
5714
  function shouldRegenerate(input) {
5241
5715
  if (input.force) return "--force was passed";
5242
- const missing = Object.values(input.paths).find((file) => !fs38.existsSync(file));
5716
+ const missing = Object.values(input.paths).find((file) => !fs39.existsSync(file));
5243
5717
  if (missing) return `${missing} is missing`;
5244
5718
  const config = input.buildProfile.app?.signingConfigs?.find((c) => c.name === input.productName);
5245
5719
  if (config?.type !== "HarmonyOS" || config.material?.profile !== input.paths.profilePath) {
@@ -5278,16 +5752,16 @@ function writeSigningConfig(opts) {
5278
5752
  for (const product of app.products ?? []) {
5279
5753
  if (product.name === opts.productName) product.signingConfig = opts.productName;
5280
5754
  }
5281
- fs38.writeFileSync(opts.buildProfilePath, JSON.stringify(opts.buildProfile, null, 2));
5755
+ fs39.writeFileSync(opts.buildProfilePath, JSON.stringify(opts.buildProfile, null, 2));
5282
5756
  }
5283
5757
  async function harmonyOsAutoSign(opts) {
5284
5758
  const logger = opts.logger ?? noopLogger;
5285
5759
  const productName = opts.productName ?? "default";
5286
- const projectDir = path37.resolve(opts.projectDir);
5287
- const buildProfilePath = path37.join(projectDir, "build-profile.json5");
5760
+ const projectDir = path38.resolve(opts.projectDir);
5761
+ const buildProfilePath = path38.join(projectDir, "build-profile.json5");
5288
5762
  let buildProfile;
5289
5763
  try {
5290
- buildProfile = import_json57.default.parse(fs38.readFileSync(buildProfilePath, "utf-8"));
5764
+ buildProfile = import_json57.default.parse(fs39.readFileSync(buildProfilePath, "utf-8"));
5291
5765
  } catch (err) {
5292
5766
  throw new OniroError(`Could not read ${buildProfilePath}: ${err.message}`, err);
5293
5767
  }
@@ -5327,7 +5801,7 @@ async function harmonyOsAutoSign(opts) {
5327
5801
  logger.info(`[harmonyos] Regenerating signing material: ${reason}.`);
5328
5802
  const agc = createAgcClient(opts.http ?? createHttpClient({ logger }), auth);
5329
5803
  const deviceIds = await registerDevices(agc, connected, logger);
5330
- for (const file of Object.values(paths)) fs38.rmSync(file, { force: true });
5804
+ for (const file of Object.values(paths)) fs39.rmSync(file, { force: true });
5331
5805
  const certName = `oniro_debug_${auth.teamId.replace(/[\\/.:]/g, "")}.cer`;
5332
5806
  const stale = (await agc.listCertificates()).find((c) => c.certName === certName);
5333
5807
  if (stale) {
@@ -5358,10 +5832,10 @@ async function harmonyOsAutoSign(opts) {
5358
5832
  try {
5359
5833
  verifySigningMaterial({ ...paths, keyAlias: KEY_ALIAS, keyPwd });
5360
5834
  } catch (err) {
5361
- fs38.rmSync(paths.profilePath, { force: true });
5835
+ fs39.rmSync(paths.profilePath, { force: true });
5362
5836
  throw err;
5363
5837
  }
5364
- const materialDir = path37.join(path37.dirname(paths.p12Path), "material");
5838
+ const materialDir = path38.join(path38.dirname(paths.p12Path), "material");
5365
5839
  try {
5366
5840
  getKey(materialDir);
5367
5841
  } catch {
@@ -5373,7 +5847,7 @@ async function harmonyOsAutoSign(opts) {
5373
5847
  }
5374
5848
 
5375
5849
  // src/index.ts
5376
- var VERSION = "0.13.0";
5850
+ var VERSION = "0.14.0";
5377
5851
  // Annotate the CommonJS export names for ESM import in node:
5378
5852
  0 && (module.exports = {
5379
5853
  ALL_SDKS,
@@ -5396,7 +5870,9 @@ var VERSION = "0.13.0";
5396
5870
  TargetNotFoundError,
5397
5871
  UnsupportedPlatformError,
5398
5872
  VERSION,
5873
+ appSandboxDir,
5399
5874
  applyChanges,
5875
+ assertDevicePath,
5400
5876
  assertLayoutSelector,
5401
5877
  attemptHdcConnection,
5402
5878
  buildCmdWrapper,
@@ -5407,6 +5883,8 @@ var VERSION = "0.13.0";
5407
5883
  buildInputCommand,
5408
5884
  buildRawTouchCommand,
5409
5885
  captureBurst,
5886
+ captureLog,
5887
+ cleanCodelinterOutput,
5410
5888
  consoleLogger,
5411
5889
  createHarmonyOsSession,
5412
5890
  createInstallTempDir,
@@ -5422,9 +5900,12 @@ var VERSION = "0.13.0";
5422
5900
  defaultPaths,
5423
5901
  describeLayoutNode,
5424
5902
  detectAaStartFailure,
5903
+ detectCmdToolsFlavour,
5904
+ detectInstallFailure,
5425
5905
  detectProjectSdkVersion,
5426
5906
  detectRuntimeOs,
5427
5907
  detectSigningConfigNames,
5908
+ diagnoseBuildFailure,
5428
5909
  diffEntryNames,
5429
5910
  diffHapAssets,
5430
5911
  discoverHaps,
@@ -5437,11 +5918,13 @@ var VERSION = "0.13.0";
5437
5918
  dumpWindow,
5438
5919
  encryptPwd,
5439
5920
  ensureFreeSpace,
5921
+ ensureInstallOk,
5440
5922
  ensureOhModules,
5441
5923
  ensureOk,
5442
5924
  ensureRemoteOk,
5443
5925
  escapeCmdArgument,
5444
5926
  escapeCmdCommand,
5927
+ existingHapOrUnsigned,
5445
5928
  extractTarball,
5446
5929
  extractZipWithProgress,
5447
5930
  findAppProcessId,
@@ -5450,6 +5933,7 @@ var VERSION = "0.13.0";
5450
5933
  findHarmonyOsSdk,
5451
5934
  findKeyboardWindow,
5452
5935
  findLayoutNodes,
5936
+ findMissingProjectSdk,
5453
5937
  findRunningProcess,
5454
5938
  flattenLayout,
5455
5939
  forceStop,
@@ -5463,6 +5947,7 @@ var VERSION = "0.13.0";
5463
5947
  getDeviceInfo,
5464
5948
  getDisplaySize,
5465
5949
  getEmulatorDir,
5950
+ getEmulatorStatus,
5466
5951
  getFreeSpaceBytes,
5467
5952
  getHdcPath,
5468
5953
  getHvigorwPath,
@@ -5478,15 +5963,21 @@ var VERSION = "0.13.0";
5478
5963
  getSupportedSdksForUi,
5479
5964
  harmonyOsAutoSign,
5480
5965
  harmonyOsSdkLabelForApi,
5966
+ hdcClientFailure,
5481
5967
  hdcExec,
5968
+ hdcTargetArgs,
5969
+ hdcTargetArgsFor,
5482
5970
  hideKeyboard,
5483
5971
  installApp,
5484
5972
  installCmdTools,
5485
5973
  installEmulator,
5974
+ installFailureReason,
5975
+ isBundleInstalled,
5486
5976
  isCmdToolsInstalled,
5487
5977
  isEmulatorInstalled,
5488
5978
  isOutOfSpaceError,
5489
5979
  isRamBackedPath,
5980
+ isUnsignedHap,
5490
5981
  isValidBundleName,
5491
5982
  isValidProjectName,
5492
5983
  killProcessTree,
@@ -5501,16 +5992,21 @@ var VERSION = "0.13.0";
5501
5992
  nonInteractivePrompter,
5502
5993
  noopLogger,
5503
5994
  noopProgress,
5995
+ normaliseLocalPath,
5504
5996
  paramGet,
5997
+ paramGetMany,
5505
5998
  paramSet,
5506
5999
  parseBounds,
5507
6000
  parseCodelinterFindings,
6001
+ parseCodelinterReport,
5508
6002
  parseDeviceList,
5509
6003
  parseHilogLine,
5510
6004
  parseImeBundles,
5511
6005
  parseMainAbilityFromBmDump,
6006
+ parseParamValues,
5512
6007
  parseRemoteExit,
5513
6008
  performInput,
6009
+ pickHap,
5514
6010
  pickSigningKind,
5515
6011
  pickTarget,
5516
6012
  prepareSigning,
@@ -5525,6 +6021,7 @@ var VERSION = "0.13.0";
5525
6021
  removeSdk,
5526
6022
  removeTempWorkDir,
5527
6023
  requireHarmonyOsSdk,
6024
+ resolveDeviceSerial,
5528
6025
  resolveKeyEvent,
5529
6026
  resolveTmpRoot,
5530
6027
  runCodelinter,
@@ -5549,6 +6046,7 @@ var VERSION = "0.13.0";
5549
6046
  toLongPath,
5550
6047
  toSpaceError,
5551
6048
  uninstallApp,
6049
+ unsignedHapRejection,
5552
6050
  validateTemplateLayout,
5553
6051
  verifySha256,
5554
6052
  waitForBoot,