@oniroproject/core 0.6.1 → 0.7.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
@@ -36,32 +36,59 @@ __export(index_exports, {
36
36
  CancelledError: () => CancelledError,
37
37
  ChecksumMismatchError: () => ChecksumMismatchError,
38
38
  CmdToolsNotInstalledError: () => CmdToolsNotInstalledError,
39
+ CommandFailedError: () => CommandFailedError,
39
40
  OHOS_URL_BASE: () => OHOS_URL_BASE,
40
41
  OniroError: () => OniroError,
41
42
  SdkNotInstalledError: () => SdkNotInstalledError,
42
43
  UnsupportedPlatformError: () => UnsupportedPlatformError,
43
44
  VERSION: () => VERSION,
45
+ applyChanges: () => applyChanges,
44
46
  attemptHdcConnection: () => attemptHdcConnection,
47
+ buildGestureCommands: () => buildGestureCommands,
48
+ buildHap: () => buildHap,
49
+ buildHvigorwArgs: () => buildHvigorwArgs,
50
+ buildInputCommand: () => buildInputCommand,
51
+ buildRawTouchCommand: () => buildRawTouchCommand,
52
+ captureBurst: () => captureBurst,
45
53
  consoleLogger: () => consoleLogger,
46
54
  createMaterial: () => createMaterial,
47
55
  createScaffold: () => createScaffold,
56
+ decideInstallMethod: () => decideInstallMethod,
57
+ decideReboot: () => decideReboot,
48
58
  decryptPwd: () => decryptPwd,
59
+ dedupEntries: () => dedupEntries,
49
60
  defaultContext: () => defaultContext,
50
61
  defaultPaths: () => defaultPaths,
62
+ detectAaStartFailure: () => detectAaStartFailure,
51
63
  detectProjectSdkVersion: () => detectProjectSdkVersion,
64
+ detectSigningConfigNames: () => detectSigningConfigNames,
65
+ diffEntryNames: () => diffEntryNames,
66
+ diffHapAssets: () => diffHapAssets,
67
+ discoverHaps: () => discoverHaps,
52
68
  downloadAndInstallSdk: () => downloadAndInstallSdk,
53
69
  downloadFile: () => downloadFile,
70
+ dumpLayout: () => dumpLayout,
71
+ dumpLog: () => dumpLog,
72
+ dumpRenderService: () => dumpRenderService,
73
+ dumpScreen: () => dumpScreen,
74
+ dumpWindow: () => dumpWindow,
54
75
  encryptPwd: () => encryptPwd,
76
+ ensureOhModules: () => ensureOhModules,
77
+ ensureOk: () => ensureOk,
55
78
  extractTarball: () => extractTarball,
56
79
  extractZipWithProgress: () => extractZipWithProgress,
57
80
  findAppProcessId: () => findAppProcessId,
58
81
  findCmdToolsSourceDir: () => findCmdToolsSourceDir,
82
+ findRunningProcess: () => findRunningProcess,
83
+ forceStop: () => forceStop,
59
84
  generateSigningConfigs: () => generateSigningConfigs,
60
85
  getBundleName: () => getBundleName,
61
86
  getCmdToolsBin: () => getCmdToolsBin,
62
87
  getCmdToolsDownloadUrl: () => getCmdToolsDownloadUrl,
63
88
  getCmdToolsPath: () => getCmdToolsPath,
64
89
  getCmdToolsStatus: () => getCmdToolsStatus,
90
+ getDeviceInfo: () => getDeviceInfo,
91
+ getDisplaySize: () => getDisplaySize,
65
92
  getEmulatorDir: () => getEmulatorDir,
66
93
  getHdcPath: () => getHdcPath,
67
94
  getHvigorwPath: () => getHvigorwPath,
@@ -69,10 +96,12 @@ __export(index_exports, {
69
96
  getKey: () => getKey,
70
97
  getMainAbility: () => getMainAbility,
71
98
  getOhosBaseSdkHome: () => getOhosBaseSdkHome,
99
+ getOhpmPath: () => getOhpmPath,
72
100
  getOsFolder: () => getOsFolder,
73
101
  getSdkFilename: () => getSdkFilename,
74
102
  getSdkRootDir: () => getSdkRootDir,
75
103
  getSupportedSdksForUi: () => getSupportedSdksForUi,
104
+ hdcExec: () => hdcExec,
76
105
  installApp: () => installApp,
77
106
  installCmdTools: () => installCmdTools,
78
107
  installEmulator: () => installEmulator,
@@ -81,25 +110,55 @@ __export(index_exports, {
81
110
  isValidBundleName: () => isValidBundleName,
82
111
  isValidProjectName: () => isValidProjectName,
83
112
  launchApp: () => launchApp,
113
+ listAbilities: () => listAbilities,
114
+ listDevices: () => listDevices,
115
+ listModules: () => listModules,
84
116
  listRunningProcesses: () => listRunningProcesses,
85
117
  listTemplates: () => listTemplates,
86
118
  nonInteractivePrompter: () => nonInteractivePrompter,
87
119
  noopLogger: () => noopLogger,
88
120
  noopProgress: () => noopProgress,
121
+ paramGet: () => paramGet,
122
+ paramSet: () => paramSet,
123
+ parseBounds: () => parseBounds,
124
+ parseCodelinterFindings: () => parseCodelinterFindings,
125
+ parseDeviceList: () => parseDeviceList,
89
126
  parseHilogLine: () => parseHilogLine,
127
+ pickSigningKind: () => pickSigningKind,
128
+ prepareSigning: () => prepareSigning,
129
+ pruneLayout: () => pruneLayout,
90
130
  readJson5File: () => readJson5File,
91
131
  readJsonFile: () => readJsonFile,
132
+ reboot: () => reboot,
133
+ recvFile: () => recvFile,
92
134
  removeCmdTools: () => removeCmdTools,
93
135
  removeEmulator: () => removeEmulator,
94
136
  removeSdk: () => removeSdk,
137
+ runCodelinter: () => runCodelinter,
95
138
  runHvigorw: () => runHvigorw,
139
+ runOhpm: () => runOhpm,
140
+ runProcess: () => runProcess,
141
+ scopedLogger: () => scopedLogger,
142
+ selectDevice: () => selectDevice,
143
+ sendFile: () => sendFile,
144
+ sendGesture: () => sendGesture,
145
+ sendInput: () => sendInput,
146
+ sendRawTouch: () => sendRawTouch,
96
147
  setHilogLevel: () => setHilogLevel,
148
+ shell: () => shell,
97
149
  startEmulator: () => startEmulator,
98
150
  staticConfig: () => staticConfig,
99
151
  stopEmulator: () => stopEmulator,
100
152
  streamHilog: () => streamHilog,
153
+ takeScreenshot: () => takeScreenshot,
154
+ uninstallApp: () => uninstallApp,
101
155
  validateTemplateLayout: () => validateTemplateLayout,
102
156
  verifySha256: () => verifySha256,
157
+ waitForBoot: () => waitForBoot,
158
+ waitForBundle: () => waitForBundle,
159
+ waitForCondition: () => waitForCondition,
160
+ waitForLog: () => waitForLog,
161
+ watchLog: () => watchLog,
103
162
  writeJson5File: () => writeJson5File,
104
163
  writeJsonFile: () => writeJsonFile
105
164
  });
@@ -122,6 +181,15 @@ var consoleLogger = {
122
181
  warn: (m) => console.warn(m),
123
182
  error: (m) => console.error(m)
124
183
  };
184
+ function scopedLogger(logger, scope) {
185
+ const prefix = `[${scope}] `;
186
+ return {
187
+ debug: (m) => logger.debug(prefix + m),
188
+ info: (m) => logger.info(prefix + m),
189
+ warn: (m) => logger.warn(prefix + m),
190
+ error: (m) => logger.error(prefix + m)
191
+ };
192
+ }
125
193
 
126
194
  // src/ports/progress.ts
127
195
  var noopProgress = {
@@ -226,6 +294,19 @@ var CancelledError = class extends OniroError {
226
294
  this.name = "CancelledError";
227
295
  }
228
296
  };
297
+ var CommandFailedError = class extends OniroError {
298
+ constructor(command, exitCode, stderr) {
299
+ super(`Command failed (exit ${exitCode}): ${command}${stderr ? `
300
+ ${stderr}` : ""}`);
301
+ this.command = command;
302
+ this.exitCode = exitCode;
303
+ this.stderr = stderr;
304
+ this.name = "CommandFailedError";
305
+ }
306
+ command;
307
+ exitCode;
308
+ stderr;
309
+ };
229
310
 
230
311
  // src/ports/context.ts
231
312
  function defaultContext(overrides = {}) {
@@ -237,15 +318,16 @@ function defaultContext(overrides = {}) {
237
318
 
238
319
  // src/sdk/constants.ts
239
320
  var ALL_SDKS = [
240
- { version: "4.0", api: "10" },
241
- { version: "4.1", api: "11" },
242
- { version: "5.0.0", api: "12" },
243
- { version: "5.0.1", api: "13" },
244
- { version: "5.0.2", api: "14" },
245
- { version: "5.0.3", api: "15" },
246
- { version: "5.1.0", api: "18" },
247
- { version: "6.0", api: "20" },
248
- { version: "6.1", api: "23" }
321
+ { version: "4.0", api: "10", tarballStrip: { linuxWindows: 1, darwin: 3 } },
322
+ { version: "4.1", api: "11", tarballStrip: { linuxWindows: 1, darwin: 3 } },
323
+ { version: "5.0.0", api: "12", tarballStrip: { linuxWindows: 0, darwin: 3 } },
324
+ { version: "5.0.1", api: "13", tarballStrip: { linuxWindows: 0, darwin: 3 } },
325
+ { version: "5.0.2", api: "14", tarballStrip: { linuxWindows: 1, darwin: 3 } },
326
+ { version: "5.0.3", api: "15", tarballStrip: { linuxWindows: 1, darwin: 3 } },
327
+ { version: "5.1.0", api: "18", tarballStrip: { linuxWindows: 1, darwin: 3 } },
328
+ { version: "5.1.1", api: "19", tarballStrip: { linuxWindows: 1, darwin: 3 } },
329
+ { version: "6.0", api: "20", tarballStrip: { linuxWindows: 0, darwin: 3 } },
330
+ { version: "6.1", api: "23", tarballStrip: { linuxWindows: 0, darwin: 3 } }
249
331
  ];
250
332
  var OHOS_URL_BASE = "https://repo.huaweicloud.com/openharmony/os";
251
333
 
@@ -261,19 +343,18 @@ function getOsFolder() {
261
343
  function getSdkFilename(version) {
262
344
  const platform6 = os2.platform();
263
345
  const v = version ?? ALL_SDKS[ALL_SDKS.length - 1].version;
346
+ const strips = ALL_SDKS.find((r) => r.version === v)?.tarballStrip ?? { linuxWindows: 1, darwin: 3 };
264
347
  if (platform6 === "linux") {
265
- const strip = v === "5.0.0" || v === "5.0.1" || v === "6.0" || v === "6.1" ? 0 : 1;
266
- return { filename: "ohos-sdk-windows_linux-public.tar.gz", osFolder: "linux", strip };
348
+ return { filename: "ohos-sdk-windows_linux-public.tar.gz", osFolder: "linux", strip: strips.linuxWindows };
267
349
  }
268
350
  if (platform6 === "darwin") {
269
351
  if (os2.arch() === "arm64") {
270
- return { filename: "L2-SDK-MAC-M1-PUBLIC.tar.gz", osFolder: "darwin", strip: 3 };
352
+ return { filename: "L2-SDK-MAC-M1-PUBLIC.tar.gz", osFolder: "darwin", strip: strips.darwin };
271
353
  }
272
- return { filename: "ohos-sdk-mac-public.tar.gz", osFolder: "darwin", strip: 3 };
354
+ return { filename: "ohos-sdk-mac-public.tar.gz", osFolder: "darwin", strip: strips.darwin };
273
355
  }
274
356
  if (platform6 === "win32") {
275
- const strip = v === "5.0.0" || v === "5.0.1" || v === "6.0" || v === "6.1" ? 0 : 1;
276
- return { filename: "ohos-sdk-windows_linux-public.tar.gz", osFolder: "windows", strip };
357
+ return { filename: "ohos-sdk-windows_linux-public.tar.gz", osFolder: "windows", strip: strips.linuxWindows };
277
358
  }
278
359
  throw new UnsupportedPlatformError(platform6);
279
360
  }
@@ -300,17 +381,20 @@ function pickExisting(candidates) {
300
381
  }
301
382
  return candidates[0];
302
383
  }
303
- function getCmdToolsBin(config) {
384
+ function getCmdToolsBin(config, name = "ohpm") {
304
385
  const binDir = path2.join(getCmdToolsPath(config), "bin");
305
386
  if (os3.platform() === "win32") {
306
387
  return pickExisting([
307
- path2.join(binDir, "ohpm.exe"),
308
- path2.join(binDir, "ohpm.cmd"),
309
- path2.join(binDir, "ohpm.bat"),
310
- path2.join(binDir, "ohpm")
388
+ path2.join(binDir, `${name}.exe`),
389
+ path2.join(binDir, `${name}.cmd`),
390
+ path2.join(binDir, `${name}.bat`),
391
+ path2.join(binDir, name)
311
392
  ]);
312
393
  }
313
- return path2.join(binDir, "ohpm");
394
+ return path2.join(binDir, name);
395
+ }
396
+ function getOhpmPath(config) {
397
+ return getCmdToolsBin(config, "ohpm");
314
398
  }
315
399
  function getHdcPath(config) {
316
400
  const base = path2.join(getCmdToolsPath(config), "sdk", "default", "openharmony", "toolchains");
@@ -325,15 +409,20 @@ function getHdcPath(config) {
325
409
  return path2.join(base, "hdc");
326
410
  }
327
411
  function getHvigorwPath(config, projectDir) {
328
- const cmdToolsBin = path2.join(getCmdToolsPath(config), "bin");
329
- return pickExisting([
412
+ const local = pickExisting([
330
413
  path2.join(projectDir, "hvigorw"),
331
414
  path2.join(projectDir, "hvigorw.bat"),
332
- path2.join(projectDir, "hvigorw.cmd"),
333
- path2.join(cmdToolsBin, "hvigorw"),
334
- path2.join(cmdToolsBin, "hvigorw.bat"),
335
- path2.join(cmdToolsBin, "hvigorw.cmd")
415
+ path2.join(projectDir, "hvigorw.cmd")
336
416
  ]);
417
+ if (fs.existsSync(local) && isProjectLocalHvigorwWorking(projectDir)) {
418
+ return local;
419
+ }
420
+ return getCmdToolsBin(config, "hvigorw");
421
+ }
422
+ function isProjectLocalHvigorwWorking(projectDir) {
423
+ const wrapper = path2.join(projectDir, "hvigor", "hvigor-wrapper.js");
424
+ const nodeModules = path2.join(projectDir, "hvigor", "node_modules");
425
+ return fs.existsSync(wrapper) && fs.existsSync(nodeModules);
337
426
  }
338
427
 
339
428
  // src/sdk/detectProjectSdk.ts
@@ -589,22 +678,37 @@ function removeSdk(config, api) {
589
678
  }
590
679
 
591
680
  // src/sdk/install.ts
592
- var fs7 = __toESM(require("fs"), 1);
681
+ var fs8 = __toESM(require("fs"), 1);
593
682
  var os5 = __toESM(require("os"), 1);
594
683
  var path7 = __toESM(require("path"), 1);
684
+
685
+ // src/sdk/move.ts
686
+ var fs7 = __toESM(require("fs"), 1);
687
+ function movePath(src, dest) {
688
+ try {
689
+ fs7.renameSync(src, dest);
690
+ } catch (err) {
691
+ if (err.code !== "EXDEV") throw err;
692
+ fs7.cpSync(src, dest, { recursive: true });
693
+ fs7.rmSync(src, { recursive: true, force: true });
694
+ }
695
+ }
696
+
697
+ // src/sdk/install.ts
595
698
  async function downloadAndInstallSdk(opts) {
596
699
  const { config, version, api, progress, abortSignal } = opts;
597
700
  const logger = opts.logger ?? noopLogger;
598
701
  const { filename, osFolder, strip } = getSdkFilename(version);
599
- const downloadUrl = `${OHOS_URL_BASE}/${version}-Release/${filename}`;
702
+ const urlBase = config.get("sdkUrlBase", OHOS_URL_BASE);
703
+ const downloadUrl = `${urlBase}/${version}-Release/${filename}`;
600
704
  const sha256Url = `${downloadUrl}.sha256`;
601
- const tmpDir = fs7.mkdtempSync(path7.join(os5.tmpdir(), "oniro-sdk-"));
705
+ const tmpDir = fs8.mkdtempSync(path7.join(os5.tmpdir(), "oniro-sdk-"));
602
706
  const tarPath = path7.join(tmpDir, filename);
603
707
  const sha256Path = path7.join(tmpDir, `${filename}.sha256`);
604
708
  const extractDir = path7.join(tmpDir, "extract");
605
- fs7.mkdirSync(extractDir);
709
+ fs8.mkdirSync(extractDir);
606
710
  const sdkInstallDir = path7.join(getSdkRootDir(config), osFolder, api);
607
- fs7.mkdirSync(path7.dirname(sdkInstallDir), { recursive: true });
711
+ fs8.mkdirSync(path7.dirname(sdkInstallDir), { recursive: true });
608
712
  const checkCancelled = () => {
609
713
  if (abortSignal?.aborted) throw new CancelledError();
610
714
  };
@@ -622,12 +726,12 @@ async function downloadAndInstallSdk(opts) {
622
726
  progress?.report({ message: "Extracting SDK (this may take a while)...", increment: 10 });
623
727
  checkCancelled();
624
728
  const osContentPath = path7.join(extractDir, osFolder);
625
- if (!fs7.existsSync(osContentPath)) {
729
+ if (!fs8.existsSync(osContentPath)) {
626
730
  throw new OniroError(
627
731
  `Expected folder '${osFolder}' not found in extracted SDK. Tarball layout may have changed.`
628
732
  );
629
733
  }
630
- const zipFiles = fs7.readdirSync(osContentPath).filter((n2) => n2.endsWith(".zip"));
734
+ const zipFiles = fs8.readdirSync(osContentPath).filter((n2) => n2.endsWith(".zip"));
631
735
  const componentsStart = 60;
632
736
  const componentsBudget = 35;
633
737
  const n = zipFiles.length;
@@ -647,26 +751,26 @@ async function downloadAndInstallSdk(opts) {
647
751
  if (rem > 0) rem--;
648
752
  await extractZipWithProgress({ zipPath, dest: osContentPath, progress, start: cursor, range: thisBudget, logger });
649
753
  cursor += thisBudget;
650
- fs7.unlinkSync(zipPath);
754
+ fs8.unlinkSync(zipPath);
651
755
  }
652
756
  progress?.report({ message: "Finalizing installation...", increment: 0 });
653
- if (fs7.existsSync(sdkInstallDir)) {
654
- fs7.rmSync(sdkInstallDir, { recursive: true, force: true });
757
+ if (fs8.existsSync(sdkInstallDir)) {
758
+ fs8.rmSync(sdkInstallDir, { recursive: true, force: true });
655
759
  }
656
- fs7.renameSync(osContentPath, sdkInstallDir);
760
+ movePath(osContentPath, sdkInstallDir);
657
761
  progress?.report({ message: "Finalizing installation...", increment: 3 });
658
762
  progress?.report({ message: "Cleaning up...", increment: 0 });
659
- fs7.rmSync(tmpDir, { recursive: true, force: true });
763
+ fs8.rmSync(tmpDir, { recursive: true, force: true });
660
764
  progress?.report({ message: "Cleaning up...", increment: 2 });
661
765
  } catch (err) {
662
766
  logger.error(`SDK install failed: ${err instanceof Error ? err.message : String(err)}`);
663
- fs7.rmSync(tmpDir, { recursive: true, force: true });
767
+ fs8.rmSync(tmpDir, { recursive: true, force: true });
664
768
  throw err;
665
769
  }
666
770
  }
667
771
 
668
772
  // src/cmdTools/install.ts
669
- var fs8 = __toESM(require("fs"), 1);
773
+ var fs9 = __toESM(require("fs"), 1);
670
774
  var os6 = __toESM(require("os"), 1);
671
775
  var path8 = __toESM(require("path"), 1);
672
776
  var import_node_child_process = require("child_process");
@@ -701,11 +805,11 @@ function findCmdToolsSourceDir(extractPath) {
701
805
  path8.join(extractPath, "commandline-tools")
702
806
  ];
703
807
  for (const c of known) {
704
- if (fs8.existsSync(c)) return c;
808
+ if (fs9.existsSync(c)) return c;
705
809
  }
706
- const entries = fs8.readdirSync(extractPath, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => path8.join(extractPath, e.name));
810
+ const entries = fs9.readdirSync(extractPath, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => path8.join(extractPath, e.name));
707
811
  for (const dir of entries) {
708
- if (fs8.existsSync(path8.join(dir, "bin"))) return dir;
812
+ if (fs9.existsSync(path8.join(dir, "bin"))) return dir;
709
813
  }
710
814
  throw new OniroError("Could not locate command line tools folder in the extracted archive.");
711
815
  }
@@ -713,7 +817,7 @@ async function installCmdTools(opts) {
713
817
  const { config, progress, abortSignal } = opts;
714
818
  const logger = opts.logger ?? noopLogger;
715
819
  const CMD_PATH = getCmdToolsPath(config);
716
- const tmpDir = fs8.mkdtempSync(path8.join(os6.tmpdir(), "oniro-cmdtools-"));
820
+ const tmpDir = fs9.mkdtempSync(path8.join(os6.tmpdir(), "oniro-cmdtools-"));
717
821
  const zipPath = opts.localZipPath ?? path8.join(tmpDir, "oh-command-line-tools.zip");
718
822
  const extractPath = path8.join(tmpDir, "oh-command-line-tools");
719
823
  try {
@@ -724,42 +828,42 @@ async function installCmdTools(opts) {
724
828
  }
725
829
  progress?.report({ message: "Extracting tools...", increment: 0 });
726
830
  await extractZipWithProgress({ zipPath, dest: extractPath, progress, start: 50, range: 45, logger });
727
- fs8.mkdirSync(CMD_PATH, { recursive: true });
831
+ fs9.mkdirSync(CMD_PATH, { recursive: true });
728
832
  const srcDir = findCmdToolsSourceDir(extractPath);
729
- for (const entry of fs8.readdirSync(srcDir)) {
833
+ for (const entry of fs9.readdirSync(srcDir)) {
730
834
  const src = path8.join(srcDir, entry);
731
835
  const dest = path8.join(CMD_PATH, entry);
732
- if (fs8.statSync(src).isDirectory()) {
733
- if (fs8.existsSync(dest)) fs8.rmSync(dest, { recursive: true, force: true });
734
- fs8.renameSync(src, dest);
836
+ if (fs9.statSync(src).isDirectory()) {
837
+ if (fs9.existsSync(dest)) fs9.rmSync(dest, { recursive: true, force: true });
838
+ movePath(src, dest);
735
839
  } else {
736
- fs8.copyFileSync(src, dest);
840
+ fs9.copyFileSync(src, dest);
737
841
  }
738
842
  }
739
843
  const binDir = path8.join(CMD_PATH, "bin");
740
- if (fs8.existsSync(binDir) && os6.platform() !== "win32") {
741
- for (const file of fs8.readdirSync(binDir)) {
742
- fs8.chmodSync(path8.join(binDir, file), 493);
844
+ if (fs9.existsSync(binDir) && os6.platform() !== "win32") {
845
+ for (const file of fs9.readdirSync(binDir)) {
846
+ fs9.chmodSync(path8.join(binDir, file), 493);
743
847
  }
744
848
  }
745
849
  progress?.report({ message: "Finalizing installation...", increment: 5 });
746
850
  progress?.report({ message: "Cleaning up...", increment: 0 });
747
851
  } finally {
748
- fs8.rmSync(tmpDir, { recursive: true, force: true });
852
+ fs9.rmSync(tmpDir, { recursive: true, force: true });
749
853
  }
750
854
  }
751
855
  function getCmdToolsStatus(config) {
752
856
  const cmdPath = getCmdToolsPath(config);
753
857
  const binDir = path8.join(cmdPath, "bin");
754
858
  const candidates = os6.platform() === "win32" ? ["ohpm.exe", "ohpm.cmd", "ohpm.bat", "ohpm"] : ["ohpm"];
755
- const bin = candidates.map((c) => path8.join(binDir, c)).find((p) => fs8.existsSync(p));
859
+ const bin = candidates.map((c) => path8.join(binDir, c)).find((p) => fs9.existsSync(p));
756
860
  if (!bin) {
757
861
  return { installed: false, status: "Not installed" };
758
862
  }
759
863
  try {
760
864
  const versionFile = path8.join(cmdPath, "version.txt");
761
- if (fs8.existsSync(versionFile)) {
762
- const content = fs8.readFileSync(versionFile, "utf8");
865
+ if (fs9.existsSync(versionFile)) {
866
+ const content = fs9.readFileSync(versionFile, "utf8");
763
867
  for (const line of content.split(/\r?\n/)) {
764
868
  const m = line.match(/^\s*#\s*Version:\s*(.+)$/);
765
869
  if (m?.[1]) return { installed: true, status: `Installed (${m[1].trim()})` };
@@ -779,13 +883,13 @@ function isCmdToolsInstalled(config) {
779
883
  }
780
884
  function removeCmdTools(config) {
781
885
  const cmdPath = getCmdToolsPath(config);
782
- if (fs8.existsSync(cmdPath)) {
783
- fs8.rmSync(cmdPath, { recursive: true, force: true });
886
+ if (fs9.existsSync(cmdPath)) {
887
+ fs9.rmSync(cmdPath, { recursive: true, force: true });
784
888
  }
785
889
  }
786
890
 
787
891
  // src/sign/encryptKey.ts
788
- var fs9 = __toESM(require("fs"), 1);
892
+ var fs10 = __toESM(require("fs"), 1);
789
893
  var path9 = __toESM(require("path"), 1);
790
894
  var crypto2 = __toESM(require("crypto"), 1);
791
895
  var DAEMON_ROOT_KEY_COMPONENT_LENGTH = 16;
@@ -859,8 +963,8 @@ function createAndStoreKey(dir, keyLength) {
859
963
  const key = crypto2.randomBytes(keyLength);
860
964
  const hash = crypto2.createHash("sha256").update(key).digest("hex");
861
965
  const filePath = path9.join(dir, hash);
862
- fs9.writeFileSync(filePath, key);
863
- fs9.chmodSync(filePath, KEY_FILE_PERMISSIONS);
966
+ fs10.writeFileSync(filePath, key);
967
+ fs10.chmodSync(filePath, KEY_FILE_PERMISSIONS);
864
968
  return key;
865
969
  }
866
970
  function createAndStoreEnKey(rootKey, ceDir) {
@@ -868,23 +972,23 @@ function createAndStoreEnKey(rootKey, ceDir) {
868
972
  const encrypted = encrypt(rootKey, workKey);
869
973
  const hash = crypto2.createHash("sha256").update(encrypted).digest("hex");
870
974
  const filePath = path9.join(ceDir, hash);
871
- fs9.writeFileSync(filePath, encrypted);
872
- fs9.chmodSync(filePath, KEY_FILE_PERMISSIONS);
975
+ fs10.writeFileSync(filePath, encrypted);
976
+ fs10.chmodSync(filePath, KEY_FILE_PERMISSIONS);
873
977
  }
874
978
  function createMaterial(materialPath) {
875
- fs9.mkdirSync(materialPath, { recursive: true });
876
- fs9.chmodSync(materialPath, KEY_FILE_DIRECTORY_PERMISSIONS);
979
+ fs10.mkdirSync(materialPath, { recursive: true });
980
+ fs10.chmodSync(materialPath, KEY_FILE_DIRECTORY_PERMISSIONS);
877
981
  const fdDir = path9.join(materialPath, "fd");
878
982
  const acDir = path9.join(materialPath, "ac");
879
983
  const ceDir = path9.join(materialPath, "ce");
880
- fs9.mkdirSync(fdDir, { recursive: true });
881
- fs9.mkdirSync(acDir, { recursive: true });
882
- fs9.mkdirSync(ceDir, { recursive: true });
984
+ fs10.mkdirSync(fdDir, { recursive: true });
985
+ fs10.mkdirSync(acDir, { recursive: true });
986
+ fs10.mkdirSync(ceDir, { recursive: true });
883
987
  const fdSubDirs = ["0", "1", "2"];
884
988
  const fdComponents = [];
885
989
  for (const sub of fdSubDirs) {
886
990
  const subDir = path9.join(fdDir, sub);
887
- fs9.mkdirSync(subDir, { recursive: true });
991
+ fs10.mkdirSync(subDir, { recursive: true });
888
992
  const comp = createAndStoreKey(subDir, DAEMON_ROOT_KEY_COMPONENT_LENGTH);
889
993
  fdComponents.push(comp);
890
994
  }
@@ -893,17 +997,17 @@ function createMaterial(materialPath) {
893
997
  createAndStoreEnKey(rootKey, ceDir);
894
998
  }
895
999
  function readSingleFile(dir) {
896
- if (!fs9.existsSync(dir) || !fs9.statSync(dir).isDirectory()) {
1000
+ if (!fs10.existsSync(dir) || !fs10.statSync(dir).isDirectory()) {
897
1001
  throw new Error(`Missing signing material directory: ${dir}`);
898
1002
  }
899
- const files = fs9.readdirSync(dir).filter((f) => f !== ".DS_Store");
1003
+ const files = fs10.readdirSync(dir).filter((f) => f !== ".DS_Store");
900
1004
  if (files.length !== 1) {
901
1005
  throw new Error(`Signing material in ${dir} is illegal (expected exactly one file).`);
902
1006
  }
903
- return fs9.readFileSync(path9.join(dir, files[0]));
1007
+ return fs10.readFileSync(path9.join(dir, files[0]));
904
1008
  }
905
1009
  function getKey(materialPath) {
906
- if (!fs9.existsSync(materialPath) || !fs9.statSync(materialPath).isDirectory()) {
1010
+ if (!fs10.existsSync(materialPath) || !fs10.statSync(materialPath).isDirectory()) {
907
1011
  throw new Error("Material directory does not exist.");
908
1012
  }
909
1013
  const fdDir = path9.join(materialPath, "fd");
@@ -925,41 +1029,120 @@ function decryptPwd(encryptedHex, materialPath) {
925
1029
  }
926
1030
 
927
1031
  // src/sign/generateSigningConfigs.ts
928
- var fs10 = __toESM(require("fs"), 1);
1032
+ var fs11 = __toESM(require("fs"), 1);
929
1033
  var path10 = __toESM(require("path"), 1);
930
1034
  var import_node_child_process2 = require("child_process");
931
1035
  var import_json52 = __toESM(require("json5"), 1);
1036
+
1037
+ // src/sign/applicationCert.ts
1038
+ var OPEN_HARMONY_APPLICATION_CERT = `-----BEGIN CERTIFICATE-----
1039
+ MIICRDCCAcmgAwIBAgIED+E4izAMBggqhkjOPQQDAwUAMGgxCzAJBgNVBAYTAkNO
1040
+ MRQwEgYDVQQKEwtPcGVuSGFybW9ueTEZMBcGA1UECxMQT3Blbkhhcm1vbnkgVGVh
1041
+ bTEoMCYGA1UEAxMfT3Blbkhhcm1vbnkgQXBwbGljYXRpb24gUm9vdCBDQTAeFw0y
1042
+ MTAyMDIxMjE0MThaFw00OTEyMzExMjE0MThaMGgxCzAJBgNVBAYTAkNOMRQwEgYD
1043
+ VQQKEwtPcGVuSGFybW9ueTEZMBcGA1UECxMQT3Blbkhhcm1vbnkgVGVhbTEoMCYG
1044
+ A1UEAxMfT3Blbkhhcm1vbnkgQXBwbGljYXRpb24gUm9vdCBDQTB2MBAGByqGSM49
1045
+ AgEGBSuBBAAiA2IABE023XmRaw2DnO8NSsb+KG/uY0FtS3u5LQucdr3qWVnRW5ui
1046
+ QIL6ttNZBEeLTUeYcJZCpayg9Llf+1SmDA7dY4iP2EcRo4UN3rilovtfFfsmH4ty
1047
+ 3SApHVFzWUl+NwdH8KNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC
1048
+ AQYwHQYDVR0OBBYEFBc6EKGrGXzlAE+s0Zgnsphadw7NMAwGCCqGSM49BAMDBQAD
1049
+ ZwAwZAIwd1p3JzHN93eoPped1li0j64npgqNzwy4OrkehYAqNXpcpaEcLZ7UxW8E
1050
+ I2lZJ3SbAjAkqySHb12sIwdSFKSN9KCMMEo/eUT5dUXlcKR2nZz0MJdxT5F51qcX
1051
+ 1CumzkcYhgU=
1052
+ -----END CERTIFICATE-----
1053
+ -----BEGIN CERTIFICATE-----
1054
+ MIICYTCCAeWgAwIBAgIEHmXAPTAMBggqhkjOPQQDAwUAMGgxCzAJBgNVBAYTAkNO
1055
+ MRQwEgYDVQQKEwtPcGVuSGFybW9ueTEZMBcGA1UECxMQT3Blbkhhcm1vbnkgVGVh
1056
+ bTEoMCYGA1UEAxMfT3Blbkhhcm1vbnkgQXBwbGljYXRpb24gUm9vdCBDQTAeFw0y
1057
+ MTAyMDIxMjE1MzJaFw00OTEyMzExMjE1MzJaMGMxCzAJBgNVBAYTAkNOMRQwEgYD
1058
+ VQQKEwtPcGVuSGFybW9ueTEZMBcGA1UECxMQT3Blbkhhcm1vbnkgVGVhbTEjMCEG
1059
+ A1UEAxMaT3Blbkhhcm1vbnkgQXBwbGljYXRpb24gQ0EwdjAQBgcqhkjOPQIBBgUr
1060
+ gQQAIgNiAAQhnu7Hna8XNa2KyqRf5+lBJScE4xqf89N0g0OuqAb2re8nGsvWkw26
1061
+ uDekfnBYicd+G3Cydqa2zFIwV7Talyg2ULW3r8KbGpyl84mJEPPRmCGJ+H9gtCsf
1062
+ +OrJ4Y76LVWjYzBhMB8GA1UdIwQYMBaAFBc6EKGrGXzlAE+s0Zgnsphadw7NMA8G
1063
+ A1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBTbhrciFtUL
1064
+ oUu33SV7ufEFfaItRzAMBggqhkjOPQQDAwUAA2gAMGUCMG3cXjiDmXTvf7D4Omhf
1065
+ qcc2nuO+EMfWE+N9ZhBP5UhV34mAGWi3SfLU6rcV0urWEQIxAMYIb3epOnKhUrcm
1066
+ Lfu1WKzFlpYQwmw73RaCHP2I3k6NcuWOYeNwWXSNZ8o0nzvaLg==
1067
+ -----END CERTIFICATE-----
1068
+ -----BEGIN CERTIFICATE-----
1069
+ MIICMzCCAbegAwIBAgIEaOC/zDAMBggqhkjOPQQDAwUAMGMxCzAJBgNVBAYTAkNO
1070
+ MRQwEgYDVQQKEwtPcGVuSGFybW9ueTEZMBcGA1UECxMQT3Blbkhhcm1vbnkgVGVh
1071
+ bTEjMCEGA1UEAxMaT3Blbkhhcm1vbnkgQXBwbGljYXRpb24gQ0EwHhcNMjEwMjAy
1072
+ MTIxOTMxWhcNNDkxMjMxMTIxOTMxWjBoMQswCQYDVQQGEwJDTjEUMBIGA1UEChML
1073
+ T3Blbkhhcm1vbnkxGTAXBgNVBAsTEE9wZW5IYXJtb255IFRlYW0xKDAmBgNVBAMT
1074
+ H09wZW5IYXJtb255IEFwcGxpY2F0aW9uIFJlbGVhc2UwWTATBgcqhkjOPQIBBggq
1075
+ hkjOPQMBBwNCAATbYOCQQpW5fdkYHN45v0X3AHax12jPBdEDosFRIZ1eXmxOYzSG
1076
+ JwMfsHhUU90E8lI0TXYZnNmgM1sovubeQqATo1IwUDAfBgNVHSMEGDAWgBTbhrci
1077
+ FtULoUu33SV7ufEFfaItRzAOBgNVHQ8BAf8EBAMCB4AwHQYDVR0OBBYEFPtxruhl
1078
+ cRBQsJdwcZqLu9oNUVgaMAwGCCqGSM49BAMDBQADaAAwZQIxAJta0PQ2p4DIu/ps
1079
+ LMdLCDgQ5UH1l0B4PGhBlMgdi2zf8nk9spazEQI/0XNwpft8QAIwHSuA2WelVi/o
1080
+ zAlF08DnbJrOOtOnQq5wHOPlDYB4OtUzOYJk9scotrEnJxJzGsh/
1081
+ -----END CERTIFICATE-----
1082
+ `;
1083
+
1084
+ // src/sign/generateSigningConfigs.ts
932
1085
  var APL_VALUES = ["normal", "system_basic", "system_core"];
933
1086
  var APP_FEATURE_VALUES = ["hos_normal_app", "hos_system_app"];
934
- function copyFilesToProject(projectDir, paths, logger) {
1087
+ var DEFAULT_PASSWORDS = { store: "123456", key: "123456" };
1088
+ function pickSigningKind(apl) {
1089
+ return apl === "normal" ? "profile-release" : "application-release";
1090
+ }
1091
+ var SIGNING_KIND_CONFIG = {
1092
+ "profile-release": {
1093
+ hapKeyAlias: "openharmony application profile release",
1094
+ certBasename: "OpenHarmonyProfileRelease.pem"
1095
+ },
1096
+ "application-release": {
1097
+ hapKeyAlias: "OpenHarmony Application Release",
1098
+ certBasename: "OpenHarmonyApplication.cer"
1099
+ }
1100
+ };
1101
+ function copyFilesToProject(projectDir, paths, kind, logger, applicationCertPath) {
935
1102
  logger.info("[sign] Copying signing material into project...");
936
1103
  const signaturesDir = path10.join(projectDir, "signatures");
937
- fs10.mkdirSync(signaturesDir, { recursive: true });
938
- fs10.copyFileSync(paths.keystore, path10.join(signaturesDir, "OpenHarmony.p12"));
939
- fs10.copyFileSync(paths.profileCert, path10.join(signaturesDir, "OpenHarmonyProfileRelease.pem"));
940
- fs10.copyFileSync(paths.unsignedProfileTemplate, path10.join(signaturesDir, "UnsgnedReleasedProfileTemplate.json"));
1104
+ fs11.mkdirSync(signaturesDir, { recursive: true });
1105
+ fs11.copyFileSync(paths.keystore, path10.join(signaturesDir, "OpenHarmony.p12"));
1106
+ fs11.copyFileSync(paths.profileCert, path10.join(signaturesDir, "OpenHarmonyProfileRelease.pem"));
1107
+ fs11.copyFileSync(paths.unsignedProfileTemplate, path10.join(signaturesDir, "UnsgnedReleasedProfileTemplate.json"));
1108
+ if (kind === "application-release") {
1109
+ const dest = path10.join(signaturesDir, "OpenHarmonyApplication.cer");
1110
+ if (applicationCertPath) {
1111
+ fs11.copyFileSync(applicationCertPath, dest);
1112
+ } else {
1113
+ fs11.writeFileSync(dest, OPEN_HARMONY_APPLICATION_CERT);
1114
+ }
1115
+ }
941
1116
  }
942
1117
  function resolveAppFeature(apl, override) {
943
1118
  if (override) return override;
944
1119
  return apl === "normal" ? "hos_normal_app" : "hos_system_app";
945
1120
  }
946
- function modifyProfileTemplate(projectDir, apl, appFeature, logger) {
947
- logger.info(`[sign] Modifying profile template (apl=${apl}, app-feature=${appFeature})...`);
1121
+ function extractLeafCertificate(chainPath) {
1122
+ const certContent = fs11.readFileSync(chainPath, "utf-8");
1123
+ const parts = certContent.split("-----END CERTIFICATE-----");
1124
+ if (parts.length < 3) {
1125
+ throw new OniroError(`${chainPath} does not contain enough certificates.`);
1126
+ }
1127
+ return parts[2].trim() + "\n-----END CERTIFICATE-----\n";
1128
+ }
1129
+ function modifyProfileTemplate(projectDir, apl, appFeature, logger, options = {}) {
1130
+ const kind = options.kind ?? pickSigningKind(apl);
1131
+ logger.info(`[sign] Modifying profile template (apl=${apl}, app-feature=${appFeature}, kind=${kind})...`);
948
1132
  const appJsonPath = path10.join(projectDir, "AppScope/app.json5");
949
1133
  const profileTemplatePath = path10.join(projectDir, "signatures/UnsgnedReleasedProfileTemplate.json");
950
- const profileCertFilePath = path10.join(projectDir, "signatures/OpenHarmonyProfileRelease.pem");
951
- if (!fs10.existsSync(appJsonPath)) {
1134
+ if (!fs11.existsSync(appJsonPath)) {
952
1135
  throw new OniroError(`${appJsonPath} does not exist.`);
953
1136
  }
954
1137
  let appJson;
955
1138
  try {
956
- appJson = import_json52.default.parse(fs10.readFileSync(appJsonPath, "utf-8"));
1139
+ appJson = import_json52.default.parse(fs11.readFileSync(appJsonPath, "utf-8"));
957
1140
  } catch (e) {
958
1141
  throw new OniroError(`Error parsing ${appJsonPath}: ${e.message}`, e);
959
1142
  }
960
1143
  let profileTemplate;
961
1144
  try {
962
- profileTemplate = JSON.parse(fs10.readFileSync(profileTemplatePath, "utf-8"));
1145
+ profileTemplate = JSON.parse(fs11.readFileSync(profileTemplatePath, "utf-8"));
963
1146
  } catch (e) {
964
1147
  throw new OniroError(`Error parsing ${profileTemplatePath}: ${e.message}`, e);
965
1148
  }
@@ -972,16 +1155,19 @@ function modifyProfileTemplate(projectDir, apl, appFeature, logger) {
972
1155
  profileTemplate["bundle-info"]["bundle-name"] = appJson.app.bundleName;
973
1156
  profileTemplate["bundle-info"]["apl"] = apl;
974
1157
  profileTemplate["bundle-info"]["app-feature"] = appFeature;
975
- const certContent = fs10.readFileSync(profileCertFilePath, "utf-8");
976
- const certs = certContent.split("-----END CERTIFICATE-----");
977
- if (certs.length < 3) {
978
- throw new OniroError(`${profileCertFilePath} does not contain enough certificates.`);
1158
+ const distCertChainPath = path10.join(
1159
+ projectDir,
1160
+ "signatures",
1161
+ SIGNING_KIND_CONFIG[kind].certBasename
1162
+ );
1163
+ profileTemplate["bundle-info"]["distribution-certificate"] = extractLeafCertificate(distCertChainPath);
1164
+ if (options.acls !== void 0) {
1165
+ profileTemplate.acls = profileTemplate.acls ?? {};
1166
+ profileTemplate.acls["allowed-acls"] = options.acls;
979
1167
  }
980
- const thirdCert = certs[2].trim() + "\n-----END CERTIFICATE-----\n";
981
- profileTemplate["bundle-info"]["distribution-certificate"] = thirdCert;
982
- fs10.writeFileSync(profileTemplatePath, JSON.stringify(profileTemplate, null, 2));
1168
+ fs11.writeFileSync(profileTemplatePath, JSON.stringify(profileTemplate, null, 2));
983
1169
  }
984
- function generateP7bFile(projectDir, paths, logger) {
1170
+ function generateP7bFile(projectDir, paths, logger, passwords) {
985
1171
  logger.info("[sign] Generating P7b profile via hap-sign-tool...");
986
1172
  const signaturesDir = path10.join(projectDir, "signatures");
987
1173
  const profileTemplatePath = path10.join(signaturesDir, "UnsgnedReleasedProfileTemplate.json");
@@ -1005,9 +1191,9 @@ function generateP7bFile(projectDir, paths, logger) {
1005
1191
  "-outFile",
1006
1192
  outputProfilePath,
1007
1193
  "-keyPwd",
1008
- "123456",
1194
+ passwords.key,
1009
1195
  "-keystorePwd",
1010
- "123456"
1196
+ passwords.store
1011
1197
  ];
1012
1198
  try {
1013
1199
  (0, import_node_child_process2.execFileSync)("java", args, { stdio: "inherit" });
@@ -1018,42 +1204,63 @@ function generateP7bFile(projectDir, paths, logger) {
1018
1204
  );
1019
1205
  }
1020
1206
  }
1021
- function updateBuildProfile(projectDir, logger) {
1207
+ function detectSigningConfigNames(projectDir) {
1208
+ const buildProfilePath = path10.join(projectDir, "build-profile.json5");
1209
+ if (!fs11.existsSync(buildProfilePath)) return ["default"];
1210
+ let parsed;
1211
+ try {
1212
+ parsed = import_json52.default.parse(fs11.readFileSync(buildProfilePath, "utf-8"));
1213
+ } catch {
1214
+ return ["default"];
1215
+ }
1216
+ const products = parsed.app?.products;
1217
+ if (!Array.isArray(products) || products.length === 0) return ["default"];
1218
+ const names = [];
1219
+ for (const product of products) {
1220
+ const name = product.signingConfig;
1221
+ if (typeof name === "string" && name.length > 0 && !names.includes(name)) {
1222
+ names.push(name);
1223
+ }
1224
+ }
1225
+ return names.length > 0 ? names : ["default"];
1226
+ }
1227
+ function updateBuildProfile(projectDir, kind, logger, passwords) {
1022
1228
  logger.info("[sign] Writing signing configs into build-profile.json5...");
1023
1229
  const materialDir = path10.join(projectDir, "signatures", "material");
1024
1230
  const buildProfilePath = path10.join(projectDir, "build-profile.json5");
1025
- const encryptedStorePassword = encryptPwd("123456", materialDir);
1026
- const encryptedKeyPassword = encryptPwd("123456", materialDir);
1231
+ const encryptedStorePassword = encryptPwd(passwords.store, materialDir);
1232
+ const encryptedKeyPassword = encryptPwd(passwords.key, materialDir);
1027
1233
  let buildProfile = { app: {} };
1028
- if (fs10.existsSync(buildProfilePath)) {
1234
+ if (fs11.existsSync(buildProfilePath)) {
1029
1235
  try {
1030
- buildProfile = import_json52.default.parse(fs10.readFileSync(buildProfilePath, "utf-8"));
1236
+ buildProfile = import_json52.default.parse(fs11.readFileSync(buildProfilePath, "utf-8"));
1031
1237
  } catch (e) {
1032
1238
  throw new OniroError(`Error parsing ${buildProfilePath}: ${e.message}`, e);
1033
1239
  }
1034
1240
  }
1241
+ const { hapKeyAlias, certBasename } = SIGNING_KIND_CONFIG[kind];
1242
+ const configNames = detectSigningConfigNames(projectDir);
1243
+ logger.info(`[sign] Writing signingConfigs entries: ${configNames.join(", ")}`);
1035
1244
  buildProfile.app = buildProfile.app ?? {};
1036
- buildProfile.app.signingConfigs = [
1037
- {
1038
- name: "default",
1039
- material: {
1040
- certpath: "./signatures/OpenHarmonyProfileRelease.pem",
1041
- storePassword: encryptedStorePassword,
1042
- keyAlias: "openharmony application profile release",
1043
- keyPassword: encryptedKeyPassword,
1044
- profile: "./signatures/app1-profile.p7b",
1045
- signAlg: "SHA256withECDSA",
1046
- storeFile: "./signatures/OpenHarmony.p12"
1047
- }
1245
+ buildProfile.app.signingConfigs = configNames.map((name) => ({
1246
+ name,
1247
+ material: {
1248
+ certpath: `./signatures/${certBasename}`,
1249
+ storePassword: encryptedStorePassword,
1250
+ keyAlias: hapKeyAlias,
1251
+ keyPassword: encryptedKeyPassword,
1252
+ profile: "./signatures/app1-profile.p7b",
1253
+ signAlg: "SHA256withECDSA",
1254
+ storeFile: "./signatures/OpenHarmony.p12"
1048
1255
  }
1049
- ];
1050
- fs10.writeFileSync(buildProfilePath, JSON.stringify(buildProfile, null, 2));
1256
+ }));
1257
+ fs11.writeFileSync(buildProfilePath, JSON.stringify(buildProfile, null, 2));
1051
1258
  }
1052
1259
  function prepareMaterialDirectory(projectDir, logger) {
1053
1260
  logger.info("[sign] Generating fresh signing material...");
1054
1261
  const materialDir = path10.join(projectDir, "signatures", "material");
1055
- if (fs10.existsSync(materialDir)) {
1056
- fs10.rmSync(materialDir, { recursive: true, force: true });
1262
+ if (fs11.existsSync(materialDir)) {
1263
+ fs11.rmSync(materialDir, { recursive: true, force: true });
1057
1264
  }
1058
1265
  createMaterial(materialDir);
1059
1266
  }
@@ -1077,7 +1284,7 @@ function generateSigningConfigs(options) {
1077
1284
  );
1078
1285
  }
1079
1286
  const sdkPath = path10.join(sdkHome, String(sdkVersion));
1080
- if (!fs10.existsSync(sdkPath)) {
1287
+ if (!fs11.existsSync(sdkPath)) {
1081
1288
  throw new OniroError(`SDK path does not exist: ${sdkPath}`);
1082
1289
  }
1083
1290
  const paths = {
@@ -1086,17 +1293,19 @@ function generateSigningConfigs(options) {
1086
1293
  profileCert: path10.join(sdkPath, "toolchains/lib/OpenHarmonyProfileRelease.pem"),
1087
1294
  unsignedProfileTemplate: path10.join(sdkPath, "toolchains/lib/UnsgnedReleasedProfileTemplate.json")
1088
1295
  };
1296
+ const kind = pickSigningKind(apl);
1297
+ const passwords = options.passwords ?? DEFAULT_PASSWORDS;
1089
1298
  logger.info("[sign] Starting signing configuration generation...");
1090
- copyFilesToProject(projectDir, paths, logger);
1091
- modifyProfileTemplate(projectDir, apl, appFeature, logger);
1092
- generateP7bFile(projectDir, paths, logger);
1299
+ copyFilesToProject(projectDir, paths, kind, logger, options.applicationCertPath);
1300
+ modifyProfileTemplate(projectDir, apl, appFeature, logger, { kind, acls: options.acls });
1301
+ generateP7bFile(projectDir, paths, logger, passwords);
1093
1302
  prepareMaterialDirectory(projectDir, logger);
1094
- updateBuildProfile(projectDir, logger);
1303
+ updateBuildProfile(projectDir, kind, logger, passwords);
1095
1304
  logger.info("[sign] Signing configuration generated successfully.");
1096
1305
  }
1097
1306
 
1098
1307
  // src/emulator/install.ts
1099
- var fs11 = __toESM(require("fs"), 1);
1308
+ var fs12 = __toESM(require("fs"), 1);
1100
1309
  var os7 = __toESM(require("os"), 1);
1101
1310
  var path11 = __toESM(require("path"), 1);
1102
1311
  async function installEmulator(opts) {
@@ -1104,63 +1313,142 @@ async function installEmulator(opts) {
1104
1313
  const logger = opts.logger ?? noopLogger;
1105
1314
  const url = config.get("emulatorUrl", defaultPaths.emulatorUrl);
1106
1315
  const emulatorDir = getEmulatorDir(config);
1107
- const tmpDir = fs11.mkdtempSync(path11.join(os7.tmpdir(), "oniro-emulator-"));
1316
+ const tmpDir = fs12.mkdtempSync(path11.join(os7.tmpdir(), "oniro-emulator-"));
1108
1317
  const tmpZip = path11.join(tmpDir, "oniro_emulator.zip");
1109
1318
  try {
1110
- fs11.mkdirSync(emulatorDir, { recursive: true });
1319
+ fs12.mkdirSync(emulatorDir, { recursive: true });
1111
1320
  progress?.report({ message: "Downloading emulator...", increment: 0 });
1112
1321
  await downloadFile({ url, dest: tmpZip, progress, abortSignal, start: 0, range: 50 });
1113
1322
  progress?.report({ message: "Extracting emulator...", increment: 0 });
1114
1323
  await extractZipWithProgress({ zipPath: tmpZip, dest: emulatorDir, progress, start: 50, range: 45, logger });
1115
1324
  const runSh = path11.join(emulatorDir, "images", "run.sh");
1116
- if (fs11.existsSync(runSh)) {
1117
- fs11.chmodSync(runSh, 493);
1325
+ if (fs12.existsSync(runSh)) {
1326
+ fs12.chmodSync(runSh, 493);
1118
1327
  }
1119
1328
  progress?.report({ message: "Finalizing installation...", increment: 5 });
1120
1329
  } finally {
1121
- fs11.rmSync(tmpDir, { recursive: true, force: true });
1330
+ fs12.rmSync(tmpDir, { recursive: true, force: true });
1122
1331
  }
1123
1332
  }
1124
1333
  function isEmulatorInstalled(config) {
1125
- return fs11.existsSync(path11.join(getEmulatorDir(config), "images", "run.sh"));
1334
+ const imagesDir = path11.join(getEmulatorDir(config), "images");
1335
+ return fs12.existsSync(path11.join(imagesDir, "run.sh")) || fs12.existsSync(path11.join(imagesDir, "run.bat"));
1126
1336
  }
1127
1337
  function removeEmulator(config) {
1128
1338
  const emulatorDir = getEmulatorDir(config);
1129
- if (fs11.existsSync(emulatorDir)) {
1130
- fs11.rmSync(emulatorDir, { recursive: true, force: true });
1339
+ if (fs12.existsSync(emulatorDir)) {
1340
+ fs12.rmSync(emulatorDir, { recursive: true, force: true });
1131
1341
  }
1132
1342
  }
1133
1343
 
1134
1344
  // src/emulator/lifecycle.ts
1135
- var fs12 = __toESM(require("fs"), 1);
1345
+ var fs13 = __toESM(require("fs"), 1);
1136
1346
  var os8 = __toESM(require("os"), 1);
1137
1347
  var path12 = __toESM(require("path"), 1);
1348
+ var import_node_child_process4 = require("child_process");
1349
+
1350
+ // src/hdc/exec.ts
1138
1351
  var import_node_child_process3 = require("child_process");
1139
- var PID_FILE = path12.join(os8.tmpdir(), "oniro_emulator.pid");
1140
- function execPromise(cmd, cwd, logger) {
1352
+ function runProcess(opts) {
1353
+ const { command, args, cwd, env, onOutput } = opts;
1354
+ const timeoutMs = opts.timeoutMs ?? 6e4;
1355
+ const { abortSignal } = opts;
1141
1356
  return new Promise((resolve2, reject) => {
1142
- (0, import_node_child_process3.exec)(cmd, { cwd }, (error, stdout, stderr) => {
1143
- if (stdout?.trim()) logger.info(`[emulator] ${stdout.trim()}`);
1144
- if (stderr?.trim()) logger.warn(`[emulator] ${stderr.trim()}`);
1145
- if (error) {
1146
- logger.error(`[emulator] ${error.message}`);
1147
- reject(error);
1148
- } else {
1149
- resolve2();
1357
+ if (abortSignal?.aborted) {
1358
+ reject(new CancelledError(`Command aborted before start: ${command}`));
1359
+ return;
1360
+ }
1361
+ const child = (0, import_node_child_process3.spawn)(command, [...args], { cwd, env, shell: false });
1362
+ let stdout = "";
1363
+ let stderr = "";
1364
+ let settled = false;
1365
+ let timedOut = false;
1366
+ const cleanup = () => {
1367
+ clearTimeout(timer);
1368
+ abortSignal?.removeEventListener("abort", onAbort);
1369
+ };
1370
+ const onAbort = () => {
1371
+ if (settled) return;
1372
+ settled = true;
1373
+ cleanup();
1374
+ try {
1375
+ child.kill("SIGKILL");
1376
+ } catch {
1150
1377
  }
1378
+ reject(new CancelledError(`Command aborted: ${command} ${args.join(" ")}`));
1379
+ };
1380
+ const timer = setTimeout(() => {
1381
+ timedOut = true;
1382
+ try {
1383
+ child.kill("SIGKILL");
1384
+ } catch {
1385
+ }
1386
+ }, timeoutMs);
1387
+ abortSignal?.addEventListener("abort", onAbort, { once: true });
1388
+ child.stdout.on("data", (d) => {
1389
+ const s = d.toString();
1390
+ stdout += s;
1391
+ onOutput?.(s, "stdout");
1392
+ });
1393
+ child.stderr.on("data", (d) => {
1394
+ const s = d.toString();
1395
+ stderr += s;
1396
+ onOutput?.(s, "stderr");
1151
1397
  });
1398
+ child.on("error", (err) => {
1399
+ if (settled) return;
1400
+ settled = true;
1401
+ cleanup();
1402
+ reject(new OniroError(`Failed to spawn ${command}: ${err.message}`, err));
1403
+ });
1404
+ child.on("close", (code) => {
1405
+ if (settled) return;
1406
+ settled = true;
1407
+ cleanup();
1408
+ if (timedOut) {
1409
+ reject(
1410
+ new OniroError(
1411
+ `Command timed out after ${timeoutMs}ms: ${command} ${args.join(" ")}` + (stderr.trim() ? `
1412
+ ${stderr.trim()}` : "")
1413
+ )
1414
+ );
1415
+ return;
1416
+ }
1417
+ resolve2({ code: code ?? -1, stdout, stderr });
1418
+ });
1419
+ });
1420
+ }
1421
+ function hdcExec(opts) {
1422
+ const hdc = getHdcPath(opts.config);
1423
+ const argv = opts.deviceSerial ? ["-t", opts.deviceSerial, ...opts.args] : [...opts.args];
1424
+ return runProcess({
1425
+ command: hdc,
1426
+ args: argv,
1427
+ timeoutMs: opts.timeoutMs,
1428
+ abortSignal: opts.abortSignal,
1429
+ onOutput: opts.onOutput,
1430
+ logger: opts.logger
1152
1431
  });
1153
1432
  }
1433
+ function shell(opts) {
1434
+ const { command, ...rest } = opts;
1435
+ return hdcExec({ ...rest, args: ["shell", command] });
1436
+ }
1437
+ function ensureOk(result, command) {
1438
+ if (result.code !== 0) {
1439
+ throw new CommandFailedError(command, result.code, result.stderr.trim());
1440
+ }
1441
+ return result;
1442
+ }
1443
+
1444
+ // src/emulator/lifecycle.ts
1445
+ var PID_FILE = path12.join(os8.tmpdir(), "oniro_emulator.pid");
1154
1446
  async function attemptHdcConnection(config, address = "127.0.0.1:55555", logger = noopLogger) {
1155
- const hdc = getHdcPath(config);
1156
1447
  try {
1157
- await execPromise(`"${hdc}" start -r`, void 0, logger);
1158
- await execPromise(`"${hdc}" tconn ${address}`, void 0, logger);
1159
- return await new Promise((resolve2) => {
1160
- (0, import_node_child_process3.exec)(`"${hdc}" list targets`, (_error, stdout) => {
1161
- resolve2(stdout?.includes(address) ?? false);
1162
- });
1163
- });
1448
+ await hdcExec({ config, args: ["start", "-r"], logger });
1449
+ await hdcExec({ config, args: ["tconn", address], logger });
1450
+ const targets = await hdcExec({ config, args: ["list", "targets"], logger });
1451
+ return targets.stdout.includes(address);
1164
1452
  } catch (err) {
1165
1453
  logger.warn(`hdc connection attempt failed: ${err.message}`);
1166
1454
  return false;
@@ -1173,18 +1461,18 @@ function resolveLauncher(imagesPath, headless, connect) {
1173
1461
  if (headless) extra.push("--headless");
1174
1462
  if (connect) extra.push("--connect", connect);
1175
1463
  if (os8.platform() === "win32") {
1176
- if (fs12.existsSync(runBat)) {
1464
+ if (fs13.existsSync(runBat)) {
1177
1465
  return { command: "cmd.exe", args: ["/c", runBat, ...extra], cwd: imagesPath };
1178
1466
  }
1179
- if (fs12.existsSync(runSh)) {
1467
+ if (fs13.existsSync(runSh)) {
1180
1468
  return { command: "bash", args: [runSh, ...extra], cwd: imagesPath };
1181
1469
  }
1182
1470
  throw new OniroError(`No emulator launcher (run.bat or run.sh) found in ${imagesPath}.`);
1183
1471
  }
1184
- if (fs12.existsSync(runSh)) {
1472
+ if (fs13.existsSync(runSh)) {
1185
1473
  return { command: "bash", args: [runSh, ...extra], cwd: imagesPath };
1186
1474
  }
1187
- if (fs12.existsSync(runBat)) {
1475
+ if (fs13.existsSync(runBat)) {
1188
1476
  return { command: "bash", args: [runBat, ...extra], cwd: imagesPath };
1189
1477
  }
1190
1478
  throw new OniroError(`No emulator launcher (run.sh or run.bat) found in ${imagesPath}.`);
@@ -1192,9 +1480,9 @@ function resolveLauncher(imagesPath, headless, connect) {
1192
1480
  async function startEmulator(opts) {
1193
1481
  const { config } = opts;
1194
1482
  const logger = opts.logger ?? noopLogger;
1195
- if (os8.platform() !== "win32" && fs12.existsSync(PID_FILE)) {
1483
+ if (os8.platform() !== "win32" && fs13.existsSync(PID_FILE)) {
1196
1484
  try {
1197
- const pid = Number(fs12.readFileSync(PID_FILE, "utf8").trim());
1485
+ const pid = Number(fs13.readFileSync(PID_FILE, "utf8").trim());
1198
1486
  if (Number.isFinite(pid) && pid > 0) {
1199
1487
  try {
1200
1488
  process.kill(pid, 0);
@@ -1211,27 +1499,27 @@ async function startEmulator(opts) {
1211
1499
  }
1212
1500
  }
1213
1501
  const imagesPath = path12.join(getEmulatorDir(config), "images");
1214
- if (!fs12.existsSync(imagesPath)) {
1502
+ if (!fs13.existsSync(imagesPath)) {
1215
1503
  throw new OniroError(`Emulator images directory not found at ${imagesPath}. Run \`oniro-app emulator install\` first.`);
1216
1504
  }
1217
1505
  const launcher = resolveLauncher(imagesPath, opts.headless === true, opts.connect);
1218
1506
  const logPath = opts.logFile ?? (os8.platform() === "win32" ? "NUL" : "/dev/null");
1219
- const logFd = fs12.openSync(logPath, "a");
1507
+ const logFd = fs13.openSync(logPath, "a");
1220
1508
  let child;
1221
1509
  try {
1222
- child = (0, import_node_child_process3.spawn)(launcher.command, [...launcher.args], {
1510
+ child = (0, import_node_child_process4.spawn)(launcher.command, [...launcher.args], {
1223
1511
  cwd: launcher.cwd,
1224
1512
  detached: true,
1225
1513
  stdio: ["ignore", logFd, logFd],
1226
1514
  windowsHide: true
1227
1515
  });
1228
1516
  } finally {
1229
- fs12.closeSync(logFd);
1517
+ fs13.closeSync(logFd);
1230
1518
  }
1231
1519
  if (!child.pid) {
1232
1520
  throw new OniroError(`Failed to spawn launcher (${launcher.command} ${launcher.args.join(" ")}).`);
1233
1521
  }
1234
- fs12.writeFileSync(PID_FILE, String(child.pid));
1522
+ fs13.writeFileSync(PID_FILE, String(child.pid));
1235
1523
  child.unref();
1236
1524
  logger.info(`Emulator launched (PID ${child.pid}). Logs: ${logPath}`);
1237
1525
  const earlyExit = new Promise((resolve2) => {
@@ -1278,24 +1566,24 @@ ${tail}` : ""}`);
1278
1566
  }
1279
1567
  function readTail(filePath, bytes) {
1280
1568
  try {
1281
- const stat = fs12.statSync(filePath);
1569
+ const stat = fs13.statSync(filePath);
1282
1570
  const start = Math.max(0, stat.size - bytes);
1283
- const fd = fs12.openSync(filePath, "r");
1571
+ const fd = fs13.openSync(filePath, "r");
1284
1572
  try {
1285
1573
  const buf = Buffer.alloc(stat.size - start);
1286
- fs12.readSync(fd, buf, 0, buf.length, start);
1574
+ fs13.readSync(fd, buf, 0, buf.length, start);
1287
1575
  return buf.toString("utf8");
1288
1576
  } finally {
1289
- fs12.closeSync(fd);
1577
+ fs13.closeSync(fd);
1290
1578
  }
1291
1579
  } catch {
1292
1580
  return "";
1293
1581
  }
1294
1582
  }
1295
1583
  async function stopEmulator(logger = noopLogger) {
1296
- if (fs12.existsSync(PID_FILE)) {
1584
+ if (fs13.existsSync(PID_FILE)) {
1297
1585
  try {
1298
- const pid = Number(fs12.readFileSync(PID_FILE, "utf8").trim());
1586
+ const pid = Number(fs13.readFileSync(PID_FILE, "utf8").trim());
1299
1587
  if (Number.isFinite(pid) && pid > 0) {
1300
1588
  try {
1301
1589
  process.kill(pid, "SIGTERM");
@@ -1308,27 +1596,194 @@ async function stopEmulator(logger = noopLogger) {
1308
1596
  logger.warn(`Could not read PID file: ${err.message}`);
1309
1597
  }
1310
1598
  }
1311
- const killCmd = os8.platform() === "win32" ? "taskkill /IM qemu-system-x86_64.exe /F" : "pkill -f qemu-system-x86_64";
1599
+ const [killCmd, ...killArgs] = os8.platform() === "win32" ? ["taskkill", "/IM", "qemu-system-x86_64.exe", "/F"] : ["pkill", "-f", "qemu-system-x86_64"];
1312
1600
  try {
1313
- await execPromise(killCmd, void 0, logger);
1314
- } catch {
1601
+ await runProcess({ command: killCmd, args: killArgs, logger });
1602
+ } catch (err) {
1603
+ logger.warn(`[emulator] could not run ${killCmd}: ${err.message}`);
1315
1604
  }
1316
- fs12.rm(PID_FILE, { force: true }, (err) => {
1605
+ fs13.rm(PID_FILE, { force: true }, (err) => {
1317
1606
  if (err) logger.warn(`Could not remove PID file: ${err.message}`);
1318
1607
  });
1319
1608
  }
1320
1609
 
1610
+ // src/hdc/devices.ts
1611
+ var STATUS_TOKENS = /* @__PURE__ */ new Set([
1612
+ "Connected",
1613
+ "Offline",
1614
+ "Unauthorized",
1615
+ "Authorizing",
1616
+ "Disconnected",
1617
+ "Recovery"
1618
+ ]);
1619
+ var CONNECTION_TOKENS = /* @__PURE__ */ new Set(["USB", "TCP", "UART", "BT", "UNKNOWN"]);
1620
+ function parseDeviceList(stdout) {
1621
+ const devices = [];
1622
+ for (const line of stdout.split("\n")) {
1623
+ const trimmed = line.trim();
1624
+ if (!trimmed || trimmed.startsWith("[Empty]")) continue;
1625
+ const cols = trimmed.split(/\s+/);
1626
+ const serial = cols[0];
1627
+ if (!serial) continue;
1628
+ const rest = cols.slice(1);
1629
+ const connection = rest.find((c) => CONNECTION_TOKENS.has(c));
1630
+ const status = rest.find((c) => STATUS_TOKENS.has(c)) ?? "Unknown";
1631
+ const info = { serial, status };
1632
+ if (connection) info.connection = connection;
1633
+ devices.push(info);
1634
+ }
1635
+ return devices;
1636
+ }
1637
+ async function listDevices(config, opts = {}) {
1638
+ const res = await hdcExec({
1639
+ config,
1640
+ args: ["list", "targets", "-v"],
1641
+ timeoutMs: opts.timeoutMs ?? 1e4,
1642
+ logger: opts.logger
1643
+ });
1644
+ return parseDeviceList(res.stdout);
1645
+ }
1646
+ async function selectDevice(config, serial, opts = {}) {
1647
+ if (serial && serial !== "auto") return serial;
1648
+ const envSerial = process.env.ONIRO_DEVICE_SERIAL || process.env.DEVICE_SERIAL;
1649
+ if (envSerial && envSerial !== "auto") return envSerial;
1650
+ const usable = (await listDevices(config, opts)).filter(
1651
+ (d) => d.status === "Connected" || d.status === "Unknown"
1652
+ );
1653
+ if (usable.length === 1) return usable[0].serial;
1654
+ if (usable.length === 0) {
1655
+ throw new OniroError("No connected device found. Connect a device/emulator or set ONIRO_DEVICE_SERIAL.");
1656
+ }
1657
+ throw new OniroError(
1658
+ `Multiple devices connected (${usable.map((d) => d.serial).join(", ")}). Set ONIRO_DEVICE_SERIAL to choose one.`
1659
+ );
1660
+ }
1661
+
1662
+ // src/hdc/param.ts
1663
+ async function paramGet(config, key, opts = {}) {
1664
+ const res = await shell({
1665
+ config,
1666
+ command: `param get ${key}`,
1667
+ deviceSerial: opts.deviceSerial,
1668
+ timeoutMs: opts.timeoutMs ?? 1e4,
1669
+ logger: opts.logger
1670
+ });
1671
+ ensureOk(res, `hdc shell param get ${key}`);
1672
+ return res.stdout.trim();
1673
+ }
1674
+ async function paramSet(config, key, value, opts = {}) {
1675
+ const res = await shell({
1676
+ config,
1677
+ command: `param set ${key} ${value}`,
1678
+ deviceSerial: opts.deviceSerial,
1679
+ timeoutMs: opts.timeoutMs ?? 1e4,
1680
+ logger: opts.logger
1681
+ });
1682
+ ensureOk(res, `hdc shell param set ${key} ${value}`);
1683
+ }
1684
+
1685
+ // src/hdc/files.ts
1686
+ async function sendFile(opts) {
1687
+ const res = await hdcExec({
1688
+ config: opts.config,
1689
+ args: ["file", "send", opts.local, opts.remote],
1690
+ deviceSerial: opts.deviceSerial,
1691
+ timeoutMs: opts.timeoutMs ?? 6e5,
1692
+ abortSignal: opts.abortSignal,
1693
+ onOutput: opts.onOutput,
1694
+ logger: opts.logger
1695
+ });
1696
+ ensureOk(res, `hdc file send ${opts.local} ${opts.remote}`);
1697
+ }
1698
+ async function recvFile(opts) {
1699
+ const res = await hdcExec({
1700
+ config: opts.config,
1701
+ args: ["file", "recv", opts.remote, opts.local],
1702
+ deviceSerial: opts.deviceSerial,
1703
+ timeoutMs: opts.timeoutMs ?? 6e5,
1704
+ abortSignal: opts.abortSignal,
1705
+ onOutput: opts.onOutput,
1706
+ logger: opts.logger
1707
+ });
1708
+ ensureOk(res, `hdc file recv ${opts.remote} ${opts.local}`);
1709
+ }
1710
+
1711
+ // src/hdc/display.ts
1712
+ async function getDisplaySize(config, opts = {}) {
1713
+ const res = await shell({
1714
+ config,
1715
+ command: "hidumper -s 10 -a screen",
1716
+ deviceSerial: opts.deviceSerial,
1717
+ timeoutMs: opts.timeoutMs ?? 1e4,
1718
+ logger: opts.logger
1719
+ });
1720
+ if (res.code !== 0) return null;
1721
+ const m = res.stdout.match(/render resolution=(\d+)x(\d+)/);
1722
+ if (!m) return null;
1723
+ return { width: Number(m[1]), height: Number(m[2]) };
1724
+ }
1725
+
1726
+ // src/hdc/wait.ts
1727
+ function delay(ms, signal) {
1728
+ return new Promise((resolve2, reject) => {
1729
+ if (signal?.aborted) {
1730
+ reject(new CancelledError("Wait cancelled."));
1731
+ return;
1732
+ }
1733
+ const onAbort = () => {
1734
+ cleanup();
1735
+ reject(new CancelledError("Wait cancelled."));
1736
+ };
1737
+ const cleanup = () => {
1738
+ clearTimeout(timer);
1739
+ signal?.removeEventListener("abort", onAbort);
1740
+ };
1741
+ const timer = setTimeout(() => {
1742
+ cleanup();
1743
+ resolve2();
1744
+ }, ms);
1745
+ signal?.addEventListener("abort", onAbort, { once: true });
1746
+ });
1747
+ }
1748
+ async function waitForCondition(opts) {
1749
+ const pollMs = opts.pollMs ?? 2e3;
1750
+ const heartbeatEvery = opts.heartbeatEvery ?? 6;
1751
+ const deadline = Date.now() + opts.timeoutMs;
1752
+ let attempt = 0;
1753
+ while (Date.now() < deadline) {
1754
+ if (opts.abortSignal?.aborted) throw new CancelledError("Wait cancelled.");
1755
+ attempt++;
1756
+ let ok = false;
1757
+ try {
1758
+ ok = await opts.probe();
1759
+ } catch {
1760
+ ok = false;
1761
+ }
1762
+ if (ok) return;
1763
+ if (opts.onHeartbeat && attempt % heartbeatEvery === 0) {
1764
+ opts.onHeartbeat(attempt, Math.max(0, deadline - Date.now()));
1765
+ }
1766
+ await delay(pollMs, opts.abortSignal);
1767
+ }
1768
+ throw new OniroError(opts.timeoutMessage ?? `Condition not met within ${opts.timeoutMs}ms.`);
1769
+ }
1770
+
1771
+ // src/hdc/app.ts
1772
+ var fs15 = __toESM(require("fs"), 1);
1773
+ var path14 = __toESM(require("path"), 1);
1774
+ var import_node_child_process5 = require("child_process");
1775
+
1321
1776
  // src/hdc/project.ts
1322
- var fs13 = __toESM(require("fs"), 1);
1777
+ var fs14 = __toESM(require("fs"), 1);
1323
1778
  var path13 = __toESM(require("path"), 1);
1324
1779
  var import_json53 = __toESM(require("json5"), 1);
1325
1780
  function readJson5(filePath) {
1326
- const content = fs13.readFileSync(filePath, "utf-8");
1781
+ const content = fs14.readFileSync(filePath, "utf-8");
1327
1782
  return import_json53.default.parse(content);
1328
1783
  }
1329
1784
  function getBundleName(projectDir) {
1330
1785
  const appJsonPath = path13.join(projectDir, "AppScope", "app.json5");
1331
- if (!fs13.existsSync(appJsonPath)) {
1786
+ if (!fs14.existsSync(appJsonPath)) {
1332
1787
  throw new OniroError(`Could not find app.json5 at ${appJsonPath}`);
1333
1788
  }
1334
1789
  const appJson = readJson5(appJsonPath);
@@ -1337,59 +1792,72 @@ function getBundleName(projectDir) {
1337
1792
  }
1338
1793
  return appJson.app.bundleName;
1339
1794
  }
1340
- function getMainAbility(projectDir, moduleName = "entry") {
1795
+ function getMainAbility(projectDir, moduleName = "entry", abilityName) {
1341
1796
  const moduleJsonPath = path13.join(projectDir, moduleName, "src", "main", "module.json5");
1342
- if (!fs13.existsSync(moduleJsonPath)) {
1797
+ if (!fs14.existsSync(moduleJsonPath)) {
1343
1798
  throw new OniroError(`Could not find module.json5 at ${moduleJsonPath}`);
1344
1799
  }
1345
1800
  const moduleJson = readJson5(moduleJsonPath);
1346
- if (!moduleJson.module?.mainElement) {
1347
- throw new OniroError("mainElement not found in module.json5");
1348
- }
1349
- return moduleJson.module.mainElement;
1801
+ const module2 = moduleJson.module ?? {};
1802
+ if (abilityName) {
1803
+ const match = module2.abilities?.find((a) => a.name === abilityName);
1804
+ if (match?.name) return match.name;
1805
+ throw new OniroError(`Ability "${abilityName}" not found in module.json5 at ${moduleJsonPath}`);
1806
+ }
1807
+ if (module2.mainElement) return module2.mainElement;
1808
+ const abilities = module2.abilities ?? [];
1809
+ const visible = abilities.find((a) => a.visible && a.name);
1810
+ if (visible?.name) return visible.name;
1811
+ const first = abilities.find((a) => a.name);
1812
+ if (first?.name) return first.name;
1813
+ throw new OniroError("mainElement not found in module.json5");
1350
1814
  }
1351
1815
 
1352
1816
  // src/hdc/app.ts
1353
- var fs14 = __toESM(require("fs"), 1);
1354
- var path14 = __toESM(require("path"), 1);
1355
- var import_node_child_process4 = require("child_process");
1356
- function execPromise2(cmd, logger) {
1357
- return new Promise((resolve2, reject) => {
1358
- (0, import_node_child_process4.exec)(cmd, (error, stdout, stderr) => {
1359
- if (stdout?.trim()) logger.info(`[hdc] ${stdout.trim()}`);
1360
- if (stderr?.trim()) logger.warn(`[hdc] ${stderr.trim()}`);
1361
- if (error) {
1362
- logger.error(`[hdc] ${error.message}`);
1363
- reject(error);
1364
- } else {
1365
- resolve2();
1366
- }
1367
- });
1368
- });
1369
- }
1370
1817
  async function installApp(opts) {
1371
- const logger = opts.logger ?? noopLogger;
1818
+ const logger = scopedLogger(opts.logger ?? noopLogger, "hdc");
1372
1819
  const relativeHapPath = opts.hapPath ?? opts.config.get("hapPath", defaultPaths.hapPath);
1373
1820
  const hapPath = path14.isAbsolute(relativeHapPath) ? relativeHapPath : path14.join(opts.projectDir, relativeHapPath);
1374
- if (!fs14.existsSync(hapPath)) {
1821
+ if (!fs15.existsSync(hapPath)) {
1375
1822
  throw new OniroError(`HAP file not found at: ${hapPath}. Build and sign the app first.`);
1376
1823
  }
1377
- const hdc = getHdcPath(opts.config);
1378
- await execPromise2(`"${hdc}" install "${hapPath}"`, logger);
1824
+ const result = await hdcExec({ config: opts.config, args: ["install", hapPath], timeoutMs: 3e5 });
1825
+ if (result.stdout.trim()) logger.info(result.stdout.trim());
1826
+ if (result.stderr.trim()) logger.warn(result.stderr.trim());
1827
+ ensureOk(result, `hdc install ${hapPath}`);
1828
+ let bundleName = "";
1829
+ try {
1830
+ bundleName = getBundleName(opts.projectDir);
1831
+ } catch {
1832
+ }
1833
+ return { installed: true, bundleName, hapPath, output: `${result.stdout}${result.stderr}`.trim() };
1834
+ }
1835
+ function detectAaStartFailure(output) {
1836
+ return /failed to start ability|Error Code:\s*\d|Error Message:/i.test(output) ? output.trim() : null;
1379
1837
  }
1380
1838
  async function launchApp(opts) {
1381
- const logger = opts.logger ?? noopLogger;
1839
+ const logger = scopedLogger(opts.logger ?? noopLogger, "hdc");
1382
1840
  const bundleName = getBundleName(opts.projectDir);
1383
- const mainAbility = getMainAbility(opts.projectDir, opts.moduleName);
1384
- const hdc = getHdcPath(opts.config);
1385
- await execPromise2(`"${hdc}" shell aa start -a ${mainAbility} -b ${bundleName}`, logger);
1841
+ const mainAbility = getMainAbility(opts.projectDir, opts.moduleName, opts.abilityName);
1842
+ const result = await hdcExec({
1843
+ config: opts.config,
1844
+ args: ["shell", "aa", "start", "-a", mainAbility, "-b", bundleName]
1845
+ });
1846
+ if (result.stdout.trim()) logger.info(result.stdout.trim());
1847
+ if (result.stderr.trim()) logger.warn(result.stderr.trim());
1848
+ ensureOk(result, `hdc shell aa start -a ${mainAbility} -b ${bundleName}`);
1849
+ const failure = detectAaStartFailure(`${result.stdout}
1850
+ ${result.stderr}`);
1851
+ if (failure) {
1852
+ throw new OniroError(`Failed to launch ${bundleName} (${mainAbility}): ${failure}`);
1853
+ }
1386
1854
  }
1387
1855
  function listRunningProcesses(config, options = {}) {
1388
1856
  const logger = options.logger ?? noopLogger;
1389
1857
  const timeoutMs = options.timeoutMs ?? 1e3;
1390
1858
  const hdc = getHdcPath(config);
1391
1859
  return new Promise((resolve2, reject) => {
1392
- const proc = (0, import_node_child_process4.spawn)(hdc, ["track-jpid"]);
1860
+ const proc = (0, import_node_child_process5.spawn)(hdc, ["track-jpid"]);
1393
1861
  const processes = [];
1394
1862
  let resolved = false;
1395
1863
  proc.stdout.on("data", (data) => {
@@ -1443,14 +1911,395 @@ async function findAppProcessId(config, projectDir, logger) {
1443
1911
  const result = await listRunningProcesses(config, { targetProcessName: bundleName, logger });
1444
1912
  return result;
1445
1913
  }
1914
+ async function findRunningProcess(opts) {
1915
+ const safe = opts.bundle.replace(/'/g, `'\\''`);
1916
+ const res = await shell({
1917
+ config: opts.config,
1918
+ command: `pidof '${safe}'`,
1919
+ deviceSerial: opts.deviceSerial,
1920
+ timeoutMs: opts.timeoutMs ?? 1e4,
1921
+ logger: opts.logger
1922
+ });
1923
+ const pid = res.stdout.trim().split(/\s+/).filter(Boolean)[0];
1924
+ if (res.code !== 0 || !pid || !/^\d+$/.test(pid)) return null;
1925
+ return { pid, name: opts.bundle };
1926
+ }
1927
+ async function uninstallApp(opts) {
1928
+ const logger = scopedLogger(opts.logger ?? noopLogger, "hdc");
1929
+ const res = await hdcExec({
1930
+ config: opts.config,
1931
+ args: ["uninstall", opts.bundle],
1932
+ deviceSerial: opts.deviceSerial,
1933
+ timeoutMs: opts.timeoutMs ?? 12e4
1934
+ });
1935
+ if (res.stdout.trim()) logger.info(res.stdout.trim());
1936
+ if (res.stderr.trim()) logger.warn(res.stderr.trim());
1937
+ ensureOk(res, `hdc uninstall ${opts.bundle}`);
1938
+ }
1939
+ async function forceStop(opts) {
1940
+ const res = await shell({
1941
+ config: opts.config,
1942
+ command: `aa force-stop ${opts.bundle}`,
1943
+ deviceSerial: opts.deviceSerial,
1944
+ timeoutMs: opts.timeoutMs ?? 3e4,
1945
+ logger: opts.logger
1946
+ });
1947
+ ensureOk(res, `hdc shell aa force-stop ${opts.bundle}`);
1948
+ }
1949
+
1950
+ // src/hdc/lifecycle.ts
1951
+ async function isReachable(config, deviceSerial) {
1952
+ try {
1953
+ const res = await shell({ config, command: "echo oniro_ping", deviceSerial, timeoutMs: 5e3 });
1954
+ return res.code === 0 && res.stdout.includes("oniro_ping");
1955
+ } catch {
1956
+ return false;
1957
+ }
1958
+ }
1959
+ var TCP_SERIAL_RE = /^.+:\d+$/;
1960
+ async function reconnectIfTcp(config, deviceSerial, logger) {
1961
+ if (!deviceSerial || !TCP_SERIAL_RE.test(deviceSerial)) return;
1962
+ try {
1963
+ await hdcExec({ config, args: ["tconn", deviceSerial], timeoutMs: 5e3, logger });
1964
+ } catch {
1965
+ }
1966
+ }
1967
+ async function waitForBundle(opts) {
1968
+ const logger = scopedLogger(opts.logger ?? noopLogger, "hdc");
1969
+ const timeoutMs = opts.timeoutMs ?? 18e4;
1970
+ await waitForCondition({
1971
+ probe: async () => {
1972
+ await reconnectIfTcp(opts.config, opts.deviceSerial, logger);
1973
+ return await findRunningProcess({ config: opts.config, bundle: opts.bundle, deviceSerial: opts.deviceSerial, timeoutMs: 8e3 }) !== null;
1974
+ },
1975
+ timeoutMs,
1976
+ pollMs: 2e3,
1977
+ abortSignal: opts.abortSignal,
1978
+ onHeartbeat: (attempt, remaining) => logger.info(`waiting for ${opts.bundle} (attempt ${attempt}, ${Math.floor(remaining / 1e3)}s left)`),
1979
+ timeoutMessage: `Bundle ${opts.bundle} did not start within ${timeoutMs}ms.`
1980
+ });
1981
+ }
1982
+ async function waitForBoot(opts) {
1983
+ const logger = scopedLogger(opts.logger ?? noopLogger, "hdc");
1984
+ const timeoutMs = opts.timeoutMs ?? 18e4;
1985
+ await waitForCondition({
1986
+ probe: async () => {
1987
+ await reconnectIfTcp(opts.config, opts.deviceSerial, logger);
1988
+ if (!await isReachable(opts.config, opts.deviceSerial)) return false;
1989
+ if (!opts.untilPidOf) return true;
1990
+ return await findRunningProcess({ config: opts.config, bundle: opts.untilPidOf, deviceSerial: opts.deviceSerial, timeoutMs: 8e3 }) !== null;
1991
+ },
1992
+ timeoutMs,
1993
+ pollMs: 2e3,
1994
+ abortSignal: opts.abortSignal,
1995
+ onHeartbeat: (attempt, remaining) => logger.info(`waiting for boot (attempt ${attempt}, ${Math.floor(remaining / 1e3)}s left)`),
1996
+ timeoutMessage: `Device did not boot within ${timeoutMs}ms.`
1997
+ });
1998
+ }
1999
+ async function reboot(opts) {
2000
+ const logger = scopedLogger(opts.logger ?? noopLogger, "hdc");
2001
+ const mode = opts.mode ?? "system";
2002
+ const timeoutMs = opts.timeoutMs ?? 18e4;
2003
+ logger.info(`Rebooting device (${mode})...`);
2004
+ try {
2005
+ if (mode === "system") {
2006
+ await paramSet(opts.config, "ohos.startup.powerctrl", "reboot", {
2007
+ deviceSerial: opts.deviceSerial,
2008
+ timeoutMs: 15e3,
2009
+ logger: opts.logger
2010
+ });
2011
+ } else {
2012
+ await shell({ config: opts.config, command: `reboot ${mode}`, deviceSerial: opts.deviceSerial, timeoutMs: 15e3 });
2013
+ }
2014
+ } catch (err) {
2015
+ logger.debug(`reboot command did not return cleanly: ${err.message}`);
2016
+ }
2017
+ if (mode !== "system" || !opts.waitForBundle) return;
2018
+ try {
2019
+ await waitForCondition({
2020
+ probe: async () => !await isReachable(opts.config, opts.deviceSerial),
2021
+ timeoutMs: Math.min(timeoutMs, 3e4),
2022
+ pollMs: 1500,
2023
+ abortSignal: opts.abortSignal,
2024
+ timeoutMessage: "device did not become unreachable"
2025
+ });
2026
+ } catch {
2027
+ }
2028
+ await waitForBundle({
2029
+ config: opts.config,
2030
+ bundle: opts.waitForBundle,
2031
+ deviceSerial: opts.deviceSerial,
2032
+ timeoutMs,
2033
+ abortSignal: opts.abortSignal,
2034
+ logger: opts.logger
2035
+ });
2036
+ }
2037
+
2038
+ // src/hdc/screen.ts
2039
+ var fs16 = __toESM(require("fs"), 1);
2040
+ var os9 = __toESM(require("os"), 1);
2041
+ var path15 = __toESM(require("path"), 1);
2042
+ var import_node_crypto = require("crypto");
2043
+ function tmpName(ext) {
2044
+ return path15.join(os9.tmpdir(), `oniro_screen_${(0, import_node_crypto.randomBytes)(6).toString("hex")}.${ext}`);
2045
+ }
2046
+ async function takeScreenshot(opts) {
2047
+ const remote = "/data/local/tmp/oniro_screenshot.jpeg";
2048
+ const timeoutMs = opts.timeoutMs ?? 3e4;
2049
+ const cap = await shell({ config: opts.config, command: `snapshot_display -f ${remote}`, deviceSerial: opts.deviceSerial, timeoutMs });
2050
+ ensureOk(cap, `snapshot_display -f ${remote}`);
2051
+ const local = tmpName("jpeg");
2052
+ try {
2053
+ await recvFile({ config: opts.config, remote, local, deviceSerial: opts.deviceSerial, timeoutMs });
2054
+ const pixels = await fs16.promises.readFile(local);
2055
+ const size = await getDisplaySize(opts.config, { deviceSerial: opts.deviceSerial }) ?? { width: 0, height: 0 };
2056
+ return { pixels, width: size.width, height: size.height };
2057
+ } finally {
2058
+ fs16.promises.unlink(local).catch(() => {
2059
+ });
2060
+ void shell({ config: opts.config, command: `rm -f ${remote}`, deviceSerial: opts.deviceSerial }).catch(() => {
2061
+ });
2062
+ }
2063
+ }
2064
+ async function captureBurst(opts) {
2065
+ const count = Math.max(1, Math.floor(opts.count));
2066
+ const remoteDir = `/data/local/tmp/oniro_burst_${(0, import_node_crypto.randomBytes)(6).toString("hex")}`;
2067
+ const sleepSec = opts.intervalMs > 0 ? (opts.intervalMs / 1e3).toFixed(3) : "0";
2068
+ const loop = `mkdir -p ${remoteDir}; i=0; while [ $i -lt ${count} ]; do snapshot_display -f ${remoteDir}/f$i.jpeg; [ ${count} -gt 1 ] && sleep ${sleepSec}; i=$((i+1)); done`;
2069
+ const cap = await shell({ config: opts.config, command: loop, deviceSerial: opts.deviceSerial, timeoutMs: opts.timeoutMs ?? 12e4 });
2070
+ ensureOk(cap, "snapshot_display burst");
2071
+ const frames = [];
2072
+ try {
2073
+ for (let i = 0; i < count; i++) {
2074
+ const local = tmpName(`burst_${i}.jpeg`);
2075
+ try {
2076
+ await recvFile({ config: opts.config, remote: `${remoteDir}/f${i}.jpeg`, local, deviceSerial: opts.deviceSerial });
2077
+ frames.push(await fs16.promises.readFile(local));
2078
+ } finally {
2079
+ fs16.promises.unlink(local).catch(() => {
2080
+ });
2081
+ }
2082
+ }
2083
+ } finally {
2084
+ void shell({ config: opts.config, command: `rm -rf ${remoteDir}`, deviceSerial: opts.deviceSerial }).catch(() => {
2085
+ });
2086
+ }
2087
+ return frames;
2088
+ }
2089
+
2090
+ // src/hdc/input.ts
2091
+ function reqNum(value, name, type) {
2092
+ if (value === void 0 || Number.isNaN(value)) {
2093
+ throw new OniroError(`send input "${type}" requires a numeric ${name}.`);
2094
+ }
2095
+ return value;
2096
+ }
2097
+ function buildInputCommand(opts) {
2098
+ switch (opts.type) {
2099
+ case "click":
2100
+ case "doubleClick":
2101
+ case "longClick":
2102
+ return `uitest uiInput ${opts.type} ${reqNum(opts.pxX, "pxX", opts.type)} ${reqNum(opts.pxY, "pxY", opts.type)}`;
2103
+ case "swipe":
2104
+ case "drag":
2105
+ case "fling": {
2106
+ const base = `uitest uiInput ${opts.type} ${reqNum(opts.pxX, "pxX", opts.type)} ${reqNum(opts.pxY, "pxY", opts.type)} ${reqNum(opts.pxX2, "pxX2", opts.type)} ${reqNum(opts.pxY2, "pxY2", opts.type)}`;
2107
+ return opts.speed !== void 0 ? `${base} ${opts.speed}` : base;
2108
+ }
2109
+ case "keyEvent":
2110
+ if (opts.key === void 0 || opts.key === "") throw new OniroError('send input "keyEvent" requires a key.');
2111
+ return `uitest uiInput keyEvent ${opts.key}`;
2112
+ case "inputText": {
2113
+ const t = String(opts.text ?? "").replace(/'/g, `'\\''`);
2114
+ if (opts.pxX !== void 0 && opts.pxY !== void 0) {
2115
+ return `uitest uiInput inputText ${opts.pxX} ${opts.pxY} '${t}'`;
2116
+ }
2117
+ return `uitest uiInput text '${t}'`;
2118
+ }
2119
+ }
2120
+ }
2121
+ async function sendInput(opts) {
2122
+ const cmd = buildInputCommand(opts);
2123
+ const res = await shell({ config: opts.config, command: cmd, deviceSerial: opts.deviceSerial, timeoutMs: 3e4, logger: opts.logger });
2124
+ ensureOk(res, `hdc shell ${cmd}`);
2125
+ }
2126
+ function buildGestureCommands(waypoints) {
2127
+ if (waypoints.length < 2) {
2128
+ throw new OniroError("sendGesture requires at least two waypoints.");
2129
+ }
2130
+ const cmds = [];
2131
+ for (let i = 1; i < waypoints.length; i++) {
2132
+ const a = waypoints[i - 1];
2133
+ const b = waypoints[i];
2134
+ const dt = b.t - a.t;
2135
+ const dist = Math.hypot(b.x - a.x, b.y - a.y);
2136
+ let cmd = `uitest uiInput drag ${a.x} ${a.y} ${b.x} ${b.y}`;
2137
+ if (dt > 0 && dist > 0) cmd += ` ${Math.round(dist / (dt / 1e3))}`;
2138
+ cmds.push(cmd);
2139
+ }
2140
+ return cmds;
2141
+ }
2142
+ function buildRawTouchCommand(events) {
2143
+ if (events.length === 0) throw new OniroError("sendRawTouch requires at least one event.");
2144
+ const ops = [];
2145
+ let prev = null;
2146
+ for (const ev of events) {
2147
+ if (prev && ev.t > prev.t) ops.push(`-i ${ev.t - prev.t}`);
2148
+ if (ev.type === "down") ops.push(`-d ${ev.x} ${ev.y}`);
2149
+ else if (ev.type === "up") ops.push(`-u ${ev.x} ${ev.y}`);
2150
+ else ops.push(`-m ${prev ? `${prev.x} ${prev.y} ` : ""}${ev.x} ${ev.y}`);
2151
+ prev = ev;
2152
+ }
2153
+ return `uinput -T ${ops.join(" ")}`;
2154
+ }
2155
+ function waypointsToRawEvents(waypoints, holdStartMs = 0, holdEndMs = 0) {
2156
+ if (waypoints.length < 2) throw new OniroError("sendGesture requires at least two waypoints.");
2157
+ const first = waypoints[0];
2158
+ const events = [{ type: "down", x: first.x, y: first.y, t: 0 }];
2159
+ for (let i = 1; i < waypoints.length; i++) {
2160
+ const w = waypoints[i];
2161
+ events.push({ type: "move", x: w.x, y: w.y, t: w.t + holdStartMs });
2162
+ }
2163
+ const last = waypoints[waypoints.length - 1];
2164
+ events.push({ type: "up", x: last.x, y: last.y, t: last.t + holdStartMs + holdEndMs });
2165
+ return events;
2166
+ }
2167
+ async function sendGesture(opts) {
2168
+ const wantsHold = (opts.holdStartMs ?? 0) > 0 || (opts.holdEndMs ?? 0) > 0;
2169
+ if (wantsHold) {
2170
+ await sendRawTouch({
2171
+ config: opts.config,
2172
+ events: waypointsToRawEvents(opts.waypoints, opts.holdStartMs, opts.holdEndMs),
2173
+ deviceSerial: opts.deviceSerial,
2174
+ logger: opts.logger
2175
+ });
2176
+ return;
2177
+ }
2178
+ for (const cmd of buildGestureCommands(opts.waypoints)) {
2179
+ const res = await shell({ config: opts.config, command: cmd, deviceSerial: opts.deviceSerial, timeoutMs: 3e4, logger: opts.logger });
2180
+ ensureOk(res, `hdc shell ${cmd}`);
2181
+ }
2182
+ }
2183
+ async function sendRawTouch(opts) {
2184
+ const cmd = buildRawTouchCommand(opts.events);
2185
+ const res = await shell({ config: opts.config, command: cmd, deviceSerial: opts.deviceSerial, timeoutMs: 3e4, logger: opts.logger });
2186
+ ensureOk(res, `hdc shell ${cmd}`);
2187
+ }
2188
+
2189
+ // src/hdc/dump.ts
2190
+ var fs17 = __toESM(require("fs"), 1);
2191
+ var os10 = __toESM(require("os"), 1);
2192
+ var path16 = __toESM(require("path"), 1);
2193
+ var import_node_crypto2 = require("crypto");
2194
+ function parseBounds(s) {
2195
+ const m = /^\[(-?\d+),(-?\d+)\]\[(-?\d+),(-?\d+)\]$/.exec(s ?? "");
2196
+ if (!m) return null;
2197
+ return [Number(m[1]), Number(m[2]), Number(m[3]), Number(m[4])];
2198
+ }
2199
+ var STRUCTURAL_TYPES = /* @__PURE__ */ new Set([
2200
+ "Stack",
2201
+ "Column",
2202
+ "Row",
2203
+ "Flex",
2204
+ "__Common__",
2205
+ "Grid",
2206
+ "GridItem",
2207
+ "List",
2208
+ "ListItem",
2209
+ "Scroll",
2210
+ "Swiper",
2211
+ "SwiperContent",
2212
+ "Navigator",
2213
+ "Tabs",
2214
+ "TabContent",
2215
+ "RelativeContainer",
2216
+ "WaterFlow",
2217
+ "FlowItem"
2218
+ ]);
2219
+ var FLAG_MAP = [
2220
+ ["clickable", "click"],
2221
+ ["checkable", "check"],
2222
+ ["checked", "checked"],
2223
+ ["selected", "sel"],
2224
+ ["focused", "focus"],
2225
+ ["scrollable", "scroll"],
2226
+ ["longClickable", "longClick"]
2227
+ ];
2228
+ function pruneLayout(node, display) {
2229
+ const a = node.attributes ?? {};
2230
+ const px = parseBounds(a.bounds);
2231
+ const out = {};
2232
+ if (px && display) {
2233
+ const w = display.width || 1;
2234
+ const h = display.height || 1;
2235
+ out.b = [
2236
+ +(px[0] / w).toFixed(3),
2237
+ +(px[1] / h).toFixed(3),
2238
+ +(px[2] / w).toFixed(3),
2239
+ +(px[3] / h).toFixed(3)
2240
+ ];
2241
+ out.c = [+((px[0] + px[2]) / 2 / w).toFixed(3), +((px[1] + px[3]) / 2 / h).toFixed(3)];
2242
+ }
2243
+ if (a.type && !STRUCTURAL_TYPES.has(a.type)) out.type = a.type;
2244
+ if (a.text) out.text = a.text;
2245
+ if (a.description) out.desc = a.description;
2246
+ if (a.id) out.id = a.id;
2247
+ if (a.key && a.key !== a.id) out.key = a.key;
2248
+ if (a.bundleName) out.bundle = a.bundleName;
2249
+ for (const [attr, short] of FLAG_MAP) {
2250
+ if (a[attr] === "true") out[short] = true;
2251
+ }
2252
+ const children = [];
2253
+ for (const c of node.children ?? []) {
2254
+ const pc = pruneLayout(c, display);
2255
+ if (pc) {
2256
+ if (Array.isArray(pc)) children.push(...pc);
2257
+ else children.push(pc);
2258
+ }
2259
+ }
2260
+ const hasSignal = Boolean(
2261
+ out.text || out.desc || out.id || out.key || out.click || out.scroll || out.check || out.longClick || out.bundle || out.type && out.type !== "root"
2262
+ );
2263
+ if (!hasSignal && children.length === 0) return null;
2264
+ if (!hasSignal) return children.length === 1 ? children[0] : children;
2265
+ if (children.length) out.children = children;
2266
+ return out;
2267
+ }
2268
+ async function dumpLayout(opts) {
2269
+ const timeoutMs = opts.timeoutMs ?? 3e4;
2270
+ const bundleArg = opts.bundleName ? ` -b '${opts.bundleName.replace(/'/g, `'\\''`)}'` : "";
2271
+ const dump = await shell({ config: opts.config, command: `uitest dumpLayout${bundleArg}`, deviceSerial: opts.deviceSerial, timeoutMs });
2272
+ ensureOk(dump, "uitest dumpLayout");
2273
+ const m = dump.stdout.match(/saved to:\s*(\S+)/i);
2274
+ if (!m) throw new OniroError(`Could not parse dumpLayout output:
2275
+ ${dump.stdout}`);
2276
+ const remote = m[1];
2277
+ const local = path16.join(os10.tmpdir(), `oniro_layout_${(0, import_node_crypto2.randomBytes)(6).toString("hex")}.json`);
2278
+ try {
2279
+ await recvFile({ config: opts.config, remote, local, deviceSerial: opts.deviceSerial, timeoutMs });
2280
+ const parsed = JSON.parse(await fs17.promises.readFile(local, "utf8"));
2281
+ const display = await getDisplaySize(opts.config, { deviceSerial: opts.deviceSerial });
2282
+ if (opts.prune === false) {
2283
+ return { display, tree: parsed };
2284
+ }
2285
+ const prunedRaw = pruneLayout(parsed, display);
2286
+ const tree = Array.isArray(prunedRaw) ? { children: prunedRaw } : prunedRaw ?? { children: [] };
2287
+ return { display, tree };
2288
+ } finally {
2289
+ fs17.promises.unlink(local).catch(() => {
2290
+ });
2291
+ void shell({ config: opts.config, command: `rm -f ${remote}`, deviceSerial: opts.deviceSerial }).catch(() => {
2292
+ });
2293
+ }
2294
+ }
1446
2295
 
1447
2296
  // src/hdc/hilog.ts
1448
- var import_node_child_process5 = require("child_process");
2297
+ var import_node_child_process6 = require("child_process");
1449
2298
  function setHilogLevel(opts) {
1450
2299
  const logger = opts.logger ?? noopLogger;
1451
2300
  const hdc = getHdcPath(opts.config);
1452
2301
  return new Promise((resolve2, reject) => {
1453
- const child = (0, import_node_child_process5.spawn)(hdc, ["shell", "hilog", "-b", opts.level]);
2302
+ const child = (0, import_node_child_process6.spawn)(hdc, ["shell", "hilog", "-b", opts.level]);
1454
2303
  let stderr = "";
1455
2304
  child.stderr.on("data", (chunk) => {
1456
2305
  stderr += chunk.toString();
@@ -1472,11 +2321,16 @@ function setHilogLevel(opts) {
1472
2321
  }
1473
2322
  function streamHilog(opts) {
1474
2323
  const hdc = getHdcPath(opts.config);
1475
- const args = ["shell", "hilog"];
2324
+ const args = [];
2325
+ if (opts.deviceSerial) args.push("-t", opts.deviceSerial);
2326
+ args.push("shell", "hilog");
1476
2327
  if (opts.processId && opts.processId.trim() !== "") {
1477
2328
  args.push("-P", opts.processId.trim());
1478
2329
  }
1479
- return (0, import_node_child_process5.spawn)(hdc, args);
2330
+ if (opts.domain !== void 0 && `${opts.domain}` !== "") {
2331
+ args.push("-D", `${opts.domain}`);
2332
+ }
2333
+ return (0, import_node_child_process6.spawn)(hdc, args);
1480
2334
  }
1481
2335
  var HILOG_LINE_RE = /^(\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d{3})\s+(\d+)\s+(\d+)\s+([DIWEF])\s+(\S+):\s+(.*)$/;
1482
2336
  function parseHilogLine(line) {
@@ -1491,30 +2345,194 @@ function parseHilogLine(line) {
1491
2345
  message: match[6]
1492
2346
  };
1493
2347
  }
2348
+ function entryText(e) {
2349
+ return `${e.tag}: ${e.message}`;
2350
+ }
2351
+ function dedupEntries(entries) {
2352
+ const out = [];
2353
+ let lastKey = "";
2354
+ for (const e of entries) {
2355
+ const key = `${e.level}|${e.tag}|${e.message}`;
2356
+ if (key === lastKey) continue;
2357
+ out.push(e);
2358
+ lastKey = key;
2359
+ }
2360
+ return out;
2361
+ }
2362
+ async function resolveProcessId(config, bundle, deviceSerial) {
2363
+ if (!bundle) return void 0;
2364
+ const proc = await findRunningProcess({ config, bundle, deviceSerial });
2365
+ return proc?.pid;
2366
+ }
2367
+ function waitForLog(opts) {
2368
+ return new Promise((resolve2, reject) => {
2369
+ const deadline = Date.now() + opts.timeoutMs;
2370
+ let settled = false;
2371
+ let child = null;
2372
+ let reconnectTimer;
2373
+ let buf = "";
2374
+ const cleanup = () => {
2375
+ clearTimeout(timer);
2376
+ if (reconnectTimer) clearTimeout(reconnectTimer);
2377
+ opts.abortSignal?.removeEventListener("abort", onAbort);
2378
+ if (child) {
2379
+ try {
2380
+ child.kill("SIGKILL");
2381
+ } catch {
2382
+ }
2383
+ child = null;
2384
+ }
2385
+ };
2386
+ const finish = (entry) => {
2387
+ if (settled) return;
2388
+ settled = true;
2389
+ cleanup();
2390
+ resolve2(entry);
2391
+ };
2392
+ const fail = (err) => {
2393
+ if (settled) return;
2394
+ settled = true;
2395
+ cleanup();
2396
+ reject(err);
2397
+ };
2398
+ const onAbort = () => fail(new CancelledError("waitForLog cancelled."));
2399
+ const timer = setTimeout(
2400
+ () => fail(new OniroError(`No log line matched ${String(opts.pattern)} within ${opts.timeoutMs}ms.`)),
2401
+ opts.timeoutMs
2402
+ );
2403
+ opts.abortSignal?.addEventListener("abort", onAbort, { once: true });
2404
+ const spawnStream = async () => {
2405
+ if (settled) return;
2406
+ const processId = await resolveProcessId(opts.config, opts.bundle, opts.deviceSerial);
2407
+ if (settled) return;
2408
+ const stream = streamHilog({ config: opts.config, processId, domain: opts.domain, deviceSerial: opts.deviceSerial });
2409
+ child = stream;
2410
+ stream.stdout.on("data", (d) => {
2411
+ buf += d.toString();
2412
+ let idx;
2413
+ while ((idx = buf.indexOf("\n")) >= 0) {
2414
+ const line = buf.slice(0, idx);
2415
+ buf = buf.slice(idx + 1);
2416
+ const entry = parseHilogLine(line);
2417
+ if (entry && opts.pattern.test(entryText(entry))) {
2418
+ finish(entry);
2419
+ return;
2420
+ }
2421
+ }
2422
+ });
2423
+ const onEnd = () => {
2424
+ if (settled || child !== stream) return;
2425
+ child = null;
2426
+ if (Date.now() < deadline) {
2427
+ reconnectTimer = setTimeout(() => void spawnStream(), 1e3);
2428
+ }
2429
+ };
2430
+ stream.once("close", onEnd);
2431
+ stream.once("error", onEnd);
2432
+ };
2433
+ void spawnStream();
2434
+ });
2435
+ }
2436
+ function watchLog(opts) {
2437
+ return new Promise((resolve2, reject) => {
2438
+ const matches = [];
2439
+ let settled = false;
2440
+ let child = null;
2441
+ let buf = "";
2442
+ const cleanup = () => {
2443
+ clearTimeout(timer);
2444
+ opts.abortSignal?.removeEventListener("abort", onAbort);
2445
+ if (child) {
2446
+ try {
2447
+ child.kill("SIGKILL");
2448
+ } catch {
2449
+ }
2450
+ child = null;
2451
+ }
2452
+ };
2453
+ const done = () => {
2454
+ if (settled) return;
2455
+ settled = true;
2456
+ cleanup();
2457
+ resolve2(opts.dedup === false ? matches : dedupEntries(matches));
2458
+ };
2459
+ const onAbort = () => {
2460
+ if (settled) return;
2461
+ settled = true;
2462
+ cleanup();
2463
+ reject(new CancelledError("watchLog cancelled."));
2464
+ };
2465
+ const timer = setTimeout(done, opts.durationMs);
2466
+ opts.abortSignal?.addEventListener("abort", onAbort, { once: true });
2467
+ void (async () => {
2468
+ const processId = await resolveProcessId(opts.config, opts.bundle, opts.deviceSerial);
2469
+ if (settled) return;
2470
+ const stream = streamHilog({ config: opts.config, processId, domain: opts.domain, deviceSerial: opts.deviceSerial });
2471
+ child = stream;
2472
+ stream.stdout.on("data", (d) => {
2473
+ buf += d.toString();
2474
+ let idx;
2475
+ while ((idx = buf.indexOf("\n")) >= 0) {
2476
+ const line = buf.slice(0, idx);
2477
+ buf = buf.slice(idx + 1);
2478
+ const entry = parseHilogLine(line);
2479
+ if (entry && opts.pattern.test(entryText(entry))) matches.push(entry);
2480
+ }
2481
+ });
2482
+ })();
2483
+ });
2484
+ }
2485
+ async function dumpLog(opts) {
2486
+ let pipe = "hilog -x";
2487
+ if (opts.domain !== void 0 && `${opts.domain}` !== "") pipe += ` -D ${opts.domain}`;
2488
+ if (opts.bundle) {
2489
+ const proc = await findRunningProcess({ config: opts.config, bundle: opts.bundle, deviceSerial: opts.deviceSerial });
2490
+ if (!proc) return [];
2491
+ pipe += ` | grep -E '\\b${proc.pid}\\b'`;
2492
+ }
2493
+ if (opts.grep) {
2494
+ const safeGrep = opts.grep.replace(/'/g, `'\\''`);
2495
+ pipe += ` | grep -E '${safeGrep}'`;
2496
+ }
2497
+ if (opts.lines && opts.lines > 0) pipe += ` | tail -n ${Math.floor(opts.lines)}`;
2498
+ const res = await shell({
2499
+ config: opts.config,
2500
+ command: pipe,
2501
+ deviceSerial: opts.deviceSerial,
2502
+ timeoutMs: opts.timeoutMs ?? 3e4,
2503
+ logger: opts.logger
2504
+ });
2505
+ return res.stdout.split("\n").map((line) => parseHilogLine(line)).filter((e) => e !== null);
2506
+ }
1494
2507
 
1495
2508
  // src/build/runHvigorw.ts
1496
- var fs15 = __toESM(require("fs"), 1);
1497
- var path15 = __toESM(require("path"), 1);
1498
- var import_node_child_process6 = require("child_process");
2509
+ var fs18 = __toESM(require("fs"), 1);
2510
+ var path17 = __toESM(require("path"), 1);
2511
+ var import_node_child_process7 = require("child_process");
2512
+ function buildHvigorwArgs(opts) {
2513
+ const args = [opts.task ?? "assembleHap", "--mode", "module"];
2514
+ args.push("-p", `product=${opts.product ?? "default"}`);
2515
+ if (opts.module) args.push("-p", `module=${opts.module}`);
2516
+ if (opts.buildMode) args.push("-p", `buildMode=${opts.buildMode}`);
2517
+ args.push("--stacktrace", "--no-daemon");
2518
+ if (opts.parallel === false) args.push("--no-parallel");
2519
+ if (opts.extraArgs?.length) args.push(...opts.extraArgs);
2520
+ return args;
2521
+ }
1499
2522
  function runHvigorw(opts) {
1500
2523
  const { config, projectDir } = opts;
1501
2524
  const logger = opts.logger ?? noopLogger;
1502
- if (!fs15.existsSync(path15.join(projectDir, "build-profile.json5"))) {
2525
+ if (!fs18.existsSync(path17.join(projectDir, "build-profile.json5"))) {
1503
2526
  throw new OniroError(`Not an OpenHarmony project: ${projectDir} (build-profile.json5 not found).`);
1504
2527
  }
1505
- if (!fs15.existsSync(getCmdToolsPath(config))) {
2528
+ if (!fs18.existsSync(getCmdToolsPath(config))) {
1506
2529
  throw new CmdToolsNotInstalledError(getCmdToolsPath(config));
1507
2530
  }
1508
2531
  const hvigorw = getHvigorwPath(config, projectDir);
1509
- const args = [opts.task ?? "assembleHap", "--mode", "module"];
1510
- args.push("-p", `product=${opts.product ?? "default"}`);
1511
- if (opts.module) args.push("-p", `module=${opts.module}`);
1512
- if (opts.buildMode) args.push("-p", `buildMode=${opts.buildMode}`);
1513
- args.push("--stacktrace", "--no-parallel", "--no-daemon");
1514
- if (opts.extraArgs?.length) args.push(...opts.extraArgs);
2532
+ const args = buildHvigorwArgs(opts);
1515
2533
  if (process.platform !== "win32") {
1516
2534
  try {
1517
- fs15.chmodSync(hvigorw, 493);
2535
+ fs18.chmodSync(hvigorw, 493);
1518
2536
  } catch {
1519
2537
  }
1520
2538
  }
@@ -1523,8 +2541,25 @@ function runHvigorw(opts) {
1523
2541
  OHOS_BASE_SDK_HOME: getOhosBaseSdkHome(config)
1524
2542
  };
1525
2543
  return new Promise((resolve2, reject) => {
2544
+ if (opts.abortSignal?.aborted) {
2545
+ reject(new CancelledError("hvigorw build cancelled."));
2546
+ return;
2547
+ }
1526
2548
  logger.info(`[build] ${hvigorw} ${args.join(" ")}`);
1527
- const child = (0, import_node_child_process6.spawn)(hvigorw, args, { cwd: projectDir, env, shell: false });
2549
+ const child = (0, import_node_child_process7.spawn)(hvigorw, args, { cwd: projectDir, env, shell: false });
2550
+ let settled = false;
2551
+ const cleanup = () => opts.abortSignal?.removeEventListener("abort", onAbort);
2552
+ const onAbort = () => {
2553
+ if (settled) return;
2554
+ settled = true;
2555
+ cleanup();
2556
+ try {
2557
+ child.kill("SIGKILL");
2558
+ } catch {
2559
+ }
2560
+ reject(new CancelledError("hvigorw build cancelled."));
2561
+ };
2562
+ opts.abortSignal?.addEventListener("abort", onAbort, { once: true });
1528
2563
  child.stdout.on("data", (chunk) => {
1529
2564
  const text = chunk.toString();
1530
2565
  opts.onOutput?.(text, "stdout");
@@ -1535,8 +2570,16 @@ function runHvigorw(opts) {
1535
2570
  opts.onOutput?.(text, "stderr");
1536
2571
  logger.warn(text.trimEnd());
1537
2572
  });
1538
- child.on("error", (err) => reject(new OniroError(`Failed to start hvigorw: ${err.message}`, err)));
2573
+ child.on("error", (err) => {
2574
+ if (settled) return;
2575
+ settled = true;
2576
+ cleanup();
2577
+ reject(new OniroError(`Failed to start hvigorw: ${err.message}`, err));
2578
+ });
1539
2579
  child.on("close", (code) => {
2580
+ if (settled) return;
2581
+ settled = true;
2582
+ cleanup();
1540
2583
  const exitCode = code ?? 1;
1541
2584
  if (exitCode === 0) resolve2({ exitCode });
1542
2585
  else reject(new OniroError(`hvigorw exited with code ${exitCode}`));
@@ -1544,6 +2587,137 @@ function runHvigorw(opts) {
1544
2587
  });
1545
2588
  }
1546
2589
 
2590
+ // src/build/discoverHaps.ts
2591
+ var fs19 = __toESM(require("fs"), 1);
2592
+ var path18 = __toESM(require("path"), 1);
2593
+ function moduleOf(projectDir, hapPath) {
2594
+ const rel = path18.relative(projectDir, hapPath);
2595
+ const first = rel.split(path18.sep)[0];
2596
+ return first && first !== ".." ? first : "(root)";
2597
+ }
2598
+ async function discoverHaps(opts) {
2599
+ const { projectDir } = opts;
2600
+ const maxDepth = opts.maxDepth ?? 8;
2601
+ const result = {};
2602
+ const walk = async (dir, depth) => {
2603
+ if (depth > maxDepth) return;
2604
+ const entries = await fs19.promises.readdir(dir, { withFileTypes: true }).catch(() => []);
2605
+ for (const e of entries) {
2606
+ const full = path18.join(dir, e.name);
2607
+ if (e.isDirectory()) {
2608
+ if (e.name === "node_modules" || e.name === "oh_modules" || e.name === ".git") continue;
2609
+ await walk(full, depth + 1);
2610
+ } else if (e.isFile() && e.name.endsWith(".hap")) {
2611
+ const moduleName = moduleOf(projectDir, full);
2612
+ const bucket = result[moduleName] ??= { signed: [], unsigned: [] };
2613
+ if (e.name.includes("unsigned")) bucket.unsigned.push(full);
2614
+ else if (e.name.includes("signed")) bucket.signed.push(full);
2615
+ }
2616
+ }
2617
+ };
2618
+ await walk(projectDir, 0);
2619
+ return result;
2620
+ }
2621
+
2622
+ // src/build/ohpm.ts
2623
+ var fs20 = __toESM(require("fs"), 1);
2624
+ var path19 = __toESM(require("path"), 1);
2625
+ function runOhpm(opts) {
2626
+ return runProcess({
2627
+ command: getOhpmPath(opts.config),
2628
+ args: [...opts.args],
2629
+ cwd: opts.projectDir,
2630
+ timeoutMs: opts.timeoutMs ?? 6e5,
2631
+ abortSignal: opts.abortSignal,
2632
+ onOutput: opts.onOutput,
2633
+ logger: opts.logger
2634
+ });
2635
+ }
2636
+ async function ensureOhModules(opts) {
2637
+ if (fs20.existsSync(path19.join(opts.projectDir, "oh_modules"))) {
2638
+ return { installed: false };
2639
+ }
2640
+ const res = await runOhpm({
2641
+ config: opts.config,
2642
+ projectDir: opts.projectDir,
2643
+ args: ["install", "--all"],
2644
+ abortSignal: opts.abortSignal,
2645
+ onOutput: opts.onOutput,
2646
+ logger: opts.logger
2647
+ });
2648
+ ensureOk(res, "ohpm install --all");
2649
+ return { installed: true };
2650
+ }
2651
+
2652
+ // src/build/codelinter.ts
2653
+ var FINDING_RE = /^(.*?):(\d+):(?:\d+:)?\s*(error|warning|warn|info)\b[:\s-]*(.*)$/i;
2654
+ var TRAILING_CODE_RE = /[([]([\w@/.\-]+)[)\]]\s*$/;
2655
+ function parseCodelinterFindings(output) {
2656
+ const findings = [];
2657
+ for (const raw of output.split("\n")) {
2658
+ const m = FINDING_RE.exec(raw.trim());
2659
+ if (!m) continue;
2660
+ let message = m[4].trim();
2661
+ let code = "";
2662
+ const codeMatch = TRAILING_CODE_RE.exec(message);
2663
+ if (codeMatch) {
2664
+ code = codeMatch[1];
2665
+ message = message.slice(0, codeMatch.index).trim();
2666
+ }
2667
+ findings.push({ file: m[1].trim(), line: Number(m[2]), severity: m[3].toLowerCase(), code, message });
2668
+ }
2669
+ return findings;
2670
+ }
2671
+ async function runCodelinter(opts) {
2672
+ const res = await runProcess({
2673
+ command: getCmdToolsBin(opts.config, "codelinter"),
2674
+ args: opts.files?.length ? [...opts.files] : [],
2675
+ cwd: opts.projectDir,
2676
+ timeoutMs: opts.timeoutMs ?? 6e5,
2677
+ abortSignal: opts.abortSignal,
2678
+ logger: opts.logger
2679
+ });
2680
+ const raw = `${res.stdout}${res.stderr}`;
2681
+ return { code: res.code, findings: parseCodelinterFindings(raw), raw };
2682
+ }
2683
+
2684
+ // src/build/buildHap.ts
2685
+ async function buildHap(opts) {
2686
+ const logger = scopedLogger(opts.logger ?? noopLogger, "build");
2687
+ const warnings = [];
2688
+ const start = Date.now();
2689
+ if (opts.autoInstallDeps !== false) {
2690
+ const r = await ensureOhModules({
2691
+ config: opts.config,
2692
+ projectDir: opts.projectDir,
2693
+ abortSignal: opts.abortSignal,
2694
+ onOutput: opts.onOutput,
2695
+ logger: opts.logger
2696
+ });
2697
+ if (r.installed) logger.info("ran ohpm install --all (oh_modules was missing)");
2698
+ }
2699
+ const { exitCode } = await runHvigorw({
2700
+ config: opts.config,
2701
+ projectDir: opts.projectDir,
2702
+ product: opts.product,
2703
+ module: opts.module,
2704
+ buildMode: opts.mode,
2705
+ task: opts.task,
2706
+ parallel: opts.parallel,
2707
+ abortSignal: opts.abortSignal,
2708
+ onOutput: opts.onOutput,
2709
+ logger: opts.logger
2710
+ });
2711
+ const discoveredHaps = await discoverHaps({ projectDir: opts.projectDir });
2712
+ const anySigned = Object.values(discoveredHaps).some((m) => m.signed.length > 0);
2713
+ if (!anySigned) {
2714
+ warnings.push(
2715
+ "No signed HAPs found \u2014 configure signingConfigs in build-profile.json5 (unsigned HAPs are rejected by most devices)."
2716
+ );
2717
+ }
2718
+ return { exitCode, durationMs: Date.now() - start, discoveredHaps, warnings };
2719
+ }
2720
+
1547
2721
  // src/project/validators.ts
1548
2722
  function isValidProjectName(name) {
1549
2723
  if (!name) return false;
@@ -1555,21 +2729,21 @@ function isValidBundleName(bundleName) {
1555
2729
  }
1556
2730
 
1557
2731
  // src/project/templates.ts
1558
- var fs16 = __toESM(require("fs"), 1);
1559
- var path16 = __toESM(require("path"), 1);
2732
+ var fs21 = __toESM(require("fs"), 1);
2733
+ var path20 = __toESM(require("path"), 1);
1560
2734
  function toHumanTemplateName(folderName) {
1561
2735
  return folderName.replace(/([a-z])([A-Z])/g, "$1 $2");
1562
2736
  }
1563
2737
  function listTemplates(templateRoot) {
1564
- if (!fs16.existsSync(templateRoot)) return [];
1565
- const entries = fs16.readdirSync(templateRoot, { withFileTypes: true });
2738
+ if (!fs21.existsSync(templateRoot)) return [];
2739
+ const entries = fs21.readdirSync(templateRoot, { withFileTypes: true });
1566
2740
  return entries.filter((e) => e.isDirectory()).map((e) => {
1567
- const dir = path16.join(templateRoot, e.name);
1568
- const metaPath = path16.join(dir, "template.json");
2741
+ const dir = path20.join(templateRoot, e.name);
2742
+ const metaPath = path20.join(dir, "template.json");
1569
2743
  let meta = {};
1570
- if (fs16.existsSync(metaPath)) {
2744
+ if (fs21.existsSync(metaPath)) {
1571
2745
  try {
1572
- meta = JSON.parse(fs16.readFileSync(metaPath, "utf8"));
2746
+ meta = JSON.parse(fs21.readFileSync(metaPath, "utf8"));
1573
2747
  } catch {
1574
2748
  }
1575
2749
  }
@@ -1584,57 +2758,57 @@ function listTemplates(templateRoot) {
1584
2758
  function validateTemplateLayout(templateDir, defaultModuleName) {
1585
2759
  const required = [
1586
2760
  "build-profile.json5",
1587
- path16.join("AppScope", "app.json5"),
1588
- path16.join(defaultModuleName, "src", "main", "module.json5"),
1589
- path16.join(defaultModuleName, "oh-package.json5"),
2761
+ path20.join("AppScope", "app.json5"),
2762
+ path20.join(defaultModuleName, "src", "main", "module.json5"),
2763
+ path20.join(defaultModuleName, "oh-package.json5"),
1590
2764
  "hvigorfile.ts"
1591
2765
  ];
1592
- return required.filter((rel) => !fs16.existsSync(path16.join(templateDir, rel)));
2766
+ return required.filter((rel) => !fs21.existsSync(path20.join(templateDir, rel)));
1593
2767
  }
1594
2768
 
1595
2769
  // src/project/createScaffold.ts
1596
- var fs18 = __toESM(require("fs"), 1);
1597
- var path17 = __toESM(require("path"), 1);
2770
+ var fs23 = __toESM(require("fs"), 1);
2771
+ var path21 = __toESM(require("path"), 1);
1598
2772
 
1599
2773
  // src/project/jsonHelpers.ts
1600
- var fs17 = __toESM(require("fs"), 1);
2774
+ var fs22 = __toESM(require("fs"), 1);
1601
2775
  var import_json54 = __toESM(require("json5"), 1);
1602
2776
  function readJson5File(filePath) {
1603
- return import_json54.default.parse(fs17.readFileSync(filePath, "utf8"));
2777
+ return import_json54.default.parse(fs22.readFileSync(filePath, "utf8"));
1604
2778
  }
1605
2779
  function writeJson5File(filePath, value) {
1606
- fs17.writeFileSync(filePath, JSON.stringify(value, null, 2) + "\n", "utf8");
2780
+ fs22.writeFileSync(filePath, JSON.stringify(value, null, 2) + "\n", "utf8");
1607
2781
  }
1608
2782
  function readJsonFile(filePath) {
1609
- return JSON.parse(fs17.readFileSync(filePath, "utf8"));
2783
+ return JSON.parse(fs22.readFileSync(filePath, "utf8"));
1610
2784
  }
1611
2785
  function writeJsonFile(filePath, value) {
1612
- fs17.writeFileSync(filePath, JSON.stringify(value, null, 2) + "\n", "utf8");
2786
+ fs22.writeFileSync(filePath, JSON.stringify(value, null, 2) + "\n", "utf8");
1613
2787
  }
1614
2788
 
1615
2789
  // src/project/createScaffold.ts
1616
2790
  var IGNORED_DIRS = /* @__PURE__ */ new Set(["oh_modules", "node_modules", "build", ".hvigor"]);
1617
2791
  async function pathExists(p) {
1618
2792
  try {
1619
- await fs18.promises.access(p);
2793
+ await fs23.promises.access(p);
1620
2794
  return true;
1621
2795
  } catch {
1622
2796
  return false;
1623
2797
  }
1624
2798
  }
1625
2799
  async function copyDirRecursive(srcDir, destDir) {
1626
- await fs18.promises.mkdir(destDir, { recursive: true });
1627
- const entries = await fs18.promises.readdir(srcDir, { withFileTypes: true });
2800
+ await fs23.promises.mkdir(destDir, { recursive: true });
2801
+ const entries = await fs23.promises.readdir(srcDir, { withFileTypes: true });
1628
2802
  for (const entry of entries) {
1629
- const src = path17.join(srcDir, entry.name);
1630
- const dest = path17.join(destDir, entry.name);
2803
+ const src = path21.join(srcDir, entry.name);
2804
+ const dest = path21.join(destDir, entry.name);
1631
2805
  if (entry.isDirectory()) {
1632
2806
  await copyDirRecursive(src, dest);
1633
2807
  } else if (entry.isSymbolicLink()) {
1634
2808
  continue;
1635
2809
  } else if (entry.isFile()) {
1636
2810
  if (entry.name === "template.json") continue;
1637
- await fs18.promises.copyFile(src, dest);
2811
+ await fs23.promises.copyFile(src, dest);
1638
2812
  }
1639
2813
  }
1640
2814
  }
@@ -1642,9 +2816,9 @@ async function normalizeJson5ToJson(projectDir, logger) {
1642
2816
  const stack = [projectDir];
1643
2817
  while (stack.length > 0) {
1644
2818
  const currentDir = stack.pop();
1645
- const entries = await fs18.promises.readdir(currentDir, { withFileTypes: true });
2819
+ const entries = await fs23.promises.readdir(currentDir, { withFileTypes: true });
1646
2820
  for (const entry of entries) {
1647
- const fullPath = path17.join(currentDir, entry.name);
2821
+ const fullPath = path21.join(currentDir, entry.name);
1648
2822
  if (entry.isDirectory()) {
1649
2823
  if (IGNORED_DIRS.has(entry.name)) continue;
1650
2824
  stack.push(fullPath);
@@ -1666,23 +2840,23 @@ function toJavaPropertiesPath(p) {
1666
2840
  function createOrUpdateLocalProperties(projectDir, sdkDir) {
1667
2841
  const content = `sdk.dir=${toJavaPropertiesPath(sdkDir)}
1668
2842
  `;
1669
- fs18.writeFileSync(path17.join(projectDir, "local.properties"), content, "utf8");
2843
+ fs23.writeFileSync(path21.join(projectDir, "local.properties"), content, "utf8");
1670
2844
  }
1671
2845
  function renameIfExists(fromPath, toPath) {
1672
- if (fs18.existsSync(fromPath) && fromPath !== toPath) {
1673
- fs18.renameSync(fromPath, toPath);
2846
+ if (fs23.existsSync(fromPath) && fromPath !== toPath) {
2847
+ fs23.renameSync(fromPath, toPath);
1674
2848
  }
1675
2849
  }
1676
2850
  function updateTemplateConfigs(projectDir, args, sdkBaseDir) {
1677
- const appJsonPath = path17.join(projectDir, "AppScope", "app.json5");
1678
- if (fs18.existsSync(appJsonPath)) {
2851
+ const appJsonPath = path21.join(projectDir, "AppScope", "app.json5");
2852
+ if (fs23.existsSync(appJsonPath)) {
1679
2853
  const appJson = readJson5File(appJsonPath);
1680
2854
  appJson.app = appJson.app ?? {};
1681
2855
  appJson.app.bundleName = args.bundleName;
1682
2856
  writeJson5File(appJsonPath, appJson);
1683
2857
  }
1684
- const stringsPath = path17.join(projectDir, "AppScope", "resources", "base", "element", "string.json");
1685
- if (fs18.existsSync(stringsPath)) {
2858
+ const stringsPath = path21.join(projectDir, "AppScope", "resources", "base", "element", "string.json");
2859
+ if (fs23.existsSync(stringsPath)) {
1686
2860
  const strings = readJsonFile(stringsPath);
1687
2861
  if (Array.isArray(strings.string)) {
1688
2862
  const appName = strings.string.find((s) => s.name === "app_name");
@@ -1690,8 +2864,8 @@ function updateTemplateConfigs(projectDir, args, sdkBaseDir) {
1690
2864
  }
1691
2865
  writeJsonFile(stringsPath, strings);
1692
2866
  }
1693
- const buildProfilePath = path17.join(projectDir, "build-profile.json5");
1694
- if (fs18.existsSync(buildProfilePath)) {
2867
+ const buildProfilePath = path21.join(projectDir, "build-profile.json5");
2868
+ if (fs23.existsSync(buildProfilePath)) {
1695
2869
  const buildProfile = readJson5File(buildProfilePath);
1696
2870
  buildProfile.app = buildProfile.app ?? {};
1697
2871
  if (Array.isArray(buildProfile.app.products) && buildProfile.app.products.length > 0) {
@@ -1704,23 +2878,23 @@ function updateTemplateConfigs(projectDir, args, sdkBaseDir) {
1704
2878
  }
1705
2879
  writeJson5File(buildProfilePath, buildProfile);
1706
2880
  }
1707
- const moduleJsonPath = path17.join(projectDir, args.moduleName, "src", "main", "module.json5");
1708
- if (fs18.existsSync(moduleJsonPath)) {
2881
+ const moduleJsonPath = path21.join(projectDir, args.moduleName, "src", "main", "module.json5");
2882
+ if (fs23.existsSync(moduleJsonPath)) {
1709
2883
  const moduleJson = readJson5File(moduleJsonPath);
1710
2884
  moduleJson.module = moduleJson.module ?? {};
1711
2885
  moduleJson.module.name = args.moduleName;
1712
2886
  writeJson5File(moduleJsonPath, moduleJson);
1713
2887
  }
1714
- const moduleOhPackagePath = path17.join(projectDir, args.moduleName, "oh-package.json5");
1715
- if (fs18.existsSync(moduleOhPackagePath)) {
2888
+ const moduleOhPackagePath = path21.join(projectDir, args.moduleName, "oh-package.json5");
2889
+ if (fs23.existsSync(moduleOhPackagePath)) {
1716
2890
  const pkg = readJson5File(moduleOhPackagePath);
1717
2891
  pkg.name = args.moduleName;
1718
2892
  writeJson5File(moduleOhPackagePath, pkg);
1719
2893
  }
1720
- const vscodeDir = path17.join(projectDir, ".vscode");
1721
- fs18.mkdirSync(vscodeDir, { recursive: true });
2894
+ const vscodeDir = path21.join(projectDir, ".vscode");
2895
+ fs23.mkdirSync(vscodeDir, { recursive: true });
1722
2896
  const hapPath = `${args.moduleName}/build/default/outputs/default/${args.moduleName}-default-signed.hap`;
1723
- writeJsonFile(path17.join(vscodeDir, "settings.json"), {
2897
+ writeJsonFile(path21.join(vscodeDir, "settings.json"), {
1724
2898
  "oniro.hapPath": hapPath,
1725
2899
  "files.associations": { "*.json5": "jsonc" }
1726
2900
  });
@@ -1737,7 +2911,7 @@ async function createScaffold(opts) {
1737
2911
  if (!opts.location || !await pathExists(opts.location)) {
1738
2912
  throw new OniroError(`Location does not exist: ${opts.location}`);
1739
2913
  }
1740
- const templateDir = path17.join(opts.templateRoot, opts.templateId);
2914
+ const templateDir = path21.join(opts.templateRoot, opts.templateId);
1741
2915
  if (!await pathExists(templateDir)) {
1742
2916
  throw new OniroError(`Template not found: ${templateDir}`);
1743
2917
  }
@@ -1750,17 +2924,17 @@ async function createScaffold(opts) {
1750
2924
  throw new OniroError(`Template '${opts.templateId}' is missing required files:
1751
2925
  - ${missing.join("\n- ")}`);
1752
2926
  }
1753
- const projectDir = path17.join(opts.location, opts.projectName);
2927
+ const projectDir = path21.join(opts.location, opts.projectName);
1754
2928
  if (await pathExists(projectDir)) {
1755
2929
  if (!opts.overwrite) {
1756
2930
  throw new OniroError(`Destination already exists: ${projectDir}. Pass overwrite: true to replace it.`);
1757
2931
  }
1758
- await fs18.promises.rm(projectDir, { recursive: true, force: true });
2932
+ await fs23.promises.rm(projectDir, { recursive: true, force: true });
1759
2933
  }
1760
2934
  logger.info(`[create] Scaffolding ${opts.templateId} at ${projectDir}`);
1761
2935
  await copyDirRecursive(templateDir, projectDir);
1762
2936
  if (moduleName !== defaultModuleName) {
1763
- renameIfExists(path17.join(projectDir, defaultModuleName), path17.join(projectDir, moduleName));
2937
+ renameIfExists(path21.join(projectDir, defaultModuleName), path21.join(projectDir, moduleName));
1764
2938
  }
1765
2939
  updateTemplateConfigs(
1766
2940
  projectDir,
@@ -1776,8 +2950,225 @@ async function createScaffold(opts) {
1776
2950
  return { projectDir };
1777
2951
  }
1778
2952
 
2953
+ // src/project/listModules.ts
2954
+ var fs24 = __toESM(require("fs"), 1);
2955
+ var path22 = __toESM(require("path"), 1);
2956
+ function listModules(opts) {
2957
+ const buildProfile = path22.join(opts.projectDir, "build-profile.json5");
2958
+ if (!fs24.existsSync(buildProfile)) {
2959
+ throw new OniroError(`build-profile.json5 not found at ${buildProfile}`);
2960
+ }
2961
+ const parsed = readJson5File(buildProfile);
2962
+ return (parsed.modules ?? []).filter((m) => Boolean(m.name)).map((m) => ({
2963
+ name: m.name,
2964
+ srcPath: m.srcPath ?? `./${m.name}`,
2965
+ targets: (m.targets ?? []).map((t) => t.name).filter((n) => Boolean(n))
2966
+ }));
2967
+ }
2968
+
2969
+ // src/project/abilities.ts
2970
+ var fs25 = __toESM(require("fs"), 1);
2971
+ var path23 = __toESM(require("path"), 1);
2972
+ function listAbilities(opts) {
2973
+ const moduleName = opts.moduleName ?? "entry";
2974
+ const moduleJsonPath = path23.join(opts.projectDir, moduleName, "src", "main", "module.json5");
2975
+ if (!fs25.existsSync(moduleJsonPath)) {
2976
+ throw new OniroError(`Could not find module.json5 at ${moduleJsonPath}`);
2977
+ }
2978
+ const parsed = readJson5File(moduleJsonPath);
2979
+ return (parsed.module?.abilities ?? []).filter((a) => Boolean(a.name)).map((a) => ({ name: a.name, type: a.type, visible: a.visible, srcEntry: a.srcEntry }));
2980
+ }
2981
+
2982
+ // src/project/prepareSigning.ts
2983
+ var fs26 = __toESM(require("fs"), 1);
2984
+ var path24 = __toESM(require("path"), 1);
2985
+ function hasSigningArtifacts(dir) {
2986
+ if (!fs26.existsSync(path24.join(dir, "signatures"))) return false;
2987
+ const buildProfile = path24.join(dir, "build-profile.json5");
2988
+ if (!fs26.existsSync(buildProfile)) return false;
2989
+ try {
2990
+ const parsed = readJson5File(buildProfile);
2991
+ return Array.isArray(parsed.app?.signingConfigs) && parsed.app.signingConfigs.length > 0;
2992
+ } catch {
2993
+ return false;
2994
+ }
2995
+ }
2996
+ function prepareSigning(opts) {
2997
+ if (hasSigningArtifacts(opts.projectDir)) {
2998
+ return { source: "present" };
2999
+ }
3000
+ generateSigningConfigs({
3001
+ projectDir: opts.projectDir,
3002
+ sdkHome: getOhosBaseSdkHome(opts.config),
3003
+ apl: opts.apl,
3004
+ appFeature: opts.appFeature,
3005
+ acls: opts.acls,
3006
+ passwords: opts.passwords,
3007
+ logger: opts.logger
3008
+ });
3009
+ return { source: "fresh" };
3010
+ }
3011
+
3012
+ // src/device/hidumper.ts
3013
+ async function dumpScreen(opts) {
3014
+ const res = await shell({ config: opts.config, command: "hidumper -s 10 -a screen", deviceSerial: opts.deviceSerial, timeoutMs: opts.timeoutMs ?? 1e4, logger: opts.logger });
3015
+ if (res.code !== 0) return null;
3016
+ const m = res.stdout.match(/render resolution=(\d+)x(\d+)/);
3017
+ if (!m) return null;
3018
+ return { width: Number(m[1]), height: Number(m[2]), raw: res.stdout };
3019
+ }
3020
+ async function dumpWindow(opts) {
3021
+ const res = await shell({ config: opts.config, command: "hidumper -s WindowManagerService -a -a", deviceSerial: opts.deviceSerial, timeoutMs: opts.timeoutMs ?? 15e3, logger: opts.logger });
3022
+ return res.stdout;
3023
+ }
3024
+ async function dumpRenderService(opts) {
3025
+ const res = await shell({ config: opts.config, command: "hidumper -s RenderService -a allInfo", deviceSerial: opts.deviceSerial, timeoutMs: opts.timeoutMs ?? 15e3, logger: opts.logger });
3026
+ return res.stdout;
3027
+ }
3028
+
3029
+ // src/device/info.ts
3030
+ var PROPS = [
3031
+ ["model", "const.product.model"],
3032
+ ["manufacturer", "const.product.manufacturer"],
3033
+ ["brand", "const.product.brand"],
3034
+ ["osFullName", "const.ohos.fullname"],
3035
+ ["osReleaseType", "const.ohos.releasetype"],
3036
+ ["sdkApiVersion", "const.ohos.apiversion"],
3037
+ ["buildVersion", "const.product.software.version"]
3038
+ ];
3039
+ async function getDeviceInfo(opts) {
3040
+ const info = { serial: opts.deviceSerial ?? "(default)", display: null };
3041
+ for (const [field, prop] of PROPS) {
3042
+ try {
3043
+ const value = await paramGet(opts.config, prop, { deviceSerial: opts.deviceSerial, timeoutMs: opts.timeoutMs ?? 1e4, logger: opts.logger });
3044
+ if (value) info[field] = value;
3045
+ } catch {
3046
+ }
3047
+ }
3048
+ const screen = await dumpScreen({ config: opts.config, deviceSerial: opts.deviceSerial, logger: opts.logger });
3049
+ if (screen) info.display = { width: screen.width, height: screen.height };
3050
+ return info;
3051
+ }
3052
+
3053
+ // src/install/diffHapAssets.ts
3054
+ var import_node_stream_zip2 = __toESM(require("node-stream-zip"), 1);
3055
+ function diffEntryNames(installed, next) {
3056
+ const installedSet = new Set(installed);
3057
+ const nextSet = new Set(next);
3058
+ return {
3059
+ addedAssetPaths: [...nextSet].filter((n) => !installedSet.has(n)).sort(),
3060
+ removedAssetPaths: [...installedSet].filter((n) => !nextSet.has(n)).sort()
3061
+ };
3062
+ }
3063
+ async function listFileEntries(hapPath) {
3064
+ const zip = new import_node_stream_zip2.default.async({ file: hapPath });
3065
+ try {
3066
+ const entries = await zip.entries();
3067
+ return Object.values(entries).filter((e) => !e.isDirectory).map((e) => e.name);
3068
+ } finally {
3069
+ await zip.close();
3070
+ }
3071
+ }
3072
+ async function diffHapAssets(opts) {
3073
+ const [installed, next] = await Promise.all([listFileEntries(opts.installedHap), listFileEntries(opts.newHap)]);
3074
+ return diffEntryNames(installed, next);
3075
+ }
3076
+
3077
+ // src/install/apply.ts
3078
+ var fs27 = __toESM(require("fs"), 1);
3079
+ var path25 = __toESM(require("path"), 1);
3080
+ function decideInstallMethod(s) {
3081
+ if (!s.signInfoInconsistent) return "replace";
3082
+ if (s.isSystemBundle && !s.allowUninstall) return "refuse";
3083
+ return "uninstall-install";
3084
+ }
3085
+ function decideReboot(s) {
3086
+ if (s.assetsChanged) return { reboot: true, reason: "asset-cache" };
3087
+ if (s.isSystemBundle && s.preInstallPid !== null && s.postInstallPid === s.preInstallPid) {
3088
+ return { reboot: true, reason: "pid-unchanged" };
3089
+ }
3090
+ return { reboot: false, reason: "none" };
3091
+ }
3092
+ var SIGN_INFO_INCONSISTENT = /9568332|sign.*info.*inconsistent/i;
3093
+ async function pidOf(config, bundle, deviceSerial) {
3094
+ const proc = await findRunningProcess({ config, bundle, deviceSerial });
3095
+ return proc ? Number(proc.pid) : null;
3096
+ }
3097
+ async function resolveHapPath(opts) {
3098
+ let hapPath = opts.hapPath;
3099
+ if (hapPath) {
3100
+ if (!path25.isAbsolute(hapPath) && opts.projectDir) hapPath = path25.join(opts.projectDir, hapPath);
3101
+ } else {
3102
+ if (!opts.projectDir) throw new OniroError("applyChanges requires hapPath or projectDir.");
3103
+ const haps = await discoverHaps({ projectDir: opts.projectDir });
3104
+ if (opts.module) {
3105
+ const signed = haps[opts.module]?.signed ?? [];
3106
+ if (signed.length === 0) throw new OniroError(`No signed HAP for module ${opts.module}. Build first.`);
3107
+ hapPath = signed[0];
3108
+ } else {
3109
+ const withSigned = Object.entries(haps).filter(([, m]) => m.signed.length > 0);
3110
+ if (withSigned.length === 0) throw new OniroError("No signed HAP found. Build first.");
3111
+ if (withSigned.length > 1) {
3112
+ throw new OniroError(
3113
+ `Multiple modules have signed HAPs (${withSigned.map(([k]) => k).join(", ")}). Pass module to choose.`
3114
+ );
3115
+ }
3116
+ hapPath = withSigned[0][1].signed[0];
3117
+ }
3118
+ }
3119
+ if (!fs27.existsSync(hapPath)) throw new OniroError(`HAP not found at ${hapPath}.`);
3120
+ return hapPath;
3121
+ }
3122
+ async function applyChanges(opts) {
3123
+ const logger = scopedLogger(opts.logger ?? noopLogger, "apply");
3124
+ const { config, bundle, deviceSerial } = opts;
3125
+ const isSystemBundle = opts.isSystemBundle ?? false;
3126
+ const allowUninstall = opts.allowUninstall ?? false;
3127
+ const hapPath = await resolveHapPath(opts);
3128
+ let assetsChanged = false;
3129
+ if (opts.installedHapPath && fs27.existsSync(opts.installedHapPath)) {
3130
+ try {
3131
+ const diff = await diffHapAssets({ installedHap: opts.installedHapPath, newHap: hapPath });
3132
+ assetsChanged = diff.addedAssetPaths.length > 0 || diff.removedAssetPaths.length > 0;
3133
+ } catch (err) {
3134
+ logger.debug(`asset diff skipped: ${err.message}`);
3135
+ }
3136
+ }
3137
+ const preInstallPid = await pidOf(config, bundle, deviceSerial);
3138
+ const replaceRes = await hdcExec({ config, args: ["install", "-r", hapPath], deviceSerial, timeoutMs: 3e5 });
3139
+ const signInfoInconsistent = SIGN_INFO_INCONSISTENT.test(`${replaceRes.stdout}${replaceRes.stderr}`);
3140
+ const decision = decideInstallMethod({ signInfoInconsistent, isSystemBundle, allowUninstall });
3141
+ let installMethod;
3142
+ if (decision === "refuse") {
3143
+ throw new OniroError(
3144
+ `Refusing to reinstall system bundle ${bundle}: signing certs are inconsistent (9568332). Align the signing certs first \u2014 uninstalling a system bundle can brick the device. Pass allowUninstall to override.`
3145
+ );
3146
+ }
3147
+ if (decision === "uninstall-install") {
3148
+ logger.warn(`sign-info inconsistent for ${bundle}; uninstalling then installing fresh.`);
3149
+ await uninstallApp({ config, bundle, deviceSerial });
3150
+ ensureOk(await hdcExec({ config, args: ["install", hapPath], deviceSerial, timeoutMs: 3e5 }), `hdc install ${hapPath}`);
3151
+ installMethod = "uninstall-install";
3152
+ } else {
3153
+ ensureOk(replaceRes, `hdc install -r ${hapPath}`);
3154
+ installMethod = "replace";
3155
+ }
3156
+ let postInstallPid = await pidOf(config, bundle, deviceSerial);
3157
+ const rebootDecision = decideReboot({ assetsChanged, isSystemBundle, preInstallPid, postInstallPid });
3158
+ let method = installMethod;
3159
+ let cacheCleared = false;
3160
+ if (rebootDecision.reboot) {
3161
+ logger.info(`rebooting (${rebootDecision.reason}) to apply changes to ${bundle}.`);
3162
+ await reboot({ config, waitForBundle: bundle, deviceSerial, abortSignal: opts.abortSignal, logger: opts.logger });
3163
+ postInstallPid = await pidOf(config, bundle, deviceSerial);
3164
+ method = "reboot";
3165
+ cacheCleared = rebootDecision.reason === "asset-cache";
3166
+ }
3167
+ return { method, preInstallPid, postInstallPid, replaced: installMethod === "replace", cacheCleared };
3168
+ }
3169
+
1779
3170
  // src/index.ts
1780
- var VERSION = "0.6.1";
3171
+ var VERSION = "0.7.0";
1781
3172
  // Annotate the CommonJS export names for ESM import in node:
1782
3173
  0 && (module.exports = {
1783
3174
  ALL_SDKS,
@@ -1786,32 +3177,59 @@ var VERSION = "0.6.1";
1786
3177
  CancelledError,
1787
3178
  ChecksumMismatchError,
1788
3179
  CmdToolsNotInstalledError,
3180
+ CommandFailedError,
1789
3181
  OHOS_URL_BASE,
1790
3182
  OniroError,
1791
3183
  SdkNotInstalledError,
1792
3184
  UnsupportedPlatformError,
1793
3185
  VERSION,
3186
+ applyChanges,
1794
3187
  attemptHdcConnection,
3188
+ buildGestureCommands,
3189
+ buildHap,
3190
+ buildHvigorwArgs,
3191
+ buildInputCommand,
3192
+ buildRawTouchCommand,
3193
+ captureBurst,
1795
3194
  consoleLogger,
1796
3195
  createMaterial,
1797
3196
  createScaffold,
3197
+ decideInstallMethod,
3198
+ decideReboot,
1798
3199
  decryptPwd,
3200
+ dedupEntries,
1799
3201
  defaultContext,
1800
3202
  defaultPaths,
3203
+ detectAaStartFailure,
1801
3204
  detectProjectSdkVersion,
3205
+ detectSigningConfigNames,
3206
+ diffEntryNames,
3207
+ diffHapAssets,
3208
+ discoverHaps,
1802
3209
  downloadAndInstallSdk,
1803
3210
  downloadFile,
3211
+ dumpLayout,
3212
+ dumpLog,
3213
+ dumpRenderService,
3214
+ dumpScreen,
3215
+ dumpWindow,
1804
3216
  encryptPwd,
3217
+ ensureOhModules,
3218
+ ensureOk,
1805
3219
  extractTarball,
1806
3220
  extractZipWithProgress,
1807
3221
  findAppProcessId,
1808
3222
  findCmdToolsSourceDir,
3223
+ findRunningProcess,
3224
+ forceStop,
1809
3225
  generateSigningConfigs,
1810
3226
  getBundleName,
1811
3227
  getCmdToolsBin,
1812
3228
  getCmdToolsDownloadUrl,
1813
3229
  getCmdToolsPath,
1814
3230
  getCmdToolsStatus,
3231
+ getDeviceInfo,
3232
+ getDisplaySize,
1815
3233
  getEmulatorDir,
1816
3234
  getHdcPath,
1817
3235
  getHvigorwPath,
@@ -1819,10 +3237,12 @@ var VERSION = "0.6.1";
1819
3237
  getKey,
1820
3238
  getMainAbility,
1821
3239
  getOhosBaseSdkHome,
3240
+ getOhpmPath,
1822
3241
  getOsFolder,
1823
3242
  getSdkFilename,
1824
3243
  getSdkRootDir,
1825
3244
  getSupportedSdksForUi,
3245
+ hdcExec,
1826
3246
  installApp,
1827
3247
  installCmdTools,
1828
3248
  installEmulator,
@@ -1831,25 +3251,55 @@ var VERSION = "0.6.1";
1831
3251
  isValidBundleName,
1832
3252
  isValidProjectName,
1833
3253
  launchApp,
3254
+ listAbilities,
3255
+ listDevices,
3256
+ listModules,
1834
3257
  listRunningProcesses,
1835
3258
  listTemplates,
1836
3259
  nonInteractivePrompter,
1837
3260
  noopLogger,
1838
3261
  noopProgress,
3262
+ paramGet,
3263
+ paramSet,
3264
+ parseBounds,
3265
+ parseCodelinterFindings,
3266
+ parseDeviceList,
1839
3267
  parseHilogLine,
3268
+ pickSigningKind,
3269
+ prepareSigning,
3270
+ pruneLayout,
1840
3271
  readJson5File,
1841
3272
  readJsonFile,
3273
+ reboot,
3274
+ recvFile,
1842
3275
  removeCmdTools,
1843
3276
  removeEmulator,
1844
3277
  removeSdk,
3278
+ runCodelinter,
1845
3279
  runHvigorw,
3280
+ runOhpm,
3281
+ runProcess,
3282
+ scopedLogger,
3283
+ selectDevice,
3284
+ sendFile,
3285
+ sendGesture,
3286
+ sendInput,
3287
+ sendRawTouch,
1846
3288
  setHilogLevel,
3289
+ shell,
1847
3290
  startEmulator,
1848
3291
  staticConfig,
1849
3292
  stopEmulator,
1850
3293
  streamHilog,
3294
+ takeScreenshot,
3295
+ uninstallApp,
1851
3296
  validateTemplateLayout,
1852
3297
  verifySha256,
3298
+ waitForBoot,
3299
+ waitForBundle,
3300
+ waitForCondition,
3301
+ waitForLog,
3302
+ watchLog,
1853
3303
  writeJson5File,
1854
3304
  writeJsonFile
1855
3305
  });