@oniroproject/core 0.10.0 → 0.12.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
@@ -33,14 +33,18 @@ __export(index_exports, {
33
33
  ALL_SDKS: () => ALL_SDKS,
34
34
  APL_VALUES: () => APL_VALUES,
35
35
  APP_FEATURE_VALUES: () => APP_FEATURE_VALUES,
36
+ AgcError: () => AgcError,
36
37
  CancelledError: () => CancelledError,
37
38
  ChecksumMismatchError: () => ChecksumMismatchError,
38
39
  CmdToolsNotInstalledError: () => CmdToolsNotInstalledError,
39
40
  CommandFailedError: () => CommandFailedError,
41
+ HARMONYOS_SDK_VERSIONS: () => HARMONYOS_SDK_VERSIONS,
42
+ HUAWEI_SITES: () => HUAWEI_SITES,
40
43
  INSTALL_TMP_DIRNAME: () => INSTALL_TMP_DIRNAME,
41
44
  InsufficientSpaceError: () => InsufficientSpaceError,
42
45
  OHOS_URL_BASE: () => OHOS_URL_BASE,
43
46
  OniroError: () => OniroError,
47
+ SIGNING_ERRORS: () => SIGNING_ERRORS,
44
48
  SdkNotInstalledError: () => SdkNotInstalledError,
45
49
  TMP_DIR_HINT: () => TMP_DIR_HINT,
46
50
  UnsupportedPlatformError: () => UnsupportedPlatformError,
@@ -56,10 +60,12 @@ __export(index_exports, {
56
60
  buildRawTouchCommand: () => buildRawTouchCommand,
57
61
  captureBurst: () => captureBurst,
58
62
  consoleLogger: () => consoleLogger,
63
+ createHarmonyOsSession: () => createHarmonyOsSession,
59
64
  createInstallTempDir: () => createInstallTempDir,
60
65
  createMaterial: () => createMaterial,
61
66
  createScaffold: () => createScaffold,
62
67
  createTempWorkDir: () => createTempWorkDir,
68
+ createTokenStore: () => createTokenStore,
63
69
  decideInstallMethod: () => decideInstallMethod,
64
70
  decideReboot: () => decideReboot,
65
71
  decryptPwd: () => decryptPwd,
@@ -68,6 +74,7 @@ __export(index_exports, {
68
74
  defaultPaths: () => defaultPaths,
69
75
  detectAaStartFailure: () => detectAaStartFailure,
70
76
  detectProjectSdkVersion: () => detectProjectSdkVersion,
77
+ detectRuntimeOs: () => detectRuntimeOs,
71
78
  detectSigningConfigNames: () => detectSigningConfigNames,
72
79
  diffEntryNames: () => diffEntryNames,
73
80
  diffHapAssets: () => diffHapAssets,
@@ -90,6 +97,7 @@ __export(index_exports, {
90
97
  extractZipWithProgress: () => extractZipWithProgress,
91
98
  findAppProcessId: () => findAppProcessId,
92
99
  findCmdToolsSourceDir: () => findCmdToolsSourceDir,
100
+ findHarmonyOsSdk: () => findHarmonyOsSdk,
93
101
  findRunningProcess: () => findRunningProcess,
94
102
  forceStop: () => forceStop,
95
103
  formatBytes: () => formatBytes,
@@ -114,6 +122,8 @@ __export(index_exports, {
114
122
  getSdkFilename: () => getSdkFilename,
115
123
  getSdkRootDir: () => getSdkRootDir,
116
124
  getSupportedSdksForUi: () => getSupportedSdksForUi,
125
+ harmonyOsAutoSign: () => harmonyOsAutoSign,
126
+ harmonyOsSdkLabelForApi: () => harmonyOsSdkLabelForApi,
117
127
  hdcExec: () => hdcExec,
118
128
  installApp: () => installApp,
119
129
  installCmdTools: () => installCmdTools,
@@ -148,12 +158,14 @@ __export(index_exports, {
148
158
  pruneLayout: () => pruneLayout,
149
159
  readJson5File: () => readJson5File,
150
160
  readJsonFile: () => readJsonFile,
161
+ readProvisionProfile: () => readProvisionProfile,
151
162
  reboot: () => reboot,
152
163
  recvFile: () => recvFile,
153
164
  removeCmdTools: () => removeCmdTools,
154
165
  removeEmulator: () => removeEmulator,
155
166
  removeSdk: () => removeSdk,
156
167
  removeTempWorkDir: () => removeTempWorkDir,
168
+ requireHarmonyOsSdk: () => requireHarmonyOsSdk,
157
169
  resolveTmpRoot: () => resolveTmpRoot,
158
170
  runCodelinter: () => runCodelinter,
159
171
  runHvigorw: () => runHvigorw,
@@ -230,6 +242,8 @@ var defaultPaths = {
230
242
  sdkRootDir: () => path.join(os.homedir(), "setup-ohos-sdk"),
231
243
  cmdToolsPath: () => path.join(os.homedir(), "command-line-tools"),
232
244
  emulatorDir: () => path.join(os.homedir(), "oniro-emulator"),
245
+ harmonyosAuthDir: () => path.join(os.homedir(), ".oniro", "harmonyos"),
246
+ harmonyosSigningDir: () => path.join(os.homedir(), ".ohos", "config"),
233
247
  hapPath: "entry/build/default/outputs/default/entry-default-signed.hap",
234
248
  emulatorUrl: "https://github.com/eclipse-oniro4openharmony/device_board_oniro/releases/latest/download/oniro_emulator.zip",
235
249
  // The Huawei mirror only hosts a public Linux build of the command-line tools.
@@ -361,7 +375,8 @@ var ALL_SDKS = [
361
375
  { version: "5.1.0", api: "18", tarballStrip: { linuxWindows: 1, darwin: 3 } },
362
376
  { version: "5.1.1", api: "19", tarballStrip: { linuxWindows: 1, darwin: 3 } },
363
377
  { version: "6.0", api: "20", tarballStrip: { linuxWindows: 0, darwin: 3 } },
364
- { version: "6.1", api: "23", tarballStrip: { linuxWindows: 0, darwin: 3 } }
378
+ { version: "6.1", api: "23", tarballStrip: { linuxWindows: 0, darwin: 3 } },
379
+ { version: "7.0", api: "26", tarballStrip: { linuxWindows: 1, darwin: 3 }, archiveSuffix: "_20260829" }
365
380
  ];
366
381
  var OHOS_URL_BASE = "https://repo.huaweicloud.com/openharmony/os";
367
382
 
@@ -377,31 +392,113 @@ function getOsFolder() {
377
392
  function getSdkFilename(version) {
378
393
  const platform7 = os2.platform();
379
394
  const v = version ?? ALL_SDKS[ALL_SDKS.length - 1].version;
380
- const strips = ALL_SDKS.find((r) => r.version === v)?.tarballStrip ?? { linuxWindows: 1, darwin: 3 };
395
+ const release = ALL_SDKS.find((r) => r.version === v);
396
+ const strips = release?.tarballStrip ?? { linuxWindows: 1, darwin: 3 };
397
+ const archive = (base) => `${base}${release?.archiveSuffix ?? ""}.tar.gz`;
381
398
  if (platform7 === "linux") {
382
- return { filename: "ohos-sdk-windows_linux-public.tar.gz", osFolder: "linux", strip: strips.linuxWindows };
399
+ return { filename: archive("ohos-sdk-windows_linux-public"), osFolder: "linux", strip: strips.linuxWindows };
383
400
  }
384
401
  if (platform7 === "darwin") {
385
402
  if (os2.arch() === "arm64") {
386
- return { filename: "L2-SDK-MAC-M1-PUBLIC.tar.gz", osFolder: "darwin", strip: strips.darwin };
403
+ return { filename: archive("L2-SDK-MAC-M1-PUBLIC"), osFolder: "darwin", strip: strips.darwin };
387
404
  }
388
- return { filename: "ohos-sdk-mac-public.tar.gz", osFolder: "darwin", strip: strips.darwin };
405
+ return { filename: archive("ohos-sdk-mac-public"), osFolder: "darwin", strip: strips.darwin };
389
406
  }
390
407
  if (platform7 === "win32") {
391
- return { filename: "ohos-sdk-windows_linux-public.tar.gz", osFolder: "windows", strip: strips.linuxWindows };
408
+ return { filename: archive("ohos-sdk-windows_linux-public"), osFolder: "windows", strip: strips.linuxWindows };
392
409
  }
393
410
  throw new UnsupportedPlatformError(platform7);
394
411
  }
395
412
 
396
413
  // src/sdk/paths.ts
414
+ var fs2 = __toESM(require("fs"), 1);
415
+ var os4 = __toESM(require("os"), 1);
416
+ var path3 = __toESM(require("path"), 1);
417
+
418
+ // src/harmonyos/sdk.ts
397
419
  var fs = __toESM(require("fs"), 1);
398
420
  var os3 = __toESM(require("os"), 1);
399
421
  var path2 = __toESM(require("path"), 1);
422
+ function readSdkPkg(sdkPath) {
423
+ let data;
424
+ try {
425
+ data = JSON.parse(fs.readFileSync(path2.join(sdkPath, "default", "sdk-pkg.json"), "utf8")).data;
426
+ } catch {
427
+ return {};
428
+ }
429
+ const text = (value) => typeof value === "string" || typeof value === "number" ? String(value) || void 0 : void 0;
430
+ const platformVersion = text(data?.platformVersion);
431
+ const apiVersion = text(data?.apiVersion);
432
+ return {
433
+ displayName: text(data?.displayName),
434
+ version: platformVersion && apiVersion ? `${platformVersion}(${apiVersion})` : void 0
435
+ };
436
+ }
437
+ function resolveSdk(root, requireHarmonyOs) {
438
+ const nested = path2.join(root, "sdk");
439
+ const sdkPath = [nested, root].find((dir) => fs.existsSync(path2.join(dir, "default", "openharmony")));
440
+ if (!sdkPath) return null;
441
+ const pkg = readSdkPkg(sdkPath);
442
+ if (requireHarmonyOs && !pkg.displayName?.startsWith("HarmonyOS")) return null;
443
+ return { sdkPath, installRoot: sdkPath === nested ? root : path2.dirname(root), ...pkg };
444
+ }
445
+ function studioCandidates() {
446
+ if (process.platform === "darwin") {
447
+ return [
448
+ "/Applications/DevEco-Studio.app/Contents",
449
+ path2.join(os3.homedir(), "Applications", "DevEco-Studio.app", "Contents")
450
+ ];
451
+ }
452
+ if (process.platform === "win32") {
453
+ return [path2.join(process.env.ProgramFiles ?? "C:\\Program Files", "Huawei", "DevEco Studio")];
454
+ }
455
+ return [];
456
+ }
457
+ function findHarmonyOsSdk(opts) {
458
+ const logger = opts.logger ?? noopLogger;
459
+ const configured = opts.config.get("harmonyosSdkPath", "");
460
+ if (configured) {
461
+ const sdk = resolveSdk(configured, false);
462
+ if (sdk) return sdk;
463
+ logger.warn(`ONIRO_HARMONYOS_SDK_PATH points at ${configured}, which holds no SDK; ignoring it.`);
464
+ }
465
+ const candidates = [
466
+ ...studioCandidates(),
467
+ opts.config.get("cmdToolsPath", defaultPaths.cmdToolsPath())
468
+ ];
469
+ for (const candidate of candidates) {
470
+ const sdk = resolveSdk(candidate, true);
471
+ if (sdk) {
472
+ logger.debug(`[harmonyos] Using the ${sdk.displayName} SDK at ${sdk.sdkPath}.`);
473
+ return sdk;
474
+ }
475
+ }
476
+ return null;
477
+ }
478
+ function requireHarmonyOsSdk(opts) {
479
+ const sdk = findHarmonyOsSdk(opts);
480
+ if (sdk) return sdk;
481
+ throw new OniroError(
482
+ "No HarmonyOS SDK found. It ships inside DevEco Studio or the HarmonyOS command-line tools and cannot be downloaded the way the OpenHarmony SDK is. Install one, then set ONIRO_HARMONYOS_SDK_PATH to the install (or its sdk directory)."
483
+ );
484
+ }
485
+ function findHarmonyOsTool(sdk, name) {
486
+ const names = process.platform === "win32" ? [`${name}.bat`, `${name}.cmd`, `${name}.exe`] : [name];
487
+ for (const dir of [path2.join(sdk.installRoot, "bin"), path2.join(sdk.installRoot, "tools", name, "bin")]) {
488
+ for (const candidate of names) {
489
+ const full = path2.join(dir, candidate);
490
+ if (fs.existsSync(full)) return full;
491
+ }
492
+ }
493
+ return null;
494
+ }
495
+
496
+ // src/sdk/paths.ts
400
497
  function getSdkRootDir(config) {
401
498
  return config.get("sdkRootDir", defaultPaths.sdkRootDir());
402
499
  }
403
500
  function getOhosBaseSdkHome(config) {
404
- return path2.join(getSdkRootDir(config), getOsFolder());
501
+ return path3.join(getSdkRootDir(config), getOsFolder());
405
502
  }
406
503
  function getCmdToolsPath(config) {
407
504
  return config.get("cmdToolsPath", defaultPaths.cmdToolsPath());
@@ -409,31 +506,31 @@ function getCmdToolsPath(config) {
409
506
  function getEmulatorDir(config) {
410
507
  return config.get("emulatorDir", defaultPaths.emulatorDir());
411
508
  }
412
- function toLongPath(p, platform7 = os3.platform()) {
509
+ function toLongPath(p, platform7 = os4.platform()) {
413
510
  if (platform7 !== "win32") return p;
414
511
  try {
415
- return fs.realpathSync.native(p);
512
+ return fs2.realpathSync.native(p);
416
513
  } catch {
417
514
  return p;
418
515
  }
419
516
  }
420
517
  function pickExisting(candidates) {
421
518
  for (const c of candidates) {
422
- if (fs.existsSync(c)) return c;
519
+ if (fs2.existsSync(c)) return c;
423
520
  }
424
521
  return candidates[0];
425
522
  }
426
523
  function getCmdToolsBin(config, name = "ohpm") {
427
- const binDir = path2.join(getCmdToolsPath(config), "bin");
428
- if (os3.platform() === "win32") {
524
+ const binDir = path3.join(getCmdToolsPath(config), "bin");
525
+ if (os4.platform() === "win32") {
429
526
  return pickExisting([
430
- path2.join(binDir, `${name}.exe`),
431
- path2.join(binDir, `${name}.cmd`),
432
- path2.join(binDir, `${name}.bat`),
433
- path2.join(binDir, name)
527
+ path3.join(binDir, `${name}.exe`),
528
+ path3.join(binDir, `${name}.cmd`),
529
+ path3.join(binDir, `${name}.bat`),
530
+ path3.join(binDir, name)
434
531
  ]);
435
532
  }
436
- return path2.join(binDir, name);
533
+ return path3.join(binDir, name);
437
534
  }
438
535
  function getOhpmPath(config) {
439
536
  return getCmdToolsBin(config, "ohpm");
@@ -441,45 +538,52 @@ function getOhpmPath(config) {
441
538
  function getHdcPath(config) {
442
539
  const override = config.get("hdcPath", "");
443
540
  if (override) return override;
444
- const base = path2.join(getCmdToolsPath(config), "sdk", "default", "openharmony", "toolchains");
445
- if (os3.platform() === "win32") {
541
+ const hdc = hdcInSdk(path3.join(getCmdToolsPath(config), "sdk"));
542
+ if (fs2.existsSync(hdc)) return hdc;
543
+ const harmonySdk = findHarmonyOsSdk({ config });
544
+ const harmonyHdc = harmonySdk ? hdcInSdk(harmonySdk.sdkPath) : void 0;
545
+ return harmonyHdc && fs2.existsSync(harmonyHdc) ? harmonyHdc : hdc;
546
+ }
547
+ function hdcInSdk(sdkDir) {
548
+ const base = path3.join(sdkDir, "default", "openharmony", "toolchains");
549
+ if (os4.platform() === "win32") {
446
550
  return pickExisting([
447
- path2.join(base, "hdc.exe"),
448
- path2.join(base, "hdc.bat"),
449
- path2.join(base, "hdc.cmd"),
450
- path2.join(base, "hdc")
551
+ path3.join(base, "hdc.exe"),
552
+ path3.join(base, "hdc.bat"),
553
+ path3.join(base, "hdc.cmd"),
554
+ path3.join(base, "hdc")
451
555
  ]);
452
556
  }
453
- return path2.join(base, "hdc");
557
+ return path3.join(base, "hdc");
454
558
  }
455
- function getHvigorwPath(config, projectDir, platform7 = os3.platform()) {
559
+ function getHvigorwPath(config, projectDir, platform7 = os4.platform()) {
456
560
  const posixFirst = ["hvigorw", "hvigorw.bat", "hvigorw.cmd"];
457
561
  const windowsFirst = ["hvigorw.bat", "hvigorw.cmd", "hvigorw"];
458
562
  const names = platform7 === "win32" ? windowsFirst : posixFirst;
459
- const local = pickExisting(names.map((n) => path2.join(projectDir, n)));
460
- if (fs.existsSync(local) && isProjectLocalHvigorwWorking(projectDir)) {
563
+ const local = pickExisting(names.map((n) => path3.join(projectDir, n)));
564
+ if (fs2.existsSync(local) && isProjectLocalHvigorwWorking(projectDir)) {
461
565
  return local;
462
566
  }
463
567
  return getCmdToolsBin(config, "hvigorw");
464
568
  }
465
569
  function isProjectLocalHvigorwWorking(projectDir) {
466
- const wrapper = path2.join(projectDir, "hvigor", "hvigor-wrapper.js");
467
- const nodeModules = path2.join(projectDir, "hvigor", "node_modules");
468
- return fs.existsSync(wrapper) && fs.existsSync(nodeModules);
570
+ const wrapper = path3.join(projectDir, "hvigor", "hvigor-wrapper.js");
571
+ const nodeModules = path3.join(projectDir, "hvigor", "node_modules");
572
+ return fs2.existsSync(wrapper) && fs2.existsSync(nodeModules);
469
573
  }
470
574
 
471
575
  // src/sdk/detectProjectSdk.ts
472
- var fs2 = __toESM(require("fs"), 1);
473
- var path3 = __toESM(require("path"), 1);
576
+ var fs3 = __toESM(require("fs"), 1);
577
+ var path4 = __toESM(require("path"), 1);
474
578
  var import_json5 = __toESM(require("json5"), 1);
475
579
  function detectProjectSdkVersion(projectRoot, logger = noopLogger) {
476
- const buildProfilePath = path3.join(projectRoot, "build-profile.json5");
477
- if (!fs2.existsSync(buildProfilePath)) {
580
+ const buildProfilePath = path4.join(projectRoot, "build-profile.json5");
581
+ if (!fs3.existsSync(buildProfilePath)) {
478
582
  logger.warn(`build-profile.json5 not found at ${projectRoot}`);
479
583
  return void 0;
480
584
  }
481
585
  try {
482
- const content = fs2.readFileSync(buildProfilePath, "utf-8");
586
+ const content = fs3.readFileSync(buildProfilePath, "utf-8");
483
587
  const config = import_json5.default.parse(content);
484
588
  const product = config.app?.products?.[0];
485
589
  const v = product?.compileSdkVersion;
@@ -491,26 +595,26 @@ function detectProjectSdkVersion(projectRoot, logger = noopLogger) {
491
595
  }
492
596
 
493
597
  // src/sdk/tmp.ts
494
- var fs3 = __toESM(require("fs"), 1);
495
- var os4 = __toESM(require("os"), 1);
496
- var path4 = __toESM(require("path"), 1);
598
+ var fs4 = __toESM(require("fs"), 1);
599
+ var os5 = __toESM(require("os"), 1);
600
+ var path5 = __toESM(require("path"), 1);
497
601
  var INSTALL_TMP_DIRNAME = ".oniro-tmp";
498
602
  var TMPFS_MAGIC = 16914836;
499
603
  var RAMFS_MAGIC = 2240043254;
500
- var HINT_EXAMPLE = os4.platform() === "win32" ? "D:\\oniro-tmp" : "~/.cache/oniro-tmp";
604
+ var HINT_EXAMPLE = os5.platform() === "win32" ? "D:\\oniro-tmp" : "~/.cache/oniro-tmp";
501
605
  var TMP_DIR_HINT = `Point the scratch directory at a filesystem with more room using --tmp-dir <path> (or the ONIRO_TMP_DIR environment variable), e.g. --tmp-dir ${HINT_EXAMPLE}.`;
502
606
  function resolveTmpRoot(config, override, installRoot) {
503
607
  const explicit = (override ?? "").trim();
504
- if (explicit) return path4.resolve(explicit);
608
+ if (explicit) return path5.resolve(explicit);
505
609
  const configured = config.get("tmpDir", "").trim();
506
- if (configured) return path4.resolve(configured);
507
- if (installRoot) return path4.join(path4.resolve(installRoot), INSTALL_TMP_DIRNAME);
508
- return os4.tmpdir();
610
+ if (configured) return path5.resolve(configured);
611
+ if (installRoot) return path5.join(path5.resolve(installRoot), INSTALL_TMP_DIRNAME);
612
+ return os5.tmpdir();
509
613
  }
510
614
  function createTempWorkDir(root, prefix) {
511
615
  try {
512
- fs3.mkdirSync(root, { recursive: true });
513
- return fs3.mkdtempSync(path4.join(root, prefix));
616
+ fs4.mkdirSync(root, { recursive: true });
617
+ return fs4.mkdtempSync(path5.join(root, prefix));
514
618
  } catch (err) {
515
619
  if (isOutOfSpaceError(err)) throw toSpaceError(err, root, "a temporary directory");
516
620
  throw new OniroError(
@@ -521,10 +625,10 @@ ${TMP_DIR_HINT}`,
521
625
  }
522
626
  }
523
627
  function removeTempWorkDir(dir, root) {
524
- fs3.rmSync(dir, { recursive: true, force: true });
525
- if (!root || path4.basename(root) !== INSTALL_TMP_DIRNAME) return;
628
+ fs4.rmSync(dir, { recursive: true, force: true });
629
+ if (!root || path5.basename(root) !== INSTALL_TMP_DIRNAME) return;
526
630
  try {
527
- fs3.rmdirSync(root);
631
+ fs4.rmdirSync(root);
528
632
  } catch {
529
633
  }
530
634
  }
@@ -534,18 +638,18 @@ function createInstallTempDir(opts) {
534
638
  try {
535
639
  return { dir: createTempWorkDir(root, opts.prefix), root };
536
640
  } catch (err) {
537
- const fallback = os4.tmpdir();
538
- if (requested || isOutOfSpaceError(err) || path4.resolve(fallback) === path4.resolve(root)) throw err;
641
+ const fallback = os5.tmpdir();
642
+ if (requested || isOutOfSpaceError(err) || path5.resolve(fallback) === path5.resolve(root)) throw err;
539
643
  const reason = err instanceof Error ? err.message.split(/\r?\n/)[0] : String(err);
540
644
  (opts.logger ?? noopLogger).warn(`${reason} Falling back to '${fallback}'.`);
541
645
  return { dir: createTempWorkDir(fallback, opts.prefix), root: fallback };
542
646
  }
543
647
  }
544
648
  function nearestExisting(dir) {
545
- let current = path4.resolve(dir);
649
+ let current = path5.resolve(dir);
546
650
  for (; ; ) {
547
- if (fs3.existsSync(current)) return current;
548
- const parent = path4.dirname(current);
651
+ if (fs4.existsSync(current)) return current;
652
+ const parent = path5.dirname(current);
549
653
  if (parent === current) return null;
550
654
  current = parent;
551
655
  }
@@ -554,7 +658,7 @@ function getFreeSpaceBytes(dir) {
554
658
  const target = nearestExisting(dir);
555
659
  if (!target) return null;
556
660
  try {
557
- const stats = fs3.statfsSync(target);
661
+ const stats = fs4.statfsSync(target);
558
662
  return Number(stats.bsize) * Number(stats.bavail);
559
663
  } catch {
560
664
  return null;
@@ -564,7 +668,7 @@ function isRamBackedPath(dir) {
564
668
  const target = nearestExisting(dir);
565
669
  if (!target) return false;
566
670
  try {
567
- const type = Number(fs3.statfsSync(target).type);
671
+ const type = Number(fs4.statfsSync(target).type);
568
672
  return type === TMPFS_MAGIC || type === RAMFS_MAGIC;
569
673
  } catch {
570
674
  return false;
@@ -628,16 +732,16 @@ function errorPath(err) {
628
732
  return null;
629
733
  }
630
734
  function isInside(child, parent) {
631
- const rel = path4.relative(path4.resolve(parent), path4.resolve(child));
632
- return rel === "" || !rel.startsWith("..") && !path4.isAbsolute(rel);
735
+ const rel = path5.relative(path5.resolve(parent), path5.resolve(child));
736
+ return rel === "" || !rel.startsWith("..") && !path5.isAbsolute(rel);
633
737
  }
634
738
  function toSpaceError(err, tmpRoot, what) {
635
739
  if (err instanceof InsufficientSpaceError) return err;
636
740
  if (!isOutOfSpaceError(err)) return err;
637
741
  const failed = errorPath(err);
638
- const failedDir = failed ? path4.dirname(path4.resolve(failed)) : path4.resolve(tmpRoot);
742
+ const failedDir = failed ? path5.dirname(path5.resolve(failed)) : path5.resolve(tmpRoot);
639
743
  const inTmp = isInside(failedDir, tmpRoot);
640
- const dir = inTmp ? path4.resolve(tmpRoot) : failedDir;
744
+ const dir = inTmp ? path5.resolve(tmpRoot) : failedDir;
641
745
  return new InsufficientSpaceError(
642
746
  spaceMessage(dir, what, { freeBytes: getFreeSpaceBytes(dir), tmpDirHint: inTmp }),
643
747
  dir,
@@ -646,8 +750,8 @@ function toSpaceError(err, tmpRoot, what) {
646
750
  }
647
751
 
648
752
  // src/sdk/download.ts
649
- var fs4 = __toESM(require("fs"), 1);
650
- var path5 = __toESM(require("path"), 1);
753
+ var fs5 = __toESM(require("fs"), 1);
754
+ var path6 = __toESM(require("path"), 1);
651
755
  var crypto = __toESM(require("crypto"), 1);
652
756
  var import_node_stream = require("stream");
653
757
  var import_node_util = require("util");
@@ -657,26 +761,26 @@ var pipelineAsync = (0, import_node_util.promisify)(import_node_stream.pipeline)
657
761
  async function downloadFile(opts) {
658
762
  const { url, dest, progress, abortSignal } = opts;
659
763
  const proto = url.startsWith("https") ? https : http;
660
- return new Promise((resolve4, reject) => {
764
+ return new Promise((resolve6, reject) => {
661
765
  const s = Math.max(0, Math.min(100, opts.start ?? 0));
662
766
  const r = Math.max(0, Math.min(100 - s, opts.range ?? 100));
663
767
  let settled = false;
664
768
  const done = (err) => {
665
769
  if (settled) return;
666
770
  settled = true;
667
- err ? reject(err) : resolve4();
771
+ err ? reject(err) : resolve6();
668
772
  };
669
773
  if (abortSignal?.aborted) {
670
774
  done(new CancelledError("Download cancelled before start."));
671
775
  return;
672
776
  }
673
- const destDir = path5.dirname(path5.resolve(dest));
777
+ const destDir = path6.dirname(path6.resolve(dest));
674
778
  const what = opts.what ?? `the download of '${url}'`;
675
779
  const writeFailed = (err) => isOutOfSpaceError(err) ? toSpaceError(err, opts.tmpRoot ?? destDir, what) : new OniroError(`Error writing '${dest}': ${err instanceof Error ? err.message : String(err)}`, err);
676
- const file = fs4.createWriteStream(dest);
780
+ const file = fs5.createWriteStream(dest);
677
781
  let activeResponse;
678
782
  const failWith = (err) => {
679
- file.close(() => fs4.rm(dest, { force: true }, () => done(err)));
783
+ file.close(() => fs5.rm(dest, { force: true }, () => done(err)));
680
784
  };
681
785
  file.on("error", (err) => {
682
786
  try {
@@ -748,9 +852,9 @@ async function downloadFile(opts) {
748
852
  });
749
853
  }
750
854
  async function verifySha256(filePath, sha256Path) {
751
- const expected = fs4.readFileSync(sha256Path, "utf8").split(/\s+/)[0];
855
+ const expected = fs5.readFileSync(sha256Path, "utf8").split(/\s+/)[0];
752
856
  const hash = crypto.createHash("sha256");
753
- await pipelineAsync(fs4.createReadStream(filePath), hash);
857
+ await pipelineAsync(fs5.createReadStream(filePath), hash);
754
858
  const actual = hash.digest("hex");
755
859
  if (actual !== expected) {
756
860
  throw new ChecksumMismatchError(expected, actual);
@@ -758,9 +862,9 @@ async function verifySha256(filePath, sha256Path) {
758
862
  }
759
863
 
760
864
  // src/sdk/extract.ts
761
- var fs5 = __toESM(require("fs"), 1);
762
- var os5 = __toESM(require("os"), 1);
763
- var path6 = __toESM(require("path"), 1);
865
+ var fs6 = __toESM(require("fs"), 1);
866
+ var os6 = __toESM(require("os"), 1);
867
+ var path7 = __toESM(require("path"), 1);
764
868
  var import_node_stream2 = require("stream");
765
869
  var import_node_util2 = require("util");
766
870
  var tar = __toESM(require("tar"), 1);
@@ -777,12 +881,12 @@ async function extractZipWithProgress(opts) {
777
881
  const r = Math.max(0, Math.min(100 - s, opts.range ?? 100));
778
882
  let processed = 0;
779
883
  let lastOverall = Math.round(s);
780
- await fs5.promises.mkdir(dest, { recursive: true });
781
- const destRoot = path6.resolve(dest);
884
+ await fs6.promises.mkdir(dest, { recursive: true });
885
+ const destRoot = path7.resolve(dest);
782
886
  const safeResolveTarget = (entryName) => {
783
- const resolved = path6.resolve(destRoot, entryName);
784
- const rel = path6.relative(destRoot, resolved);
785
- if (rel.startsWith("..") || path6.isAbsolute(rel)) {
887
+ const resolved = path7.resolve(destRoot, entryName);
888
+ const rel = path7.relative(destRoot, resolved);
889
+ if (rel.startsWith("..") || path7.isAbsolute(rel)) {
786
890
  throw new OniroError(`Blocked ZIP entry with illegal path: ${entryName}`);
787
891
  }
788
892
  return resolved;
@@ -794,25 +898,25 @@ async function extractZipWithProgress(opts) {
794
898
  const attr = file.attr ? file.attr >>> 16 : 0;
795
899
  const isSymlink = "isSymbolicLink" in file ? Boolean(file.isSymbolicLink) : (attr & 61440) === 40960;
796
900
  if (file.isDirectory) {
797
- await fs5.promises.mkdir(targetPath, { recursive: true });
901
+ await fs6.promises.mkdir(targetPath, { recursive: true });
798
902
  } else if (isSymlink) {
799
- await fs5.promises.mkdir(path6.dirname(targetPath), { recursive: true });
903
+ await fs6.promises.mkdir(path7.dirname(targetPath), { recursive: true });
800
904
  const linkTargetBuffer = await zip.entryData(file);
801
905
  const linkTarget = linkTargetBuffer.toString("utf8");
802
906
  try {
803
- const isWin = os5.platform() === "win32";
804
- await fs5.promises.symlink(linkTarget, targetPath, isWin ? "file" : void 0);
907
+ const isWin = os6.platform() === "win32";
908
+ await fs6.promises.symlink(linkTarget, targetPath, isWin ? "file" : void 0);
805
909
  } catch (e) {
806
910
  logger.warn(`Failed to create symlink at ${targetPath}: ${String(e)}`);
807
911
  }
808
912
  } else {
809
- await fs5.promises.mkdir(path6.dirname(targetPath), { recursive: true });
913
+ await fs6.promises.mkdir(path7.dirname(targetPath), { recursive: true });
810
914
  const readStream = await zip.stream(file);
811
- const writeStream = fs5.createWriteStream(targetPath);
915
+ const writeStream = fs6.createWriteStream(targetPath);
812
916
  await pipelineAsync2(readStream, writeStream);
813
917
  if (attr > 0) {
814
918
  try {
815
- await fs5.promises.chmod(targetPath, attr & 511);
919
+ await fs6.promises.chmod(targetPath, attr & 511);
816
920
  } catch {
817
921
  }
818
922
  }
@@ -844,41 +948,41 @@ async function extractTarball(tarPath, dest, strip = 0) {
844
948
  }
845
949
 
846
950
  // src/sdk/list.ts
847
- var fs6 = __toESM(require("fs"), 1);
848
- var path7 = __toESM(require("path"), 1);
951
+ var fs7 = __toESM(require("fs"), 1);
952
+ var path8 = __toESM(require("path"), 1);
849
953
  function getSupportedSdksForUi(config) {
850
- const base = path7.join(getSdkRootDir(config), getOsFolder());
954
+ const base = path8.join(getSdkRootDir(config), getOsFolder());
851
955
  return ALL_SDKS.map((sdk) => ({
852
956
  version: sdk.version,
853
957
  api: sdk.api,
854
- installed: fs6.existsSync(path7.join(base, sdk.api))
958
+ installed: fs7.existsSync(path8.join(base, sdk.api))
855
959
  })).sort((a, b) => Number(b.api) - Number(a.api));
856
960
  }
857
961
  function getInstalledSdks(config) {
858
962
  const sdkRoot = getSdkRootDir(config);
859
963
  const versions = /* @__PURE__ */ new Set();
860
- if (!fs6.existsSync(sdkRoot)) return [];
964
+ if (!fs7.existsSync(sdkRoot)) return [];
861
965
  for (const osFolder of ["linux", "darwin", "windows"]) {
862
- const osPath = path7.join(sdkRoot, osFolder);
863
- if (!fs6.existsSync(osPath) || !fs6.statSync(osPath).isDirectory()) continue;
864
- for (const api of fs6.readdirSync(osPath)) {
865
- const apiPath = path7.join(osPath, api);
866
- if (fs6.statSync(apiPath).isDirectory()) versions.add(api);
966
+ const osPath = path8.join(sdkRoot, osFolder);
967
+ if (!fs7.existsSync(osPath) || !fs7.statSync(osPath).isDirectory()) continue;
968
+ for (const api of fs7.readdirSync(osPath)) {
969
+ const apiPath = path8.join(osPath, api);
970
+ if (fs7.statSync(apiPath).isDirectory()) versions.add(api);
867
971
  }
868
972
  }
869
973
  return ALL_SDKS.filter((sdk) => versions.has(sdk.api)).map((sdk) => sdk.version);
870
974
  }
871
975
 
872
976
  // src/sdk/remove.ts
873
- var fs7 = __toESM(require("fs"), 1);
874
- var path8 = __toESM(require("path"), 1);
977
+ var fs8 = __toESM(require("fs"), 1);
978
+ var path9 = __toESM(require("path"), 1);
875
979
  function removeSdk(config, api) {
876
980
  const sdkRoot = getSdkRootDir(config);
877
981
  let removed = false;
878
982
  for (const osFolder of ["linux", "darwin", "windows"]) {
879
- const sdkPath = path8.join(sdkRoot, osFolder, api);
880
- if (fs7.existsSync(sdkPath)) {
881
- fs7.rmSync(sdkPath, { recursive: true, force: true });
983
+ const sdkPath = path9.join(sdkRoot, osFolder, api);
984
+ if (fs8.existsSync(sdkPath)) {
985
+ fs8.rmSync(sdkPath, { recursive: true, force: true });
882
986
  removed = true;
883
987
  }
884
988
  }
@@ -886,18 +990,18 @@ function removeSdk(config, api) {
886
990
  }
887
991
 
888
992
  // src/sdk/install.ts
889
- var fs9 = __toESM(require("fs"), 1);
890
- var path9 = __toESM(require("path"), 1);
993
+ var fs10 = __toESM(require("fs"), 1);
994
+ var path10 = __toESM(require("path"), 1);
891
995
 
892
996
  // src/sdk/move.ts
893
- var fs8 = __toESM(require("fs"), 1);
997
+ var fs9 = __toESM(require("fs"), 1);
894
998
  function movePath(src, dest) {
895
999
  try {
896
- fs8.renameSync(src, dest);
1000
+ fs9.renameSync(src, dest);
897
1001
  } catch (err) {
898
1002
  if (err.code !== "EXDEV") throw err;
899
- fs8.cpSync(src, dest, { recursive: true });
900
- fs8.rmSync(src, { recursive: true, force: true });
1003
+ fs9.cpSync(src, dest, { recursive: true });
1004
+ fs9.rmSync(src, { recursive: true, force: true });
901
1005
  }
902
1006
  }
903
1007
 
@@ -918,12 +1022,12 @@ async function downloadAndInstallSdk(opts) {
918
1022
  prefix: "oniro-sdk-",
919
1023
  logger
920
1024
  });
921
- const tarPath = path9.join(tmpDir, filename);
922
- const sha256Path = path9.join(tmpDir, `${filename}.sha256`);
923
- const extractDir = path9.join(tmpDir, "extract");
924
- fs9.mkdirSync(extractDir);
925
- const sdkInstallDir = path9.join(sdkRoot, osFolder, api);
926
- fs9.mkdirSync(path9.dirname(sdkInstallDir), { recursive: true });
1025
+ const tarPath = path10.join(tmpDir, filename);
1026
+ const sha256Path = path10.join(tmpDir, `${filename}.sha256`);
1027
+ const extractDir = path10.join(tmpDir, "extract");
1028
+ fs10.mkdirSync(extractDir);
1029
+ const sdkInstallDir = path10.join(sdkRoot, osFolder, api);
1030
+ fs10.mkdirSync(path10.dirname(sdkInstallDir), { recursive: true });
927
1031
  const checkCancelled = () => {
928
1032
  if (abortSignal?.aborted) throw new CancelledError();
929
1033
  };
@@ -940,13 +1044,13 @@ async function downloadAndInstallSdk(opts) {
940
1044
  await extractTarball(tarPath, extractDir, strip);
941
1045
  progress?.report({ message: "Extracting SDK (this may take a while)...", increment: 10 });
942
1046
  checkCancelled();
943
- const osContentPath = path9.join(extractDir, osFolder);
944
- if (!fs9.existsSync(osContentPath)) {
1047
+ const osContentPath = path10.join(extractDir, osFolder);
1048
+ if (!fs10.existsSync(osContentPath)) {
945
1049
  throw new OniroError(
946
1050
  `Expected folder '${osFolder}' not found in extracted SDK. Tarball layout may have changed.`
947
1051
  );
948
1052
  }
949
- const zipFiles = fs9.readdirSync(osContentPath).filter((n2) => n2.endsWith(".zip"));
1053
+ const zipFiles = fs10.readdirSync(osContentPath).filter((n2) => n2.endsWith(".zip"));
950
1054
  const componentsStart = 60;
951
1055
  const componentsBudget = 35;
952
1056
  const n = zipFiles.length;
@@ -961,16 +1065,16 @@ async function downloadAndInstallSdk(opts) {
961
1065
  for (const zipFile of zipFiles) {
962
1066
  checkCancelled();
963
1067
  logger.info(`Extracting component ${zipFile}`);
964
- const zipPath = path9.join(osContentPath, zipFile);
1068
+ const zipPath = path10.join(osContentPath, zipFile);
965
1069
  const thisBudget = base + (rem > 0 ? 1 : 0);
966
1070
  if (rem > 0) rem--;
967
1071
  await extractZipWithProgress({ zipPath, dest: osContentPath, progress, start: cursor, range: thisBudget, logger });
968
1072
  cursor += thisBudget;
969
- fs9.unlinkSync(zipPath);
1073
+ fs10.unlinkSync(zipPath);
970
1074
  }
971
1075
  progress?.report({ message: "Finalizing installation...", increment: 0 });
972
- if (fs9.existsSync(sdkInstallDir)) {
973
- fs9.rmSync(sdkInstallDir, { recursive: true, force: true });
1076
+ if (fs10.existsSync(sdkInstallDir)) {
1077
+ fs10.rmSync(sdkInstallDir, { recursive: true, force: true });
974
1078
  }
975
1079
  movePath(osContentPath, sdkInstallDir);
976
1080
  progress?.report({ message: "Finalizing installation...", increment: 3 });
@@ -986,13 +1090,13 @@ async function downloadAndInstallSdk(opts) {
986
1090
  }
987
1091
 
988
1092
  // src/cmdTools/install.ts
989
- var fs10 = __toESM(require("fs"), 1);
990
- var os6 = __toESM(require("os"), 1);
991
- var path11 = __toESM(require("path"), 1);
1093
+ var fs11 = __toESM(require("fs"), 1);
1094
+ var os7 = __toESM(require("os"), 1);
1095
+ var path12 = __toESM(require("path"), 1);
992
1096
  var import_node_child_process2 = require("child_process");
993
1097
 
994
1098
  // src/hdc/spawnCompat.ts
995
- var path10 = __toESM(require("path"), 1);
1099
+ var path11 = __toESM(require("path"), 1);
996
1100
  var import_node_child_process = require("child_process");
997
1101
  var WINDOWS_BATCH_RE = /\.(bat|cmd)$/i;
998
1102
  var CMD_META_RE = /([()[\]%!^"`<>&|;, *?])/g;
@@ -1016,7 +1120,7 @@ function escapeCmdCommand(command) {
1016
1120
  function buildCmdWrapper(command, args) {
1017
1121
  assertCmdSafe(command, `command ${command}`);
1018
1122
  args.forEach((a, i) => assertCmdSafe(a, `argument ${i} (${a})`));
1019
- const normalized = path10.normalize(command);
1123
+ const normalized = path11.normalize(command);
1020
1124
  const double = CMD_SHIM_RE.test(normalized);
1021
1125
  const line = [escapeCmdCommand(normalized), ...args.map((a) => escapeCmdArgument(a, double))].join(" ");
1022
1126
  return {
@@ -1064,7 +1168,7 @@ function killProcessTree(child) {
1064
1168
  }
1065
1169
 
1066
1170
  // src/cmdTools/install.ts
1067
- function getCmdToolsDownloadUrl(config, platform7 = os6.platform()) {
1171
+ function getCmdToolsDownloadUrl(config, platform7 = os7.platform()) {
1068
1172
  if (platform7 === "linux") {
1069
1173
  return config.get("cmdToolsUrlLinux", defaultPaths.cmdToolsUrlLinux);
1070
1174
  }
@@ -1090,16 +1194,16 @@ function getCmdToolsDownloadUrl(config, platform7 = os6.platform()) {
1090
1194
  }
1091
1195
  function findCmdToolsSourceDir(extractPath) {
1092
1196
  const known = [
1093
- path11.join(extractPath, "command-line-tools"),
1094
- path11.join(extractPath, "oh-command-line-tools"),
1095
- path11.join(extractPath, "commandline-tools")
1197
+ path12.join(extractPath, "command-line-tools"),
1198
+ path12.join(extractPath, "oh-command-line-tools"),
1199
+ path12.join(extractPath, "commandline-tools")
1096
1200
  ];
1097
1201
  for (const c of known) {
1098
- if (fs10.existsSync(c)) return c;
1202
+ if (fs11.existsSync(c)) return c;
1099
1203
  }
1100
- const entries = fs10.readdirSync(extractPath, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => path11.join(extractPath, e.name));
1204
+ const entries = fs11.readdirSync(extractPath, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => path12.join(extractPath, e.name));
1101
1205
  for (const dir of entries) {
1102
- if (fs10.existsSync(path11.join(dir, "bin"))) return dir;
1206
+ if (fs11.existsSync(path12.join(dir, "bin"))) return dir;
1103
1207
  }
1104
1208
  throw new OniroError("Could not locate command line tools folder in the extracted archive.");
1105
1209
  }
@@ -1111,12 +1215,12 @@ async function installCmdTools(opts) {
1111
1215
  const { dir: tmpDir, root: tmpRoot } = createInstallTempDir({
1112
1216
  config,
1113
1217
  override: opts.tmpDir,
1114
- installRoot: path11.dirname(CMD_PATH),
1218
+ installRoot: path12.dirname(CMD_PATH),
1115
1219
  prefix: "oniro-cmdtools-",
1116
1220
  logger
1117
1221
  });
1118
- const zipPath = opts.localZipPath ?? path11.join(tmpDir, "oh-command-line-tools.zip");
1119
- const extractPath = path11.join(tmpDir, "oh-command-line-tools");
1222
+ const zipPath = opts.localZipPath ?? path12.join(tmpDir, "oh-command-line-tools.zip");
1223
+ const extractPath = path12.join(tmpDir, "oh-command-line-tools");
1120
1224
  try {
1121
1225
  if (!opts.localZipPath) {
1122
1226
  const url = getCmdToolsDownloadUrl(config);
@@ -1125,22 +1229,22 @@ async function installCmdTools(opts) {
1125
1229
  }
1126
1230
  progress?.report({ message: "Extracting tools...", increment: 0 });
1127
1231
  await extractZipWithProgress({ zipPath, dest: extractPath, progress, start: 50, range: 45, logger });
1128
- fs10.mkdirSync(CMD_PATH, { recursive: true });
1232
+ fs11.mkdirSync(CMD_PATH, { recursive: true });
1129
1233
  const srcDir = findCmdToolsSourceDir(extractPath);
1130
- for (const entry of fs10.readdirSync(srcDir)) {
1131
- const src = path11.join(srcDir, entry);
1132
- const dest = path11.join(CMD_PATH, entry);
1133
- if (fs10.statSync(src).isDirectory()) {
1134
- if (fs10.existsSync(dest)) fs10.rmSync(dest, { recursive: true, force: true });
1234
+ for (const entry of fs11.readdirSync(srcDir)) {
1235
+ const src = path12.join(srcDir, entry);
1236
+ const dest = path12.join(CMD_PATH, entry);
1237
+ if (fs11.statSync(src).isDirectory()) {
1238
+ if (fs11.existsSync(dest)) fs11.rmSync(dest, { recursive: true, force: true });
1135
1239
  movePath(src, dest);
1136
1240
  } else {
1137
- fs10.copyFileSync(src, dest);
1241
+ fs11.copyFileSync(src, dest);
1138
1242
  }
1139
1243
  }
1140
- const binDir = path11.join(CMD_PATH, "bin");
1141
- if (fs10.existsSync(binDir) && os6.platform() !== "win32") {
1142
- for (const file of fs10.readdirSync(binDir)) {
1143
- fs10.chmodSync(path11.join(binDir, file), 493);
1244
+ const binDir = path12.join(CMD_PATH, "bin");
1245
+ if (fs11.existsSync(binDir) && os7.platform() !== "win32") {
1246
+ for (const file of fs11.readdirSync(binDir)) {
1247
+ fs11.chmodSync(path12.join(binDir, file), 493);
1144
1248
  }
1145
1249
  }
1146
1250
  progress?.report({ message: "Finalizing installation...", increment: 5 });
@@ -1153,16 +1257,16 @@ async function installCmdTools(opts) {
1153
1257
  }
1154
1258
  function getCmdToolsStatus(config) {
1155
1259
  const cmdPath = getCmdToolsPath(config);
1156
- const binDir = path11.join(cmdPath, "bin");
1157
- const candidates = os6.platform() === "win32" ? ["ohpm.exe", "ohpm.cmd", "ohpm.bat", "ohpm"] : ["ohpm"];
1158
- const bin = candidates.map((c) => path11.join(binDir, c)).find((p) => fs10.existsSync(p));
1260
+ const binDir = path12.join(cmdPath, "bin");
1261
+ const candidates = os7.platform() === "win32" ? ["ohpm.exe", "ohpm.cmd", "ohpm.bat", "ohpm"] : ["ohpm"];
1262
+ const bin = candidates.map((c) => path12.join(binDir, c)).find((p) => fs11.existsSync(p));
1159
1263
  if (!bin) {
1160
1264
  return { installed: false, status: "Not installed" };
1161
1265
  }
1162
1266
  try {
1163
- const versionFile = path11.join(cmdPath, "version.txt");
1164
- if (fs10.existsSync(versionFile)) {
1165
- const content = fs10.readFileSync(versionFile, "utf8");
1267
+ const versionFile = path12.join(cmdPath, "version.txt");
1268
+ if (fs11.existsSync(versionFile)) {
1269
+ const content = fs11.readFileSync(versionFile, "utf8");
1166
1270
  for (const line of content.split(/\r?\n/)) {
1167
1271
  const m = line.match(/^\s*#\s*Version:\s*(.+)$/);
1168
1272
  if (m?.[1]) return { installed: true, status: `Installed (${m[1].trim()})` };
@@ -1189,14 +1293,14 @@ function isCmdToolsInstalled(config) {
1189
1293
  }
1190
1294
  function removeCmdTools(config) {
1191
1295
  const cmdPath = getCmdToolsPath(config);
1192
- if (fs10.existsSync(cmdPath)) {
1193
- fs10.rmSync(cmdPath, { recursive: true, force: true });
1296
+ if (fs11.existsSync(cmdPath)) {
1297
+ fs11.rmSync(cmdPath, { recursive: true, force: true });
1194
1298
  }
1195
1299
  }
1196
1300
 
1197
1301
  // src/sign/encryptKey.ts
1198
- var fs11 = __toESM(require("fs"), 1);
1199
- var path12 = __toESM(require("path"), 1);
1302
+ var fs12 = __toESM(require("fs"), 1);
1303
+ var path13 = __toESM(require("path"), 1);
1200
1304
  var crypto2 = __toESM(require("crypto"), 1);
1201
1305
  var DAEMON_ROOT_KEY_COMPONENT_LENGTH = 16;
1202
1306
  var DAEMON_SALT_KEY_LENGTH = 16;
@@ -1268,33 +1372,33 @@ function getRootKey(fdComponents, salt) {
1268
1372
  function createAndStoreKey(dir, keyLength) {
1269
1373
  const key = crypto2.randomBytes(keyLength);
1270
1374
  const hash = crypto2.createHash("sha256").update(key).digest("hex");
1271
- const filePath = path12.join(dir, hash);
1272
- fs11.writeFileSync(filePath, key);
1273
- fs11.chmodSync(filePath, KEY_FILE_PERMISSIONS);
1375
+ const filePath = path13.join(dir, hash);
1376
+ fs12.writeFileSync(filePath, key);
1377
+ fs12.chmodSync(filePath, KEY_FILE_PERMISSIONS);
1274
1378
  return key;
1275
1379
  }
1276
1380
  function createAndStoreEnKey(rootKey, ceDir) {
1277
1381
  const workKey = crypto2.randomBytes(DAEMON_WORK_KEY_LENGTH);
1278
1382
  const encrypted = encrypt(rootKey, workKey);
1279
1383
  const hash = crypto2.createHash("sha256").update(encrypted).digest("hex");
1280
- const filePath = path12.join(ceDir, hash);
1281
- fs11.writeFileSync(filePath, encrypted);
1282
- fs11.chmodSync(filePath, KEY_FILE_PERMISSIONS);
1384
+ const filePath = path13.join(ceDir, hash);
1385
+ fs12.writeFileSync(filePath, encrypted);
1386
+ fs12.chmodSync(filePath, KEY_FILE_PERMISSIONS);
1283
1387
  }
1284
1388
  function createMaterial(materialPath) {
1285
- fs11.mkdirSync(materialPath, { recursive: true });
1286
- fs11.chmodSync(materialPath, KEY_FILE_DIRECTORY_PERMISSIONS);
1287
- const fdDir = path12.join(materialPath, "fd");
1288
- const acDir = path12.join(materialPath, "ac");
1289
- const ceDir = path12.join(materialPath, "ce");
1290
- fs11.mkdirSync(fdDir, { recursive: true });
1291
- fs11.mkdirSync(acDir, { recursive: true });
1292
- fs11.mkdirSync(ceDir, { recursive: true });
1389
+ fs12.mkdirSync(materialPath, { recursive: true });
1390
+ fs12.chmodSync(materialPath, KEY_FILE_DIRECTORY_PERMISSIONS);
1391
+ const fdDir = path13.join(materialPath, "fd");
1392
+ const acDir = path13.join(materialPath, "ac");
1393
+ const ceDir = path13.join(materialPath, "ce");
1394
+ fs12.mkdirSync(fdDir, { recursive: true });
1395
+ fs12.mkdirSync(acDir, { recursive: true });
1396
+ fs12.mkdirSync(ceDir, { recursive: true });
1293
1397
  const fdSubDirs = ["0", "1", "2"];
1294
1398
  const fdComponents = [];
1295
1399
  for (const sub of fdSubDirs) {
1296
- const subDir = path12.join(fdDir, sub);
1297
- fs11.mkdirSync(subDir, { recursive: true });
1400
+ const subDir = path13.join(fdDir, sub);
1401
+ fs12.mkdirSync(subDir, { recursive: true });
1298
1402
  const comp = createAndStoreKey(subDir, DAEMON_ROOT_KEY_COMPONENT_LENGTH);
1299
1403
  fdComponents.push(comp);
1300
1404
  }
@@ -1303,24 +1407,24 @@ function createMaterial(materialPath) {
1303
1407
  createAndStoreEnKey(rootKey, ceDir);
1304
1408
  }
1305
1409
  function readSingleFile(dir) {
1306
- if (!fs11.existsSync(dir) || !fs11.statSync(dir).isDirectory()) {
1410
+ if (!fs12.existsSync(dir) || !fs12.statSync(dir).isDirectory()) {
1307
1411
  throw new Error(`Missing signing material directory: ${dir}`);
1308
1412
  }
1309
- const files = fs11.readdirSync(dir).filter((f) => f !== ".DS_Store");
1413
+ const files = fs12.readdirSync(dir).filter((f) => f !== ".DS_Store");
1310
1414
  if (files.length !== 1) {
1311
1415
  throw new Error(`Signing material in ${dir} is illegal (expected exactly one file).`);
1312
1416
  }
1313
- return fs11.readFileSync(path12.join(dir, files[0]));
1417
+ return fs12.readFileSync(path13.join(dir, files[0]));
1314
1418
  }
1315
1419
  function getKey(materialPath) {
1316
- if (!fs11.existsSync(materialPath) || !fs11.statSync(materialPath).isDirectory()) {
1420
+ if (!fs12.existsSync(materialPath) || !fs12.statSync(materialPath).isDirectory()) {
1317
1421
  throw new Error("Material directory does not exist.");
1318
1422
  }
1319
- const fdDir = path12.join(materialPath, "fd");
1320
- const fdComponents = ["0", "1", "2"].map((sub) => readSingleFile(path12.join(fdDir, sub)));
1321
- const salt = readSingleFile(path12.join(materialPath, "ac"));
1423
+ const fdDir = path13.join(materialPath, "fd");
1424
+ const fdComponents = ["0", "1", "2"].map((sub) => readSingleFile(path13.join(fdDir, sub)));
1425
+ const salt = readSingleFile(path13.join(materialPath, "ac"));
1322
1426
  const rootKey = getRootKey(fdComponents, salt);
1323
- const workMaterial = readSingleFile(path12.join(materialPath, "ce"));
1427
+ const workMaterial = readSingleFile(path13.join(materialPath, "ce"));
1324
1428
  return decrypt(rootKey, workMaterial);
1325
1429
  }
1326
1430
  function encryptPwd(password, materialPath) {
@@ -1335,8 +1439,8 @@ function decryptPwd(encryptedHex, materialPath) {
1335
1439
  }
1336
1440
 
1337
1441
  // src/sign/generateSigningConfigs.ts
1338
- var fs12 = __toESM(require("fs"), 1);
1339
- var path13 = __toESM(require("path"), 1);
1442
+ var fs13 = __toESM(require("fs"), 1);
1443
+ var path14 = __toESM(require("path"), 1);
1340
1444
  var import_node_child_process3 = require("child_process");
1341
1445
  var import_json52 = __toESM(require("json5"), 1);
1342
1446
 
@@ -1406,17 +1510,17 @@ var SIGNING_KIND_CONFIG = {
1406
1510
  };
1407
1511
  function copyFilesToProject(projectDir, paths, kind, logger, applicationCertPath) {
1408
1512
  logger.info("[sign] Copying signing material into project...");
1409
- const signaturesDir = path13.join(projectDir, "signatures");
1410
- fs12.mkdirSync(signaturesDir, { recursive: true });
1411
- fs12.copyFileSync(paths.keystore, path13.join(signaturesDir, "OpenHarmony.p12"));
1412
- fs12.copyFileSync(paths.profileCert, path13.join(signaturesDir, "OpenHarmonyProfileRelease.pem"));
1413
- fs12.copyFileSync(paths.unsignedProfileTemplate, path13.join(signaturesDir, "UnsgnedReleasedProfileTemplate.json"));
1513
+ const signaturesDir = path14.join(projectDir, "signatures");
1514
+ fs13.mkdirSync(signaturesDir, { recursive: true });
1515
+ fs13.copyFileSync(paths.keystore, path14.join(signaturesDir, "OpenHarmony.p12"));
1516
+ fs13.copyFileSync(paths.profileCert, path14.join(signaturesDir, "OpenHarmonyProfileRelease.pem"));
1517
+ fs13.copyFileSync(paths.unsignedProfileTemplate, path14.join(signaturesDir, "UnsgnedReleasedProfileTemplate.json"));
1414
1518
  if (kind === "application-release") {
1415
- const dest = path13.join(signaturesDir, "OpenHarmonyApplication.cer");
1519
+ const dest = path14.join(signaturesDir, "OpenHarmonyApplication.cer");
1416
1520
  if (applicationCertPath) {
1417
- fs12.copyFileSync(applicationCertPath, dest);
1521
+ fs13.copyFileSync(applicationCertPath, dest);
1418
1522
  } else {
1419
- fs12.writeFileSync(dest, OPEN_HARMONY_APPLICATION_CERT);
1523
+ fs13.writeFileSync(dest, OPEN_HARMONY_APPLICATION_CERT);
1420
1524
  }
1421
1525
  }
1422
1526
  }
@@ -1425,7 +1529,7 @@ function resolveAppFeature(apl, override) {
1425
1529
  return apl === "normal" ? "hos_normal_app" : "hos_system_app";
1426
1530
  }
1427
1531
  function extractLeafCertificate(chainPath) {
1428
- const certContent = fs12.readFileSync(chainPath, "utf-8");
1532
+ const certContent = fs13.readFileSync(chainPath, "utf-8");
1429
1533
  const parts = certContent.split("-----END CERTIFICATE-----");
1430
1534
  if (parts.length < 3) {
1431
1535
  throw new OniroError(`${chainPath} does not contain enough certificates.`);
@@ -1435,20 +1539,20 @@ function extractLeafCertificate(chainPath) {
1435
1539
  function modifyProfileTemplate(projectDir, apl, appFeature, logger, options = {}) {
1436
1540
  const kind = options.kind ?? pickSigningKind(apl);
1437
1541
  logger.info(`[sign] Modifying profile template (apl=${apl}, app-feature=${appFeature}, kind=${kind})...`);
1438
- const appJsonPath = path13.join(projectDir, "AppScope/app.json5");
1439
- const profileTemplatePath = path13.join(projectDir, "signatures/UnsgnedReleasedProfileTemplate.json");
1440
- if (!fs12.existsSync(appJsonPath)) {
1542
+ const appJsonPath = path14.join(projectDir, "AppScope/app.json5");
1543
+ const profileTemplatePath = path14.join(projectDir, "signatures/UnsgnedReleasedProfileTemplate.json");
1544
+ if (!fs13.existsSync(appJsonPath)) {
1441
1545
  throw new OniroError(`${appJsonPath} does not exist.`);
1442
1546
  }
1443
1547
  let appJson;
1444
1548
  try {
1445
- appJson = import_json52.default.parse(fs12.readFileSync(appJsonPath, "utf-8"));
1549
+ appJson = import_json52.default.parse(fs13.readFileSync(appJsonPath, "utf-8"));
1446
1550
  } catch (e) {
1447
1551
  throw new OniroError(`Error parsing ${appJsonPath}: ${e.message}`, e);
1448
1552
  }
1449
1553
  let profileTemplate;
1450
1554
  try {
1451
- profileTemplate = JSON.parse(fs12.readFileSync(profileTemplatePath, "utf-8"));
1555
+ profileTemplate = JSON.parse(fs13.readFileSync(profileTemplatePath, "utf-8"));
1452
1556
  } catch (e) {
1453
1557
  throw new OniroError(`Error parsing ${profileTemplatePath}: ${e.message}`, e);
1454
1558
  }
@@ -1461,7 +1565,7 @@ function modifyProfileTemplate(projectDir, apl, appFeature, logger, options = {}
1461
1565
  profileTemplate["bundle-info"]["bundle-name"] = appJson.app.bundleName;
1462
1566
  profileTemplate["bundle-info"]["apl"] = apl;
1463
1567
  profileTemplate["bundle-info"]["app-feature"] = appFeature;
1464
- const distCertChainPath = path13.join(
1568
+ const distCertChainPath = path14.join(
1465
1569
  projectDir,
1466
1570
  "signatures",
1467
1571
  SIGNING_KIND_CONFIG[kind].certBasename
@@ -1471,13 +1575,13 @@ function modifyProfileTemplate(projectDir, apl, appFeature, logger, options = {}
1471
1575
  profileTemplate.acls = profileTemplate.acls ?? {};
1472
1576
  profileTemplate.acls["allowed-acls"] = options.acls;
1473
1577
  }
1474
- fs12.writeFileSync(profileTemplatePath, JSON.stringify(profileTemplate, null, 2));
1578
+ fs13.writeFileSync(profileTemplatePath, JSON.stringify(profileTemplate, null, 2));
1475
1579
  }
1476
1580
  function generateP7bFile(projectDir, paths, logger, passwords) {
1477
1581
  logger.info("[sign] Generating P7b profile via hap-sign-tool...");
1478
- const signaturesDir = path13.join(projectDir, "signatures");
1479
- const profileTemplatePath = path13.join(signaturesDir, "UnsgnedReleasedProfileTemplate.json");
1480
- const outputProfilePath = path13.join(signaturesDir, "app1-profile.p7b");
1582
+ const signaturesDir = path14.join(projectDir, "signatures");
1583
+ const profileTemplatePath = path14.join(signaturesDir, "UnsgnedReleasedProfileTemplate.json");
1584
+ const outputProfilePath = path14.join(signaturesDir, "app1-profile.p7b");
1481
1585
  const args = [
1482
1586
  "-jar",
1483
1587
  paths.signTool,
@@ -1511,11 +1615,11 @@ function generateP7bFile(projectDir, paths, logger, passwords) {
1511
1615
  }
1512
1616
  }
1513
1617
  function detectSigningConfigNames(projectDir) {
1514
- const buildProfilePath = path13.join(projectDir, "build-profile.json5");
1515
- if (!fs12.existsSync(buildProfilePath)) return ["default"];
1618
+ const buildProfilePath = path14.join(projectDir, "build-profile.json5");
1619
+ if (!fs13.existsSync(buildProfilePath)) return ["default"];
1516
1620
  let parsed;
1517
1621
  try {
1518
- parsed = import_json52.default.parse(fs12.readFileSync(buildProfilePath, "utf-8"));
1622
+ parsed = import_json52.default.parse(fs13.readFileSync(buildProfilePath, "utf-8"));
1519
1623
  } catch {
1520
1624
  return ["default"];
1521
1625
  }
@@ -1532,14 +1636,14 @@ function detectSigningConfigNames(projectDir) {
1532
1636
  }
1533
1637
  function updateBuildProfile(projectDir, kind, logger, passwords) {
1534
1638
  logger.info("[sign] Writing signing configs into build-profile.json5...");
1535
- const materialDir = path13.join(projectDir, "signatures", "material");
1536
- const buildProfilePath = path13.join(projectDir, "build-profile.json5");
1639
+ const materialDir = path14.join(projectDir, "signatures", "material");
1640
+ const buildProfilePath = path14.join(projectDir, "build-profile.json5");
1537
1641
  const encryptedStorePassword = encryptPwd(passwords.store, materialDir);
1538
1642
  const encryptedKeyPassword = encryptPwd(passwords.key, materialDir);
1539
1643
  let buildProfile = { app: {} };
1540
- if (fs12.existsSync(buildProfilePath)) {
1644
+ if (fs13.existsSync(buildProfilePath)) {
1541
1645
  try {
1542
- buildProfile = import_json52.default.parse(fs12.readFileSync(buildProfilePath, "utf-8"));
1646
+ buildProfile = import_json52.default.parse(fs13.readFileSync(buildProfilePath, "utf-8"));
1543
1647
  } catch (e) {
1544
1648
  throw new OniroError(`Error parsing ${buildProfilePath}: ${e.message}`, e);
1545
1649
  }
@@ -1560,13 +1664,13 @@ function updateBuildProfile(projectDir, kind, logger, passwords) {
1560
1664
  storeFile: "./signatures/OpenHarmony.p12"
1561
1665
  }
1562
1666
  }));
1563
- fs12.writeFileSync(buildProfilePath, JSON.stringify(buildProfile, null, 2));
1667
+ fs13.writeFileSync(buildProfilePath, JSON.stringify(buildProfile, null, 2));
1564
1668
  }
1565
1669
  function prepareMaterialDirectory(projectDir, logger) {
1566
1670
  logger.info("[sign] Generating fresh signing material...");
1567
- const materialDir = path13.join(projectDir, "signatures", "material");
1568
- if (fs12.existsSync(materialDir)) {
1569
- fs12.rmSync(materialDir, { recursive: true, force: true });
1671
+ const materialDir = path14.join(projectDir, "signatures", "material");
1672
+ if (fs13.existsSync(materialDir)) {
1673
+ fs13.rmSync(materialDir, { recursive: true, force: true });
1570
1674
  }
1571
1675
  createMaterial(materialDir);
1572
1676
  }
@@ -1589,15 +1693,15 @@ function generateSigningConfigs(options) {
1589
1693
  "Could not detect project SDK version (compileSdkVersion missing in build-profile.json5)."
1590
1694
  );
1591
1695
  }
1592
- const sdkPath = path13.join(sdkHome, String(sdkVersion));
1593
- if (!fs12.existsSync(sdkPath)) {
1696
+ const sdkPath = path14.join(sdkHome, String(sdkVersion));
1697
+ if (!fs13.existsSync(sdkPath)) {
1594
1698
  throw new OniroError(`SDK path does not exist: ${sdkPath}`);
1595
1699
  }
1596
1700
  const paths = {
1597
- signTool: path13.join(sdkPath, "toolchains/lib/hap-sign-tool.jar"),
1598
- keystore: path13.join(sdkPath, "toolchains/lib/OpenHarmony.p12"),
1599
- profileCert: path13.join(sdkPath, "toolchains/lib/OpenHarmonyProfileRelease.pem"),
1600
- unsignedProfileTemplate: path13.join(sdkPath, "toolchains/lib/UnsgnedReleasedProfileTemplate.json")
1701
+ signTool: path14.join(sdkPath, "toolchains/lib/hap-sign-tool.jar"),
1702
+ keystore: path14.join(sdkPath, "toolchains/lib/OpenHarmony.p12"),
1703
+ profileCert: path14.join(sdkPath, "toolchains/lib/OpenHarmonyProfileRelease.pem"),
1704
+ unsignedProfileTemplate: path14.join(sdkPath, "toolchains/lib/UnsgnedReleasedProfileTemplate.json")
1601
1705
  };
1602
1706
  const kind = pickSigningKind(apl);
1603
1707
  const passwords = options.passwords ?? DEFAULT_PASSWORDS;
@@ -1611,8 +1715,8 @@ function generateSigningConfigs(options) {
1611
1715
  }
1612
1716
 
1613
1717
  // src/emulator/install.ts
1614
- var fs13 = __toESM(require("fs"), 1);
1615
- var path14 = __toESM(require("path"), 1);
1718
+ var fs14 = __toESM(require("fs"), 1);
1719
+ var path15 = __toESM(require("path"), 1);
1616
1720
  async function installEmulator(opts) {
1617
1721
  const { config, progress, abortSignal } = opts;
1618
1722
  const logger = opts.logger ?? noopLogger;
@@ -1622,20 +1726,20 @@ async function installEmulator(opts) {
1622
1726
  const { dir: tmpDir, root: tmpRoot } = createInstallTempDir({
1623
1727
  config,
1624
1728
  override: opts.tmpDir,
1625
- installRoot: path14.dirname(emulatorDir),
1729
+ installRoot: path15.dirname(emulatorDir),
1626
1730
  prefix: "oniro-emulator-",
1627
1731
  logger
1628
1732
  });
1629
- const tmpZip = path14.join(tmpDir, "oniro_emulator.zip");
1733
+ const tmpZip = path15.join(tmpDir, "oniro_emulator.zip");
1630
1734
  try {
1631
- fs13.mkdirSync(emulatorDir, { recursive: true });
1735
+ fs14.mkdirSync(emulatorDir, { recursive: true });
1632
1736
  progress?.report({ message: "Downloading emulator...", increment: 0 });
1633
1737
  await downloadFile({ url, dest: tmpZip, progress, abortSignal, start: 0, range: 50, what, tmpRoot });
1634
1738
  progress?.report({ message: "Extracting emulator...", increment: 0 });
1635
1739
  await extractZipWithProgress({ zipPath: tmpZip, dest: emulatorDir, progress, start: 50, range: 45, logger });
1636
- const runSh = path14.join(emulatorDir, "images", "run.sh");
1637
- if (fs13.existsSync(runSh)) {
1638
- fs13.chmodSync(runSh, 493);
1740
+ const runSh = path15.join(emulatorDir, "images", "run.sh");
1741
+ if (fs14.existsSync(runSh)) {
1742
+ fs14.chmodSync(runSh, 493);
1639
1743
  }
1640
1744
  progress?.report({ message: "Finalizing installation...", increment: 5 });
1641
1745
  } catch (err) {
@@ -1645,24 +1749,24 @@ async function installEmulator(opts) {
1645
1749
  }
1646
1750
  }
1647
1751
  function isEmulatorInstalled(config) {
1648
- const imagesDir = path14.join(getEmulatorDir(config), "images");
1649
- return fs13.existsSync(path14.join(imagesDir, "run.sh")) || fs13.existsSync(path14.join(imagesDir, "run.bat"));
1752
+ const imagesDir = path15.join(getEmulatorDir(config), "images");
1753
+ return fs14.existsSync(path15.join(imagesDir, "run.sh")) || fs14.existsSync(path15.join(imagesDir, "run.bat"));
1650
1754
  }
1651
1755
  function removeEmulator(config) {
1652
1756
  const emulatorDir = getEmulatorDir(config);
1653
- if (fs13.existsSync(emulatorDir)) {
1654
- fs13.rmSync(emulatorDir, { recursive: true, force: true });
1757
+ if (fs14.existsSync(emulatorDir)) {
1758
+ fs14.rmSync(emulatorDir, { recursive: true, force: true });
1655
1759
  }
1656
1760
  }
1657
1761
 
1658
1762
  // src/emulator/lifecycle.ts
1659
- var fs15 = __toESM(require("fs"), 1);
1660
- var os7 = __toESM(require("os"), 1);
1661
- var path15 = __toESM(require("path"), 1);
1763
+ var fs16 = __toESM(require("fs"), 1);
1764
+ var os8 = __toESM(require("os"), 1);
1765
+ var path16 = __toESM(require("path"), 1);
1662
1766
  var import_node_child_process4 = require("child_process");
1663
1767
 
1664
1768
  // src/hdc/exec.ts
1665
- var fs14 = __toESM(require("fs"), 1);
1769
+ var fs15 = __toESM(require("fs"), 1);
1666
1770
  function errorMessage(err) {
1667
1771
  return err instanceof Error ? err.message : String(err);
1668
1772
  }
@@ -1670,12 +1774,12 @@ function runProcess(opts) {
1670
1774
  const { command, args, cwd, env, onOutput } = opts;
1671
1775
  const timeoutMs = opts.timeoutMs ?? 6e4;
1672
1776
  const { abortSignal } = opts;
1673
- return new Promise((resolve4, reject) => {
1777
+ return new Promise((resolve6, reject) => {
1674
1778
  if (abortSignal?.aborted) {
1675
1779
  reject(new CancelledError(`Command aborted before start: ${command}`));
1676
1780
  return;
1677
1781
  }
1678
- if (needsCmdWrapper(command) && !fs14.existsSync(command)) {
1782
+ if (needsCmdWrapper(command) && !fs15.existsSync(command)) {
1679
1783
  reject(new OniroError(`Failed to spawn ${command}: ENOENT (no such file)`));
1680
1784
  return;
1681
1785
  }
@@ -1735,7 +1839,7 @@ ${stderr.trim()}` : "")
1735
1839
  );
1736
1840
  return;
1737
1841
  }
1738
- resolve4({ code: code ?? -1, stdout, stderr });
1842
+ resolve6({ code: code ?? -1, stdout, stderr });
1739
1843
  });
1740
1844
  });
1741
1845
  }
@@ -1783,7 +1887,7 @@ function ensureRemoteOk(result, command) {
1783
1887
  }
1784
1888
 
1785
1889
  // src/emulator/lifecycle.ts
1786
- var PID_FILE = path15.join(os7.tmpdir(), "oniro_emulator.pid");
1890
+ var PID_FILE = path16.join(os8.tmpdir(), "oniro_emulator.pid");
1787
1891
  async function attemptHdcConnection(config, address = "127.0.0.1:55555", logger = noopLogger) {
1788
1892
  try {
1789
1893
  await hdcExec({ config, args: ["start", "-r"], logger });
@@ -1796,24 +1900,24 @@ async function attemptHdcConnection(config, address = "127.0.0.1:55555", logger
1796
1900
  }
1797
1901
  }
1798
1902
  function resolveLauncher(imagesPath, headless, connect) {
1799
- const runSh = path15.join(imagesPath, "run.sh");
1800
- const runBat = path15.join(imagesPath, "run.bat");
1903
+ const runSh = path16.join(imagesPath, "run.sh");
1904
+ const runBat = path16.join(imagesPath, "run.bat");
1801
1905
  const extra = [];
1802
1906
  if (headless) extra.push("--headless");
1803
1907
  if (connect) extra.push("--connect", connect);
1804
- if (os7.platform() === "win32") {
1805
- if (fs15.existsSync(runBat)) {
1908
+ if (os8.platform() === "win32") {
1909
+ if (fs16.existsSync(runBat)) {
1806
1910
  return { command: "cmd.exe", args: ["/c", runBat, ...extra], cwd: imagesPath };
1807
1911
  }
1808
- if (fs15.existsSync(runSh)) {
1912
+ if (fs16.existsSync(runSh)) {
1809
1913
  return { command: "bash", args: [runSh, ...extra], cwd: imagesPath };
1810
1914
  }
1811
1915
  throw new OniroError(`No emulator launcher (run.bat or run.sh) found in ${imagesPath}.`);
1812
1916
  }
1813
- if (fs15.existsSync(runSh)) {
1917
+ if (fs16.existsSync(runSh)) {
1814
1918
  return { command: "bash", args: [runSh, ...extra], cwd: imagesPath };
1815
1919
  }
1816
- if (fs15.existsSync(runBat)) {
1920
+ if (fs16.existsSync(runBat)) {
1817
1921
  return { command: "bash", args: [runBat, ...extra], cwd: imagesPath };
1818
1922
  }
1819
1923
  throw new OniroError(`No emulator launcher (run.sh or run.bat) found in ${imagesPath}.`);
@@ -1821,9 +1925,9 @@ function resolveLauncher(imagesPath, headless, connect) {
1821
1925
  async function startEmulator(opts) {
1822
1926
  const { config } = opts;
1823
1927
  const logger = opts.logger ?? noopLogger;
1824
- if (os7.platform() !== "win32" && fs15.existsSync(PID_FILE)) {
1928
+ if (os8.platform() !== "win32" && fs16.existsSync(PID_FILE)) {
1825
1929
  try {
1826
- const pid = Number(fs15.readFileSync(PID_FILE, "utf8").trim());
1930
+ const pid = Number(fs16.readFileSync(PID_FILE, "utf8").trim());
1827
1931
  if (Number.isFinite(pid) && pid > 0) {
1828
1932
  try {
1829
1933
  process.kill(pid, 0);
@@ -1839,13 +1943,13 @@ async function startEmulator(opts) {
1839
1943
  logger.warn(`Could not read PID file: ${err.message}`);
1840
1944
  }
1841
1945
  }
1842
- const imagesPath = path15.join(getEmulatorDir(config), "images");
1843
- if (!fs15.existsSync(imagesPath)) {
1946
+ const imagesPath = path16.join(getEmulatorDir(config), "images");
1947
+ if (!fs16.existsSync(imagesPath)) {
1844
1948
  throw new OniroError(`Emulator images directory not found at ${imagesPath}. Run \`oniro-app emulator install\` first.`);
1845
1949
  }
1846
1950
  const launcher = resolveLauncher(imagesPath, opts.headless === true, opts.connect);
1847
- const logPath = opts.logFile ?? (os7.platform() === "win32" ? "NUL" : "/dev/null");
1848
- const logFd = fs15.openSync(logPath, "a");
1951
+ const logPath = opts.logFile ?? (os8.platform() === "win32" ? "NUL" : "/dev/null");
1952
+ const logFd = fs16.openSync(logPath, "a");
1849
1953
  let child;
1850
1954
  try {
1851
1955
  child = (0, import_node_child_process4.spawn)(launcher.command, [...launcher.args], {
@@ -1855,19 +1959,19 @@ async function startEmulator(opts) {
1855
1959
  windowsHide: true
1856
1960
  });
1857
1961
  } finally {
1858
- fs15.closeSync(logFd);
1962
+ fs16.closeSync(logFd);
1859
1963
  }
1860
1964
  if (!child.pid) {
1861
1965
  throw new OniroError(`Failed to spawn launcher (${launcher.command} ${launcher.args.join(" ")}).`);
1862
1966
  }
1863
- fs15.writeFileSync(PID_FILE, String(child.pid));
1967
+ fs16.writeFileSync(PID_FILE, String(child.pid));
1864
1968
  child.unref();
1865
1969
  logger.info(`Emulator launched (PID ${child.pid}). Logs: ${logPath}`);
1866
- const earlyExit = new Promise((resolve4) => {
1867
- child.once("exit", (code) => resolve4(code));
1868
- child.once("error", () => resolve4(-1));
1970
+ const earlyExit = new Promise((resolve6) => {
1971
+ child.once("exit", (code) => resolve6(code));
1972
+ child.once("error", () => resolve6(-1));
1869
1973
  });
1870
- const liveness = new Promise((resolve4) => setTimeout(() => resolve4("alive"), 2e3));
1974
+ const liveness = new Promise((resolve6) => setTimeout(() => resolve6("alive"), 2e3));
1871
1975
  const outcome = await Promise.race([earlyExit, liveness]);
1872
1976
  if (outcome !== "alive") {
1873
1977
  const tail = readTail(logPath, 4e3);
@@ -1907,24 +2011,24 @@ ${tail}` : ""}`);
1907
2011
  }
1908
2012
  function readTail(filePath, bytes) {
1909
2013
  try {
1910
- const stat = fs15.statSync(filePath);
2014
+ const stat = fs16.statSync(filePath);
1911
2015
  const start = Math.max(0, stat.size - bytes);
1912
- const fd = fs15.openSync(filePath, "r");
2016
+ const fd = fs16.openSync(filePath, "r");
1913
2017
  try {
1914
2018
  const buf = Buffer.alloc(stat.size - start);
1915
- fs15.readSync(fd, buf, 0, buf.length, start);
2019
+ fs16.readSync(fd, buf, 0, buf.length, start);
1916
2020
  return buf.toString("utf8");
1917
2021
  } finally {
1918
- fs15.closeSync(fd);
2022
+ fs16.closeSync(fd);
1919
2023
  }
1920
2024
  } catch {
1921
2025
  return "";
1922
2026
  }
1923
2027
  }
1924
2028
  async function stopEmulator(logger = noopLogger) {
1925
- if (fs15.existsSync(PID_FILE)) {
2029
+ if (fs16.existsSync(PID_FILE)) {
1926
2030
  try {
1927
- const pid = Number(fs15.readFileSync(PID_FILE, "utf8").trim());
2031
+ const pid = Number(fs16.readFileSync(PID_FILE, "utf8").trim());
1928
2032
  if (Number.isFinite(pid) && pid > 0) {
1929
2033
  try {
1930
2034
  process.kill(pid, "SIGTERM");
@@ -1937,13 +2041,13 @@ async function stopEmulator(logger = noopLogger) {
1937
2041
  logger.warn(`Could not read PID file: ${err.message}`);
1938
2042
  }
1939
2043
  }
1940
- const [killCmd, ...killArgs] = os7.platform() === "win32" ? ["taskkill", "/IM", "qemu-system-x86_64.exe", "/F"] : ["pkill", "-f", "qemu-system-x86_64"];
2044
+ const [killCmd, ...killArgs] = os8.platform() === "win32" ? ["taskkill", "/IM", "qemu-system-x86_64.exe", "/F"] : ["pkill", "-f", "qemu-system-x86_64"];
1941
2045
  try {
1942
2046
  await runProcess({ command: killCmd, args: killArgs, logger });
1943
2047
  } catch (err) {
1944
2048
  logger.warn(`[emulator] could not run ${killCmd}: ${err.message}`);
1945
2049
  }
1946
- fs15.rm(PID_FILE, { force: true }, (err) => {
2050
+ fs16.rm(PID_FILE, { force: true }, (err) => {
1947
2051
  if (err) logger.warn(`Could not remove PID file: ${err.message}`);
1948
2052
  });
1949
2053
  }
@@ -2066,7 +2170,7 @@ async function getDisplaySize(config, opts = {}) {
2066
2170
 
2067
2171
  // src/hdc/wait.ts
2068
2172
  function delay(ms, signal) {
2069
- return new Promise((resolve4, reject) => {
2173
+ return new Promise((resolve6, reject) => {
2070
2174
  if (signal?.aborted) {
2071
2175
  reject(new CancelledError("Wait cancelled."));
2072
2176
  return;
@@ -2081,7 +2185,7 @@ function delay(ms, signal) {
2081
2185
  };
2082
2186
  const timer = setTimeout(() => {
2083
2187
  cleanup();
2084
- resolve4();
2188
+ resolve6();
2085
2189
  }, ms);
2086
2190
  signal?.addEventListener("abort", onAbort, { once: true });
2087
2191
  });
@@ -2110,20 +2214,20 @@ async function waitForCondition(opts) {
2110
2214
  }
2111
2215
 
2112
2216
  // src/hdc/app.ts
2113
- var fs17 = __toESM(require("fs"), 1);
2114
- var path17 = __toESM(require("path"), 1);
2217
+ var fs18 = __toESM(require("fs"), 1);
2218
+ var path18 = __toESM(require("path"), 1);
2115
2219
 
2116
2220
  // src/hdc/project.ts
2117
- var fs16 = __toESM(require("fs"), 1);
2118
- var path16 = __toESM(require("path"), 1);
2221
+ var fs17 = __toESM(require("fs"), 1);
2222
+ var path17 = __toESM(require("path"), 1);
2119
2223
  var import_json53 = __toESM(require("json5"), 1);
2120
2224
  function readJson5(filePath) {
2121
- const content = fs16.readFileSync(filePath, "utf-8");
2225
+ const content = fs17.readFileSync(filePath, "utf-8");
2122
2226
  return import_json53.default.parse(content);
2123
2227
  }
2124
2228
  function getBundleName(projectDir) {
2125
- const appJsonPath = path16.join(projectDir, "AppScope", "app.json5");
2126
- if (!fs16.existsSync(appJsonPath)) {
2229
+ const appJsonPath = path17.join(projectDir, "AppScope", "app.json5");
2230
+ if (!fs17.existsSync(appJsonPath)) {
2127
2231
  throw new OniroError(`Could not find app.json5 at ${appJsonPath}`);
2128
2232
  }
2129
2233
  const appJson = readJson5(appJsonPath);
@@ -2133,8 +2237,8 @@ function getBundleName(projectDir) {
2133
2237
  return appJson.app.bundleName;
2134
2238
  }
2135
2239
  function getMainAbility(projectDir, moduleName = "entry", abilityName) {
2136
- const moduleJsonPath = path16.join(projectDir, moduleName, "src", "main", "module.json5");
2137
- if (!fs16.existsSync(moduleJsonPath)) {
2240
+ const moduleJsonPath = path17.join(projectDir, moduleName, "src", "main", "module.json5");
2241
+ if (!fs17.existsSync(moduleJsonPath)) {
2138
2242
  throw new OniroError(`Could not find module.json5 at ${moduleJsonPath}`);
2139
2243
  }
2140
2244
  const moduleJson = readJson5(moduleJsonPath);
@@ -2157,8 +2261,8 @@ function getMainAbility(projectDir, moduleName = "entry", abilityName) {
2157
2261
  async function installApp(opts) {
2158
2262
  const logger = scopedLogger(opts.logger ?? noopLogger, "hdc");
2159
2263
  const relativeHapPath = opts.hapPath ?? opts.config.get("hapPath", defaultPaths.hapPath);
2160
- const hapPath = path17.isAbsolute(relativeHapPath) ? relativeHapPath : path17.join(opts.projectDir, relativeHapPath);
2161
- if (!fs17.existsSync(hapPath)) {
2264
+ const hapPath = path18.isAbsolute(relativeHapPath) ? relativeHapPath : path18.join(opts.projectDir, relativeHapPath);
2265
+ if (!fs18.existsSync(hapPath)) {
2162
2266
  throw new OniroError(`HAP file not found at: ${hapPath}. Build and sign the app first.`);
2163
2267
  }
2164
2268
  const result = await hdcExec({
@@ -2247,7 +2351,7 @@ function listRunningProcesses(config, options = {}) {
2247
2351
  const logger = options.logger ?? noopLogger;
2248
2352
  const timeoutMs = options.timeoutMs ?? 1e3;
2249
2353
  const hdc = getHdcPath(config);
2250
- return new Promise((resolve4, reject) => {
2354
+ return new Promise((resolve6, reject) => {
2251
2355
  let proc;
2252
2356
  try {
2253
2357
  proc = spawnCompat(hdc, ["track-jpid"]);
@@ -2267,7 +2371,7 @@ function listRunningProcesses(config, options = {}) {
2267
2371
  if (options.targetProcessName && name === options.targetProcessName && !resolved) {
2268
2372
  resolved = true;
2269
2373
  killProcessTree(proc);
2270
- resolve4(pid);
2374
+ resolve6(pid);
2271
2375
  return;
2272
2376
  }
2273
2377
  }
@@ -2288,7 +2392,7 @@ function listRunningProcesses(config, options = {}) {
2288
2392
  if (options.targetProcessName) {
2289
2393
  reject(new OniroError(`Could not find process for bundle: ${options.targetProcessName}`));
2290
2394
  } else {
2291
- resolve4(processes);
2395
+ resolve6(processes);
2292
2396
  }
2293
2397
  }, timeoutMs);
2294
2398
  proc.on("close", () => {
@@ -2298,7 +2402,7 @@ function listRunningProcesses(config, options = {}) {
2298
2402
  if (options.targetProcessName) {
2299
2403
  reject(new OniroError(`Could not find process for bundle: ${options.targetProcessName}`));
2300
2404
  } else {
2301
- resolve4(processes);
2405
+ resolve6(processes);
2302
2406
  }
2303
2407
  });
2304
2408
  });
@@ -2363,19 +2467,39 @@ async function isReachable(config, deviceSerial) {
2363
2467
  }
2364
2468
  }
2365
2469
  var TCP_SERIAL_RE = /^.+:\d+$/;
2366
- async function reconnectIfTcp(config, deviceSerial, logger) {
2367
- if (!deviceSerial || !TCP_SERIAL_RE.test(deviceSerial)) return;
2368
- try {
2369
- await hdcExec({ config, args: ["tconn", deviceSerial], timeoutMs: 5e3, logger });
2370
- } catch {
2371
- }
2470
+ var WEDGED_POLLS_BEFORE_RESTART = 3;
2471
+ function tcpReconnector(config, deviceSerial, logger) {
2472
+ let wedgedPolls = 0;
2473
+ return async () => {
2474
+ if (!deviceSerial || !TCP_SERIAL_RE.test(deviceSerial)) return;
2475
+ let claimsConnected = false;
2476
+ try {
2477
+ const res = await hdcExec({ config, args: ["tconn", deviceSerial], timeoutMs: 5e3, logger });
2478
+ claimsConnected = /already connected|repeat operation/i.test(`${res.stdout}
2479
+ ${res.stderr}`);
2480
+ } catch {
2481
+ }
2482
+ if (!claimsConnected || await isReachable(config, deviceSerial)) {
2483
+ wedgedPolls = 0;
2484
+ return;
2485
+ }
2486
+ if (++wedgedPolls < WEDGED_POLLS_BEFORE_RESTART) return;
2487
+ wedgedPolls = 0;
2488
+ logger.warn(`hdc reports ${deviceSerial} as connected but it does not answer; restarting the hdc server.`);
2489
+ try {
2490
+ await hdcExec({ config, args: ["start", "-r"], timeoutMs: 15e3, logger });
2491
+ await hdcExec({ config, args: ["tconn", deviceSerial], timeoutMs: 5e3, logger });
2492
+ } catch {
2493
+ }
2494
+ };
2372
2495
  }
2373
2496
  async function waitForBundle(opts) {
2374
2497
  const logger = scopedLogger(opts.logger ?? noopLogger, "hdc");
2375
2498
  const timeoutMs = opts.timeoutMs ?? 18e4;
2499
+ const reconnectIfTcp = tcpReconnector(opts.config, opts.deviceSerial, logger);
2376
2500
  await waitForCondition({
2377
2501
  probe: async () => {
2378
- await reconnectIfTcp(opts.config, opts.deviceSerial, logger);
2502
+ await reconnectIfTcp();
2379
2503
  return await findRunningProcess({ config: opts.config, bundle: opts.bundle, deviceSerial: opts.deviceSerial, timeoutMs: 8e3 }) !== null;
2380
2504
  },
2381
2505
  timeoutMs,
@@ -2388,9 +2512,10 @@ async function waitForBundle(opts) {
2388
2512
  async function waitForBoot(opts) {
2389
2513
  const logger = scopedLogger(opts.logger ?? noopLogger, "hdc");
2390
2514
  const timeoutMs = opts.timeoutMs ?? 18e4;
2515
+ const reconnectIfTcp = tcpReconnector(opts.config, opts.deviceSerial, logger);
2391
2516
  await waitForCondition({
2392
2517
  probe: async () => {
2393
- await reconnectIfTcp(opts.config, opts.deviceSerial, logger);
2518
+ await reconnectIfTcp();
2394
2519
  if (!await isReachable(opts.config, opts.deviceSerial)) return false;
2395
2520
  if (!opts.untilPidOf) return true;
2396
2521
  return await findRunningProcess({ config: opts.config, bundle: opts.untilPidOf, deviceSerial: opts.deviceSerial, timeoutMs: 8e3 }) !== null;
@@ -2442,12 +2567,12 @@ async function reboot(opts) {
2442
2567
  }
2443
2568
 
2444
2569
  // src/hdc/screen.ts
2445
- var fs18 = __toESM(require("fs"), 1);
2446
- var os8 = __toESM(require("os"), 1);
2447
- var path18 = __toESM(require("path"), 1);
2570
+ var fs19 = __toESM(require("fs"), 1);
2571
+ var os9 = __toESM(require("os"), 1);
2572
+ var path19 = __toESM(require("path"), 1);
2448
2573
  var import_node_crypto = require("crypto");
2449
2574
  function tmpName(ext) {
2450
- return path18.join(os8.tmpdir(), `oniro_screen_${(0, import_node_crypto.randomBytes)(6).toString("hex")}.${ext}`);
2575
+ return path19.join(os9.tmpdir(), `oniro_screen_${(0, import_node_crypto.randomBytes)(6).toString("hex")}.${ext}`);
2451
2576
  }
2452
2577
  async function takeScreenshot(opts) {
2453
2578
  const remote = "/data/local/tmp/oniro_screenshot.jpeg";
@@ -2457,11 +2582,11 @@ async function takeScreenshot(opts) {
2457
2582
  const local = tmpName("jpeg");
2458
2583
  try {
2459
2584
  await recvFile({ config: opts.config, remote, local, deviceSerial: opts.deviceSerial, timeoutMs });
2460
- const pixels = await fs18.promises.readFile(local);
2585
+ const pixels = await fs19.promises.readFile(local);
2461
2586
  const size = await getDisplaySize(opts.config, { deviceSerial: opts.deviceSerial }) ?? { width: 0, height: 0 };
2462
2587
  return { pixels, width: size.width, height: size.height };
2463
2588
  } finally {
2464
- fs18.promises.unlink(local).catch(() => {
2589
+ fs19.promises.unlink(local).catch(() => {
2465
2590
  });
2466
2591
  void shell({ config: opts.config, command: `rm -f ${remote}`, deviceSerial: opts.deviceSerial }).catch(() => {
2467
2592
  });
@@ -2480,9 +2605,9 @@ async function captureBurst(opts) {
2480
2605
  const local = tmpName(`burst_${i}.jpeg`);
2481
2606
  try {
2482
2607
  await recvFile({ config: opts.config, remote: `${remoteDir}/f${i}.jpeg`, local, deviceSerial: opts.deviceSerial });
2483
- frames.push(await fs18.promises.readFile(local));
2608
+ frames.push(await fs19.promises.readFile(local));
2484
2609
  } finally {
2485
- fs18.promises.unlink(local).catch(() => {
2610
+ fs19.promises.unlink(local).catch(() => {
2486
2611
  });
2487
2612
  }
2488
2613
  }
@@ -2608,9 +2733,9 @@ async function sendRawTouch(opts) {
2608
2733
  }
2609
2734
 
2610
2735
  // src/hdc/dump.ts
2611
- var fs19 = __toESM(require("fs"), 1);
2612
- var os9 = __toESM(require("os"), 1);
2613
- var path19 = __toESM(require("path"), 1);
2736
+ var fs20 = __toESM(require("fs"), 1);
2737
+ var os10 = __toESM(require("os"), 1);
2738
+ var path20 = __toESM(require("path"), 1);
2614
2739
  var import_node_crypto2 = require("crypto");
2615
2740
  function parseBounds(s) {
2616
2741
  const m = /^\[(-?\d+),(-?\d+)\]\[(-?\d+),(-?\d+)\]$/.exec(s ?? "");
@@ -2695,10 +2820,10 @@ async function dumpLayout(opts) {
2695
2820
  if (!m) throw new OniroError(`Could not parse dumpLayout output:
2696
2821
  ${dump.stdout}`);
2697
2822
  const remote = m[1];
2698
- const local = path19.join(os9.tmpdir(), `oniro_layout_${(0, import_node_crypto2.randomBytes)(6).toString("hex")}.json`);
2823
+ const local = path20.join(os10.tmpdir(), `oniro_layout_${(0, import_node_crypto2.randomBytes)(6).toString("hex")}.json`);
2699
2824
  try {
2700
2825
  await recvFile({ config: opts.config, remote, local, deviceSerial: opts.deviceSerial, timeoutMs });
2701
- const parsed = JSON.parse(await fs19.promises.readFile(local, "utf8"));
2826
+ const parsed = JSON.parse(await fs20.promises.readFile(local, "utf8"));
2702
2827
  const display = await getDisplaySize(opts.config, { deviceSerial: opts.deviceSerial });
2703
2828
  if (opts.prune === false) {
2704
2829
  return { display, tree: parsed };
@@ -2707,7 +2832,7 @@ ${dump.stdout}`);
2707
2832
  const tree = Array.isArray(prunedRaw) ? { children: prunedRaw } : prunedRaw ?? { children: [] };
2708
2833
  return { display, tree };
2709
2834
  } finally {
2710
- fs19.promises.unlink(local).catch(() => {
2835
+ fs20.promises.unlink(local).catch(() => {
2711
2836
  });
2712
2837
  void shell({ config: opts.config, command: `rm -f ${remote}`, deviceSerial: opts.deviceSerial }).catch(() => {
2713
2838
  });
@@ -2718,7 +2843,7 @@ ${dump.stdout}`);
2718
2843
  function setHilogLevel(opts) {
2719
2844
  const logger = opts.logger ?? noopLogger;
2720
2845
  const hdc = getHdcPath(opts.config);
2721
- return new Promise((resolve4, reject) => {
2846
+ return new Promise((resolve6, reject) => {
2722
2847
  let child;
2723
2848
  try {
2724
2849
  child = spawnCompat(hdc, ["shell", "hilog", "-b", opts.level]);
@@ -2738,7 +2863,7 @@ function setHilogLevel(opts) {
2738
2863
  });
2739
2864
  child.once("close", (code) => {
2740
2865
  if (code === 0) {
2741
- resolve4();
2866
+ resolve6();
2742
2867
  } else {
2743
2868
  const msg = stderr.trim() || `hdc shell hilog -b ${opts.level} exited with code ${code}`;
2744
2869
  logger.error(`[hilog] ${msg}`);
@@ -2793,7 +2918,7 @@ async function resolveProcessId(config, bundle, deviceSerial) {
2793
2918
  return proc?.pid;
2794
2919
  }
2795
2920
  function waitForLog(opts) {
2796
- return new Promise((resolve4, reject) => {
2921
+ return new Promise((resolve6, reject) => {
2797
2922
  const deadline = Date.now() + opts.timeoutMs;
2798
2923
  let settled = false;
2799
2924
  let child = null;
@@ -2812,7 +2937,7 @@ function waitForLog(opts) {
2812
2937
  if (settled) return;
2813
2938
  settled = true;
2814
2939
  cleanup();
2815
- resolve4(entry);
2940
+ resolve6(entry);
2816
2941
  };
2817
2942
  const fail = (err) => {
2818
2943
  if (settled) return;
@@ -2859,7 +2984,7 @@ function waitForLog(opts) {
2859
2984
  });
2860
2985
  }
2861
2986
  function watchLog(opts) {
2862
- return new Promise((resolve4, reject) => {
2987
+ return new Promise((resolve6, reject) => {
2863
2988
  const matches = [];
2864
2989
  let settled = false;
2865
2990
  let child = null;
@@ -2876,7 +3001,7 @@ function watchLog(opts) {
2876
3001
  if (settled) return;
2877
3002
  settled = true;
2878
3003
  cleanup();
2879
- resolve4(opts.dedup === false ? matches : dedupEntries(matches));
3004
+ resolve6(opts.dedup === false ? matches : dedupEntries(matches));
2880
3005
  };
2881
3006
  const onAbort = () => {
2882
3007
  if (settled) return;
@@ -2932,8 +3057,43 @@ async function dumpLog(opts) {
2932
3057
  }
2933
3058
 
2934
3059
  // src/build/runHvigorw.ts
2935
- var fs20 = __toESM(require("fs"), 1);
2936
- var path20 = __toESM(require("path"), 1);
3060
+ var fs22 = __toESM(require("fs"), 1);
3061
+ var path22 = __toESM(require("path"), 1);
3062
+
3063
+ // src/harmonyos/target.ts
3064
+ var fs21 = __toESM(require("fs"), 1);
3065
+ var path21 = __toESM(require("path"), 1);
3066
+ var import_json54 = __toESM(require("json5"), 1);
3067
+ function detectRuntimeOs(projectDir, productName) {
3068
+ let products = [];
3069
+ try {
3070
+ const parsed = import_json54.default.parse(fs21.readFileSync(path21.join(projectDir, "build-profile.json5"), "utf-8"));
3071
+ if (Array.isArray(parsed.app?.products)) products = parsed.app.products;
3072
+ } catch {
3073
+ return "OpenHarmony";
3074
+ }
3075
+ const product = products.find((p) => p.name === productName) ?? products[0];
3076
+ return product?.runtimeOS === "HarmonyOS" ? "HarmonyOS" : "OpenHarmony";
3077
+ }
3078
+ var HARMONYOS_SDK_VERSIONS = /* @__PURE__ */ new Map([
3079
+ [12, "5.0.0"],
3080
+ [13, "5.0.1"],
3081
+ [14, "5.0.2"],
3082
+ [15, "5.0.3"],
3083
+ [16, "5.0.4"],
3084
+ [17, "5.0.5"],
3085
+ [18, "5.1.0"],
3086
+ [19, "5.1.1"],
3087
+ [20, "6.0.0"],
3088
+ [21, "6.0.1"],
3089
+ [24, "6.1.1"]
3090
+ ]);
3091
+ function harmonyOsSdkLabelForApi(api) {
3092
+ const version = HARMONYOS_SDK_VERSIONS.get(api);
3093
+ return version ? `${version}(${api})` : void 0;
3094
+ }
3095
+
3096
+ // src/build/runHvigorw.ts
2937
3097
  function buildHvigorwArgs(opts) {
2938
3098
  const args = [opts.task ?? "assembleHap", "--mode", "module"];
2939
3099
  args.push("-p", `product=${opts.product ?? "default"}`);
@@ -2948,28 +3108,36 @@ function runHvigorw(opts) {
2948
3108
  const { config } = opts;
2949
3109
  const projectDir = toLongPath(opts.projectDir);
2950
3110
  const logger = opts.logger ?? noopLogger;
2951
- if (!fs20.existsSync(path20.join(projectDir, "build-profile.json5"))) {
3111
+ if (!fs22.existsSync(path22.join(projectDir, "build-profile.json5"))) {
2952
3112
  throw new OniroError(`Not an OpenHarmony project: ${projectDir} (build-profile.json5 not found).`);
2953
3113
  }
2954
- if (!fs20.existsSync(getCmdToolsPath(config))) {
2955
- throw new CmdToolsNotInstalledError(getCmdToolsPath(config));
3114
+ let hvigorw;
3115
+ let sdkEnv;
3116
+ if (detectRuntimeOs(projectDir, opts.product) === "HarmonyOS") {
3117
+ const sdk = requireHarmonyOsSdk({ config, logger });
3118
+ sdkEnv = { DEVECO_SDK_HOME: sdk.sdkPath };
3119
+ const resolved = getHvigorwPath(config, projectDir);
3120
+ hvigorw = resolved.startsWith(projectDir) ? resolved : findHarmonyOsTool(sdk, "hvigorw") ?? resolved;
3121
+ logger.info(`[build] Building for HarmonyOS against the SDK at ${sdk.sdkPath}.`);
3122
+ } else {
3123
+ if (!fs22.existsSync(getCmdToolsPath(config))) {
3124
+ throw new CmdToolsNotInstalledError(getCmdToolsPath(config));
3125
+ }
3126
+ sdkEnv = { OHOS_BASE_SDK_HOME: getOhosBaseSdkHome(config) };
3127
+ hvigorw = getHvigorwPath(config, projectDir);
2956
3128
  }
2957
- const hvigorw = getHvigorwPath(config, projectDir);
2958
- if (!fs20.existsSync(hvigorw)) {
3129
+ if (!fs22.existsSync(hvigorw)) {
2959
3130
  throw new OniroError(`hvigorw wrapper not found at ${hvigorw}.`);
2960
3131
  }
2961
3132
  const args = buildHvigorwArgs(opts);
2962
3133
  if (process.platform !== "win32") {
2963
3134
  try {
2964
- fs20.chmodSync(hvigorw, 493);
3135
+ fs22.chmodSync(hvigorw, 493);
2965
3136
  } catch {
2966
3137
  }
2967
3138
  }
2968
- const env = {
2969
- ...process.env,
2970
- OHOS_BASE_SDK_HOME: getOhosBaseSdkHome(config)
2971
- };
2972
- return new Promise((resolve4, reject) => {
3139
+ const env = { ...process.env, ...sdkEnv };
3140
+ return new Promise((resolve6, reject) => {
2973
3141
  if (opts.abortSignal?.aborted) {
2974
3142
  reject(new CancelledError("hvigorw build cancelled."));
2975
3143
  return;
@@ -3013,15 +3181,15 @@ function runHvigorw(opts) {
3013
3181
  settled = true;
3014
3182
  cleanup();
3015
3183
  const exitCode = code ?? 1;
3016
- if (exitCode === 0) resolve4({ exitCode });
3184
+ if (exitCode === 0) resolve6({ exitCode });
3017
3185
  else reject(new OniroError(`hvigorw exited with code ${exitCode}`));
3018
3186
  });
3019
3187
  });
3020
3188
  }
3021
3189
 
3022
3190
  // src/build/discoverHaps.ts
3023
- var fs21 = __toESM(require("fs"), 1);
3024
- var path21 = __toESM(require("path"), 1);
3191
+ var fs23 = __toESM(require("fs"), 1);
3192
+ var path23 = __toESM(require("path"), 1);
3025
3193
  function moduleOfHap(hapFileName) {
3026
3194
  const dash = hapFileName.indexOf("-");
3027
3195
  return dash > 0 ? hapFileName.slice(0, dash) : hapFileName.replace(/\.hap$/i, "");
@@ -3032,9 +3200,9 @@ async function discoverHaps(opts) {
3032
3200
  const result = {};
3033
3201
  const walk = async (dir, depth) => {
3034
3202
  if (depth > maxDepth) return;
3035
- const entries = await fs21.promises.readdir(dir, { withFileTypes: true }).catch(() => []);
3203
+ const entries = await fs23.promises.readdir(dir, { withFileTypes: true }).catch(() => []);
3036
3204
  for (const e of entries) {
3037
- const full = path21.join(dir, e.name);
3205
+ const full = path23.join(dir, e.name);
3038
3206
  if (e.isDirectory()) {
3039
3207
  if (e.name === "node_modules" || e.name === "oh_modules" || e.name === ".git") continue;
3040
3208
  await walk(full, depth + 1);
@@ -3051,11 +3219,11 @@ async function discoverHaps(opts) {
3051
3219
  }
3052
3220
 
3053
3221
  // src/build/ohpm.ts
3054
- var fs22 = __toESM(require("fs"), 1);
3055
- var path22 = __toESM(require("path"), 1);
3222
+ var fs24 = __toESM(require("fs"), 1);
3223
+ var path24 = __toESM(require("path"), 1);
3056
3224
  function runOhpm(opts) {
3057
3225
  return runProcess({
3058
- command: getOhpmPath(opts.config),
3226
+ command: opts.ohpmPath ?? getOhpmPath(opts.config),
3059
3227
  args: [...opts.args],
3060
3228
  cwd: opts.projectDir,
3061
3229
  timeoutMs: opts.timeoutMs ?? 6e5,
@@ -3065,12 +3233,13 @@ function runOhpm(opts) {
3065
3233
  });
3066
3234
  }
3067
3235
  async function ensureOhModules(opts) {
3068
- if (fs22.existsSync(path22.join(opts.projectDir, "oh_modules"))) {
3236
+ if (fs24.existsSync(path24.join(opts.projectDir, "oh_modules"))) {
3069
3237
  return { installed: false };
3070
3238
  }
3071
3239
  const res = await runOhpm({
3072
3240
  config: opts.config,
3073
3241
  projectDir: opts.projectDir,
3242
+ ohpmPath: opts.ohpmPath,
3074
3243
  args: ["install", "--all"],
3075
3244
  abortSignal: opts.abortSignal,
3076
3245
  onOutput: opts.onOutput,
@@ -3118,10 +3287,13 @@ async function buildHap(opts) {
3118
3287
  const warnings = [];
3119
3288
  const start = Date.now();
3120
3289
  const projectDir = toLongPath(opts.projectDir);
3290
+ const isHarmonyOs = detectRuntimeOs(projectDir, opts.product) === "HarmonyOS";
3121
3291
  if (opts.autoInstallDeps !== false) {
3292
+ const harmonySdk = isHarmonyOs ? findHarmonyOsSdk({ config: opts.config, logger: opts.logger }) : null;
3122
3293
  const r = await ensureOhModules({
3123
3294
  config: opts.config,
3124
3295
  projectDir,
3296
+ ohpmPath: (harmonySdk && findHarmonyOsTool(harmonySdk, "ohpm")) ?? void 0,
3125
3297
  abortSignal: opts.abortSignal,
3126
3298
  onOutput: opts.onOutput,
3127
3299
  logger: opts.logger
@@ -3144,7 +3316,7 @@ async function buildHap(opts) {
3144
3316
  const anySigned = Object.values(discoveredHaps).some((m) => m.signed.length > 0);
3145
3317
  if (!anySigned) {
3146
3318
  warnings.push(
3147
- "No signed HAPs found \u2014 configure signingConfigs in build-profile.json5 (unsigned HAPs are rejected by most devices)."
3319
+ isHarmonyOs ? "No signed HAPs found \u2014 run `oniro-app sign --harmonyos` to have AppGallery Connect issue a certificate and profile." : "No signed HAPs found \u2014 configure signingConfigs in build-profile.json5 (unsigned HAPs are rejected by most devices)."
3148
3320
  );
3149
3321
  }
3150
3322
  return { exitCode, durationMs: Date.now() - start, discoveredHaps, warnings };
@@ -3161,21 +3333,21 @@ function isValidBundleName(bundleName) {
3161
3333
  }
3162
3334
 
3163
3335
  // src/project/templates.ts
3164
- var fs23 = __toESM(require("fs"), 1);
3165
- var path23 = __toESM(require("path"), 1);
3336
+ var fs25 = __toESM(require("fs"), 1);
3337
+ var path25 = __toESM(require("path"), 1);
3166
3338
  function toHumanTemplateName(folderName) {
3167
3339
  return folderName.replace(/([a-z])([A-Z])/g, "$1 $2");
3168
3340
  }
3169
3341
  function listTemplates(templateRoot) {
3170
- if (!fs23.existsSync(templateRoot)) return [];
3171
- const entries = fs23.readdirSync(templateRoot, { withFileTypes: true });
3342
+ if (!fs25.existsSync(templateRoot)) return [];
3343
+ const entries = fs25.readdirSync(templateRoot, { withFileTypes: true });
3172
3344
  return entries.filter((e) => e.isDirectory()).map((e) => {
3173
- const dir = path23.join(templateRoot, e.name);
3174
- const metaPath = path23.join(dir, "template.json");
3345
+ const dir = path25.join(templateRoot, e.name);
3346
+ const metaPath = path25.join(dir, "template.json");
3175
3347
  let meta = {};
3176
- if (fs23.existsSync(metaPath)) {
3348
+ if (fs25.existsSync(metaPath)) {
3177
3349
  try {
3178
- meta = JSON.parse(fs23.readFileSync(metaPath, "utf8"));
3350
+ meta = JSON.parse(fs25.readFileSync(metaPath, "utf8"));
3179
3351
  } catch {
3180
3352
  }
3181
3353
  }
@@ -3190,57 +3362,57 @@ function listTemplates(templateRoot) {
3190
3362
  function validateTemplateLayout(templateDir, defaultModuleName) {
3191
3363
  const required = [
3192
3364
  "build-profile.json5",
3193
- path23.join("AppScope", "app.json5"),
3194
- path23.join(defaultModuleName, "src", "main", "module.json5"),
3195
- path23.join(defaultModuleName, "oh-package.json5"),
3365
+ path25.join("AppScope", "app.json5"),
3366
+ path25.join(defaultModuleName, "src", "main", "module.json5"),
3367
+ path25.join(defaultModuleName, "oh-package.json5"),
3196
3368
  "hvigorfile.ts"
3197
3369
  ];
3198
- return required.filter((rel) => !fs23.existsSync(path23.join(templateDir, rel)));
3370
+ return required.filter((rel) => !fs25.existsSync(path25.join(templateDir, rel)));
3199
3371
  }
3200
3372
 
3201
3373
  // src/project/createScaffold.ts
3202
- var fs25 = __toESM(require("fs"), 1);
3203
- var path24 = __toESM(require("path"), 1);
3374
+ var fs27 = __toESM(require("fs"), 1);
3375
+ var path26 = __toESM(require("path"), 1);
3204
3376
 
3205
3377
  // src/project/jsonHelpers.ts
3206
- var fs24 = __toESM(require("fs"), 1);
3207
- var import_json54 = __toESM(require("json5"), 1);
3378
+ var fs26 = __toESM(require("fs"), 1);
3379
+ var import_json55 = __toESM(require("json5"), 1);
3208
3380
  function readJson5File(filePath) {
3209
- return import_json54.default.parse(fs24.readFileSync(filePath, "utf8"));
3381
+ return import_json55.default.parse(fs26.readFileSync(filePath, "utf8"));
3210
3382
  }
3211
3383
  function writeJson5File(filePath, value) {
3212
- fs24.writeFileSync(filePath, JSON.stringify(value, null, 2) + "\n", "utf8");
3384
+ fs26.writeFileSync(filePath, JSON.stringify(value, null, 2) + "\n", "utf8");
3213
3385
  }
3214
3386
  function readJsonFile(filePath) {
3215
- return JSON.parse(fs24.readFileSync(filePath, "utf8"));
3387
+ return JSON.parse(fs26.readFileSync(filePath, "utf8"));
3216
3388
  }
3217
3389
  function writeJsonFile(filePath, value) {
3218
- fs24.writeFileSync(filePath, JSON.stringify(value, null, 2) + "\n", "utf8");
3390
+ fs26.writeFileSync(filePath, JSON.stringify(value, null, 2) + "\n", "utf8");
3219
3391
  }
3220
3392
 
3221
3393
  // src/project/createScaffold.ts
3222
3394
  var IGNORED_DIRS = /* @__PURE__ */ new Set(["oh_modules", "node_modules", "build", ".hvigor"]);
3223
3395
  async function pathExists(p) {
3224
3396
  try {
3225
- await fs25.promises.access(p);
3397
+ await fs27.promises.access(p);
3226
3398
  return true;
3227
3399
  } catch {
3228
3400
  return false;
3229
3401
  }
3230
3402
  }
3231
3403
  async function copyDirRecursive(srcDir, destDir) {
3232
- await fs25.promises.mkdir(destDir, { recursive: true });
3233
- const entries = await fs25.promises.readdir(srcDir, { withFileTypes: true });
3404
+ await fs27.promises.mkdir(destDir, { recursive: true });
3405
+ const entries = await fs27.promises.readdir(srcDir, { withFileTypes: true });
3234
3406
  for (const entry of entries) {
3235
- const src = path24.join(srcDir, entry.name);
3236
- const dest = path24.join(destDir, entry.name);
3407
+ const src = path26.join(srcDir, entry.name);
3408
+ const dest = path26.join(destDir, entry.name);
3237
3409
  if (entry.isDirectory()) {
3238
3410
  await copyDirRecursive(src, dest);
3239
3411
  } else if (entry.isSymbolicLink()) {
3240
3412
  continue;
3241
3413
  } else if (entry.isFile()) {
3242
3414
  if (entry.name === "template.json") continue;
3243
- await fs25.promises.copyFile(src, dest);
3415
+ await fs27.promises.copyFile(src, dest);
3244
3416
  }
3245
3417
  }
3246
3418
  }
@@ -3248,9 +3420,9 @@ async function normalizeJson5ToJson(projectDir, logger) {
3248
3420
  const stack = [projectDir];
3249
3421
  while (stack.length > 0) {
3250
3422
  const currentDir = stack.pop();
3251
- const entries = await fs25.promises.readdir(currentDir, { withFileTypes: true });
3423
+ const entries = await fs27.promises.readdir(currentDir, { withFileTypes: true });
3252
3424
  for (const entry of entries) {
3253
- const fullPath = path24.join(currentDir, entry.name);
3425
+ const fullPath = path26.join(currentDir, entry.name);
3254
3426
  if (entry.isDirectory()) {
3255
3427
  if (IGNORED_DIRS.has(entry.name)) continue;
3256
3428
  stack.push(fullPath);
@@ -3272,23 +3444,23 @@ function toJavaPropertiesPath(p) {
3272
3444
  function createOrUpdateLocalProperties(projectDir, sdkDir) {
3273
3445
  const content = `sdk.dir=${toJavaPropertiesPath(sdkDir)}
3274
3446
  `;
3275
- fs25.writeFileSync(path24.join(projectDir, "local.properties"), content, "utf8");
3447
+ fs27.writeFileSync(path26.join(projectDir, "local.properties"), content, "utf8");
3276
3448
  }
3277
3449
  function renameIfExists(fromPath, toPath) {
3278
- if (fs25.existsSync(fromPath) && fromPath !== toPath) {
3279
- fs25.renameSync(fromPath, toPath);
3450
+ if (fs27.existsSync(fromPath) && fromPath !== toPath) {
3451
+ fs27.renameSync(fromPath, toPath);
3280
3452
  }
3281
3453
  }
3282
3454
  function updateTemplateConfigs(projectDir, args, sdkBaseDir) {
3283
- const appJsonPath = path24.join(projectDir, "AppScope", "app.json5");
3284
- if (fs25.existsSync(appJsonPath)) {
3455
+ const appJsonPath = path26.join(projectDir, "AppScope", "app.json5");
3456
+ if (fs27.existsSync(appJsonPath)) {
3285
3457
  const appJson = readJson5File(appJsonPath);
3286
3458
  appJson.app = appJson.app ?? {};
3287
3459
  appJson.app.bundleName = args.bundleName;
3288
3460
  writeJson5File(appJsonPath, appJson);
3289
3461
  }
3290
- const stringsPath = path24.join(projectDir, "AppScope", "resources", "base", "element", "string.json");
3291
- if (fs25.existsSync(stringsPath)) {
3462
+ const stringsPath = path26.join(projectDir, "AppScope", "resources", "base", "element", "string.json");
3463
+ if (fs27.existsSync(stringsPath)) {
3292
3464
  const strings = readJsonFile(stringsPath);
3293
3465
  if (Array.isArray(strings.string)) {
3294
3466
  const appName = strings.string.find((s) => s.name === "app_name");
@@ -3296,13 +3468,13 @@ function updateTemplateConfigs(projectDir, args, sdkBaseDir) {
3296
3468
  }
3297
3469
  writeJsonFile(stringsPath, strings);
3298
3470
  }
3299
- const buildProfilePath = path24.join(projectDir, "build-profile.json5");
3300
- if (fs25.existsSync(buildProfilePath)) {
3471
+ const buildProfilePath = path26.join(projectDir, "build-profile.json5");
3472
+ if (fs27.existsSync(buildProfilePath)) {
3301
3473
  const buildProfile = readJson5File(buildProfilePath);
3302
3474
  buildProfile.app = buildProfile.app ?? {};
3303
3475
  if (Array.isArray(buildProfile.app.products) && buildProfile.app.products.length > 0) {
3304
- buildProfile.app.products[0].compileSdkVersion = args.sdkApi;
3305
- buildProfile.app.products[0].compatibleSdkVersion = args.sdkApi;
3476
+ buildProfile.app.products[0].compileSdkVersion = args.sdkVersion;
3477
+ buildProfile.app.products[0].compatibleSdkVersion = args.sdkVersion;
3306
3478
  }
3307
3479
  if (Array.isArray(buildProfile.modules) && buildProfile.modules.length > 0) {
3308
3480
  buildProfile.modules[0].name = args.moduleName;
@@ -3310,23 +3482,23 @@ function updateTemplateConfigs(projectDir, args, sdkBaseDir) {
3310
3482
  }
3311
3483
  writeJson5File(buildProfilePath, buildProfile);
3312
3484
  }
3313
- const moduleJsonPath = path24.join(projectDir, args.moduleName, "src", "main", "module.json5");
3314
- if (fs25.existsSync(moduleJsonPath)) {
3485
+ const moduleJsonPath = path26.join(projectDir, args.moduleName, "src", "main", "module.json5");
3486
+ if (fs27.existsSync(moduleJsonPath)) {
3315
3487
  const moduleJson = readJson5File(moduleJsonPath);
3316
3488
  moduleJson.module = moduleJson.module ?? {};
3317
3489
  moduleJson.module.name = args.moduleName;
3318
3490
  writeJson5File(moduleJsonPath, moduleJson);
3319
3491
  }
3320
- const moduleOhPackagePath = path24.join(projectDir, args.moduleName, "oh-package.json5");
3321
- if (fs25.existsSync(moduleOhPackagePath)) {
3492
+ const moduleOhPackagePath = path26.join(projectDir, args.moduleName, "oh-package.json5");
3493
+ if (fs27.existsSync(moduleOhPackagePath)) {
3322
3494
  const pkg = readJson5File(moduleOhPackagePath);
3323
3495
  pkg.name = args.moduleName;
3324
3496
  writeJson5File(moduleOhPackagePath, pkg);
3325
3497
  }
3326
- const vscodeDir = path24.join(projectDir, ".vscode");
3327
- fs25.mkdirSync(vscodeDir, { recursive: true });
3498
+ const vscodeDir = path26.join(projectDir, ".vscode");
3499
+ fs27.mkdirSync(vscodeDir, { recursive: true });
3328
3500
  const hapPath = `${args.moduleName}/build/default/outputs/default/${args.moduleName}-default-signed.hap`;
3329
- writeJsonFile(path24.join(vscodeDir, "settings.json"), {
3501
+ writeJsonFile(path26.join(vscodeDir, "settings.json"), {
3330
3502
  "oniro.hapPath": hapPath,
3331
3503
  "files.associations": { "*.json5": "jsonc" }
3332
3504
  });
@@ -3343,7 +3515,7 @@ async function createScaffold(opts) {
3343
3515
  if (!opts.location || !await pathExists(opts.location)) {
3344
3516
  throw new OniroError(`Location does not exist: ${opts.location}`);
3345
3517
  }
3346
- const templateDir = path24.join(opts.templateRoot, opts.templateId);
3518
+ const templateDir = path26.join(opts.templateRoot, opts.templateId);
3347
3519
  if (!await pathExists(templateDir)) {
3348
3520
  throw new OniroError(`Template not found: ${templateDir}`);
3349
3521
  }
@@ -3356,38 +3528,60 @@ async function createScaffold(opts) {
3356
3528
  throw new OniroError(`Template '${opts.templateId}' is missing required files:
3357
3529
  - ${missing.join("\n- ")}`);
3358
3530
  }
3359
- const projectDir = path24.join(opts.location, opts.projectName);
3531
+ let sdkVersion = opts.sdkApi;
3532
+ let sdkBaseDir = getOhosBaseSdkHome(opts.config);
3533
+ if (detectRuntimeOs(templateDir) === "HarmonyOS") {
3534
+ const harmonySdk = findHarmonyOsSdk({ config: opts.config, logger });
3535
+ const label = harmonySdk?.version?.endsWith(`(${opts.sdkApi})`) ? harmonySdk.version : harmonyOsSdkLabelForApi(opts.sdkApi);
3536
+ if (!label) {
3537
+ throw new OniroError(
3538
+ `No HarmonyOS release is known for API ${opts.sdkApi}. Known: ${[...HARMONYOS_SDK_VERSIONS.keys()].join(", ")}` + (harmonySdk?.version ? `, and the installed SDK's ${harmonySdk.version}.` : ".")
3539
+ );
3540
+ }
3541
+ sdkVersion = label;
3542
+ if (!harmonySdk) {
3543
+ logger.warn("[create] No HarmonyOS SDK found; set ONIRO_HARMONYOS_SDK_PATH before building.");
3544
+ } else {
3545
+ sdkBaseDir = harmonySdk.sdkPath;
3546
+ if (harmonySdk.version && harmonySdk.version !== label) {
3547
+ logger.warn(
3548
+ `[create] The HarmonyOS SDK at ${harmonySdk.sdkPath} is ${harmonySdk.version}, so a project declaring ${label} will not build against it. Install ${label}, or create with the installed API level.`
3549
+ );
3550
+ }
3551
+ }
3552
+ }
3553
+ const projectDir = path26.join(opts.location, opts.projectName);
3360
3554
  if (await pathExists(projectDir)) {
3361
3555
  if (!opts.overwrite) {
3362
3556
  throw new OniroError(`Destination already exists: ${projectDir}. Pass overwrite: true to replace it.`);
3363
3557
  }
3364
- await fs25.promises.rm(projectDir, { recursive: true, force: true });
3558
+ await fs27.promises.rm(projectDir, { recursive: true, force: true });
3365
3559
  }
3366
3560
  logger.info(`[create] Scaffolding ${opts.templateId} at ${projectDir}`);
3367
3561
  await copyDirRecursive(templateDir, projectDir);
3368
3562
  if (moduleName !== defaultModuleName) {
3369
- renameIfExists(path24.join(projectDir, defaultModuleName), path24.join(projectDir, moduleName));
3563
+ renameIfExists(path26.join(projectDir, defaultModuleName), path26.join(projectDir, moduleName));
3370
3564
  }
3371
3565
  updateTemplateConfigs(
3372
3566
  projectDir,
3373
3567
  {
3374
3568
  projectName: opts.projectName,
3375
3569
  bundleName: opts.bundleName,
3376
- sdkApi: opts.sdkApi,
3570
+ sdkVersion,
3377
3571
  moduleName
3378
3572
  },
3379
- getOhosBaseSdkHome(opts.config)
3573
+ sdkBaseDir
3380
3574
  );
3381
3575
  await normalizeJson5ToJson(projectDir, logger);
3382
3576
  return { projectDir };
3383
3577
  }
3384
3578
 
3385
3579
  // src/project/listModules.ts
3386
- var fs26 = __toESM(require("fs"), 1);
3387
- var path25 = __toESM(require("path"), 1);
3580
+ var fs28 = __toESM(require("fs"), 1);
3581
+ var path27 = __toESM(require("path"), 1);
3388
3582
  function listModules(opts) {
3389
- const buildProfile = path25.join(opts.projectDir, "build-profile.json5");
3390
- if (!fs26.existsSync(buildProfile)) {
3583
+ const buildProfile = path27.join(opts.projectDir, "build-profile.json5");
3584
+ if (!fs28.existsSync(buildProfile)) {
3391
3585
  throw new OniroError(`build-profile.json5 not found at ${buildProfile}`);
3392
3586
  }
3393
3587
  const parsed = readJson5File(buildProfile);
@@ -3399,12 +3593,12 @@ function listModules(opts) {
3399
3593
  }
3400
3594
 
3401
3595
  // src/project/abilities.ts
3402
- var fs27 = __toESM(require("fs"), 1);
3403
- var path26 = __toESM(require("path"), 1);
3596
+ var fs29 = __toESM(require("fs"), 1);
3597
+ var path28 = __toESM(require("path"), 1);
3404
3598
  function listAbilities(opts) {
3405
3599
  const moduleName = opts.moduleName ?? "entry";
3406
- const moduleJsonPath = path26.join(opts.projectDir, moduleName, "src", "main", "module.json5");
3407
- if (!fs27.existsSync(moduleJsonPath)) {
3600
+ const moduleJsonPath = path28.join(opts.projectDir, moduleName, "src", "main", "module.json5");
3601
+ if (!fs29.existsSync(moduleJsonPath)) {
3408
3602
  throw new OniroError(`Could not find module.json5 at ${moduleJsonPath}`);
3409
3603
  }
3410
3604
  const parsed = readJson5File(moduleJsonPath);
@@ -3412,12 +3606,12 @@ function listAbilities(opts) {
3412
3606
  }
3413
3607
 
3414
3608
  // src/project/prepareSigning.ts
3415
- var fs28 = __toESM(require("fs"), 1);
3416
- var path27 = __toESM(require("path"), 1);
3609
+ var fs30 = __toESM(require("fs"), 1);
3610
+ var path29 = __toESM(require("path"), 1);
3417
3611
  function hasSigningArtifacts(dir) {
3418
- if (!fs28.existsSync(path27.join(dir, "signatures"))) return false;
3419
- const buildProfile = path27.join(dir, "build-profile.json5");
3420
- if (!fs28.existsSync(buildProfile)) return false;
3612
+ if (!fs30.existsSync(path29.join(dir, "signatures"))) return false;
3613
+ const buildProfile = path29.join(dir, "build-profile.json5");
3614
+ if (!fs30.existsSync(buildProfile)) return false;
3421
3615
  try {
3422
3616
  const parsed = readJson5File(buildProfile);
3423
3617
  return Array.isArray(parsed.app?.signingConfigs) && parsed.app.signingConfigs.length > 0;
@@ -3507,8 +3701,8 @@ async function diffHapAssets(opts) {
3507
3701
  }
3508
3702
 
3509
3703
  // src/install/apply.ts
3510
- var fs29 = __toESM(require("fs"), 1);
3511
- var path28 = __toESM(require("path"), 1);
3704
+ var fs31 = __toESM(require("fs"), 1);
3705
+ var path30 = __toESM(require("path"), 1);
3512
3706
  function decideInstallMethod(s) {
3513
3707
  if (!s.signInfoInconsistent) return "replace";
3514
3708
  if (s.isSystemBundle && !s.allowUninstall) return "refuse";
@@ -3529,7 +3723,7 @@ async function pidOf(config, bundle, deviceSerial) {
3529
3723
  async function resolveHapPath(opts) {
3530
3724
  let hapPath = opts.hapPath;
3531
3725
  if (hapPath) {
3532
- if (!path28.isAbsolute(hapPath) && opts.projectDir) hapPath = path28.join(opts.projectDir, hapPath);
3726
+ if (!path30.isAbsolute(hapPath) && opts.projectDir) hapPath = path30.join(opts.projectDir, hapPath);
3533
3727
  } else {
3534
3728
  if (!opts.projectDir) throw new OniroError("applyChanges requires hapPath or projectDir.");
3535
3729
  const haps = await discoverHaps({ projectDir: opts.projectDir });
@@ -3548,7 +3742,7 @@ async function resolveHapPath(opts) {
3548
3742
  hapPath = withSigned[0][1].signed[0];
3549
3743
  }
3550
3744
  }
3551
- if (!fs29.existsSync(hapPath)) throw new OniroError(`HAP not found at ${hapPath}.`);
3745
+ if (!fs31.existsSync(hapPath)) throw new OniroError(`HAP not found at ${hapPath}.`);
3552
3746
  return hapPath;
3553
3747
  }
3554
3748
  async function applyChanges(opts) {
@@ -3558,7 +3752,7 @@ async function applyChanges(opts) {
3558
3752
  const allowUninstall = opts.allowUninstall ?? false;
3559
3753
  const hapPath = await resolveHapPath(opts);
3560
3754
  let assetsChanged = false;
3561
- if (opts.installedHapPath && fs29.existsSync(opts.installedHapPath)) {
3755
+ if (opts.installedHapPath && fs31.existsSync(opts.installedHapPath)) {
3562
3756
  try {
3563
3757
  const diff = await diffHapAssets({ installedHap: opts.installedHapPath, newHap: hapPath });
3564
3758
  assetsChanged = diff.addedAssetPaths.length > 0 || diff.removedAssetPaths.length > 0;
@@ -3599,21 +3793,1202 @@ async function applyChanges(opts) {
3599
3793
  return { method, preInstallPid, postInstallPid, replaced: installMethod === "replace", cacheCleared };
3600
3794
  }
3601
3795
 
3796
+ // src/harmonyos/auth/endpoints.ts
3797
+ var HUAWEI_APP_ID = "1009";
3798
+ var HUAWEI_API_VERSION = "1.0.0";
3799
+ var LOGIN_TIMEOUT_MS = 6e5;
3800
+ var HUAWEI_SITES = {
3801
+ CN: {
3802
+ code: "CN",
3803
+ siteId: "1",
3804
+ siteParam: "CN",
3805
+ loginUrl: "https://cn.devecostudio.huawei.com",
3806
+ agcBaseUrl: "https://connect-api.cloud.huawei.com",
3807
+ language: "zh_CN"
3808
+ },
3809
+ SG: {
3810
+ code: "SG",
3811
+ siteId: "5",
3812
+ siteParam: "SG",
3813
+ loginUrl: "https://sg.devecostudio.huawei.com",
3814
+ agcBaseUrl: "https://connect-api-dra.cloud.huawei.com",
3815
+ language: "zh_CN"
3816
+ },
3817
+ EU: {
3818
+ code: "EU",
3819
+ siteId: "7",
3820
+ siteParam: "DE",
3821
+ loginUrl: "https://de.devecostudio.huawei.com",
3822
+ agcBaseUrl: "https://connect-api-dre.cloud.huawei.com",
3823
+ language: "de_DE"
3824
+ },
3825
+ RU: {
3826
+ code: "RU",
3827
+ siteId: "8",
3828
+ siteParam: "RU",
3829
+ loginUrl: "https://ru.devecostudio.huawei.com",
3830
+ agcBaseUrl: "https://connect-api-drru.cloud.huawei.com",
3831
+ language: "ru_RU"
3832
+ }
3833
+ };
3834
+ var AGC_TEAMS_URL = "https://connect-api.cloud.huawei.com/api/ups/user-permission-service/v1/user-team-list";
3835
+ function siteForId(siteId) {
3836
+ return Object.values(HUAWEI_SITES).find((s) => s.siteId === siteId) ?? null;
3837
+ }
3838
+ var AUTH_PATHS = {
3839
+ /** The browser authenticates here, then redirects to the loopback server. */
3840
+ AUTH_APPLY: "console/DevEcoIDE/apply",
3841
+ /** Exchanges the browser's temp token for a JWT. */
3842
+ TEMP_TOKEN_CHECK: "authrouter/auth/api/temptoken/check",
3843
+ /** Validates a JWT and, with `refresh`, mints an AGC access token. */
3844
+ JWT_TOKEN_CHECK: "authrouter/auth/api/jwToken/check",
3845
+ LOGIN_SUCCESS: "console/DevEcoCLI/loginSuccess",
3846
+ LOGIN_FAILED: "console/DevEcoCLI/loginFailed",
3847
+ LOGOUT: "authrouter/auth/api/logout",
3848
+ /**
3849
+ * Whether an account without real-name verification accepted the developer
3850
+ * agreement, which is what lets it sign. From DevEco Studio (`space.agr.url`).
3851
+ */
3852
+ UNREALNAME_AGREEMENT: "authrouter/unrealname/agreement"
3853
+ };
3854
+
3855
+ // src/harmonyos/auth/tokenStore.ts
3856
+ var fs32 = __toESM(require("fs"), 1);
3857
+ var os11 = __toESM(require("os"), 1);
3858
+ var path31 = __toESM(require("path"), 1);
3859
+ var crypto3 = __toESM(require("crypto"), 1);
3860
+ var ALGORITHM = "aes-256-gcm";
3861
+ var KEY_LENGTH = 32;
3862
+ var IV_LENGTH = 12;
3863
+ var TOKEN_FILE = "token.enc";
3864
+ var KEY_FILE = "kek.bin";
3865
+ var DIR_MODE = 448;
3866
+ var FILE_MODE = 384;
3867
+ function encrypt2(key, plaintext) {
3868
+ const iv = crypto3.randomBytes(IV_LENGTH);
3869
+ const cipher = crypto3.createCipheriv(ALGORITHM, key, iv);
3870
+ const ciphertext = Buffer.concat([cipher.update(plaintext), cipher.final()]);
3871
+ return {
3872
+ version: 1,
3873
+ algorithm: ALGORITHM,
3874
+ ciphertext: ciphertext.toString("base64"),
3875
+ iv: iv.toString("base64"),
3876
+ authTag: cipher.getAuthTag().toString("base64")
3877
+ };
3878
+ }
3879
+ function decrypt2(key, blob) {
3880
+ const decipher = crypto3.createDecipheriv(ALGORITHM, key, Buffer.from(blob.iv, "base64"));
3881
+ decipher.setAuthTag(Buffer.from(blob.authTag, "base64"));
3882
+ return Buffer.concat([decipher.update(Buffer.from(blob.ciphertext, "base64")), decipher.final()]);
3883
+ }
3884
+ function isEncryptedBlob(value) {
3885
+ if (!value || typeof value !== "object") return false;
3886
+ const b = value;
3887
+ return b.algorithm === ALGORITHM && typeof b.ciphertext === "string" && typeof b.iv === "string" && typeof b.authTag === "string";
3888
+ }
3889
+ function permissionHint(dir) {
3890
+ return process.platform === "win32" ? `Permission denied writing ${dir}. Grant write permission to that directory.` : `Permission denied writing ${dir}. Try: chown -R "$(whoami)" ${dir}`;
3891
+ }
3892
+ function ensureDir(dir) {
3893
+ try {
3894
+ fs32.mkdirSync(dir, { recursive: true, mode: DIR_MODE });
3895
+ } catch (err) {
3896
+ if (err.code === "EACCES") {
3897
+ throw new OniroError(permissionHint(dir), err);
3898
+ }
3899
+ throw err;
3900
+ }
3901
+ }
3902
+ function createTokenStore(opts) {
3903
+ const logger = opts.logger ?? noopLogger;
3904
+ const authDir = opts.config.get("harmonyosAuthDir", defaultPaths.harmonyosAuthDir());
3905
+ const tokenPath = path31.join(authDir, TOKEN_FILE);
3906
+ const keyPath = path31.join(os11.homedir(), ".local", "share", "oniro-app", "keys", KEY_FILE);
3907
+ function loadOrCreateKey() {
3908
+ if (fs32.existsSync(keyPath)) {
3909
+ const existing = fs32.readFileSync(keyPath);
3910
+ if (existing.length === KEY_LENGTH) return existing;
3911
+ logger.warn("[harmonyos] The token key file was malformed; regenerating (you will need to log in again).");
3912
+ }
3913
+ ensureDir(path31.dirname(keyPath));
3914
+ const key = crypto3.randomBytes(KEY_LENGTH);
3915
+ fs32.writeFileSync(keyPath, key, { mode: FILE_MODE });
3916
+ return key;
3917
+ }
3918
+ return {
3919
+ tokenPath,
3920
+ save(session) {
3921
+ if (!session.jwtToken) throw new OniroError("Refusing to store an empty token.");
3922
+ const payload = Buffer.from(JSON.stringify(session), "utf8");
3923
+ const blob = encrypt2(loadOrCreateKey(), payload);
3924
+ ensureDir(authDir);
3925
+ fs32.writeFileSync(tokenPath, JSON.stringify(blob, null, 2), { mode: FILE_MODE });
3926
+ },
3927
+ load() {
3928
+ if (!fs32.existsSync(tokenPath)) return null;
3929
+ try {
3930
+ const blob = JSON.parse(fs32.readFileSync(tokenPath, "utf8"));
3931
+ if (!isEncryptedBlob(blob)) return null;
3932
+ const { jwtToken, siteId } = JSON.parse(decrypt2(loadOrCreateKey(), blob).toString("utf8"));
3933
+ return typeof jwtToken === "string" && jwtToken && typeof siteId === "string" ? { jwtToken, siteId } : null;
3934
+ } catch (err) {
3935
+ const code = err.code;
3936
+ if (code === "EACCES" || code === "EPERM") {
3937
+ logger.warn(`[harmonyos] Cannot read ${tokenPath}: ${permissionHint(authDir)}`);
3938
+ return null;
3939
+ }
3940
+ logger.debug(`[harmonyos] Discarding unreadable token at ${tokenPath}.`);
3941
+ try {
3942
+ fs32.rmSync(tokenPath, { force: true });
3943
+ } catch {
3944
+ }
3945
+ return null;
3946
+ }
3947
+ },
3948
+ clear() {
3949
+ try {
3950
+ fs32.rmSync(tokenPath, { force: true });
3951
+ } catch (err) {
3952
+ throw new OniroError(`Failed to remove ${tokenPath}.`, err);
3953
+ }
3954
+ }
3955
+ };
3956
+ }
3957
+
3958
+ // src/harmonyos/auth/session.ts
3959
+ var crypto5 = __toESM(require("crypto"), 1);
3960
+
3961
+ // src/harmonyos/http.ts
3962
+ var import_undici = require("undici");
3963
+ var DEFAULT_HEADERS = {
3964
+ "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36",
3965
+ "accept-language": "zh-CN",
3966
+ accept: "application/json, text/plain, */*"
3967
+ };
3968
+ var DEFAULT_TIMEOUT_MS = 2e4;
3969
+ var SENSITIVE_PARAMS = /* @__PURE__ */ new Set(["temptoken", "jwttoken", "code", "oauth2token", "accesstoken"]);
3970
+ function redactUrl(url) {
3971
+ let parsed;
3972
+ try {
3973
+ parsed = new URL(url);
3974
+ } catch {
3975
+ return url;
3976
+ }
3977
+ for (const key of [...parsed.searchParams.keys()]) {
3978
+ if (SENSITIVE_PARAMS.has(key.toLowerCase())) parsed.searchParams.set(key, "***");
3979
+ }
3980
+ return parsed.toString();
3981
+ }
3982
+ function previewBody(body, limit = 300) {
3983
+ const trimmed = body.trim();
3984
+ if (!trimmed) return "(empty body)";
3985
+ if (/^[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+$/.test(trimmed)) return "(a token-shaped value)";
3986
+ const collapsed = trimmed.replace(/\s+/g, " ");
3987
+ return collapsed.length > limit ? `${collapsed.slice(0, limit)}\u2026` : collapsed;
3988
+ }
3989
+ function describeResponse(response) {
3990
+ const status = `HTTP ${response.statusCode}${response.statusText ? ` ${response.statusText}` : ""}`;
3991
+ return `${status}: ${previewBody(response.data)}`;
3992
+ }
3993
+ function buildUrl(url, query) {
3994
+ if (!query) return url;
3995
+ const parsed = new URL(url);
3996
+ for (const [key, value] of Object.entries(query)) {
3997
+ if (value !== void 0 && value !== null) parsed.searchParams.set(key, String(value));
3998
+ }
3999
+ return parsed.toString();
4000
+ }
4001
+ function createHttpClient(opts = {}) {
4002
+ const logger = opts.logger ?? noopLogger;
4003
+ const dispatcher = new import_undici.EnvHttpProxyAgent();
4004
+ async function send(method, url, options) {
4005
+ const target = buildUrl(url, options.query);
4006
+ const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
4007
+ const headers = { ...DEFAULT_HEADERS, ...options.headers };
4008
+ let body;
4009
+ if (options.body !== void 0) {
4010
+ body = JSON.stringify(options.body);
4011
+ headers["content-type"] = "application/json";
4012
+ }
4013
+ logger.debug(`[harmonyos] ${method} ${redactUrl(target)}`);
4014
+ try {
4015
+ return await (0, import_undici.fetch)(target, {
4016
+ method,
4017
+ headers,
4018
+ body,
4019
+ signal: AbortSignal.timeout(timeoutMs),
4020
+ dispatcher
4021
+ });
4022
+ } catch (err) {
4023
+ const reason = err instanceof Error && err.name === "TimeoutError" ? `timed out after ${timeoutMs}ms` : err instanceof Error ? err.message : String(err);
4024
+ throw new OniroError(
4025
+ `${method} ${redactUrl(target)} failed: ${reason}. Check the network connection and any proxy configuration (HTTPS_PROXY / HTTP_PROXY).`,
4026
+ err
4027
+ );
4028
+ }
4029
+ }
4030
+ async function request(method, url, options = {}) {
4031
+ const response = await send(method, url, options);
4032
+ return { data: await response.text(), statusCode: response.status, statusText: response.statusText };
4033
+ }
4034
+ return {
4035
+ get: (url, o) => request("GET", url, o),
4036
+ post: (url, o) => request("POST", url, o),
4037
+ delete: (url, o) => request("DELETE", url, o),
4038
+ async getBinary(url, o = {}) {
4039
+ const response = await send("GET", url, o);
4040
+ if (!response.ok) {
4041
+ throw new OniroError(`Download of ${redactUrl(response.url || url)} failed (HTTP ${response.status}).`);
4042
+ }
4043
+ return Buffer.from(await response.arrayBuffer());
4044
+ }
4045
+ };
4046
+ }
4047
+
4048
+ // src/harmonyos/auth/callbackServer.ts
4049
+ var http2 = __toESM(require("http"), 1);
4050
+ var crypto4 = __toESM(require("crypto"), 1);
4051
+ var CALLBACK_PATH = "/callback";
4052
+ var MAX_BODY_BYTES = 65536;
4053
+ function codeMatches(code, secret) {
4054
+ const a = Buffer.from(code ?? "", "utf8");
4055
+ const b = Buffer.from(secret, "utf8");
4056
+ return a.length === b.length && crypto4.timingSafeEqual(a, b);
4057
+ }
4058
+ function isQuit(quit) {
4059
+ return quit === "true" || quit === "access_denied" || quit === "quit";
4060
+ }
4061
+ async function startCallbackServer(opts) {
4062
+ let resolveCallback;
4063
+ let rejectCallback;
4064
+ const callback = new Promise((resolve6, reject) => {
4065
+ resolveCallback = resolve6;
4066
+ rejectCallback = reject;
4067
+ });
4068
+ callback.catch(() => {
4069
+ });
4070
+ const server = http2.createServer((req, res) => {
4071
+ const host = (req.headers.host ?? "").toLowerCase();
4072
+ const port2 = server.address()?.port;
4073
+ if (host !== `127.0.0.1:${port2}` && host !== `localhost:${port2}`) {
4074
+ res.writeHead(400);
4075
+ res.end("Bad Host");
4076
+ return;
4077
+ }
4078
+ const url = new URL(req.url ?? "", `http://${host}`);
4079
+ if (url.pathname !== CALLBACK_PATH) {
4080
+ res.writeHead(404);
4081
+ res.end("Not Found");
4082
+ return;
4083
+ }
4084
+ const finish = (params) => {
4085
+ if (!codeMatches(params.get("code"), opts.clientSecret)) {
4086
+ res.writeHead(400);
4087
+ res.end("Bad Request");
4088
+ return;
4089
+ }
4090
+ if (isQuit(params.get("quit"))) {
4091
+ res.writeHead(302, { Location: `${opts.baseUrl}/${opts.failedPath}` });
4092
+ res.end();
4093
+ rejectCallback(new CancelledError("Login was cancelled in the browser."));
4094
+ return;
4095
+ }
4096
+ const tempToken = params.get("tempToken");
4097
+ const siteId = params.get("siteId");
4098
+ if (!tempToken || !siteId) {
4099
+ res.writeHead(400);
4100
+ res.end("Bad Request");
4101
+ return;
4102
+ }
4103
+ res.writeHead(302, { Location: `${opts.baseUrl}/${opts.successPath}` });
4104
+ res.end();
4105
+ resolveCallback({ tempToken, siteId });
4106
+ };
4107
+ if (req.method === "POST") {
4108
+ let body = "";
4109
+ let size = 0;
4110
+ req.on("data", (chunk) => {
4111
+ size += chunk.length;
4112
+ if (size > MAX_BODY_BYTES) {
4113
+ req.destroy(new Error("Request body too large"));
4114
+ return;
4115
+ }
4116
+ body += chunk.toString();
4117
+ });
4118
+ req.on("end", () => finish(body.trim() ? new URLSearchParams(body) : url.searchParams));
4119
+ } else {
4120
+ finish(url.searchParams);
4121
+ }
4122
+ });
4123
+ server.keepAliveTimeout = 1;
4124
+ await new Promise((resolve6, reject) => {
4125
+ server.once("error", (err) => reject(new OniroError("Failed to start the local login callback server.", err)));
4126
+ server.listen(0, "127.0.0.1", resolve6);
4127
+ });
4128
+ const port = server.address().port;
4129
+ return {
4130
+ port,
4131
+ waitForCallback(timeoutMs) {
4132
+ let timer;
4133
+ const timeout = new Promise((_, reject) => {
4134
+ timer = setTimeout(
4135
+ () => reject(new OniroError(`Timed out after ${Math.round(timeoutMs / 1e3)}s waiting for the browser login to complete.`)),
4136
+ timeoutMs
4137
+ );
4138
+ });
4139
+ return Promise.race([callback, timeout]).finally(() => clearTimeout(timer));
4140
+ },
4141
+ stop() {
4142
+ return new Promise((resolve6) => {
4143
+ server.closeAllConnections?.();
4144
+ const timer = setTimeout(resolve6, 100);
4145
+ server.close(() => {
4146
+ clearTimeout(timer);
4147
+ resolve6();
4148
+ });
4149
+ });
4150
+ }
4151
+ };
4152
+ }
4153
+
4154
+ // src/harmonyos/auth/browser.ts
4155
+ var import_node_child_process5 = require("child_process");
4156
+ function isSafeUrl(url) {
4157
+ try {
4158
+ const parsed = new URL(url);
4159
+ return ["http:", "https:"].includes(parsed.protocol) && parsed.hostname !== "" && !url.includes('"');
4160
+ } catch {
4161
+ return false;
4162
+ }
4163
+ }
4164
+ function escapeForCmd(s) {
4165
+ return s.replace(/[&|<>()^%!]/g, (c) => `^${c}`);
4166
+ }
4167
+ function openBrowser(url) {
4168
+ if (!isSafeUrl(url)) {
4169
+ return Promise.reject(new OniroError(`Refusing to open unsafe URL: ${JSON.stringify(url)}`));
4170
+ }
4171
+ const [command, args] = process.platform === "win32" ? ["cmd", ["/c", "start", '""', escapeForCmd(url)]] : process.platform === "darwin" ? ["open", [url]] : ["xdg-open", [url]];
4172
+ const child = (0, import_node_child_process5.spawn)(command, args, {
4173
+ stdio: "ignore",
4174
+ shell: false,
4175
+ detached: true,
4176
+ windowsHide: true
4177
+ });
4178
+ child.unref();
4179
+ return new Promise((resolve6, reject) => {
4180
+ child.on("error", (err) => reject(new OniroError(`Failed to open a browser (${command}).`, err)));
4181
+ child.on(
4182
+ "exit",
4183
+ (code) => code === 0 ? resolve6() : reject(new OniroError(`Browser launcher '${command}' exited with code ${code}.`))
4184
+ );
4185
+ });
4186
+ }
4187
+
4188
+ // src/harmonyos/auth/session.ts
4189
+ var NOT_SIGNED_IN = "Not signed in. Run `oniro-app auth login` first.";
4190
+ function createHarmonyOsSession(opts) {
4191
+ const logger = opts.logger ?? noopLogger;
4192
+ const http3 = opts.http ?? createHttpClient({ logger });
4193
+ const store = opts.tokenStore ?? createTokenStore({ config: opts.config, logger });
4194
+ async function exchangeTempToken(site, tempToken) {
4195
+ const response = await http3.get(`${site.loginUrl}/${AUTH_PATHS.TEMP_TOKEN_CHECK}`, {
4196
+ query: {
4197
+ // The page sometimes appends extra `&`-joined parameters to the token.
4198
+ tempToken: tempToken.split("&")[0],
4199
+ site: site.siteParam,
4200
+ version: HUAWEI_API_VERSION,
4201
+ appid: HUAWEI_APP_ID
4202
+ }
4203
+ });
4204
+ const jwtToken = response.data.trim();
4205
+ if (response.statusCode === 200 && /^[^.]+\.[^.]+\.[^.]+$/.test(jwtToken)) return jwtToken;
4206
+ throw new OniroError(
4207
+ `${site.loginUrl} would not exchange the login token for a session (${describeResponse(response)}). Login tokens are single-use and short-lived; run \`oniro-app auth login\` again.`
4208
+ );
4209
+ }
4210
+ async function userFromJwt(site, jwtToken, refresh) {
4211
+ const response = await http3.get(`${site.loginUrl}/${AUTH_PATHS.JWT_TOKEN_CHECK}`, {
4212
+ headers: { refresh: String(refresh), jwtToken }
4213
+ });
4214
+ let info;
4215
+ try {
4216
+ if (response.statusCode !== 200) throw new Error();
4217
+ info = JSON.parse(response.data);
4218
+ } catch {
4219
+ throw new OniroError(`Could not validate the session with ${site.loginUrl} (${describeResponse(response)}).`);
4220
+ }
4221
+ const user = info.userInfo;
4222
+ if (!info.status || !user?.accessToken) return null;
4223
+ return {
4224
+ userId: user.userId ?? "",
4225
+ userName: user.name ?? "",
4226
+ accessToken: user.accessToken,
4227
+ countryCode: user.nationalCode ?? "",
4228
+ siteCode: site.code,
4229
+ isRealName: String(user.realName) === "true"
4230
+ };
4231
+ }
4232
+ async function getUserInfo(o = {}) {
4233
+ const stored = store.load();
4234
+ const site = stored && siteForId(stored.siteId);
4235
+ const user = site ? await userFromJwt(site, stored.jwtToken, o.refresh ?? false) : null;
4236
+ if (stored && !user) {
4237
+ logger.debug("[harmonyos] The stored session is no longer valid; clearing it.");
4238
+ store.clear();
4239
+ }
4240
+ return user;
4241
+ }
4242
+ async function requireUser() {
4243
+ const user = await getUserInfo({ refresh: true });
4244
+ if (!user) throw new OniroError(NOT_SIGNED_IN);
4245
+ return user;
4246
+ }
4247
+ async function getDeveloperAgreement(user) {
4248
+ try {
4249
+ const response = await http3.get(`${HUAWEI_SITES[user.siteCode].loginUrl}/${AUTH_PATHS.UNREALNAME_AGREEMENT}`, {
4250
+ headers: { accessToken: user.accessToken }
4251
+ });
4252
+ const payload = JSON.parse(response.data);
4253
+ if (response.statusCode !== 200 || String(payload.success) !== "true" || !payload.body) return null;
4254
+ return { signed: payload.body.signedFlag === true, latest: payload.body.signLastestFlag === true };
4255
+ } catch {
4256
+ return null;
4257
+ }
4258
+ }
4259
+ return {
4260
+ tokenPath: store.tokenPath,
4261
+ getUserInfo,
4262
+ getDeveloperAgreement,
4263
+ async login(o = {}) {
4264
+ const start = HUAWEI_SITES.CN.loginUrl;
4265
+ const clientSecret = crypto5.randomUUID().replace(/-/g, "");
4266
+ const server = await startCallbackServer({
4267
+ clientSecret,
4268
+ baseUrl: start,
4269
+ successPath: AUTH_PATHS.LOGIN_SUCCESS,
4270
+ failedPath: AUTH_PATHS.LOGIN_FAILED
4271
+ });
4272
+ try {
4273
+ const loginUrl = `${start}/${AUTH_PATHS.AUTH_APPLY}?port=${server.port}&appid=${HUAWEI_APP_ID}&code=${clientSecret}`;
4274
+ opts.onLoginUrl?.(loginUrl);
4275
+ openBrowser(loginUrl).catch((err) => {
4276
+ logger.warn(`[harmonyos] Could not open a browser automatically: ${err.message}`);
4277
+ });
4278
+ const callback = await server.waitForCallback(o.timeoutMs ?? LOGIN_TIMEOUT_MS);
4279
+ const site = siteForId(callback.siteId);
4280
+ if (!site) {
4281
+ throw new OniroError(`The account belongs to a Huawei region oniro-app does not know (siteId ${callback.siteId}).`);
4282
+ }
4283
+ const jwtToken = await exchangeTempToken(site, callback.tempToken);
4284
+ const user = await userFromJwt(site, jwtToken, false);
4285
+ if (!user) throw new OniroError("Login did not yield a usable session. Try again.");
4286
+ store.save({ jwtToken, siteId: site.siteId });
4287
+ logger.info(`[harmonyos] Signed in as ${user.userName || user.userId} (${site.code}).`);
4288
+ return user;
4289
+ } finally {
4290
+ await server.stop();
4291
+ }
4292
+ },
4293
+ async logout() {
4294
+ const stored = store.load();
4295
+ if (!stored) return false;
4296
+ const site = siteForId(stored.siteId);
4297
+ try {
4298
+ if (site) {
4299
+ await http3.post(`${site.loginUrl}/${AUTH_PATHS.LOGOUT}`, {
4300
+ query: { jwtToken: stored.jwtToken },
4301
+ timeoutMs: 5e3
4302
+ });
4303
+ }
4304
+ } catch {
4305
+ } finally {
4306
+ store.clear();
4307
+ }
4308
+ return true;
4309
+ },
4310
+ async listTeams() {
4311
+ const user = await requireUser();
4312
+ const response = await http3.get(AGC_TEAMS_URL, {
4313
+ headers: {
4314
+ oauth2Token: user.accessToken,
4315
+ uid: user.userId,
4316
+ source: "cli",
4317
+ lang: HUAWEI_SITES[user.siteCode].language
4318
+ },
4319
+ timeoutMs: 15e3
4320
+ });
4321
+ if (response.statusCode === 401) {
4322
+ throw new OniroError("The session has expired. Run `oniro-app auth login` again.");
4323
+ }
4324
+ if (response.statusCode !== 200) {
4325
+ throw new OniroError(`Could not list AGC teams (${describeResponse(response)}).`);
4326
+ }
4327
+ return parseTeamList(response.data);
4328
+ },
4329
+ async resolveAgcAuth(o = {}) {
4330
+ const user = await requireUser();
4331
+ if (!user.isRealName) {
4332
+ const agreement = await getDeveloperAgreement(user);
4333
+ if (agreement && !(agreement.signed && agreement.latest)) {
4334
+ throw new OniroError(
4335
+ "This Huawei account has not accepted the current HUAWEI Developer Basic Service Agreement, which accounts without real-name verification need before AppGallery Connect issues signing material. Accept it in AppGallery Connect (https://developer.huawei.com/consumer/en/service/josp/agc/index.html) or by running automatic signing once in DevEco Studio, then try again."
4336
+ );
4337
+ }
4338
+ }
4339
+ return {
4340
+ uid: user.userId,
4341
+ // Personal accounts sign under their own user id; `--team-id` picks another team.
4342
+ teamId: o.teamId ?? user.userId,
4343
+ accessToken: user.accessToken,
4344
+ baseUrl: HUAWEI_SITES[user.siteCode].agcBaseUrl
4345
+ };
4346
+ }
4347
+ };
4348
+ }
4349
+ function parseTeamList(body) {
4350
+ let payload;
4351
+ try {
4352
+ payload = JSON.parse(body);
4353
+ } catch {
4354
+ return [];
4355
+ }
4356
+ if (payload.ret && payload.ret.code !== 0) {
4357
+ throw new OniroError(
4358
+ `AppGallery Connect refused the team list (code ${payload.ret.code}${payload.ret.msg ? `: ${payload.ret.msg}` : ""}).`
4359
+ );
4360
+ }
4361
+ if (!Array.isArray(payload.teams)) return [];
4362
+ return payload.teams.map((t) => ({ id: String(t?.id ?? ""), name: String(t?.name ?? "") })).filter((t) => t.id);
4363
+ }
4364
+
4365
+ // src/harmonyos/signing/errors.ts
4366
+ var AgcError = class extends OniroError {
4367
+ constructor(message, statusCode, retCode) {
4368
+ super(message);
4369
+ this.statusCode = statusCode;
4370
+ this.retCode = retCode;
4371
+ this.name = "AgcError";
4372
+ }
4373
+ statusCode;
4374
+ retCode;
4375
+ };
4376
+ var SIGNING_ERRORS = {
4377
+ FORBIDDEN: "This account has no AppGallery Connect permission for the selected team. Ask the team administrator for access, or pass --team-id for a team you do have access to (`oniro-app auth team list`).",
4378
+ UNAUTHORIZED: "AppGallery Connect rejected the access token. Run `oniro-app auth login` again.",
4379
+ NETWORK: "Could not reach AppGallery Connect. Check the network connection and any proxy configuration (HTTPS_PROXY / HTTP_PROXY).",
4380
+ USER_NOT_HARMONY: "This Huawei account is not enrolled for HarmonyOS development. Request HarmonyOS access for it in AppGallery Connect.",
4381
+ CERT_REQUEST: "AppGallery Connect did not issue the debug certificate.",
4382
+ CERT_LIMIT: "This team has reached its debug-certificate limit. Delete an unused certificate in AppGallery Connect and retry.",
4383
+ DEVICE_ADD: "AppGallery Connect did not register the device.",
4384
+ DEVICE_LIMIT: "This team has reached its device limit. Remove an unused device in AppGallery Connect and retry.",
4385
+ DEVICE_NAME_REPEAT: "A device with that name is already registered. Retry.",
4386
+ DEVICE_NONE: "No device is connected and none is registered to this team, but a debug profile must name at least one. Connect a device and retry.",
4387
+ PROFILE_ADD: "AppGallery Connect did not issue the provisioning profile.",
4388
+ PROFILE_NAME_REPEAT: "A provisioning profile with that name already exists in AppGallery Connect. Retry.",
4389
+ PROVISION_LIMIT: "This team has reached its debug-profile limit. Delete unused profiles in AppGallery Connect and retry.",
4390
+ CERT_INVALID: "AppGallery Connect returned an unreadable certificate. Retry with --force.",
4391
+ CERT_EXPIRED: "The signing certificate is outside its validity window. Check the system clock, then retry with --force.",
4392
+ CERT_PROFILE_MISMATCH: "The provisioning profile was not issued for the downloaded certificate. Retry with --force.",
4393
+ KEYSTORE_CERT_MISMATCH: "The local keystore does not hold the key the downloaded certificate was issued for. Retry with --force.",
4394
+ BUNDLE_NAME_INVALID: "Invalid bundle name. It must be 7\u2013128 characters, start with a letter, and have at least three dot-separated segments (for example com.example.app)."
4395
+ };
4396
+ var RET_CODE_ERRORS = {
4397
+ "205389872": SIGNING_ERRORS.CERT_LIMIT,
4398
+ "205389904": SIGNING_ERRORS.USER_NOT_HARMONY,
4399
+ "205389859": SIGNING_ERRORS.DEVICE_LIMIT,
4400
+ "205389857": SIGNING_ERRORS.DEVICE_NAME_REPEAT,
4401
+ "205389938": SIGNING_ERRORS.PROVISION_LIMIT,
4402
+ "205389830": SIGNING_ERRORS.PROFILE_NAME_REPEAT
4403
+ };
4404
+ var PROXY_BLOCKED = "Openproxy_Blocked_URL_list";
4405
+ function readRet(body) {
4406
+ try {
4407
+ let ret = JSON.parse(body).ret;
4408
+ if (typeof ret === "string") ret = JSON.parse(ret);
4409
+ return ret && typeof ret === "object" ? ret : null;
4410
+ } catch {
4411
+ return null;
4412
+ }
4413
+ }
4414
+ var FALLBACK = {
4415
+ cert: SIGNING_ERRORS.CERT_REQUEST,
4416
+ device: SIGNING_ERRORS.DEVICE_ADD,
4417
+ profile: SIGNING_ERRORS.PROFILE_ADD
4418
+ };
4419
+ function mapAgcError(operation, statusCode, reasonPhrase, body) {
4420
+ if (statusCode === 403) {
4421
+ return new AgcError(reasonPhrase === PROXY_BLOCKED ? SIGNING_ERRORS.NETWORK : SIGNING_ERRORS.FORBIDDEN, 403);
4422
+ }
4423
+ if (statusCode === 401) return new AgcError(SIGNING_ERRORS.UNAUTHORIZED, 401);
4424
+ const ret = readRet(body);
4425
+ const retCode = ret?.code === void 0 ? void 0 : String(ret.code);
4426
+ const msg = typeof ret?.msg === "string" && ret.msg.trim() ? ret.msg : void 0;
4427
+ return new AgcError(retCode && RET_CODE_ERRORS[retCode] || msg || FALLBACK[operation], statusCode, retCode);
4428
+ }
4429
+
4430
+ // src/harmonyos/signing/profile.ts
4431
+ var fs33 = __toESM(require("fs"), 1);
4432
+ var import_node_crypto3 = require("crypto");
4433
+ var import_node_forge = __toESM(require("node-forge"), 1);
4434
+ function extractProfileJson(buffer) {
4435
+ const text = buffer.toString("latin1");
4436
+ for (let start = text.indexOf("{"); start !== -1; start = text.indexOf("{", start + 1)) {
4437
+ let depth = 0;
4438
+ let inString = false;
4439
+ let escaped = false;
4440
+ for (let i = start; i < text.length; i++) {
4441
+ const ch = text[i];
4442
+ if (escaped) escaped = false;
4443
+ else if (inString) {
4444
+ if (ch === "\\") escaped = true;
4445
+ else if (ch === '"') inString = false;
4446
+ } else if (ch === '"') inString = true;
4447
+ else if (ch === "{") depth++;
4448
+ else if (ch === "}" && --depth === 0) {
4449
+ const json = Buffer.from(text.slice(start, i + 1), "latin1").toString("utf8");
4450
+ try {
4451
+ if ("bundle-info" in JSON.parse(json)) return json;
4452
+ } catch {
4453
+ }
4454
+ break;
4455
+ }
4456
+ }
4457
+ }
4458
+ return null;
4459
+ }
4460
+ function readProvisionProfile(profilePath) {
4461
+ let raw;
4462
+ try {
4463
+ const json = extractProfileJson(fs33.readFileSync(profilePath));
4464
+ if (!json) return null;
4465
+ raw = JSON.parse(json);
4466
+ } catch {
4467
+ return null;
4468
+ }
4469
+ const str = (v) => typeof v === "string" ? v : void 0;
4470
+ const strings = (v) => Array.isArray(v) ? v.filter((x2) => typeof x2 === "string") : [];
4471
+ const notAfter = raw.validity?.["not-after"];
4472
+ const bundle = raw["bundle-info"] ?? {};
4473
+ return {
4474
+ bundleName: str(bundle["bundle-name"]),
4475
+ developerId: str(bundle["developer-id"]),
4476
+ // Epoch seconds.
4477
+ notAfter: typeof notAfter === "number" ? new Date(notAfter * 1e3) : void 0,
4478
+ deviceIds: strings(raw["debug-info"]?.["device-ids"]).map((d) => d.toUpperCase()),
4479
+ aclPermissions: strings(raw.acls?.["allowed-acls"]),
4480
+ developmentCertificate: str(bundle["development-certificate"])
4481
+ };
4482
+ }
4483
+ function parseCertificate(pem) {
4484
+ try {
4485
+ return new import_node_crypto3.X509Certificate(pem);
4486
+ } catch {
4487
+ throw new AgcError(SIGNING_ERRORS.CERT_INVALID);
4488
+ }
4489
+ }
4490
+ function keystoreHoldsKey(p12Path, keyAlias, keyPwd, certificate) {
4491
+ try {
4492
+ const der = import_node_forge.default.util.createBuffer(fs33.readFileSync(p12Path).toString("binary"));
4493
+ const p12 = import_node_forge.default.pkcs12.pkcs12FromAsn1(import_node_forge.default.asn1.fromDer(der), keyPwd);
4494
+ const certBag = import_node_forge.default.pki.oids.certBag ?? "1.2.840.113549.1.12.10.1.3";
4495
+ const wanted = certificate.publicKey.export({ type: "spki", format: "der" });
4496
+ return (p12.getBags({ bagType: certBag })[certBag] ?? []).some((bag) => {
4497
+ if (bag.attributes?.friendlyName?.[0]?.toLowerCase() !== keyAlias.toLowerCase()) return false;
4498
+ const asn1 = bag.cert ? import_node_forge.default.pki.certificateToAsn1(bag.cert) : bag.asn1;
4499
+ if (!asn1) return false;
4500
+ const bagCert = new import_node_crypto3.X509Certificate(Buffer.from(import_node_forge.default.asn1.toDer(asn1).getBytes(), "binary"));
4501
+ return bagCert.publicKey.export({ type: "spki", format: "der" }).equals(wanted);
4502
+ });
4503
+ } catch {
4504
+ return false;
4505
+ }
4506
+ }
4507
+ function verifySigningMaterial(opts) {
4508
+ const pems = fs33.readFileSync(opts.cerPath, "utf8").match(/-----BEGIN CERTIFICATE-----[\s\S]*?-----END CERTIFICATE-----/g);
4509
+ const chain = (pems ?? []).map(parseCertificate);
4510
+ const leaf = chain.find((c) => !c.ca) ?? chain.at(-1);
4511
+ if (!leaf) throw new AgcError(SIGNING_ERRORS.CERT_INVALID);
4512
+ const now = opts.now ?? /* @__PURE__ */ new Date();
4513
+ if (chain.some((c) => now < new Date(c.validFrom) || now > new Date(c.validTo))) {
4514
+ throw new AgcError(SIGNING_ERRORS.CERT_EXPIRED);
4515
+ }
4516
+ const issuedFor = readProvisionProfile(opts.profilePath)?.developmentCertificate;
4517
+ if (!issuedFor || parseCertificate(issuedFor).fingerprint256 !== leaf.fingerprint256) {
4518
+ throw new AgcError(SIGNING_ERRORS.CERT_PROFILE_MISMATCH);
4519
+ }
4520
+ if (!keystoreHoldsKey(opts.p12Path, opts.keyAlias, opts.keyPwd, leaf)) {
4521
+ throw new AgcError(SIGNING_ERRORS.KEYSTORE_CERT_MISMATCH);
4522
+ }
4523
+ }
4524
+
4525
+ // src/harmonyos/signing/autoSign.ts
4526
+ var crypto9 = __toESM(require("crypto"), 1);
4527
+ var fs38 = __toESM(require("fs"), 1);
4528
+ var path37 = __toESM(require("path"), 1);
4529
+ var import_json57 = __toESM(require("json5"), 1);
4530
+
4531
+ // src/harmonyos/signing/agc.ts
4532
+ var crypto6 = __toESM(require("crypto"), 1);
4533
+ var fs34 = __toESM(require("fs"), 1);
4534
+ var path32 = __toESM(require("path"), 1);
4535
+ var ENDPOINTS = {
4536
+ CERT_LIST: "/api/cps/harmony-cert-manage/v1/cert/list",
4537
+ CERT_ADD: "/api/cps/harmony-cert-manage/v1/cert/add",
4538
+ CERT_DELETE: "/api/cps/harmony-cert-manage/v1/cert/delete",
4539
+ /** Exchanges an object id for a short-lived signed download URL. */
4540
+ OBJECT_URL: "/api/amis/app-manage/v1/objects/url/reapply",
4541
+ DEVICE_LIST: "/api/cps/device-manage/v1/device/list",
4542
+ DEVICE_ADD: "/api/cps/device-manage/v1/device/add",
4543
+ /** Debug ("test") provisioning profiles — the only kind oniro-app issues. */
4544
+ PROVISION_ADD: "/api/cps/provision-manage/v1/ide/test/provision/add",
4545
+ PROVISION_DELETE: "/api/cps/provision-manage/v1/provision/delete"
4546
+ };
4547
+ var DEVICE_TYPE_CODES = { liteWearable: 1, wearable: 2, tv: 3, phone: 4 };
4548
+ function createAgcClient(http3, auth) {
4549
+ async function call(operation, method, endpoint, opts = {}) {
4550
+ const response = await http3[method](`${auth.baseUrl}${endpoint}`, {
4551
+ ...opts,
4552
+ headers: { uid: auth.uid, teamId: auth.teamId, oauth2Token: auth.accessToken }
4553
+ });
4554
+ if (response.statusCode !== 200 || readRet(response.data)?.code !== 0) {
4555
+ throw mapAgcError(operation, response.statusCode, response.statusText, response.data);
4556
+ }
4557
+ return JSON.parse(response.data);
4558
+ }
4559
+ return {
4560
+ async listCertificates() {
4561
+ return (await call("cert", "post", ENDPOINTS.CERT_LIST)).certList ?? [];
4562
+ },
4563
+ async addCertificate(certName, csr) {
4564
+ await call("cert", "post", ENDPOINTS.CERT_ADD, { body: { csr, certName, certType: "1" } });
4565
+ },
4566
+ async deleteCertificate(certId) {
4567
+ await call("cert", "delete", ENDPOINTS.CERT_DELETE, { body: { certIds: [certId] } });
4568
+ },
4569
+ /** Every device registered to the team, across pages. */
4570
+ async listDevices() {
4571
+ const pageSize = 100;
4572
+ const devices = [];
4573
+ for (let start = 1; ; start++) {
4574
+ const page = await call("device", "get", ENDPOINTS.DEVICE_LIST, {
4575
+ query: { encodeFlag: 0, start, pageSize }
4576
+ });
4577
+ devices.push(...page.list ?? []);
4578
+ if (!page.list?.length || devices.length >= (page.totalCount ?? 0)) return devices;
4579
+ }
4580
+ },
4581
+ async addDevice(udid, deviceKind) {
4582
+ const deviceName = `oniro_app_device_${crypto6.randomBytes(4).toString("hex")}`;
4583
+ await call("device", "post", ENDPOINTS.DEVICE_ADD, {
4584
+ body: { deviceName, udid, deviceType: String(DEVICE_TYPE_CODES[deviceKind] ?? DEVICE_TYPE_CODES.phone) }
4585
+ });
4586
+ },
4587
+ /** Issue a debug profile; returns its id and object handle. */
4588
+ async addDebugProfile(opts) {
4589
+ return call("profile", "post", ENDPOINTS.PROVISION_ADD, {
4590
+ body: {
4591
+ provisionName: opts.provisionName,
4592
+ packageName: opts.bundleName,
4593
+ certList: [opts.certId],
4594
+ deviceList: opts.deviceIds,
4595
+ ...opts.aclPermissions.length ? { aclPermissionList: opts.aclPermissions } : {}
4596
+ }
4597
+ });
4598
+ },
4599
+ async deleteProfile(id) {
4600
+ await call("profile", "delete", ENDPOINTS.PROVISION_DELETE, { query: { id } });
4601
+ },
4602
+ /**
4603
+ * Download a certificate or profile object to `dest`, checking the digest AGC
4604
+ * supplies with the URL. Nothing is written unless it matches.
4605
+ */
4606
+ async download(operation, objectId, dest) {
4607
+ const { urlsInfo } = await call(
4608
+ operation,
4609
+ "post",
4610
+ ENDPOINTS.OBJECT_URL,
4611
+ { body: { sourceUrls: objectId } }
4612
+ );
4613
+ const target = urlsInfo?.[0];
4614
+ if (!target) throw mapAgcError(operation, 200, "", "");
4615
+ const buffer = await http3.getBinary(target.newUrl, { timeoutMs: 6e4 });
4616
+ const actual = crypto6.createHash("sha256").update(buffer).digest("hex");
4617
+ if (target.sha256 && actual !== target.sha256.toLowerCase()) {
4618
+ throw new ChecksumMismatchError(target.sha256, actual);
4619
+ }
4620
+ fs34.mkdirSync(path32.dirname(dest), { recursive: true, mode: 448 });
4621
+ fs34.writeFileSync(dest, buffer, { mode: 384 });
4622
+ }
4623
+ };
4624
+ }
4625
+
4626
+ // src/harmonyos/signing/devices.ts
4627
+ function parseUdid(stdout) {
4628
+ return stdout.match(/\b[A-Fa-f0-9]{64}\b/)?.[0].toUpperCase() ?? "";
4629
+ }
4630
+ function parseDeviceKind(stdout) {
4631
+ return ["liteWearable", "wearable", "tv"].find((kind) => stdout.includes(kind)) ?? "phone";
4632
+ }
4633
+ async function collectLocalDevices(config, logger) {
4634
+ const devices = [];
4635
+ for (const target of await listDevices(config, { logger, timeoutMs: 15e3 })) {
4636
+ if (target.status !== "Connected") continue;
4637
+ const shell2 = async (...args) => (await hdcExec({ config, deviceSerial: target.serial, args: ["shell", ...args], timeoutMs: 15e3, logger })).stdout;
4638
+ try {
4639
+ const udid = parseUdid(await shell2("bm", "get", "-u"));
4640
+ if (!udid) {
4641
+ logger.warn(`[harmonyos] ${target.serial} did not report a UDID; skipping it.`);
4642
+ continue;
4643
+ }
4644
+ devices.push({ serial: target.serial, udid, kind: parseDeviceKind(await shell2("getprop", "hw_sc.build.os.deviceType")) });
4645
+ } catch (err) {
4646
+ logger.warn(`[harmonyos] Could not read ${target.serial}: ${err.message}`);
4647
+ }
4648
+ }
4649
+ return devices;
4650
+ }
4651
+ async function registerDevices(agc, connected, logger) {
4652
+ let devices = await agc.listDevices();
4653
+ const registered = new Set(devices.map((d) => d.udid.toUpperCase()));
4654
+ const unregistered = connected.filter((d) => !registered.has(d.udid));
4655
+ for (const device of unregistered) {
4656
+ logger.info(`[harmonyos] Registering ${device.serial} with AppGallery Connect...`);
4657
+ await agc.addDevice(device.udid, device.kind);
4658
+ }
4659
+ if (unregistered.length > 0) devices = await agc.listDevices();
4660
+ const deviceIds = devices.map((d) => d.id);
4661
+ if (deviceIds.length === 0) throw new AgcError(SIGNING_ERRORS.DEVICE_NONE);
4662
+ if (connected.length === 0) {
4663
+ logger.warn(
4664
+ `[harmonyos] No device is connected; the profile will name the ${deviceIds.length} device(s) already registered to this team.`
4665
+ );
4666
+ }
4667
+ return deviceIds;
4668
+ }
4669
+
4670
+ // src/harmonyos/signing/keystore.ts
4671
+ var crypto7 = __toESM(require("crypto"), 1);
4672
+ var fs36 = __toESM(require("fs"), 1);
4673
+ var path34 = __toESM(require("path"), 1);
4674
+
4675
+ // src/harmonyos/java.ts
4676
+ var fs35 = __toESM(require("fs"), 1);
4677
+ var path33 = __toESM(require("path"), 1);
4678
+ var JAVA_BIN = process.platform === "win32" ? "java.exe" : "java";
4679
+ function findJava(sdk, env = process.env) {
4680
+ const candidates = [
4681
+ env.JAVA_HOME && path33.join(env.JAVA_HOME, "bin", JAVA_BIN),
4682
+ sdk && path33.join(sdk.installRoot, "jbr", "bin", JAVA_BIN),
4683
+ // macOS: the JBR is itself a bundle inside Studio's Contents.
4684
+ sdk && path33.join(sdk.installRoot, "jbr", "Contents", "Home", "bin", JAVA_BIN),
4685
+ ...(env.PATH ?? "").split(path33.delimiter).map((dir) => dir && path33.join(dir, JAVA_BIN))
4686
+ ];
4687
+ const found = candidates.find((c) => !!c && fs35.existsSync(c));
4688
+ if (found) return found;
4689
+ throw new OniroError(
4690
+ "No Java runtime found. HarmonyOS signing runs the SDK's hap-sign-tool.jar, which needs one: install a JDK and set JAVA_HOME, or install DevEco Studio, whose bundled runtime is used automatically."
4691
+ );
4692
+ }
4693
+
4694
+ // src/harmonyos/signing/keystore.ts
4695
+ var KEY_ALIAS = "debugKey";
4696
+ var SIGN_ALG = "SHA256withECDSA";
4697
+ function redactArgs(args) {
4698
+ return args.map((arg, i) => args[i - 1] === "-keyPwd" || args[i - 1] === "-keystorePwd" ? "******" : arg);
4699
+ }
4700
+ async function generateKeystoreAndCsr(opts) {
4701
+ const jar = path34.join(opts.sdk.sdkPath, "default", "openharmony", "toolchains", "lib", "hap-sign-tool.jar");
4702
+ if (!fs36.existsSync(jar)) {
4703
+ throw new OniroError(`hap-sign-tool.jar not found at ${jar}; the HarmonyOS SDK looks incomplete.`);
4704
+ }
4705
+ const java = findJava(opts.sdk);
4706
+ const keyPwd = crypto7.randomBytes(12).toString("base64url").replaceAll(/[-_]/g, "");
4707
+ fs36.mkdirSync(path34.dirname(opts.p12Path), { recursive: true, mode: 448 });
4708
+ const run = async (what, args) => {
4709
+ const argv = ["-jar", jar, ...args, "-keyAlias", KEY_ALIAS, "-keystoreFile", opts.p12Path, "-keystorePwd", keyPwd, "-keyPwd", keyPwd];
4710
+ opts.logger.debug(`[harmonyos] ${java} ${redactArgs(argv).join(" ")}`);
4711
+ const result = await runProcess({ command: java, args: argv, timeoutMs: 12e4, logger: opts.logger });
4712
+ if (result.code !== 0) {
4713
+ const detail = (result.stderr || result.stdout || "").trim();
4714
+ throw new OniroError(`hap-sign-tool failed to ${what} (exit ${result.code}).${detail ? `
4715
+ ${detail}` : ""}`);
4716
+ }
4717
+ };
4718
+ opts.logger.info("[harmonyos] Generating the local keystore and certificate signing request...");
4719
+ await run("generate the key pair", ["generate-keypair", "-keyAlg", "ECC", "-keySize", "NIST-P-256"]);
4720
+ await run("generate the CSR", ["generate-csr", "-subject", "CN=DebugKey", "-signAlg", SIGN_ALG, "-outFile", opts.csrPath]);
4721
+ return { keyPwd, csr: fs36.readFileSync(opts.csrPath, "utf8") };
4722
+ }
4723
+
4724
+ // src/harmonyos/signing/paths.ts
4725
+ var crypto8 = __toESM(require("crypto"), 1);
4726
+ var path35 = __toESM(require("path"), 1);
4727
+ function sanitizeNamePart(raw) {
4728
+ const safe = (raw?.trim() ?? "").replace(/[\\/:*?"<>|=.-]+/g, "_");
4729
+ return safe.slice(0, 64) || "default";
4730
+ }
4731
+ function resolveSigningMaterialPaths(config, productName, projectRoot) {
4732
+ const hash = crypto8.createHash("sha256").update(projectRoot, "utf8").digest("base64url").replaceAll(/[-_]/g, "");
4733
+ const base = path35.join(
4734
+ config.get("harmonyosSigningDir", defaultPaths.harmonyosSigningDir()),
4735
+ `${sanitizeNamePart(productName)}_${path35.basename(projectRoot)}_${hash}=`
4736
+ );
4737
+ return { p12Path: `${base}.p12`, csrPath: `${base}.csr`, cerPath: `${base}.cer`, profilePath: `${base}.p7b` };
4738
+ }
4739
+
4740
+ // src/harmonyos/signing/project.ts
4741
+ var fs37 = __toESM(require("fs"), 1);
4742
+ var path36 = __toESM(require("path"), 1);
4743
+ var import_json56 = __toESM(require("json5"), 1);
4744
+
4745
+ // src/harmonyos/signing/aclPermissionList.ts
4746
+ var ACL_PERMISSIONS = /* @__PURE__ */ new Set([
4747
+ "ohos.permission.ACCESS_DDK_HID",
4748
+ "ohos.permission.ACCESS_DDK_SCSI_PERIPHERAL",
4749
+ "ohos.permission.ACCESS_DDK_USB",
4750
+ "ohos.permission.ACCESS_DDK_USB_SERIAL",
4751
+ "ohos.permission.ACCESS_DISK_PHY_INFO",
4752
+ "ohos.permission.ACCESS_FIDO2_ONLINEAUTH",
4753
+ "ohos.permission.ACCESS_NET_TRACE_INFO",
4754
+ "ohos.permission.ACCESS_USER_FULL_DISK",
4755
+ "ohos.permission.ACCESS_VIRTUAL_SCREEN",
4756
+ "ohos.permission.CUSTOMIZE_SAVE_BUTTON",
4757
+ "ohos.permission.CUSTOM_SCREEN_RECORDING",
4758
+ "ohos.permission.DLP_GET_HIDE_STATUS",
4759
+ "ohos.permission.FILE_ACCESS_PERSIST",
4760
+ "ohos.permission.GET_ABILITY_INFO",
4761
+ "ohos.permission.GET_ETHERNET_LOCAL_MAC",
4762
+ "ohos.permission.GET_IP_MAC_INFO",
4763
+ "ohos.permission.GET_WIFI_LOCAL_MAC",
4764
+ "ohos.permission.GET_WIFI_PEERS_MAC",
4765
+ "ohos.permission.HOOK_KEY_EVENT",
4766
+ "ohos.permission.INPUT_MONITORING",
4767
+ "ohos.permission.INTERCEPT_INPUT_EVENT",
4768
+ "ohos.permission.KEEP_BACKGROUND_RUNNING_SYSTEM",
4769
+ "ohos.permission.LINKTURBO",
4770
+ "ohos.permission.MANAGE_APN_SETTING",
4771
+ "ohos.permission.MANAGE_PASTEBOARD_APP_SHARE_OPTION",
4772
+ "ohos.permission.MANAGE_SCREEN_TIME_GUARD",
4773
+ "ohos.permission.MANAGE_UDMF_APP_SHARE_OPTION",
4774
+ "ohos.permission.PERSISTENT_BLUETOOTH_PEERS_MAC",
4775
+ "ohos.permission.PERSONAL_MANAGE_RESTRICTIONS",
4776
+ "ohos.permission.PRELOAD_FILE",
4777
+ "ohos.permission.READ_AUDIO",
4778
+ "ohos.permission.READ_CONTACTS",
4779
+ "ohos.permission.READ_IMAGEVIDEO",
4780
+ "ohos.permission.READ_LOCAL_DEVICE_NAME",
4781
+ "ohos.permission.READ_PASTEBOARD",
4782
+ "ohos.permission.READ_WRITE_DESKTOP_DIRECTORY",
4783
+ "ohos.permission.READ_WRITE_DOCUMENTS_DIRECTORY",
4784
+ "ohos.permission.READ_WRITE_DOWNLOAD_DIRECTORY",
4785
+ "ohos.permission.READ_WRITE_USB_DEV",
4786
+ "ohos.permission.READ_WRITE_USER_FILE",
4787
+ "ohos.permission.SET_PAC_URL",
4788
+ "ohos.permission.SET_SYSTEMSHARE_APPLAUNCHTRUSTLIST",
4789
+ "ohos.permission.SHORT_TERM_WRITE_IMAGEVIDEO",
4790
+ "ohos.permission.START_PROVISIONING_MESSAGE",
4791
+ "ohos.permission.SUBSCRIBE_NOTIFICATION",
4792
+ "ohos.permission.SYSTEM_FLOAT_WINDOW",
4793
+ "ohos.permission.USE_FLOAT_BALL",
4794
+ "ohos.permission.USE_FRAUD_APP_PICKER",
4795
+ "ohos.permission.USE_FRAUD_CALL_LOG_PICKER",
4796
+ "ohos.permission.USE_FRAUD_MESSAGES_PICKER",
4797
+ "ohos.permission.WEB_NATIVE_MESSAGING",
4798
+ "ohos.permission.WRITE_AUDIO",
4799
+ "ohos.permission.WRITE_CONTACTS",
4800
+ "ohos.permission.WRITE_IMAGEVIDEO",
4801
+ "ohos.permission.atomicService.MANAGE_STORAGE",
4802
+ "ohos.permission.kernel.ALLOW_EXECUTABLE_FORT_MEMORY",
4803
+ "ohos.permission.kernel.ALLOW_WRITABLE_CODE_MEMORY",
4804
+ "ohos.permission.kernel.DISABLE_CODE_MEMORY_PROTECTION",
4805
+ "ohos.permission.kernel.DISABLE_GOTPLT_RO_PROTECTION"
4806
+ ]);
4807
+
4808
+ // src/harmonyos/signing/project.ts
4809
+ function readRequestedPermissions(moduleConfigPath) {
4810
+ if (!fs37.existsSync(moduleConfigPath)) return [];
4811
+ try {
4812
+ const parsed = import_json56.default.parse(fs37.readFileSync(moduleConfigPath, "utf-8"));
4813
+ return (parsed.module?.requestPermissions ?? []).map((p) => p?.name).filter((n) => typeof n === "string" && n.length > 0);
4814
+ } catch {
4815
+ return [];
4816
+ }
4817
+ }
4818
+ function collectAclPermissions(projectDir, logger = noopLogger) {
4819
+ const requested = /* @__PURE__ */ new Set();
4820
+ for (const module2 of listModules({ projectDir })) {
4821
+ const moduleRoot = path36.resolve(projectDir, module2.srcPath);
4822
+ for (const sourceSet of ["main", "ohosTest"]) {
4823
+ for (const name of readRequestedPermissions(
4824
+ path36.join(moduleRoot, "src", sourceSet, "module.json5")
4825
+ )) {
4826
+ requested.add(name);
4827
+ }
4828
+ }
4829
+ }
4830
+ const acls = [...requested].filter((p) => ACL_PERMISSIONS.has(p)).sort();
4831
+ if (acls.length > 0) {
4832
+ logger.info(`[harmonyos] Project requests ${acls.length} ACL permission(s): ${acls.join(", ")}`);
4833
+ }
4834
+ return acls;
4835
+ }
4836
+
4837
+ // src/harmonyos/signing/autoSign.ts
4838
+ function shouldRegenerate(input) {
4839
+ if (input.force) return "--force was passed";
4840
+ const missing = Object.values(input.paths).find((file) => !fs38.existsSync(file));
4841
+ if (missing) return `${missing} is missing`;
4842
+ const config = input.buildProfile.app?.signingConfigs?.find((c) => c.name === input.productName);
4843
+ if (config?.type !== "HarmonyOS" || config.material?.profile !== input.paths.profilePath) {
4844
+ return "build-profile.json5 has no signingConfig for this material";
4845
+ }
4846
+ const profile = readProvisionProfile(input.paths.profilePath);
4847
+ if (!profile) return "the provisioning profile could not be read";
4848
+ if (profile.bundleName !== input.bundleName) return `the profile is for bundle '${profile.bundleName}'`;
4849
+ if (profile.developerId && profile.developerId !== input.teamId) {
4850
+ return `the profile was issued to team '${profile.developerId}'`;
4851
+ }
4852
+ if (profile.notAfter && profile.notAfter <= (input.now ?? /* @__PURE__ */ new Date())) return "the profile has expired";
4853
+ const unnamed = input.connectedUdids.filter((udid) => !profile.deviceIds.includes(udid.toUpperCase()));
4854
+ if (unnamed.length > 0) return `${unnamed.length} connected device(s) are not named in the profile`;
4855
+ const acls = input.aclPermissions.filter((p) => !profile.aclPermissions.includes(p));
4856
+ if (acls.length > 0) return `the profile lacks ACL permission(s) ${acls.join(", ")}`;
4857
+ return null;
4858
+ }
4859
+ function writeSigningConfig(opts) {
4860
+ const app = opts.buildProfile.app ??= {};
4861
+ const entry = {
4862
+ name: opts.productName,
4863
+ // `type` is what makes hvigor treat this as HarmonyOS signing material.
4864
+ type: "HarmonyOS",
4865
+ material: {
4866
+ certpath: opts.paths.cerPath,
4867
+ keyAlias: KEY_ALIAS,
4868
+ keyPassword: opts.encryptedPassword,
4869
+ profile: opts.paths.profilePath,
4870
+ signAlg: SIGN_ALG,
4871
+ storeFile: opts.paths.p12Path,
4872
+ storePassword: opts.encryptedPassword
4873
+ }
4874
+ };
4875
+ app.signingConfigs = [...(app.signingConfigs ?? []).filter((c) => c.name !== opts.productName), entry];
4876
+ for (const product of app.products ?? []) {
4877
+ if (product.name === opts.productName) product.signingConfig = opts.productName;
4878
+ }
4879
+ fs38.writeFileSync(opts.buildProfilePath, JSON.stringify(opts.buildProfile, null, 2));
4880
+ }
4881
+ async function harmonyOsAutoSign(opts) {
4882
+ const logger = opts.logger ?? noopLogger;
4883
+ const productName = opts.productName ?? "default";
4884
+ const projectDir = path37.resolve(opts.projectDir);
4885
+ const buildProfilePath = path37.join(projectDir, "build-profile.json5");
4886
+ let buildProfile;
4887
+ try {
4888
+ buildProfile = import_json57.default.parse(fs38.readFileSync(buildProfilePath, "utf-8"));
4889
+ } catch (err) {
4890
+ throw new OniroError(`Could not read ${buildProfilePath}: ${err.message}`, err);
4891
+ }
4892
+ const product = buildProfile.app?.products?.find((p) => p.name === productName);
4893
+ if (!product) throw new OniroError(`${buildProfilePath} declares no product named '${productName}'.`);
4894
+ const bundleName = product.bundleName || getBundleName(projectDir);
4895
+ if (!isValidBundleName(bundleName) || bundleName.split(".").length < 3 || bundleName.length < 7 || bundleName.length > 128) {
4896
+ throw new AgcError(SIGNING_ERRORS.BUNDLE_NAME_INVALID);
4897
+ }
4898
+ const sdk = requireHarmonyOsSdk({ config: opts.config, logger });
4899
+ const auth = await opts.session.resolveAgcAuth({ teamId: opts.teamId });
4900
+ const aclPermissions = collectAclPermissions(projectDir, logger);
4901
+ const paths = resolveSigningMaterialPaths(opts.config, productName, projectDir);
4902
+ logger.info(`[harmonyos] Signing ${bundleName} (product '${productName}', team ${auth.teamId}).`);
4903
+ const connected = await collectLocalDevices(opts.config, logger);
4904
+ const result = (regenerated) => ({
4905
+ bundleName,
4906
+ teamId: auth.teamId,
4907
+ paths,
4908
+ regenerated,
4909
+ deviceIds: readProvisionProfile(paths.profilePath)?.deviceIds ?? []
4910
+ });
4911
+ const reason = shouldRegenerate({
4912
+ paths,
4913
+ buildProfile,
4914
+ productName,
4915
+ bundleName,
4916
+ teamId: auth.teamId,
4917
+ connectedUdids: connected.map((d) => d.udid),
4918
+ aclPermissions,
4919
+ force: opts.force
4920
+ });
4921
+ if (!reason) {
4922
+ logger.info("[harmonyos] Reusing the existing signing material, which is still valid.");
4923
+ return result(false);
4924
+ }
4925
+ logger.info(`[harmonyos] Regenerating signing material: ${reason}.`);
4926
+ const agc = createAgcClient(opts.http ?? createHttpClient({ logger }), auth);
4927
+ const deviceIds = await registerDevices(agc, connected, logger);
4928
+ for (const file of Object.values(paths)) fs38.rmSync(file, { force: true });
4929
+ const certName = `oniro_debug_${auth.teamId.replace(/[\\/.:]/g, "")}.cer`;
4930
+ const stale = (await agc.listCertificates()).find((c) => c.certName === certName);
4931
+ if (stale) {
4932
+ logger.info(`[harmonyos] Replacing the previous '${certName}' certificate in AppGallery Connect...`);
4933
+ await agc.deleteCertificate(stale.id);
4934
+ }
4935
+ const { keyPwd, csr } = await generateKeystoreAndCsr({ sdk, p12Path: paths.p12Path, csrPath: paths.csrPath, logger });
4936
+ logger.info("[harmonyos] Requesting a debug certificate from AppGallery Connect...");
4937
+ await agc.addCertificate(certName, csr);
4938
+ const cert = (await agc.listCertificates()).find((c) => c.certName === certName);
4939
+ if (!cert) throw new AgcError(SIGNING_ERRORS.CERT_REQUEST);
4940
+ await agc.download("cert", cert.certObjectId, paths.cerPath);
4941
+ logger.info("[harmonyos] Issuing the debug provisioning profile...");
4942
+ const profile = await agc.addDebugProfile({
4943
+ provisionName: `oniro_${crypto9.randomBytes(6).toString("hex")}`,
4944
+ bundleName,
4945
+ certId: cert.id,
4946
+ deviceIds,
4947
+ aclPermissions
4948
+ });
4949
+ try {
4950
+ await agc.download("profile", profile.provisionFileUrl, paths.profilePath);
4951
+ } finally {
4952
+ await agc.deleteProfile(profile.id).catch((err) => {
4953
+ logger.debug(`[harmonyos] Could not delete profile ${profile.id} from AppGallery Connect: ${err.message}`);
4954
+ });
4955
+ }
4956
+ try {
4957
+ verifySigningMaterial({ ...paths, keyAlias: KEY_ALIAS, keyPwd });
4958
+ } catch (err) {
4959
+ fs38.rmSync(paths.profilePath, { force: true });
4960
+ throw err;
4961
+ }
4962
+ const materialDir = path37.join(path37.dirname(paths.p12Path), "material");
4963
+ try {
4964
+ getKey(materialDir);
4965
+ } catch {
4966
+ createMaterial(materialDir);
4967
+ }
4968
+ writeSigningConfig({ buildProfilePath, buildProfile, productName, paths, encryptedPassword: encryptPwd(keyPwd, materialDir) });
4969
+ logger.info(`[harmonyos] Wrote signingConfigs['${productName}'] to ${buildProfilePath}.`);
4970
+ return result(true);
4971
+ }
4972
+
3602
4973
  // src/index.ts
3603
- var VERSION = "0.10.0";
4974
+ var VERSION = "0.12.0";
3604
4975
  // Annotate the CommonJS export names for ESM import in node:
3605
4976
  0 && (module.exports = {
3606
4977
  ALL_SDKS,
3607
4978
  APL_VALUES,
3608
4979
  APP_FEATURE_VALUES,
4980
+ AgcError,
3609
4981
  CancelledError,
3610
4982
  ChecksumMismatchError,
3611
4983
  CmdToolsNotInstalledError,
3612
4984
  CommandFailedError,
4985
+ HARMONYOS_SDK_VERSIONS,
4986
+ HUAWEI_SITES,
3613
4987
  INSTALL_TMP_DIRNAME,
3614
4988
  InsufficientSpaceError,
3615
4989
  OHOS_URL_BASE,
3616
4990
  OniroError,
4991
+ SIGNING_ERRORS,
3617
4992
  SdkNotInstalledError,
3618
4993
  TMP_DIR_HINT,
3619
4994
  UnsupportedPlatformError,
@@ -3629,10 +5004,12 @@ var VERSION = "0.10.0";
3629
5004
  buildRawTouchCommand,
3630
5005
  captureBurst,
3631
5006
  consoleLogger,
5007
+ createHarmonyOsSession,
3632
5008
  createInstallTempDir,
3633
5009
  createMaterial,
3634
5010
  createScaffold,
3635
5011
  createTempWorkDir,
5012
+ createTokenStore,
3636
5013
  decideInstallMethod,
3637
5014
  decideReboot,
3638
5015
  decryptPwd,
@@ -3641,6 +5018,7 @@ var VERSION = "0.10.0";
3641
5018
  defaultPaths,
3642
5019
  detectAaStartFailure,
3643
5020
  detectProjectSdkVersion,
5021
+ detectRuntimeOs,
3644
5022
  detectSigningConfigNames,
3645
5023
  diffEntryNames,
3646
5024
  diffHapAssets,
@@ -3663,6 +5041,7 @@ var VERSION = "0.10.0";
3663
5041
  extractZipWithProgress,
3664
5042
  findAppProcessId,
3665
5043
  findCmdToolsSourceDir,
5044
+ findHarmonyOsSdk,
3666
5045
  findRunningProcess,
3667
5046
  forceStop,
3668
5047
  formatBytes,
@@ -3687,6 +5066,8 @@ var VERSION = "0.10.0";
3687
5066
  getSdkFilename,
3688
5067
  getSdkRootDir,
3689
5068
  getSupportedSdksForUi,
5069
+ harmonyOsAutoSign,
5070
+ harmonyOsSdkLabelForApi,
3690
5071
  hdcExec,
3691
5072
  installApp,
3692
5073
  installCmdTools,
@@ -3721,12 +5102,14 @@ var VERSION = "0.10.0";
3721
5102
  pruneLayout,
3722
5103
  readJson5File,
3723
5104
  readJsonFile,
5105
+ readProvisionProfile,
3724
5106
  reboot,
3725
5107
  recvFile,
3726
5108
  removeCmdTools,
3727
5109
  removeEmulator,
3728
5110
  removeSdk,
3729
5111
  removeTempWorkDir,
5112
+ requireHarmonyOsSdk,
3730
5113
  resolveTmpRoot,
3731
5114
  runCodelinter,
3732
5115
  runHvigorw,