@oniroproject/core 0.9.0 → 0.11.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.js CHANGED
@@ -38,6 +38,8 @@ var defaultPaths = {
38
38
  sdkRootDir: () => path.join(os.homedir(), "setup-ohos-sdk"),
39
39
  cmdToolsPath: () => path.join(os.homedir(), "command-line-tools"),
40
40
  emulatorDir: () => path.join(os.homedir(), "oniro-emulator"),
41
+ harmonyosAuthDir: () => path.join(os.homedir(), ".oniro", "harmonyos"),
42
+ harmonyosSigningDir: () => path.join(os.homedir(), ".ohos", "config"),
41
43
  hapPath: "entry/build/default/outputs/default/entry-default-signed.hap",
42
44
  emulatorUrl: "https://github.com/eclipse-oniro4openharmony/device_board_oniro/releases/latest/download/oniro_emulator.zip",
43
45
  // The Huawei mirror only hosts a public Linux build of the command-line tools.
@@ -202,14 +204,94 @@ function getSdkFilename(version) {
202
204
  }
203
205
 
204
206
  // src/sdk/paths.ts
207
+ import * as fs2 from "fs";
208
+ import * as os4 from "os";
209
+ import * as path3 from "path";
210
+
211
+ // src/harmonyos/sdk.ts
205
212
  import * as fs from "fs";
206
213
  import * as os3 from "os";
207
214
  import * as path2 from "path";
215
+ function readSdkPkg(sdkPath) {
216
+ let data;
217
+ try {
218
+ data = JSON.parse(fs.readFileSync(path2.join(sdkPath, "default", "sdk-pkg.json"), "utf8")).data;
219
+ } catch {
220
+ return {};
221
+ }
222
+ const text = (value) => typeof value === "string" || typeof value === "number" ? String(value) || void 0 : void 0;
223
+ const platformVersion = text(data?.platformVersion);
224
+ const apiVersion = text(data?.apiVersion);
225
+ return {
226
+ displayName: text(data?.displayName),
227
+ version: platformVersion && apiVersion ? `${platformVersion}(${apiVersion})` : void 0
228
+ };
229
+ }
230
+ function resolveSdk(root, requireHarmonyOs) {
231
+ const nested = path2.join(root, "sdk");
232
+ const sdkPath = [nested, root].find((dir) => fs.existsSync(path2.join(dir, "default", "openharmony")));
233
+ if (!sdkPath) return null;
234
+ const pkg = readSdkPkg(sdkPath);
235
+ if (requireHarmonyOs && !pkg.displayName?.startsWith("HarmonyOS")) return null;
236
+ return { sdkPath, installRoot: sdkPath === nested ? root : path2.dirname(root), ...pkg };
237
+ }
238
+ function studioCandidates() {
239
+ if (process.platform === "darwin") {
240
+ return [
241
+ "/Applications/DevEco-Studio.app/Contents",
242
+ path2.join(os3.homedir(), "Applications", "DevEco-Studio.app", "Contents")
243
+ ];
244
+ }
245
+ if (process.platform === "win32") {
246
+ return [path2.join(process.env.ProgramFiles ?? "C:\\Program Files", "Huawei", "DevEco Studio")];
247
+ }
248
+ return [];
249
+ }
250
+ function findHarmonyOsSdk(opts) {
251
+ const logger = opts.logger ?? noopLogger;
252
+ const configured = opts.config.get("harmonyosSdkPath", "");
253
+ if (configured) {
254
+ const sdk = resolveSdk(configured, false);
255
+ if (sdk) return sdk;
256
+ logger.warn(`ONIRO_HARMONYOS_SDK_PATH points at ${configured}, which holds no SDK; ignoring it.`);
257
+ }
258
+ const candidates = [
259
+ ...studioCandidates(),
260
+ opts.config.get("cmdToolsPath", defaultPaths.cmdToolsPath())
261
+ ];
262
+ for (const candidate of candidates) {
263
+ const sdk = resolveSdk(candidate, true);
264
+ if (sdk) {
265
+ logger.debug(`[harmonyos] Using the ${sdk.displayName} SDK at ${sdk.sdkPath}.`);
266
+ return sdk;
267
+ }
268
+ }
269
+ return null;
270
+ }
271
+ function requireHarmonyOsSdk(opts) {
272
+ const sdk = findHarmonyOsSdk(opts);
273
+ if (sdk) return sdk;
274
+ throw new OniroError(
275
+ "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)."
276
+ );
277
+ }
278
+ function findHarmonyOsTool(sdk, name) {
279
+ const names = process.platform === "win32" ? [`${name}.bat`, `${name}.cmd`, `${name}.exe`] : [name];
280
+ for (const dir of [path2.join(sdk.installRoot, "bin"), path2.join(sdk.installRoot, "tools", name, "bin")]) {
281
+ for (const candidate of names) {
282
+ const full = path2.join(dir, candidate);
283
+ if (fs.existsSync(full)) return full;
284
+ }
285
+ }
286
+ return null;
287
+ }
288
+
289
+ // src/sdk/paths.ts
208
290
  function getSdkRootDir(config) {
209
291
  return config.get("sdkRootDir", defaultPaths.sdkRootDir());
210
292
  }
211
293
  function getOhosBaseSdkHome(config) {
212
- return path2.join(getSdkRootDir(config), getOsFolder());
294
+ return path3.join(getSdkRootDir(config), getOsFolder());
213
295
  }
214
296
  function getCmdToolsPath(config) {
215
297
  return config.get("cmdToolsPath", defaultPaths.cmdToolsPath());
@@ -217,31 +299,31 @@ function getCmdToolsPath(config) {
217
299
  function getEmulatorDir(config) {
218
300
  return config.get("emulatorDir", defaultPaths.emulatorDir());
219
301
  }
220
- function toLongPath(p, platform7 = os3.platform()) {
302
+ function toLongPath(p, platform7 = os4.platform()) {
221
303
  if (platform7 !== "win32") return p;
222
304
  try {
223
- return fs.realpathSync.native(p);
305
+ return fs2.realpathSync.native(p);
224
306
  } catch {
225
307
  return p;
226
308
  }
227
309
  }
228
310
  function pickExisting(candidates) {
229
311
  for (const c of candidates) {
230
- if (fs.existsSync(c)) return c;
312
+ if (fs2.existsSync(c)) return c;
231
313
  }
232
314
  return candidates[0];
233
315
  }
234
316
  function getCmdToolsBin(config, name = "ohpm") {
235
- const binDir = path2.join(getCmdToolsPath(config), "bin");
236
- if (os3.platform() === "win32") {
317
+ const binDir = path3.join(getCmdToolsPath(config), "bin");
318
+ if (os4.platform() === "win32") {
237
319
  return pickExisting([
238
- path2.join(binDir, `${name}.exe`),
239
- path2.join(binDir, `${name}.cmd`),
240
- path2.join(binDir, `${name}.bat`),
241
- path2.join(binDir, name)
320
+ path3.join(binDir, `${name}.exe`),
321
+ path3.join(binDir, `${name}.cmd`),
322
+ path3.join(binDir, `${name}.bat`),
323
+ path3.join(binDir, name)
242
324
  ]);
243
325
  }
244
- return path2.join(binDir, name);
326
+ return path3.join(binDir, name);
245
327
  }
246
328
  function getOhpmPath(config) {
247
329
  return getCmdToolsBin(config, "ohpm");
@@ -249,45 +331,52 @@ function getOhpmPath(config) {
249
331
  function getHdcPath(config) {
250
332
  const override = config.get("hdcPath", "");
251
333
  if (override) return override;
252
- const base = path2.join(getCmdToolsPath(config), "sdk", "default", "openharmony", "toolchains");
253
- if (os3.platform() === "win32") {
334
+ const hdc = hdcInSdk(path3.join(getCmdToolsPath(config), "sdk"));
335
+ if (fs2.existsSync(hdc)) return hdc;
336
+ const harmonySdk = findHarmonyOsSdk({ config });
337
+ const harmonyHdc = harmonySdk ? hdcInSdk(harmonySdk.sdkPath) : void 0;
338
+ return harmonyHdc && fs2.existsSync(harmonyHdc) ? harmonyHdc : hdc;
339
+ }
340
+ function hdcInSdk(sdkDir) {
341
+ const base = path3.join(sdkDir, "default", "openharmony", "toolchains");
342
+ if (os4.platform() === "win32") {
254
343
  return pickExisting([
255
- path2.join(base, "hdc.exe"),
256
- path2.join(base, "hdc.bat"),
257
- path2.join(base, "hdc.cmd"),
258
- path2.join(base, "hdc")
344
+ path3.join(base, "hdc.exe"),
345
+ path3.join(base, "hdc.bat"),
346
+ path3.join(base, "hdc.cmd"),
347
+ path3.join(base, "hdc")
259
348
  ]);
260
349
  }
261
- return path2.join(base, "hdc");
350
+ return path3.join(base, "hdc");
262
351
  }
263
- function getHvigorwPath(config, projectDir, platform7 = os3.platform()) {
352
+ function getHvigorwPath(config, projectDir, platform7 = os4.platform()) {
264
353
  const posixFirst = ["hvigorw", "hvigorw.bat", "hvigorw.cmd"];
265
354
  const windowsFirst = ["hvigorw.bat", "hvigorw.cmd", "hvigorw"];
266
355
  const names = platform7 === "win32" ? windowsFirst : posixFirst;
267
- const local = pickExisting(names.map((n) => path2.join(projectDir, n)));
268
- if (fs.existsSync(local) && isProjectLocalHvigorwWorking(projectDir)) {
356
+ const local = pickExisting(names.map((n) => path3.join(projectDir, n)));
357
+ if (fs2.existsSync(local) && isProjectLocalHvigorwWorking(projectDir)) {
269
358
  return local;
270
359
  }
271
360
  return getCmdToolsBin(config, "hvigorw");
272
361
  }
273
362
  function isProjectLocalHvigorwWorking(projectDir) {
274
- const wrapper = path2.join(projectDir, "hvigor", "hvigor-wrapper.js");
275
- const nodeModules = path2.join(projectDir, "hvigor", "node_modules");
276
- return fs.existsSync(wrapper) && fs.existsSync(nodeModules);
363
+ const wrapper = path3.join(projectDir, "hvigor", "hvigor-wrapper.js");
364
+ const nodeModules = path3.join(projectDir, "hvigor", "node_modules");
365
+ return fs2.existsSync(wrapper) && fs2.existsSync(nodeModules);
277
366
  }
278
367
 
279
368
  // src/sdk/detectProjectSdk.ts
280
- import * as fs2 from "fs";
281
- import * as path3 from "path";
369
+ import * as fs3 from "fs";
370
+ import * as path4 from "path";
282
371
  import JSON5 from "json5";
283
372
  function detectProjectSdkVersion(projectRoot, logger = noopLogger) {
284
- const buildProfilePath = path3.join(projectRoot, "build-profile.json5");
285
- if (!fs2.existsSync(buildProfilePath)) {
373
+ const buildProfilePath = path4.join(projectRoot, "build-profile.json5");
374
+ if (!fs3.existsSync(buildProfilePath)) {
286
375
  logger.warn(`build-profile.json5 not found at ${projectRoot}`);
287
376
  return void 0;
288
377
  }
289
378
  try {
290
- const content = fs2.readFileSync(buildProfilePath, "utf-8");
379
+ const content = fs3.readFileSync(buildProfilePath, "utf-8");
291
380
  const config = JSON5.parse(content);
292
381
  const product = config.app?.products?.[0];
293
382
  const v = product?.compileSdkVersion;
@@ -299,26 +388,26 @@ function detectProjectSdkVersion(projectRoot, logger = noopLogger) {
299
388
  }
300
389
 
301
390
  // src/sdk/tmp.ts
302
- import * as fs3 from "fs";
303
- import * as os4 from "os";
304
- import * as path4 from "path";
391
+ import * as fs4 from "fs";
392
+ import * as os5 from "os";
393
+ import * as path5 from "path";
305
394
  var INSTALL_TMP_DIRNAME = ".oniro-tmp";
306
395
  var TMPFS_MAGIC = 16914836;
307
396
  var RAMFS_MAGIC = 2240043254;
308
- var HINT_EXAMPLE = os4.platform() === "win32" ? "D:\\oniro-tmp" : "~/.cache/oniro-tmp";
397
+ var HINT_EXAMPLE = os5.platform() === "win32" ? "D:\\oniro-tmp" : "~/.cache/oniro-tmp";
309
398
  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}.`;
310
399
  function resolveTmpRoot(config, override, installRoot) {
311
400
  const explicit = (override ?? "").trim();
312
- if (explicit) return path4.resolve(explicit);
401
+ if (explicit) return path5.resolve(explicit);
313
402
  const configured = config.get("tmpDir", "").trim();
314
- if (configured) return path4.resolve(configured);
315
- if (installRoot) return path4.join(path4.resolve(installRoot), INSTALL_TMP_DIRNAME);
316
- return os4.tmpdir();
403
+ if (configured) return path5.resolve(configured);
404
+ if (installRoot) return path5.join(path5.resolve(installRoot), INSTALL_TMP_DIRNAME);
405
+ return os5.tmpdir();
317
406
  }
318
407
  function createTempWorkDir(root, prefix) {
319
408
  try {
320
- fs3.mkdirSync(root, { recursive: true });
321
- return fs3.mkdtempSync(path4.join(root, prefix));
409
+ fs4.mkdirSync(root, { recursive: true });
410
+ return fs4.mkdtempSync(path5.join(root, prefix));
322
411
  } catch (err) {
323
412
  if (isOutOfSpaceError(err)) throw toSpaceError(err, root, "a temporary directory");
324
413
  throw new OniroError(
@@ -329,10 +418,10 @@ ${TMP_DIR_HINT}`,
329
418
  }
330
419
  }
331
420
  function removeTempWorkDir(dir, root) {
332
- fs3.rmSync(dir, { recursive: true, force: true });
333
- if (!root || path4.basename(root) !== INSTALL_TMP_DIRNAME) return;
421
+ fs4.rmSync(dir, { recursive: true, force: true });
422
+ if (!root || path5.basename(root) !== INSTALL_TMP_DIRNAME) return;
334
423
  try {
335
- fs3.rmdirSync(root);
424
+ fs4.rmdirSync(root);
336
425
  } catch {
337
426
  }
338
427
  }
@@ -342,18 +431,18 @@ function createInstallTempDir(opts) {
342
431
  try {
343
432
  return { dir: createTempWorkDir(root, opts.prefix), root };
344
433
  } catch (err) {
345
- const fallback = os4.tmpdir();
346
- if (requested || isOutOfSpaceError(err) || path4.resolve(fallback) === path4.resolve(root)) throw err;
347
- const reason = err instanceof Error ? err.message.split("\n")[0] : String(err);
434
+ const fallback = os5.tmpdir();
435
+ if (requested || isOutOfSpaceError(err) || path5.resolve(fallback) === path5.resolve(root)) throw err;
436
+ const reason = err instanceof Error ? err.message.split(/\r?\n/)[0] : String(err);
348
437
  (opts.logger ?? noopLogger).warn(`${reason} Falling back to '${fallback}'.`);
349
438
  return { dir: createTempWorkDir(fallback, opts.prefix), root: fallback };
350
439
  }
351
440
  }
352
441
  function nearestExisting(dir) {
353
- let current = path4.resolve(dir);
442
+ let current = path5.resolve(dir);
354
443
  for (; ; ) {
355
- if (fs3.existsSync(current)) return current;
356
- const parent = path4.dirname(current);
444
+ if (fs4.existsSync(current)) return current;
445
+ const parent = path5.dirname(current);
357
446
  if (parent === current) return null;
358
447
  current = parent;
359
448
  }
@@ -362,7 +451,7 @@ function getFreeSpaceBytes(dir) {
362
451
  const target = nearestExisting(dir);
363
452
  if (!target) return null;
364
453
  try {
365
- const stats = fs3.statfsSync(target);
454
+ const stats = fs4.statfsSync(target);
366
455
  return Number(stats.bsize) * Number(stats.bavail);
367
456
  } catch {
368
457
  return null;
@@ -372,7 +461,7 @@ function isRamBackedPath(dir) {
372
461
  const target = nearestExisting(dir);
373
462
  if (!target) return false;
374
463
  try {
375
- const type = Number(fs3.statfsSync(target).type);
464
+ const type = Number(fs4.statfsSync(target).type);
376
465
  return type === TMPFS_MAGIC || type === RAMFS_MAGIC;
377
466
  } catch {
378
467
  return false;
@@ -436,16 +525,16 @@ function errorPath(err) {
436
525
  return null;
437
526
  }
438
527
  function isInside(child, parent) {
439
- const rel = path4.relative(path4.resolve(parent), path4.resolve(child));
440
- return rel === "" || !rel.startsWith("..") && !path4.isAbsolute(rel);
528
+ const rel = path5.relative(path5.resolve(parent), path5.resolve(child));
529
+ return rel === "" || !rel.startsWith("..") && !path5.isAbsolute(rel);
441
530
  }
442
531
  function toSpaceError(err, tmpRoot, what) {
443
532
  if (err instanceof InsufficientSpaceError) return err;
444
533
  if (!isOutOfSpaceError(err)) return err;
445
534
  const failed = errorPath(err);
446
- const failedDir = failed ? path4.dirname(path4.resolve(failed)) : path4.resolve(tmpRoot);
535
+ const failedDir = failed ? path5.dirname(path5.resolve(failed)) : path5.resolve(tmpRoot);
447
536
  const inTmp = isInside(failedDir, tmpRoot);
448
- const dir = inTmp ? path4.resolve(tmpRoot) : failedDir;
537
+ const dir = inTmp ? path5.resolve(tmpRoot) : failedDir;
449
538
  return new InsufficientSpaceError(
450
539
  spaceMessage(dir, what, { freeBytes: getFreeSpaceBytes(dir), tmpDirHint: inTmp }),
451
540
  dir,
@@ -454,8 +543,8 @@ function toSpaceError(err, tmpRoot, what) {
454
543
  }
455
544
 
456
545
  // src/sdk/download.ts
457
- import * as fs4 from "fs";
458
- import * as path5 from "path";
546
+ import * as fs5 from "fs";
547
+ import * as path6 from "path";
459
548
  import * as crypto from "crypto";
460
549
  import { pipeline } from "stream";
461
550
  import { promisify } from "util";
@@ -465,26 +554,26 @@ var pipelineAsync = promisify(pipeline);
465
554
  async function downloadFile(opts) {
466
555
  const { url, dest, progress, abortSignal } = opts;
467
556
  const proto = url.startsWith("https") ? https : http;
468
- return new Promise((resolve4, reject) => {
557
+ return new Promise((resolve6, reject) => {
469
558
  const s = Math.max(0, Math.min(100, opts.start ?? 0));
470
559
  const r = Math.max(0, Math.min(100 - s, opts.range ?? 100));
471
560
  let settled = false;
472
561
  const done = (err) => {
473
562
  if (settled) return;
474
563
  settled = true;
475
- err ? reject(err) : resolve4();
564
+ err ? reject(err) : resolve6();
476
565
  };
477
566
  if (abortSignal?.aborted) {
478
567
  done(new CancelledError("Download cancelled before start."));
479
568
  return;
480
569
  }
481
- const destDir = path5.dirname(path5.resolve(dest));
570
+ const destDir = path6.dirname(path6.resolve(dest));
482
571
  const what = opts.what ?? `the download of '${url}'`;
483
572
  const writeFailed = (err) => isOutOfSpaceError(err) ? toSpaceError(err, opts.tmpRoot ?? destDir, what) : new OniroError(`Error writing '${dest}': ${err instanceof Error ? err.message : String(err)}`, err);
484
- const file = fs4.createWriteStream(dest);
573
+ const file = fs5.createWriteStream(dest);
485
574
  let activeResponse;
486
575
  const failWith = (err) => {
487
- file.close(() => fs4.rm(dest, { force: true }, () => done(err)));
576
+ file.close(() => fs5.rm(dest, { force: true }, () => done(err)));
488
577
  };
489
578
  file.on("error", (err) => {
490
579
  try {
@@ -556,9 +645,9 @@ async function downloadFile(opts) {
556
645
  });
557
646
  }
558
647
  async function verifySha256(filePath, sha256Path) {
559
- const expected = fs4.readFileSync(sha256Path, "utf8").split(/\s+/)[0];
648
+ const expected = fs5.readFileSync(sha256Path, "utf8").split(/\s+/)[0];
560
649
  const hash = crypto.createHash("sha256");
561
- await pipelineAsync(fs4.createReadStream(filePath), hash);
650
+ await pipelineAsync(fs5.createReadStream(filePath), hash);
562
651
  const actual = hash.digest("hex");
563
652
  if (actual !== expected) {
564
653
  throw new ChecksumMismatchError(expected, actual);
@@ -566,9 +655,9 @@ async function verifySha256(filePath, sha256Path) {
566
655
  }
567
656
 
568
657
  // src/sdk/extract.ts
569
- import * as fs5 from "fs";
570
- import * as os5 from "os";
571
- import * as path6 from "path";
658
+ import * as fs6 from "fs";
659
+ import * as os6 from "os";
660
+ import * as path7 from "path";
572
661
  import { pipeline as pipeline2 } from "stream";
573
662
  import { promisify as promisify2 } from "util";
574
663
  import * as tar from "tar";
@@ -585,12 +674,12 @@ async function extractZipWithProgress(opts) {
585
674
  const r = Math.max(0, Math.min(100 - s, opts.range ?? 100));
586
675
  let processed = 0;
587
676
  let lastOverall = Math.round(s);
588
- await fs5.promises.mkdir(dest, { recursive: true });
589
- const destRoot = path6.resolve(dest);
677
+ await fs6.promises.mkdir(dest, { recursive: true });
678
+ const destRoot = path7.resolve(dest);
590
679
  const safeResolveTarget = (entryName) => {
591
- const resolved = path6.resolve(destRoot, entryName);
592
- const rel = path6.relative(destRoot, resolved);
593
- if (rel.startsWith("..") || path6.isAbsolute(rel)) {
680
+ const resolved = path7.resolve(destRoot, entryName);
681
+ const rel = path7.relative(destRoot, resolved);
682
+ if (rel.startsWith("..") || path7.isAbsolute(rel)) {
594
683
  throw new OniroError(`Blocked ZIP entry with illegal path: ${entryName}`);
595
684
  }
596
685
  return resolved;
@@ -602,25 +691,25 @@ async function extractZipWithProgress(opts) {
602
691
  const attr = file.attr ? file.attr >>> 16 : 0;
603
692
  const isSymlink = "isSymbolicLink" in file ? Boolean(file.isSymbolicLink) : (attr & 61440) === 40960;
604
693
  if (file.isDirectory) {
605
- await fs5.promises.mkdir(targetPath, { recursive: true });
694
+ await fs6.promises.mkdir(targetPath, { recursive: true });
606
695
  } else if (isSymlink) {
607
- await fs5.promises.mkdir(path6.dirname(targetPath), { recursive: true });
696
+ await fs6.promises.mkdir(path7.dirname(targetPath), { recursive: true });
608
697
  const linkTargetBuffer = await zip.entryData(file);
609
698
  const linkTarget = linkTargetBuffer.toString("utf8");
610
699
  try {
611
- const isWin = os5.platform() === "win32";
612
- await fs5.promises.symlink(linkTarget, targetPath, isWin ? "file" : void 0);
700
+ const isWin = os6.platform() === "win32";
701
+ await fs6.promises.symlink(linkTarget, targetPath, isWin ? "file" : void 0);
613
702
  } catch (e) {
614
703
  logger.warn(`Failed to create symlink at ${targetPath}: ${String(e)}`);
615
704
  }
616
705
  } else {
617
- await fs5.promises.mkdir(path6.dirname(targetPath), { recursive: true });
706
+ await fs6.promises.mkdir(path7.dirname(targetPath), { recursive: true });
618
707
  const readStream = await zip.stream(file);
619
- const writeStream = fs5.createWriteStream(targetPath);
708
+ const writeStream = fs6.createWriteStream(targetPath);
620
709
  await pipelineAsync2(readStream, writeStream);
621
710
  if (attr > 0) {
622
711
  try {
623
- await fs5.promises.chmod(targetPath, attr & 511);
712
+ await fs6.promises.chmod(targetPath, attr & 511);
624
713
  } catch {
625
714
  }
626
715
  }
@@ -652,41 +741,41 @@ async function extractTarball(tarPath, dest, strip = 0) {
652
741
  }
653
742
 
654
743
  // src/sdk/list.ts
655
- import * as fs6 from "fs";
656
- import * as path7 from "path";
744
+ import * as fs7 from "fs";
745
+ import * as path8 from "path";
657
746
  function getSupportedSdksForUi(config) {
658
- const base = path7.join(getSdkRootDir(config), getOsFolder());
747
+ const base = path8.join(getSdkRootDir(config), getOsFolder());
659
748
  return ALL_SDKS.map((sdk) => ({
660
749
  version: sdk.version,
661
750
  api: sdk.api,
662
- installed: fs6.existsSync(path7.join(base, sdk.api))
751
+ installed: fs7.existsSync(path8.join(base, sdk.api))
663
752
  })).sort((a, b) => Number(b.api) - Number(a.api));
664
753
  }
665
754
  function getInstalledSdks(config) {
666
755
  const sdkRoot = getSdkRootDir(config);
667
756
  const versions = /* @__PURE__ */ new Set();
668
- if (!fs6.existsSync(sdkRoot)) return [];
757
+ if (!fs7.existsSync(sdkRoot)) return [];
669
758
  for (const osFolder of ["linux", "darwin", "windows"]) {
670
- const osPath = path7.join(sdkRoot, osFolder);
671
- if (!fs6.existsSync(osPath) || !fs6.statSync(osPath).isDirectory()) continue;
672
- for (const api of fs6.readdirSync(osPath)) {
673
- const apiPath = path7.join(osPath, api);
674
- if (fs6.statSync(apiPath).isDirectory()) versions.add(api);
759
+ const osPath = path8.join(sdkRoot, osFolder);
760
+ if (!fs7.existsSync(osPath) || !fs7.statSync(osPath).isDirectory()) continue;
761
+ for (const api of fs7.readdirSync(osPath)) {
762
+ const apiPath = path8.join(osPath, api);
763
+ if (fs7.statSync(apiPath).isDirectory()) versions.add(api);
675
764
  }
676
765
  }
677
766
  return ALL_SDKS.filter((sdk) => versions.has(sdk.api)).map((sdk) => sdk.version);
678
767
  }
679
768
 
680
769
  // src/sdk/remove.ts
681
- import * as fs7 from "fs";
682
- import * as path8 from "path";
770
+ import * as fs8 from "fs";
771
+ import * as path9 from "path";
683
772
  function removeSdk(config, api) {
684
773
  const sdkRoot = getSdkRootDir(config);
685
774
  let removed = false;
686
775
  for (const osFolder of ["linux", "darwin", "windows"]) {
687
- const sdkPath = path8.join(sdkRoot, osFolder, api);
688
- if (fs7.existsSync(sdkPath)) {
689
- fs7.rmSync(sdkPath, { recursive: true, force: true });
776
+ const sdkPath = path9.join(sdkRoot, osFolder, api);
777
+ if (fs8.existsSync(sdkPath)) {
778
+ fs8.rmSync(sdkPath, { recursive: true, force: true });
690
779
  removed = true;
691
780
  }
692
781
  }
@@ -694,18 +783,18 @@ function removeSdk(config, api) {
694
783
  }
695
784
 
696
785
  // src/sdk/install.ts
697
- import * as fs9 from "fs";
698
- import * as path9 from "path";
786
+ import * as fs10 from "fs";
787
+ import * as path10 from "path";
699
788
 
700
789
  // src/sdk/move.ts
701
- import * as fs8 from "fs";
790
+ import * as fs9 from "fs";
702
791
  function movePath(src, dest) {
703
792
  try {
704
- fs8.renameSync(src, dest);
793
+ fs9.renameSync(src, dest);
705
794
  } catch (err) {
706
795
  if (err.code !== "EXDEV") throw err;
707
- fs8.cpSync(src, dest, { recursive: true });
708
- fs8.rmSync(src, { recursive: true, force: true });
796
+ fs9.cpSync(src, dest, { recursive: true });
797
+ fs9.rmSync(src, { recursive: true, force: true });
709
798
  }
710
799
  }
711
800
 
@@ -726,12 +815,12 @@ async function downloadAndInstallSdk(opts) {
726
815
  prefix: "oniro-sdk-",
727
816
  logger
728
817
  });
729
- const tarPath = path9.join(tmpDir, filename);
730
- const sha256Path = path9.join(tmpDir, `${filename}.sha256`);
731
- const extractDir = path9.join(tmpDir, "extract");
732
- fs9.mkdirSync(extractDir);
733
- const sdkInstallDir = path9.join(sdkRoot, osFolder, api);
734
- fs9.mkdirSync(path9.dirname(sdkInstallDir), { recursive: true });
818
+ const tarPath = path10.join(tmpDir, filename);
819
+ const sha256Path = path10.join(tmpDir, `${filename}.sha256`);
820
+ const extractDir = path10.join(tmpDir, "extract");
821
+ fs10.mkdirSync(extractDir);
822
+ const sdkInstallDir = path10.join(sdkRoot, osFolder, api);
823
+ fs10.mkdirSync(path10.dirname(sdkInstallDir), { recursive: true });
735
824
  const checkCancelled = () => {
736
825
  if (abortSignal?.aborted) throw new CancelledError();
737
826
  };
@@ -748,13 +837,13 @@ async function downloadAndInstallSdk(opts) {
748
837
  await extractTarball(tarPath, extractDir, strip);
749
838
  progress?.report({ message: "Extracting SDK (this may take a while)...", increment: 10 });
750
839
  checkCancelled();
751
- const osContentPath = path9.join(extractDir, osFolder);
752
- if (!fs9.existsSync(osContentPath)) {
840
+ const osContentPath = path10.join(extractDir, osFolder);
841
+ if (!fs10.existsSync(osContentPath)) {
753
842
  throw new OniroError(
754
843
  `Expected folder '${osFolder}' not found in extracted SDK. Tarball layout may have changed.`
755
844
  );
756
845
  }
757
- const zipFiles = fs9.readdirSync(osContentPath).filter((n2) => n2.endsWith(".zip"));
846
+ const zipFiles = fs10.readdirSync(osContentPath).filter((n2) => n2.endsWith(".zip"));
758
847
  const componentsStart = 60;
759
848
  const componentsBudget = 35;
760
849
  const n = zipFiles.length;
@@ -769,16 +858,16 @@ async function downloadAndInstallSdk(opts) {
769
858
  for (const zipFile of zipFiles) {
770
859
  checkCancelled();
771
860
  logger.info(`Extracting component ${zipFile}`);
772
- const zipPath = path9.join(osContentPath, zipFile);
861
+ const zipPath = path10.join(osContentPath, zipFile);
773
862
  const thisBudget = base + (rem > 0 ? 1 : 0);
774
863
  if (rem > 0) rem--;
775
864
  await extractZipWithProgress({ zipPath, dest: osContentPath, progress, start: cursor, range: thisBudget, logger });
776
865
  cursor += thisBudget;
777
- fs9.unlinkSync(zipPath);
866
+ fs10.unlinkSync(zipPath);
778
867
  }
779
868
  progress?.report({ message: "Finalizing installation...", increment: 0 });
780
- if (fs9.existsSync(sdkInstallDir)) {
781
- fs9.rmSync(sdkInstallDir, { recursive: true, force: true });
869
+ if (fs10.existsSync(sdkInstallDir)) {
870
+ fs10.rmSync(sdkInstallDir, { recursive: true, force: true });
782
871
  }
783
872
  movePath(osContentPath, sdkInstallDir);
784
873
  progress?.report({ message: "Finalizing installation...", increment: 3 });
@@ -794,13 +883,13 @@ async function downloadAndInstallSdk(opts) {
794
883
  }
795
884
 
796
885
  // src/cmdTools/install.ts
797
- import * as fs10 from "fs";
798
- import * as os6 from "os";
799
- import * as path11 from "path";
886
+ import * as fs11 from "fs";
887
+ import * as os7 from "os";
888
+ import * as path12 from "path";
800
889
  import { spawnSync } from "child_process";
801
890
 
802
891
  // src/hdc/spawnCompat.ts
803
- import * as path10 from "path";
892
+ import * as path11 from "path";
804
893
  import { spawn } from "child_process";
805
894
  var WINDOWS_BATCH_RE = /\.(bat|cmd)$/i;
806
895
  var CMD_META_RE = /([()[\]%!^"`<>&|;, *?])/g;
@@ -824,7 +913,7 @@ function escapeCmdCommand(command) {
824
913
  function buildCmdWrapper(command, args) {
825
914
  assertCmdSafe(command, `command ${command}`);
826
915
  args.forEach((a, i) => assertCmdSafe(a, `argument ${i} (${a})`));
827
- const normalized = path10.normalize(command);
916
+ const normalized = path11.normalize(command);
828
917
  const double = CMD_SHIM_RE.test(normalized);
829
918
  const line = [escapeCmdCommand(normalized), ...args.map((a) => escapeCmdArgument(a, double))].join(" ");
830
919
  return {
@@ -872,7 +961,7 @@ function killProcessTree(child) {
872
961
  }
873
962
 
874
963
  // src/cmdTools/install.ts
875
- function getCmdToolsDownloadUrl(config, platform7 = os6.platform()) {
964
+ function getCmdToolsDownloadUrl(config, platform7 = os7.platform()) {
876
965
  if (platform7 === "linux") {
877
966
  return config.get("cmdToolsUrlLinux", defaultPaths.cmdToolsUrlLinux);
878
967
  }
@@ -898,16 +987,16 @@ function getCmdToolsDownloadUrl(config, platform7 = os6.platform()) {
898
987
  }
899
988
  function findCmdToolsSourceDir(extractPath) {
900
989
  const known = [
901
- path11.join(extractPath, "command-line-tools"),
902
- path11.join(extractPath, "oh-command-line-tools"),
903
- path11.join(extractPath, "commandline-tools")
990
+ path12.join(extractPath, "command-line-tools"),
991
+ path12.join(extractPath, "oh-command-line-tools"),
992
+ path12.join(extractPath, "commandline-tools")
904
993
  ];
905
994
  for (const c of known) {
906
- if (fs10.existsSync(c)) return c;
995
+ if (fs11.existsSync(c)) return c;
907
996
  }
908
- const entries = fs10.readdirSync(extractPath, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => path11.join(extractPath, e.name));
997
+ const entries = fs11.readdirSync(extractPath, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => path12.join(extractPath, e.name));
909
998
  for (const dir of entries) {
910
- if (fs10.existsSync(path11.join(dir, "bin"))) return dir;
999
+ if (fs11.existsSync(path12.join(dir, "bin"))) return dir;
911
1000
  }
912
1001
  throw new OniroError("Could not locate command line tools folder in the extracted archive.");
913
1002
  }
@@ -919,12 +1008,12 @@ async function installCmdTools(opts) {
919
1008
  const { dir: tmpDir, root: tmpRoot } = createInstallTempDir({
920
1009
  config,
921
1010
  override: opts.tmpDir,
922
- installRoot: path11.dirname(CMD_PATH),
1011
+ installRoot: path12.dirname(CMD_PATH),
923
1012
  prefix: "oniro-cmdtools-",
924
1013
  logger
925
1014
  });
926
- const zipPath = opts.localZipPath ?? path11.join(tmpDir, "oh-command-line-tools.zip");
927
- const extractPath = path11.join(tmpDir, "oh-command-line-tools");
1015
+ const zipPath = opts.localZipPath ?? path12.join(tmpDir, "oh-command-line-tools.zip");
1016
+ const extractPath = path12.join(tmpDir, "oh-command-line-tools");
928
1017
  try {
929
1018
  if (!opts.localZipPath) {
930
1019
  const url = getCmdToolsDownloadUrl(config);
@@ -933,22 +1022,22 @@ async function installCmdTools(opts) {
933
1022
  }
934
1023
  progress?.report({ message: "Extracting tools...", increment: 0 });
935
1024
  await extractZipWithProgress({ zipPath, dest: extractPath, progress, start: 50, range: 45, logger });
936
- fs10.mkdirSync(CMD_PATH, { recursive: true });
1025
+ fs11.mkdirSync(CMD_PATH, { recursive: true });
937
1026
  const srcDir = findCmdToolsSourceDir(extractPath);
938
- for (const entry of fs10.readdirSync(srcDir)) {
939
- const src = path11.join(srcDir, entry);
940
- const dest = path11.join(CMD_PATH, entry);
941
- if (fs10.statSync(src).isDirectory()) {
942
- if (fs10.existsSync(dest)) fs10.rmSync(dest, { recursive: true, force: true });
1027
+ for (const entry of fs11.readdirSync(srcDir)) {
1028
+ const src = path12.join(srcDir, entry);
1029
+ const dest = path12.join(CMD_PATH, entry);
1030
+ if (fs11.statSync(src).isDirectory()) {
1031
+ if (fs11.existsSync(dest)) fs11.rmSync(dest, { recursive: true, force: true });
943
1032
  movePath(src, dest);
944
1033
  } else {
945
- fs10.copyFileSync(src, dest);
1034
+ fs11.copyFileSync(src, dest);
946
1035
  }
947
1036
  }
948
- const binDir = path11.join(CMD_PATH, "bin");
949
- if (fs10.existsSync(binDir) && os6.platform() !== "win32") {
950
- for (const file of fs10.readdirSync(binDir)) {
951
- fs10.chmodSync(path11.join(binDir, file), 493);
1037
+ const binDir = path12.join(CMD_PATH, "bin");
1038
+ if (fs11.existsSync(binDir) && os7.platform() !== "win32") {
1039
+ for (const file of fs11.readdirSync(binDir)) {
1040
+ fs11.chmodSync(path12.join(binDir, file), 493);
952
1041
  }
953
1042
  }
954
1043
  progress?.report({ message: "Finalizing installation...", increment: 5 });
@@ -961,16 +1050,16 @@ async function installCmdTools(opts) {
961
1050
  }
962
1051
  function getCmdToolsStatus(config) {
963
1052
  const cmdPath = getCmdToolsPath(config);
964
- const binDir = path11.join(cmdPath, "bin");
965
- const candidates = os6.platform() === "win32" ? ["ohpm.exe", "ohpm.cmd", "ohpm.bat", "ohpm"] : ["ohpm"];
966
- const bin = candidates.map((c) => path11.join(binDir, c)).find((p) => fs10.existsSync(p));
1053
+ const binDir = path12.join(cmdPath, "bin");
1054
+ const candidates = os7.platform() === "win32" ? ["ohpm.exe", "ohpm.cmd", "ohpm.bat", "ohpm"] : ["ohpm"];
1055
+ const bin = candidates.map((c) => path12.join(binDir, c)).find((p) => fs11.existsSync(p));
967
1056
  if (!bin) {
968
1057
  return { installed: false, status: "Not installed" };
969
1058
  }
970
1059
  try {
971
- const versionFile = path11.join(cmdPath, "version.txt");
972
- if (fs10.existsSync(versionFile)) {
973
- const content = fs10.readFileSync(versionFile, "utf8");
1060
+ const versionFile = path12.join(cmdPath, "version.txt");
1061
+ if (fs11.existsSync(versionFile)) {
1062
+ const content = fs11.readFileSync(versionFile, "utf8");
974
1063
  for (const line of content.split(/\r?\n/)) {
975
1064
  const m = line.match(/^\s*#\s*Version:\s*(.+)$/);
976
1065
  if (m?.[1]) return { installed: true, status: `Installed (${m[1].trim()})` };
@@ -997,14 +1086,14 @@ function isCmdToolsInstalled(config) {
997
1086
  }
998
1087
  function removeCmdTools(config) {
999
1088
  const cmdPath = getCmdToolsPath(config);
1000
- if (fs10.existsSync(cmdPath)) {
1001
- fs10.rmSync(cmdPath, { recursive: true, force: true });
1089
+ if (fs11.existsSync(cmdPath)) {
1090
+ fs11.rmSync(cmdPath, { recursive: true, force: true });
1002
1091
  }
1003
1092
  }
1004
1093
 
1005
1094
  // src/sign/encryptKey.ts
1006
- import * as fs11 from "fs";
1007
- import * as path12 from "path";
1095
+ import * as fs12 from "fs";
1096
+ import * as path13 from "path";
1008
1097
  import * as crypto2 from "crypto";
1009
1098
  var DAEMON_ROOT_KEY_COMPONENT_LENGTH = 16;
1010
1099
  var DAEMON_SALT_KEY_LENGTH = 16;
@@ -1076,33 +1165,33 @@ function getRootKey(fdComponents, salt) {
1076
1165
  function createAndStoreKey(dir, keyLength) {
1077
1166
  const key = crypto2.randomBytes(keyLength);
1078
1167
  const hash = crypto2.createHash("sha256").update(key).digest("hex");
1079
- const filePath = path12.join(dir, hash);
1080
- fs11.writeFileSync(filePath, key);
1081
- fs11.chmodSync(filePath, KEY_FILE_PERMISSIONS);
1168
+ const filePath = path13.join(dir, hash);
1169
+ fs12.writeFileSync(filePath, key);
1170
+ fs12.chmodSync(filePath, KEY_FILE_PERMISSIONS);
1082
1171
  return key;
1083
1172
  }
1084
1173
  function createAndStoreEnKey(rootKey, ceDir) {
1085
1174
  const workKey = crypto2.randomBytes(DAEMON_WORK_KEY_LENGTH);
1086
1175
  const encrypted = encrypt(rootKey, workKey);
1087
1176
  const hash = crypto2.createHash("sha256").update(encrypted).digest("hex");
1088
- const filePath = path12.join(ceDir, hash);
1089
- fs11.writeFileSync(filePath, encrypted);
1090
- fs11.chmodSync(filePath, KEY_FILE_PERMISSIONS);
1177
+ const filePath = path13.join(ceDir, hash);
1178
+ fs12.writeFileSync(filePath, encrypted);
1179
+ fs12.chmodSync(filePath, KEY_FILE_PERMISSIONS);
1091
1180
  }
1092
1181
  function createMaterial(materialPath) {
1093
- fs11.mkdirSync(materialPath, { recursive: true });
1094
- fs11.chmodSync(materialPath, KEY_FILE_DIRECTORY_PERMISSIONS);
1095
- const fdDir = path12.join(materialPath, "fd");
1096
- const acDir = path12.join(materialPath, "ac");
1097
- const ceDir = path12.join(materialPath, "ce");
1098
- fs11.mkdirSync(fdDir, { recursive: true });
1099
- fs11.mkdirSync(acDir, { recursive: true });
1100
- fs11.mkdirSync(ceDir, { recursive: true });
1182
+ fs12.mkdirSync(materialPath, { recursive: true });
1183
+ fs12.chmodSync(materialPath, KEY_FILE_DIRECTORY_PERMISSIONS);
1184
+ const fdDir = path13.join(materialPath, "fd");
1185
+ const acDir = path13.join(materialPath, "ac");
1186
+ const ceDir = path13.join(materialPath, "ce");
1187
+ fs12.mkdirSync(fdDir, { recursive: true });
1188
+ fs12.mkdirSync(acDir, { recursive: true });
1189
+ fs12.mkdirSync(ceDir, { recursive: true });
1101
1190
  const fdSubDirs = ["0", "1", "2"];
1102
1191
  const fdComponents = [];
1103
1192
  for (const sub of fdSubDirs) {
1104
- const subDir = path12.join(fdDir, sub);
1105
- fs11.mkdirSync(subDir, { recursive: true });
1193
+ const subDir = path13.join(fdDir, sub);
1194
+ fs12.mkdirSync(subDir, { recursive: true });
1106
1195
  const comp = createAndStoreKey(subDir, DAEMON_ROOT_KEY_COMPONENT_LENGTH);
1107
1196
  fdComponents.push(comp);
1108
1197
  }
@@ -1111,24 +1200,24 @@ function createMaterial(materialPath) {
1111
1200
  createAndStoreEnKey(rootKey, ceDir);
1112
1201
  }
1113
1202
  function readSingleFile(dir) {
1114
- if (!fs11.existsSync(dir) || !fs11.statSync(dir).isDirectory()) {
1203
+ if (!fs12.existsSync(dir) || !fs12.statSync(dir).isDirectory()) {
1115
1204
  throw new Error(`Missing signing material directory: ${dir}`);
1116
1205
  }
1117
- const files = fs11.readdirSync(dir).filter((f) => f !== ".DS_Store");
1206
+ const files = fs12.readdirSync(dir).filter((f) => f !== ".DS_Store");
1118
1207
  if (files.length !== 1) {
1119
1208
  throw new Error(`Signing material in ${dir} is illegal (expected exactly one file).`);
1120
1209
  }
1121
- return fs11.readFileSync(path12.join(dir, files[0]));
1210
+ return fs12.readFileSync(path13.join(dir, files[0]));
1122
1211
  }
1123
1212
  function getKey(materialPath) {
1124
- if (!fs11.existsSync(materialPath) || !fs11.statSync(materialPath).isDirectory()) {
1213
+ if (!fs12.existsSync(materialPath) || !fs12.statSync(materialPath).isDirectory()) {
1125
1214
  throw new Error("Material directory does not exist.");
1126
1215
  }
1127
- const fdDir = path12.join(materialPath, "fd");
1128
- const fdComponents = ["0", "1", "2"].map((sub) => readSingleFile(path12.join(fdDir, sub)));
1129
- const salt = readSingleFile(path12.join(materialPath, "ac"));
1216
+ const fdDir = path13.join(materialPath, "fd");
1217
+ const fdComponents = ["0", "1", "2"].map((sub) => readSingleFile(path13.join(fdDir, sub)));
1218
+ const salt = readSingleFile(path13.join(materialPath, "ac"));
1130
1219
  const rootKey = getRootKey(fdComponents, salt);
1131
- const workMaterial = readSingleFile(path12.join(materialPath, "ce"));
1220
+ const workMaterial = readSingleFile(path13.join(materialPath, "ce"));
1132
1221
  return decrypt(rootKey, workMaterial);
1133
1222
  }
1134
1223
  function encryptPwd(password, materialPath) {
@@ -1143,8 +1232,8 @@ function decryptPwd(encryptedHex, materialPath) {
1143
1232
  }
1144
1233
 
1145
1234
  // src/sign/generateSigningConfigs.ts
1146
- import * as fs12 from "fs";
1147
- import * as path13 from "path";
1235
+ import * as fs13 from "fs";
1236
+ import * as path14 from "path";
1148
1237
  import { execFileSync } from "child_process";
1149
1238
  import JSON52 from "json5";
1150
1239
 
@@ -1214,17 +1303,17 @@ var SIGNING_KIND_CONFIG = {
1214
1303
  };
1215
1304
  function copyFilesToProject(projectDir, paths, kind, logger, applicationCertPath) {
1216
1305
  logger.info("[sign] Copying signing material into project...");
1217
- const signaturesDir = path13.join(projectDir, "signatures");
1218
- fs12.mkdirSync(signaturesDir, { recursive: true });
1219
- fs12.copyFileSync(paths.keystore, path13.join(signaturesDir, "OpenHarmony.p12"));
1220
- fs12.copyFileSync(paths.profileCert, path13.join(signaturesDir, "OpenHarmonyProfileRelease.pem"));
1221
- fs12.copyFileSync(paths.unsignedProfileTemplate, path13.join(signaturesDir, "UnsgnedReleasedProfileTemplate.json"));
1306
+ const signaturesDir = path14.join(projectDir, "signatures");
1307
+ fs13.mkdirSync(signaturesDir, { recursive: true });
1308
+ fs13.copyFileSync(paths.keystore, path14.join(signaturesDir, "OpenHarmony.p12"));
1309
+ fs13.copyFileSync(paths.profileCert, path14.join(signaturesDir, "OpenHarmonyProfileRelease.pem"));
1310
+ fs13.copyFileSync(paths.unsignedProfileTemplate, path14.join(signaturesDir, "UnsgnedReleasedProfileTemplate.json"));
1222
1311
  if (kind === "application-release") {
1223
- const dest = path13.join(signaturesDir, "OpenHarmonyApplication.cer");
1312
+ const dest = path14.join(signaturesDir, "OpenHarmonyApplication.cer");
1224
1313
  if (applicationCertPath) {
1225
- fs12.copyFileSync(applicationCertPath, dest);
1314
+ fs13.copyFileSync(applicationCertPath, dest);
1226
1315
  } else {
1227
- fs12.writeFileSync(dest, OPEN_HARMONY_APPLICATION_CERT);
1316
+ fs13.writeFileSync(dest, OPEN_HARMONY_APPLICATION_CERT);
1228
1317
  }
1229
1318
  }
1230
1319
  }
@@ -1233,7 +1322,7 @@ function resolveAppFeature(apl, override) {
1233
1322
  return apl === "normal" ? "hos_normal_app" : "hos_system_app";
1234
1323
  }
1235
1324
  function extractLeafCertificate(chainPath) {
1236
- const certContent = fs12.readFileSync(chainPath, "utf-8");
1325
+ const certContent = fs13.readFileSync(chainPath, "utf-8");
1237
1326
  const parts = certContent.split("-----END CERTIFICATE-----");
1238
1327
  if (parts.length < 3) {
1239
1328
  throw new OniroError(`${chainPath} does not contain enough certificates.`);
@@ -1243,20 +1332,20 @@ function extractLeafCertificate(chainPath) {
1243
1332
  function modifyProfileTemplate(projectDir, apl, appFeature, logger, options = {}) {
1244
1333
  const kind = options.kind ?? pickSigningKind(apl);
1245
1334
  logger.info(`[sign] Modifying profile template (apl=${apl}, app-feature=${appFeature}, kind=${kind})...`);
1246
- const appJsonPath = path13.join(projectDir, "AppScope/app.json5");
1247
- const profileTemplatePath = path13.join(projectDir, "signatures/UnsgnedReleasedProfileTemplate.json");
1248
- if (!fs12.existsSync(appJsonPath)) {
1335
+ const appJsonPath = path14.join(projectDir, "AppScope/app.json5");
1336
+ const profileTemplatePath = path14.join(projectDir, "signatures/UnsgnedReleasedProfileTemplate.json");
1337
+ if (!fs13.existsSync(appJsonPath)) {
1249
1338
  throw new OniroError(`${appJsonPath} does not exist.`);
1250
1339
  }
1251
1340
  let appJson;
1252
1341
  try {
1253
- appJson = JSON52.parse(fs12.readFileSync(appJsonPath, "utf-8"));
1342
+ appJson = JSON52.parse(fs13.readFileSync(appJsonPath, "utf-8"));
1254
1343
  } catch (e) {
1255
1344
  throw new OniroError(`Error parsing ${appJsonPath}: ${e.message}`, e);
1256
1345
  }
1257
1346
  let profileTemplate;
1258
1347
  try {
1259
- profileTemplate = JSON.parse(fs12.readFileSync(profileTemplatePath, "utf-8"));
1348
+ profileTemplate = JSON.parse(fs13.readFileSync(profileTemplatePath, "utf-8"));
1260
1349
  } catch (e) {
1261
1350
  throw new OniroError(`Error parsing ${profileTemplatePath}: ${e.message}`, e);
1262
1351
  }
@@ -1269,7 +1358,7 @@ function modifyProfileTemplate(projectDir, apl, appFeature, logger, options = {}
1269
1358
  profileTemplate["bundle-info"]["bundle-name"] = appJson.app.bundleName;
1270
1359
  profileTemplate["bundle-info"]["apl"] = apl;
1271
1360
  profileTemplate["bundle-info"]["app-feature"] = appFeature;
1272
- const distCertChainPath = path13.join(
1361
+ const distCertChainPath = path14.join(
1273
1362
  projectDir,
1274
1363
  "signatures",
1275
1364
  SIGNING_KIND_CONFIG[kind].certBasename
@@ -1279,13 +1368,13 @@ function modifyProfileTemplate(projectDir, apl, appFeature, logger, options = {}
1279
1368
  profileTemplate.acls = profileTemplate.acls ?? {};
1280
1369
  profileTemplate.acls["allowed-acls"] = options.acls;
1281
1370
  }
1282
- fs12.writeFileSync(profileTemplatePath, JSON.stringify(profileTemplate, null, 2));
1371
+ fs13.writeFileSync(profileTemplatePath, JSON.stringify(profileTemplate, null, 2));
1283
1372
  }
1284
1373
  function generateP7bFile(projectDir, paths, logger, passwords) {
1285
1374
  logger.info("[sign] Generating P7b profile via hap-sign-tool...");
1286
- const signaturesDir = path13.join(projectDir, "signatures");
1287
- const profileTemplatePath = path13.join(signaturesDir, "UnsgnedReleasedProfileTemplate.json");
1288
- const outputProfilePath = path13.join(signaturesDir, "app1-profile.p7b");
1375
+ const signaturesDir = path14.join(projectDir, "signatures");
1376
+ const profileTemplatePath = path14.join(signaturesDir, "UnsgnedReleasedProfileTemplate.json");
1377
+ const outputProfilePath = path14.join(signaturesDir, "app1-profile.p7b");
1289
1378
  const args = [
1290
1379
  "-jar",
1291
1380
  paths.signTool,
@@ -1319,11 +1408,11 @@ function generateP7bFile(projectDir, paths, logger, passwords) {
1319
1408
  }
1320
1409
  }
1321
1410
  function detectSigningConfigNames(projectDir) {
1322
- const buildProfilePath = path13.join(projectDir, "build-profile.json5");
1323
- if (!fs12.existsSync(buildProfilePath)) return ["default"];
1411
+ const buildProfilePath = path14.join(projectDir, "build-profile.json5");
1412
+ if (!fs13.existsSync(buildProfilePath)) return ["default"];
1324
1413
  let parsed;
1325
1414
  try {
1326
- parsed = JSON52.parse(fs12.readFileSync(buildProfilePath, "utf-8"));
1415
+ parsed = JSON52.parse(fs13.readFileSync(buildProfilePath, "utf-8"));
1327
1416
  } catch {
1328
1417
  return ["default"];
1329
1418
  }
@@ -1340,14 +1429,14 @@ function detectSigningConfigNames(projectDir) {
1340
1429
  }
1341
1430
  function updateBuildProfile(projectDir, kind, logger, passwords) {
1342
1431
  logger.info("[sign] Writing signing configs into build-profile.json5...");
1343
- const materialDir = path13.join(projectDir, "signatures", "material");
1344
- const buildProfilePath = path13.join(projectDir, "build-profile.json5");
1432
+ const materialDir = path14.join(projectDir, "signatures", "material");
1433
+ const buildProfilePath = path14.join(projectDir, "build-profile.json5");
1345
1434
  const encryptedStorePassword = encryptPwd(passwords.store, materialDir);
1346
1435
  const encryptedKeyPassword = encryptPwd(passwords.key, materialDir);
1347
1436
  let buildProfile = { app: {} };
1348
- if (fs12.existsSync(buildProfilePath)) {
1437
+ if (fs13.existsSync(buildProfilePath)) {
1349
1438
  try {
1350
- buildProfile = JSON52.parse(fs12.readFileSync(buildProfilePath, "utf-8"));
1439
+ buildProfile = JSON52.parse(fs13.readFileSync(buildProfilePath, "utf-8"));
1351
1440
  } catch (e) {
1352
1441
  throw new OniroError(`Error parsing ${buildProfilePath}: ${e.message}`, e);
1353
1442
  }
@@ -1368,13 +1457,13 @@ function updateBuildProfile(projectDir, kind, logger, passwords) {
1368
1457
  storeFile: "./signatures/OpenHarmony.p12"
1369
1458
  }
1370
1459
  }));
1371
- fs12.writeFileSync(buildProfilePath, JSON.stringify(buildProfile, null, 2));
1460
+ fs13.writeFileSync(buildProfilePath, JSON.stringify(buildProfile, null, 2));
1372
1461
  }
1373
1462
  function prepareMaterialDirectory(projectDir, logger) {
1374
1463
  logger.info("[sign] Generating fresh signing material...");
1375
- const materialDir = path13.join(projectDir, "signatures", "material");
1376
- if (fs12.existsSync(materialDir)) {
1377
- fs12.rmSync(materialDir, { recursive: true, force: true });
1464
+ const materialDir = path14.join(projectDir, "signatures", "material");
1465
+ if (fs13.existsSync(materialDir)) {
1466
+ fs13.rmSync(materialDir, { recursive: true, force: true });
1378
1467
  }
1379
1468
  createMaterial(materialDir);
1380
1469
  }
@@ -1397,15 +1486,15 @@ function generateSigningConfigs(options) {
1397
1486
  "Could not detect project SDK version (compileSdkVersion missing in build-profile.json5)."
1398
1487
  );
1399
1488
  }
1400
- const sdkPath = path13.join(sdkHome, String(sdkVersion));
1401
- if (!fs12.existsSync(sdkPath)) {
1489
+ const sdkPath = path14.join(sdkHome, String(sdkVersion));
1490
+ if (!fs13.existsSync(sdkPath)) {
1402
1491
  throw new OniroError(`SDK path does not exist: ${sdkPath}`);
1403
1492
  }
1404
1493
  const paths = {
1405
- signTool: path13.join(sdkPath, "toolchains/lib/hap-sign-tool.jar"),
1406
- keystore: path13.join(sdkPath, "toolchains/lib/OpenHarmony.p12"),
1407
- profileCert: path13.join(sdkPath, "toolchains/lib/OpenHarmonyProfileRelease.pem"),
1408
- unsignedProfileTemplate: path13.join(sdkPath, "toolchains/lib/UnsgnedReleasedProfileTemplate.json")
1494
+ signTool: path14.join(sdkPath, "toolchains/lib/hap-sign-tool.jar"),
1495
+ keystore: path14.join(sdkPath, "toolchains/lib/OpenHarmony.p12"),
1496
+ profileCert: path14.join(sdkPath, "toolchains/lib/OpenHarmonyProfileRelease.pem"),
1497
+ unsignedProfileTemplate: path14.join(sdkPath, "toolchains/lib/UnsgnedReleasedProfileTemplate.json")
1409
1498
  };
1410
1499
  const kind = pickSigningKind(apl);
1411
1500
  const passwords = options.passwords ?? DEFAULT_PASSWORDS;
@@ -1419,8 +1508,8 @@ function generateSigningConfigs(options) {
1419
1508
  }
1420
1509
 
1421
1510
  // src/emulator/install.ts
1422
- import * as fs13 from "fs";
1423
- import * as path14 from "path";
1511
+ import * as fs14 from "fs";
1512
+ import * as path15 from "path";
1424
1513
  async function installEmulator(opts) {
1425
1514
  const { config, progress, abortSignal } = opts;
1426
1515
  const logger = opts.logger ?? noopLogger;
@@ -1430,20 +1519,20 @@ async function installEmulator(opts) {
1430
1519
  const { dir: tmpDir, root: tmpRoot } = createInstallTempDir({
1431
1520
  config,
1432
1521
  override: opts.tmpDir,
1433
- installRoot: path14.dirname(emulatorDir),
1522
+ installRoot: path15.dirname(emulatorDir),
1434
1523
  prefix: "oniro-emulator-",
1435
1524
  logger
1436
1525
  });
1437
- const tmpZip = path14.join(tmpDir, "oniro_emulator.zip");
1526
+ const tmpZip = path15.join(tmpDir, "oniro_emulator.zip");
1438
1527
  try {
1439
- fs13.mkdirSync(emulatorDir, { recursive: true });
1528
+ fs14.mkdirSync(emulatorDir, { recursive: true });
1440
1529
  progress?.report({ message: "Downloading emulator...", increment: 0 });
1441
1530
  await downloadFile({ url, dest: tmpZip, progress, abortSignal, start: 0, range: 50, what, tmpRoot });
1442
1531
  progress?.report({ message: "Extracting emulator...", increment: 0 });
1443
1532
  await extractZipWithProgress({ zipPath: tmpZip, dest: emulatorDir, progress, start: 50, range: 45, logger });
1444
- const runSh = path14.join(emulatorDir, "images", "run.sh");
1445
- if (fs13.existsSync(runSh)) {
1446
- fs13.chmodSync(runSh, 493);
1533
+ const runSh = path15.join(emulatorDir, "images", "run.sh");
1534
+ if (fs14.existsSync(runSh)) {
1535
+ fs14.chmodSync(runSh, 493);
1447
1536
  }
1448
1537
  progress?.report({ message: "Finalizing installation...", increment: 5 });
1449
1538
  } catch (err) {
@@ -1453,24 +1542,24 @@ async function installEmulator(opts) {
1453
1542
  }
1454
1543
  }
1455
1544
  function isEmulatorInstalled(config) {
1456
- const imagesDir = path14.join(getEmulatorDir(config), "images");
1457
- return fs13.existsSync(path14.join(imagesDir, "run.sh")) || fs13.existsSync(path14.join(imagesDir, "run.bat"));
1545
+ const imagesDir = path15.join(getEmulatorDir(config), "images");
1546
+ return fs14.existsSync(path15.join(imagesDir, "run.sh")) || fs14.existsSync(path15.join(imagesDir, "run.bat"));
1458
1547
  }
1459
1548
  function removeEmulator(config) {
1460
1549
  const emulatorDir = getEmulatorDir(config);
1461
- if (fs13.existsSync(emulatorDir)) {
1462
- fs13.rmSync(emulatorDir, { recursive: true, force: true });
1550
+ if (fs14.existsSync(emulatorDir)) {
1551
+ fs14.rmSync(emulatorDir, { recursive: true, force: true });
1463
1552
  }
1464
1553
  }
1465
1554
 
1466
1555
  // src/emulator/lifecycle.ts
1467
- import * as fs15 from "fs";
1468
- import * as os7 from "os";
1469
- import * as path15 from "path";
1556
+ import * as fs16 from "fs";
1557
+ import * as os8 from "os";
1558
+ import * as path16 from "path";
1470
1559
  import { spawn as spawn2 } from "child_process";
1471
1560
 
1472
1561
  // src/hdc/exec.ts
1473
- import * as fs14 from "fs";
1562
+ import * as fs15 from "fs";
1474
1563
  function errorMessage(err) {
1475
1564
  return err instanceof Error ? err.message : String(err);
1476
1565
  }
@@ -1478,12 +1567,12 @@ function runProcess(opts) {
1478
1567
  const { command, args, cwd, env, onOutput } = opts;
1479
1568
  const timeoutMs = opts.timeoutMs ?? 6e4;
1480
1569
  const { abortSignal } = opts;
1481
- return new Promise((resolve4, reject) => {
1570
+ return new Promise((resolve6, reject) => {
1482
1571
  if (abortSignal?.aborted) {
1483
1572
  reject(new CancelledError(`Command aborted before start: ${command}`));
1484
1573
  return;
1485
1574
  }
1486
- if (needsCmdWrapper(command) && !fs14.existsSync(command)) {
1575
+ if (needsCmdWrapper(command) && !fs15.existsSync(command)) {
1487
1576
  reject(new OniroError(`Failed to spawn ${command}: ENOENT (no such file)`));
1488
1577
  return;
1489
1578
  }
@@ -1543,7 +1632,7 @@ ${stderr.trim()}` : "")
1543
1632
  );
1544
1633
  return;
1545
1634
  }
1546
- resolve4({ code: code ?? -1, stdout, stderr });
1635
+ resolve6({ code: code ?? -1, stdout, stderr });
1547
1636
  });
1548
1637
  });
1549
1638
  }
@@ -1591,7 +1680,7 @@ function ensureRemoteOk(result, command) {
1591
1680
  }
1592
1681
 
1593
1682
  // src/emulator/lifecycle.ts
1594
- var PID_FILE = path15.join(os7.tmpdir(), "oniro_emulator.pid");
1683
+ var PID_FILE = path16.join(os8.tmpdir(), "oniro_emulator.pid");
1595
1684
  async function attemptHdcConnection(config, address = "127.0.0.1:55555", logger = noopLogger) {
1596
1685
  try {
1597
1686
  await hdcExec({ config, args: ["start", "-r"], logger });
@@ -1604,24 +1693,24 @@ async function attemptHdcConnection(config, address = "127.0.0.1:55555", logger
1604
1693
  }
1605
1694
  }
1606
1695
  function resolveLauncher(imagesPath, headless, connect) {
1607
- const runSh = path15.join(imagesPath, "run.sh");
1608
- const runBat = path15.join(imagesPath, "run.bat");
1696
+ const runSh = path16.join(imagesPath, "run.sh");
1697
+ const runBat = path16.join(imagesPath, "run.bat");
1609
1698
  const extra = [];
1610
1699
  if (headless) extra.push("--headless");
1611
1700
  if (connect) extra.push("--connect", connect);
1612
- if (os7.platform() === "win32") {
1613
- if (fs15.existsSync(runBat)) {
1701
+ if (os8.platform() === "win32") {
1702
+ if (fs16.existsSync(runBat)) {
1614
1703
  return { command: "cmd.exe", args: ["/c", runBat, ...extra], cwd: imagesPath };
1615
1704
  }
1616
- if (fs15.existsSync(runSh)) {
1705
+ if (fs16.existsSync(runSh)) {
1617
1706
  return { command: "bash", args: [runSh, ...extra], cwd: imagesPath };
1618
1707
  }
1619
1708
  throw new OniroError(`No emulator launcher (run.bat or run.sh) found in ${imagesPath}.`);
1620
1709
  }
1621
- if (fs15.existsSync(runSh)) {
1710
+ if (fs16.existsSync(runSh)) {
1622
1711
  return { command: "bash", args: [runSh, ...extra], cwd: imagesPath };
1623
1712
  }
1624
- if (fs15.existsSync(runBat)) {
1713
+ if (fs16.existsSync(runBat)) {
1625
1714
  return { command: "bash", args: [runBat, ...extra], cwd: imagesPath };
1626
1715
  }
1627
1716
  throw new OniroError(`No emulator launcher (run.sh or run.bat) found in ${imagesPath}.`);
@@ -1629,9 +1718,9 @@ function resolveLauncher(imagesPath, headless, connect) {
1629
1718
  async function startEmulator(opts) {
1630
1719
  const { config } = opts;
1631
1720
  const logger = opts.logger ?? noopLogger;
1632
- if (os7.platform() !== "win32" && fs15.existsSync(PID_FILE)) {
1721
+ if (os8.platform() !== "win32" && fs16.existsSync(PID_FILE)) {
1633
1722
  try {
1634
- const pid = Number(fs15.readFileSync(PID_FILE, "utf8").trim());
1723
+ const pid = Number(fs16.readFileSync(PID_FILE, "utf8").trim());
1635
1724
  if (Number.isFinite(pid) && pid > 0) {
1636
1725
  try {
1637
1726
  process.kill(pid, 0);
@@ -1647,13 +1736,13 @@ async function startEmulator(opts) {
1647
1736
  logger.warn(`Could not read PID file: ${err.message}`);
1648
1737
  }
1649
1738
  }
1650
- const imagesPath = path15.join(getEmulatorDir(config), "images");
1651
- if (!fs15.existsSync(imagesPath)) {
1739
+ const imagesPath = path16.join(getEmulatorDir(config), "images");
1740
+ if (!fs16.existsSync(imagesPath)) {
1652
1741
  throw new OniroError(`Emulator images directory not found at ${imagesPath}. Run \`oniro-app emulator install\` first.`);
1653
1742
  }
1654
1743
  const launcher = resolveLauncher(imagesPath, opts.headless === true, opts.connect);
1655
- const logPath = opts.logFile ?? (os7.platform() === "win32" ? "NUL" : "/dev/null");
1656
- const logFd = fs15.openSync(logPath, "a");
1744
+ const logPath = opts.logFile ?? (os8.platform() === "win32" ? "NUL" : "/dev/null");
1745
+ const logFd = fs16.openSync(logPath, "a");
1657
1746
  let child;
1658
1747
  try {
1659
1748
  child = spawn2(launcher.command, [...launcher.args], {
@@ -1663,19 +1752,19 @@ async function startEmulator(opts) {
1663
1752
  windowsHide: true
1664
1753
  });
1665
1754
  } finally {
1666
- fs15.closeSync(logFd);
1755
+ fs16.closeSync(logFd);
1667
1756
  }
1668
1757
  if (!child.pid) {
1669
1758
  throw new OniroError(`Failed to spawn launcher (${launcher.command} ${launcher.args.join(" ")}).`);
1670
1759
  }
1671
- fs15.writeFileSync(PID_FILE, String(child.pid));
1760
+ fs16.writeFileSync(PID_FILE, String(child.pid));
1672
1761
  child.unref();
1673
1762
  logger.info(`Emulator launched (PID ${child.pid}). Logs: ${logPath}`);
1674
- const earlyExit = new Promise((resolve4) => {
1675
- child.once("exit", (code) => resolve4(code));
1676
- child.once("error", () => resolve4(-1));
1763
+ const earlyExit = new Promise((resolve6) => {
1764
+ child.once("exit", (code) => resolve6(code));
1765
+ child.once("error", () => resolve6(-1));
1677
1766
  });
1678
- const liveness = new Promise((resolve4) => setTimeout(() => resolve4("alive"), 2e3));
1767
+ const liveness = new Promise((resolve6) => setTimeout(() => resolve6("alive"), 2e3));
1679
1768
  const outcome = await Promise.race([earlyExit, liveness]);
1680
1769
  if (outcome !== "alive") {
1681
1770
  const tail = readTail(logPath, 4e3);
@@ -1715,24 +1804,24 @@ ${tail}` : ""}`);
1715
1804
  }
1716
1805
  function readTail(filePath, bytes) {
1717
1806
  try {
1718
- const stat = fs15.statSync(filePath);
1807
+ const stat = fs16.statSync(filePath);
1719
1808
  const start = Math.max(0, stat.size - bytes);
1720
- const fd = fs15.openSync(filePath, "r");
1809
+ const fd = fs16.openSync(filePath, "r");
1721
1810
  try {
1722
1811
  const buf = Buffer.alloc(stat.size - start);
1723
- fs15.readSync(fd, buf, 0, buf.length, start);
1812
+ fs16.readSync(fd, buf, 0, buf.length, start);
1724
1813
  return buf.toString("utf8");
1725
1814
  } finally {
1726
- fs15.closeSync(fd);
1815
+ fs16.closeSync(fd);
1727
1816
  }
1728
1817
  } catch {
1729
1818
  return "";
1730
1819
  }
1731
1820
  }
1732
1821
  async function stopEmulator(logger = noopLogger) {
1733
- if (fs15.existsSync(PID_FILE)) {
1822
+ if (fs16.existsSync(PID_FILE)) {
1734
1823
  try {
1735
- const pid = Number(fs15.readFileSync(PID_FILE, "utf8").trim());
1824
+ const pid = Number(fs16.readFileSync(PID_FILE, "utf8").trim());
1736
1825
  if (Number.isFinite(pid) && pid > 0) {
1737
1826
  try {
1738
1827
  process.kill(pid, "SIGTERM");
@@ -1745,13 +1834,13 @@ async function stopEmulator(logger = noopLogger) {
1745
1834
  logger.warn(`Could not read PID file: ${err.message}`);
1746
1835
  }
1747
1836
  }
1748
- const [killCmd, ...killArgs] = os7.platform() === "win32" ? ["taskkill", "/IM", "qemu-system-x86_64.exe", "/F"] : ["pkill", "-f", "qemu-system-x86_64"];
1837
+ const [killCmd, ...killArgs] = os8.platform() === "win32" ? ["taskkill", "/IM", "qemu-system-x86_64.exe", "/F"] : ["pkill", "-f", "qemu-system-x86_64"];
1749
1838
  try {
1750
1839
  await runProcess({ command: killCmd, args: killArgs, logger });
1751
1840
  } catch (err) {
1752
1841
  logger.warn(`[emulator] could not run ${killCmd}: ${err.message}`);
1753
1842
  }
1754
- fs15.rm(PID_FILE, { force: true }, (err) => {
1843
+ fs16.rm(PID_FILE, { force: true }, (err) => {
1755
1844
  if (err) logger.warn(`Could not remove PID file: ${err.message}`);
1756
1845
  });
1757
1846
  }
@@ -1874,7 +1963,7 @@ async function getDisplaySize(config, opts = {}) {
1874
1963
 
1875
1964
  // src/hdc/wait.ts
1876
1965
  function delay(ms, signal) {
1877
- return new Promise((resolve4, reject) => {
1966
+ return new Promise((resolve6, reject) => {
1878
1967
  if (signal?.aborted) {
1879
1968
  reject(new CancelledError("Wait cancelled."));
1880
1969
  return;
@@ -1889,7 +1978,7 @@ function delay(ms, signal) {
1889
1978
  };
1890
1979
  const timer = setTimeout(() => {
1891
1980
  cleanup();
1892
- resolve4();
1981
+ resolve6();
1893
1982
  }, ms);
1894
1983
  signal?.addEventListener("abort", onAbort, { once: true });
1895
1984
  });
@@ -1918,20 +2007,20 @@ async function waitForCondition(opts) {
1918
2007
  }
1919
2008
 
1920
2009
  // src/hdc/app.ts
1921
- import * as fs17 from "fs";
1922
- import * as path17 from "path";
2010
+ import * as fs18 from "fs";
2011
+ import * as path18 from "path";
1923
2012
 
1924
2013
  // src/hdc/project.ts
1925
- import * as fs16 from "fs";
1926
- import * as path16 from "path";
2014
+ import * as fs17 from "fs";
2015
+ import * as path17 from "path";
1927
2016
  import JSON53 from "json5";
1928
2017
  function readJson5(filePath) {
1929
- const content = fs16.readFileSync(filePath, "utf-8");
2018
+ const content = fs17.readFileSync(filePath, "utf-8");
1930
2019
  return JSON53.parse(content);
1931
2020
  }
1932
2021
  function getBundleName(projectDir) {
1933
- const appJsonPath = path16.join(projectDir, "AppScope", "app.json5");
1934
- if (!fs16.existsSync(appJsonPath)) {
2022
+ const appJsonPath = path17.join(projectDir, "AppScope", "app.json5");
2023
+ if (!fs17.existsSync(appJsonPath)) {
1935
2024
  throw new OniroError(`Could not find app.json5 at ${appJsonPath}`);
1936
2025
  }
1937
2026
  const appJson = readJson5(appJsonPath);
@@ -1941,8 +2030,8 @@ function getBundleName(projectDir) {
1941
2030
  return appJson.app.bundleName;
1942
2031
  }
1943
2032
  function getMainAbility(projectDir, moduleName = "entry", abilityName) {
1944
- const moduleJsonPath = path16.join(projectDir, moduleName, "src", "main", "module.json5");
1945
- if (!fs16.existsSync(moduleJsonPath)) {
2033
+ const moduleJsonPath = path17.join(projectDir, moduleName, "src", "main", "module.json5");
2034
+ if (!fs17.existsSync(moduleJsonPath)) {
1946
2035
  throw new OniroError(`Could not find module.json5 at ${moduleJsonPath}`);
1947
2036
  }
1948
2037
  const moduleJson = readJson5(moduleJsonPath);
@@ -1965,11 +2054,16 @@ function getMainAbility(projectDir, moduleName = "entry", abilityName) {
1965
2054
  async function installApp(opts) {
1966
2055
  const logger = scopedLogger(opts.logger ?? noopLogger, "hdc");
1967
2056
  const relativeHapPath = opts.hapPath ?? opts.config.get("hapPath", defaultPaths.hapPath);
1968
- const hapPath = path17.isAbsolute(relativeHapPath) ? relativeHapPath : path17.join(opts.projectDir, relativeHapPath);
1969
- if (!fs17.existsSync(hapPath)) {
2057
+ const hapPath = path18.isAbsolute(relativeHapPath) ? relativeHapPath : path18.join(opts.projectDir, relativeHapPath);
2058
+ if (!fs18.existsSync(hapPath)) {
1970
2059
  throw new OniroError(`HAP file not found at: ${hapPath}. Build and sign the app first.`);
1971
2060
  }
1972
- const result = await hdcExec({ config: opts.config, args: ["install", hapPath], timeoutMs: 3e5 });
2061
+ const result = await hdcExec({
2062
+ config: opts.config,
2063
+ args: ["install", hapPath],
2064
+ deviceSerial: opts.deviceSerial,
2065
+ timeoutMs: 3e5
2066
+ });
1973
2067
  if (result.stdout.trim()) logger.info(result.stdout.trim());
1974
2068
  if (result.stderr.trim()) logger.warn(result.stderr.trim());
1975
2069
  ensureOk(result, `hdc install ${hapPath}`);
@@ -2050,7 +2144,7 @@ function listRunningProcesses(config, options = {}) {
2050
2144
  const logger = options.logger ?? noopLogger;
2051
2145
  const timeoutMs = options.timeoutMs ?? 1e3;
2052
2146
  const hdc = getHdcPath(config);
2053
- return new Promise((resolve4, reject) => {
2147
+ return new Promise((resolve6, reject) => {
2054
2148
  let proc;
2055
2149
  try {
2056
2150
  proc = spawnCompat(hdc, ["track-jpid"]);
@@ -2061,7 +2155,7 @@ function listRunningProcesses(config, options = {}) {
2061
2155
  const processes = [];
2062
2156
  let resolved = false;
2063
2157
  proc.stdout?.on("data", (data) => {
2064
- for (const line of data.toString().split("\n").filter(Boolean)) {
2158
+ for (const line of data.toString().split(/\r?\n/).filter(Boolean)) {
2065
2159
  const match = line.match(/^(\d+)\s+(.+)$/);
2066
2160
  if (!match) continue;
2067
2161
  const pid = match[1];
@@ -2070,7 +2164,7 @@ function listRunningProcesses(config, options = {}) {
2070
2164
  if (options.targetProcessName && name === options.targetProcessName && !resolved) {
2071
2165
  resolved = true;
2072
2166
  killProcessTree(proc);
2073
- resolve4(pid);
2167
+ resolve6(pid);
2074
2168
  return;
2075
2169
  }
2076
2170
  }
@@ -2091,7 +2185,7 @@ function listRunningProcesses(config, options = {}) {
2091
2185
  if (options.targetProcessName) {
2092
2186
  reject(new OniroError(`Could not find process for bundle: ${options.targetProcessName}`));
2093
2187
  } else {
2094
- resolve4(processes);
2188
+ resolve6(processes);
2095
2189
  }
2096
2190
  }, timeoutMs);
2097
2191
  proc.on("close", () => {
@@ -2101,7 +2195,7 @@ function listRunningProcesses(config, options = {}) {
2101
2195
  if (options.targetProcessName) {
2102
2196
  reject(new OniroError(`Could not find process for bundle: ${options.targetProcessName}`));
2103
2197
  } else {
2104
- resolve4(processes);
2198
+ resolve6(processes);
2105
2199
  }
2106
2200
  });
2107
2201
  });
@@ -2245,12 +2339,12 @@ async function reboot(opts) {
2245
2339
  }
2246
2340
 
2247
2341
  // src/hdc/screen.ts
2248
- import * as fs18 from "fs";
2249
- import * as os8 from "os";
2250
- import * as path18 from "path";
2342
+ import * as fs19 from "fs";
2343
+ import * as os9 from "os";
2344
+ import * as path19 from "path";
2251
2345
  import { randomBytes as randomBytes2 } from "crypto";
2252
2346
  function tmpName(ext) {
2253
- return path18.join(os8.tmpdir(), `oniro_screen_${randomBytes2(6).toString("hex")}.${ext}`);
2347
+ return path19.join(os9.tmpdir(), `oniro_screen_${randomBytes2(6).toString("hex")}.${ext}`);
2254
2348
  }
2255
2349
  async function takeScreenshot(opts) {
2256
2350
  const remote = "/data/local/tmp/oniro_screenshot.jpeg";
@@ -2260,11 +2354,11 @@ async function takeScreenshot(opts) {
2260
2354
  const local = tmpName("jpeg");
2261
2355
  try {
2262
2356
  await recvFile({ config: opts.config, remote, local, deviceSerial: opts.deviceSerial, timeoutMs });
2263
- const pixels = await fs18.promises.readFile(local);
2357
+ const pixels = await fs19.promises.readFile(local);
2264
2358
  const size = await getDisplaySize(opts.config, { deviceSerial: opts.deviceSerial }) ?? { width: 0, height: 0 };
2265
2359
  return { pixels, width: size.width, height: size.height };
2266
2360
  } finally {
2267
- fs18.promises.unlink(local).catch(() => {
2361
+ fs19.promises.unlink(local).catch(() => {
2268
2362
  });
2269
2363
  void shell({ config: opts.config, command: `rm -f ${remote}`, deviceSerial: opts.deviceSerial }).catch(() => {
2270
2364
  });
@@ -2283,9 +2377,9 @@ async function captureBurst(opts) {
2283
2377
  const local = tmpName(`burst_${i}.jpeg`);
2284
2378
  try {
2285
2379
  await recvFile({ config: opts.config, remote: `${remoteDir}/f${i}.jpeg`, local, deviceSerial: opts.deviceSerial });
2286
- frames.push(await fs18.promises.readFile(local));
2380
+ frames.push(await fs19.promises.readFile(local));
2287
2381
  } finally {
2288
- fs18.promises.unlink(local).catch(() => {
2382
+ fs19.promises.unlink(local).catch(() => {
2289
2383
  });
2290
2384
  }
2291
2385
  }
@@ -2411,9 +2505,9 @@ async function sendRawTouch(opts) {
2411
2505
  }
2412
2506
 
2413
2507
  // src/hdc/dump.ts
2414
- import * as fs19 from "fs";
2415
- import * as os9 from "os";
2416
- import * as path19 from "path";
2508
+ import * as fs20 from "fs";
2509
+ import * as os10 from "os";
2510
+ import * as path20 from "path";
2417
2511
  import { randomBytes as randomBytes3 } from "crypto";
2418
2512
  function parseBounds(s) {
2419
2513
  const m = /^\[(-?\d+),(-?\d+)\]\[(-?\d+),(-?\d+)\]$/.exec(s ?? "");
@@ -2498,10 +2592,10 @@ async function dumpLayout(opts) {
2498
2592
  if (!m) throw new OniroError(`Could not parse dumpLayout output:
2499
2593
  ${dump.stdout}`);
2500
2594
  const remote = m[1];
2501
- const local = path19.join(os9.tmpdir(), `oniro_layout_${randomBytes3(6).toString("hex")}.json`);
2595
+ const local = path20.join(os10.tmpdir(), `oniro_layout_${randomBytes3(6).toString("hex")}.json`);
2502
2596
  try {
2503
2597
  await recvFile({ config: opts.config, remote, local, deviceSerial: opts.deviceSerial, timeoutMs });
2504
- const parsed = JSON.parse(await fs19.promises.readFile(local, "utf8"));
2598
+ const parsed = JSON.parse(await fs20.promises.readFile(local, "utf8"));
2505
2599
  const display = await getDisplaySize(opts.config, { deviceSerial: opts.deviceSerial });
2506
2600
  if (opts.prune === false) {
2507
2601
  return { display, tree: parsed };
@@ -2510,7 +2604,7 @@ ${dump.stdout}`);
2510
2604
  const tree = Array.isArray(prunedRaw) ? { children: prunedRaw } : prunedRaw ?? { children: [] };
2511
2605
  return { display, tree };
2512
2606
  } finally {
2513
- fs19.promises.unlink(local).catch(() => {
2607
+ fs20.promises.unlink(local).catch(() => {
2514
2608
  });
2515
2609
  void shell({ config: opts.config, command: `rm -f ${remote}`, deviceSerial: opts.deviceSerial }).catch(() => {
2516
2610
  });
@@ -2521,7 +2615,7 @@ ${dump.stdout}`);
2521
2615
  function setHilogLevel(opts) {
2522
2616
  const logger = opts.logger ?? noopLogger;
2523
2617
  const hdc = getHdcPath(opts.config);
2524
- return new Promise((resolve4, reject) => {
2618
+ return new Promise((resolve6, reject) => {
2525
2619
  let child;
2526
2620
  try {
2527
2621
  child = spawnCompat(hdc, ["shell", "hilog", "-b", opts.level]);
@@ -2541,7 +2635,7 @@ function setHilogLevel(opts) {
2541
2635
  });
2542
2636
  child.once("close", (code) => {
2543
2637
  if (code === 0) {
2544
- resolve4();
2638
+ resolve6();
2545
2639
  } else {
2546
2640
  const msg = stderr.trim() || `hdc shell hilog -b ${opts.level} exited with code ${code}`;
2547
2641
  logger.error(`[hilog] ${msg}`);
@@ -2596,7 +2690,7 @@ async function resolveProcessId(config, bundle, deviceSerial) {
2596
2690
  return proc?.pid;
2597
2691
  }
2598
2692
  function waitForLog(opts) {
2599
- return new Promise((resolve4, reject) => {
2693
+ return new Promise((resolve6, reject) => {
2600
2694
  const deadline = Date.now() + opts.timeoutMs;
2601
2695
  let settled = false;
2602
2696
  let child = null;
@@ -2615,7 +2709,7 @@ function waitForLog(opts) {
2615
2709
  if (settled) return;
2616
2710
  settled = true;
2617
2711
  cleanup();
2618
- resolve4(entry);
2712
+ resolve6(entry);
2619
2713
  };
2620
2714
  const fail = (err) => {
2621
2715
  if (settled) return;
@@ -2638,10 +2732,9 @@ function waitForLog(opts) {
2638
2732
  child = stream;
2639
2733
  stream.stdout.on("data", (d) => {
2640
2734
  buf += d.toString();
2641
- let idx;
2642
- while ((idx = buf.indexOf("\n")) >= 0) {
2643
- const line = buf.slice(0, idx);
2644
- buf = buf.slice(idx + 1);
2735
+ const lines = buf.split(/\r?\n/);
2736
+ buf = lines.pop() ?? "";
2737
+ for (const line of lines) {
2645
2738
  const entry = parseHilogLine(line);
2646
2739
  if (entry && opts.pattern.test(entryText(entry))) {
2647
2740
  finish(entry);
@@ -2663,7 +2756,7 @@ function waitForLog(opts) {
2663
2756
  });
2664
2757
  }
2665
2758
  function watchLog(opts) {
2666
- return new Promise((resolve4, reject) => {
2759
+ return new Promise((resolve6, reject) => {
2667
2760
  const matches = [];
2668
2761
  let settled = false;
2669
2762
  let child = null;
@@ -2680,7 +2773,7 @@ function watchLog(opts) {
2680
2773
  if (settled) return;
2681
2774
  settled = true;
2682
2775
  cleanup();
2683
- resolve4(opts.dedup === false ? matches : dedupEntries(matches));
2776
+ resolve6(opts.dedup === false ? matches : dedupEntries(matches));
2684
2777
  };
2685
2778
  const onAbort = () => {
2686
2779
  if (settled) return;
@@ -2697,10 +2790,9 @@ function watchLog(opts) {
2697
2790
  child = stream;
2698
2791
  stream.stdout.on("data", (d) => {
2699
2792
  buf += d.toString();
2700
- let idx;
2701
- while ((idx = buf.indexOf("\n")) >= 0) {
2702
- const line = buf.slice(0, idx);
2703
- buf = buf.slice(idx + 1);
2793
+ const lines = buf.split(/\r?\n/);
2794
+ buf = lines.pop() ?? "";
2795
+ for (const line of lines) {
2704
2796
  const entry = parseHilogLine(line);
2705
2797
  if (entry && opts.pattern.test(entryText(entry))) matches.push(entry);
2706
2798
  }
@@ -2733,12 +2825,47 @@ async function dumpLog(opts) {
2733
2825
  timeoutMs: opts.timeoutMs ?? 3e4,
2734
2826
  logger: opts.logger
2735
2827
  });
2736
- return res.stdout.split("\n").map((line) => parseHilogLine(line)).filter((e) => e !== null);
2828
+ return res.stdout.split(/\r?\n/).map((line) => parseHilogLine(line)).filter((e) => e !== null);
2829
+ }
2830
+
2831
+ // src/build/runHvigorw.ts
2832
+ import * as fs22 from "fs";
2833
+ import * as path22 from "path";
2834
+
2835
+ // src/harmonyos/target.ts
2836
+ import * as fs21 from "fs";
2837
+ import * as path21 from "path";
2838
+ import JSON54 from "json5";
2839
+ function detectRuntimeOs(projectDir, productName) {
2840
+ let products = [];
2841
+ try {
2842
+ const parsed = JSON54.parse(fs21.readFileSync(path21.join(projectDir, "build-profile.json5"), "utf-8"));
2843
+ if (Array.isArray(parsed.app?.products)) products = parsed.app.products;
2844
+ } catch {
2845
+ return "OpenHarmony";
2846
+ }
2847
+ const product = products.find((p) => p.name === productName) ?? products[0];
2848
+ return product?.runtimeOS === "HarmonyOS" ? "HarmonyOS" : "OpenHarmony";
2849
+ }
2850
+ var HARMONYOS_SDK_VERSIONS = /* @__PURE__ */ new Map([
2851
+ [12, "5.0.0"],
2852
+ [13, "5.0.1"],
2853
+ [14, "5.0.2"],
2854
+ [15, "5.0.3"],
2855
+ [16, "5.0.4"],
2856
+ [17, "5.0.5"],
2857
+ [18, "5.1.0"],
2858
+ [19, "5.1.1"],
2859
+ [20, "6.0.0"],
2860
+ [21, "6.0.1"],
2861
+ [24, "6.1.1"]
2862
+ ]);
2863
+ function harmonyOsSdkLabelForApi(api) {
2864
+ const version = HARMONYOS_SDK_VERSIONS.get(api);
2865
+ return version ? `${version}(${api})` : void 0;
2737
2866
  }
2738
2867
 
2739
2868
  // src/build/runHvigorw.ts
2740
- import * as fs20 from "fs";
2741
- import * as path20 from "path";
2742
2869
  function buildHvigorwArgs(opts) {
2743
2870
  const args = [opts.task ?? "assembleHap", "--mode", "module"];
2744
2871
  args.push("-p", `product=${opts.product ?? "default"}`);
@@ -2753,28 +2880,36 @@ function runHvigorw(opts) {
2753
2880
  const { config } = opts;
2754
2881
  const projectDir = toLongPath(opts.projectDir);
2755
2882
  const logger = opts.logger ?? noopLogger;
2756
- if (!fs20.existsSync(path20.join(projectDir, "build-profile.json5"))) {
2883
+ if (!fs22.existsSync(path22.join(projectDir, "build-profile.json5"))) {
2757
2884
  throw new OniroError(`Not an OpenHarmony project: ${projectDir} (build-profile.json5 not found).`);
2758
2885
  }
2759
- if (!fs20.existsSync(getCmdToolsPath(config))) {
2760
- throw new CmdToolsNotInstalledError(getCmdToolsPath(config));
2886
+ let hvigorw;
2887
+ let sdkEnv;
2888
+ if (detectRuntimeOs(projectDir, opts.product) === "HarmonyOS") {
2889
+ const sdk = requireHarmonyOsSdk({ config, logger });
2890
+ sdkEnv = { DEVECO_SDK_HOME: sdk.sdkPath };
2891
+ const resolved = getHvigorwPath(config, projectDir);
2892
+ hvigorw = resolved.startsWith(projectDir) ? resolved : findHarmonyOsTool(sdk, "hvigorw") ?? resolved;
2893
+ logger.info(`[build] Building for HarmonyOS against the SDK at ${sdk.sdkPath}.`);
2894
+ } else {
2895
+ if (!fs22.existsSync(getCmdToolsPath(config))) {
2896
+ throw new CmdToolsNotInstalledError(getCmdToolsPath(config));
2897
+ }
2898
+ sdkEnv = { OHOS_BASE_SDK_HOME: getOhosBaseSdkHome(config) };
2899
+ hvigorw = getHvigorwPath(config, projectDir);
2761
2900
  }
2762
- const hvigorw = getHvigorwPath(config, projectDir);
2763
- if (!fs20.existsSync(hvigorw)) {
2901
+ if (!fs22.existsSync(hvigorw)) {
2764
2902
  throw new OniroError(`hvigorw wrapper not found at ${hvigorw}.`);
2765
2903
  }
2766
2904
  const args = buildHvigorwArgs(opts);
2767
2905
  if (process.platform !== "win32") {
2768
2906
  try {
2769
- fs20.chmodSync(hvigorw, 493);
2907
+ fs22.chmodSync(hvigorw, 493);
2770
2908
  } catch {
2771
2909
  }
2772
2910
  }
2773
- const env = {
2774
- ...process.env,
2775
- OHOS_BASE_SDK_HOME: getOhosBaseSdkHome(config)
2776
- };
2777
- return new Promise((resolve4, reject) => {
2911
+ const env = { ...process.env, ...sdkEnv };
2912
+ return new Promise((resolve6, reject) => {
2778
2913
  if (opts.abortSignal?.aborted) {
2779
2914
  reject(new CancelledError("hvigorw build cancelled."));
2780
2915
  return;
@@ -2818,15 +2953,15 @@ function runHvigorw(opts) {
2818
2953
  settled = true;
2819
2954
  cleanup();
2820
2955
  const exitCode = code ?? 1;
2821
- if (exitCode === 0) resolve4({ exitCode });
2956
+ if (exitCode === 0) resolve6({ exitCode });
2822
2957
  else reject(new OniroError(`hvigorw exited with code ${exitCode}`));
2823
2958
  });
2824
2959
  });
2825
2960
  }
2826
2961
 
2827
2962
  // src/build/discoverHaps.ts
2828
- import * as fs21 from "fs";
2829
- import * as path21 from "path";
2963
+ import * as fs23 from "fs";
2964
+ import * as path23 from "path";
2830
2965
  function moduleOfHap(hapFileName) {
2831
2966
  const dash = hapFileName.indexOf("-");
2832
2967
  return dash > 0 ? hapFileName.slice(0, dash) : hapFileName.replace(/\.hap$/i, "");
@@ -2837,9 +2972,9 @@ async function discoverHaps(opts) {
2837
2972
  const result = {};
2838
2973
  const walk = async (dir, depth) => {
2839
2974
  if (depth > maxDepth) return;
2840
- const entries = await fs21.promises.readdir(dir, { withFileTypes: true }).catch(() => []);
2975
+ const entries = await fs23.promises.readdir(dir, { withFileTypes: true }).catch(() => []);
2841
2976
  for (const e of entries) {
2842
- const full = path21.join(dir, e.name);
2977
+ const full = path23.join(dir, e.name);
2843
2978
  if (e.isDirectory()) {
2844
2979
  if (e.name === "node_modules" || e.name === "oh_modules" || e.name === ".git") continue;
2845
2980
  await walk(full, depth + 1);
@@ -2856,11 +2991,11 @@ async function discoverHaps(opts) {
2856
2991
  }
2857
2992
 
2858
2993
  // src/build/ohpm.ts
2859
- import * as fs22 from "fs";
2860
- import * as path22 from "path";
2994
+ import * as fs24 from "fs";
2995
+ import * as path24 from "path";
2861
2996
  function runOhpm(opts) {
2862
2997
  return runProcess({
2863
- command: getOhpmPath(opts.config),
2998
+ command: opts.ohpmPath ?? getOhpmPath(opts.config),
2864
2999
  args: [...opts.args],
2865
3000
  cwd: opts.projectDir,
2866
3001
  timeoutMs: opts.timeoutMs ?? 6e5,
@@ -2870,12 +3005,13 @@ function runOhpm(opts) {
2870
3005
  });
2871
3006
  }
2872
3007
  async function ensureOhModules(opts) {
2873
- if (fs22.existsSync(path22.join(opts.projectDir, "oh_modules"))) {
3008
+ if (fs24.existsSync(path24.join(opts.projectDir, "oh_modules"))) {
2874
3009
  return { installed: false };
2875
3010
  }
2876
3011
  const res = await runOhpm({
2877
3012
  config: opts.config,
2878
3013
  projectDir: opts.projectDir,
3014
+ ohpmPath: opts.ohpmPath,
2879
3015
  args: ["install", "--all"],
2880
3016
  abortSignal: opts.abortSignal,
2881
3017
  onOutput: opts.onOutput,
@@ -2923,10 +3059,13 @@ async function buildHap(opts) {
2923
3059
  const warnings = [];
2924
3060
  const start = Date.now();
2925
3061
  const projectDir = toLongPath(opts.projectDir);
3062
+ const isHarmonyOs = detectRuntimeOs(projectDir, opts.product) === "HarmonyOS";
2926
3063
  if (opts.autoInstallDeps !== false) {
3064
+ const harmonySdk = isHarmonyOs ? findHarmonyOsSdk({ config: opts.config, logger: opts.logger }) : null;
2927
3065
  const r = await ensureOhModules({
2928
3066
  config: opts.config,
2929
3067
  projectDir,
3068
+ ohpmPath: (harmonySdk && findHarmonyOsTool(harmonySdk, "ohpm")) ?? void 0,
2930
3069
  abortSignal: opts.abortSignal,
2931
3070
  onOutput: opts.onOutput,
2932
3071
  logger: opts.logger
@@ -2949,7 +3088,7 @@ async function buildHap(opts) {
2949
3088
  const anySigned = Object.values(discoveredHaps).some((m) => m.signed.length > 0);
2950
3089
  if (!anySigned) {
2951
3090
  warnings.push(
2952
- "No signed HAPs found \u2014 configure signingConfigs in build-profile.json5 (unsigned HAPs are rejected by most devices)."
3091
+ 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)."
2953
3092
  );
2954
3093
  }
2955
3094
  return { exitCode, durationMs: Date.now() - start, discoveredHaps, warnings };
@@ -2966,21 +3105,21 @@ function isValidBundleName(bundleName) {
2966
3105
  }
2967
3106
 
2968
3107
  // src/project/templates.ts
2969
- import * as fs23 from "fs";
2970
- import * as path23 from "path";
3108
+ import * as fs25 from "fs";
3109
+ import * as path25 from "path";
2971
3110
  function toHumanTemplateName(folderName) {
2972
3111
  return folderName.replace(/([a-z])([A-Z])/g, "$1 $2");
2973
3112
  }
2974
3113
  function listTemplates(templateRoot) {
2975
- if (!fs23.existsSync(templateRoot)) return [];
2976
- const entries = fs23.readdirSync(templateRoot, { withFileTypes: true });
3114
+ if (!fs25.existsSync(templateRoot)) return [];
3115
+ const entries = fs25.readdirSync(templateRoot, { withFileTypes: true });
2977
3116
  return entries.filter((e) => e.isDirectory()).map((e) => {
2978
- const dir = path23.join(templateRoot, e.name);
2979
- const metaPath = path23.join(dir, "template.json");
3117
+ const dir = path25.join(templateRoot, e.name);
3118
+ const metaPath = path25.join(dir, "template.json");
2980
3119
  let meta = {};
2981
- if (fs23.existsSync(metaPath)) {
3120
+ if (fs25.existsSync(metaPath)) {
2982
3121
  try {
2983
- meta = JSON.parse(fs23.readFileSync(metaPath, "utf8"));
3122
+ meta = JSON.parse(fs25.readFileSync(metaPath, "utf8"));
2984
3123
  } catch {
2985
3124
  }
2986
3125
  }
@@ -2995,57 +3134,57 @@ function listTemplates(templateRoot) {
2995
3134
  function validateTemplateLayout(templateDir, defaultModuleName) {
2996
3135
  const required = [
2997
3136
  "build-profile.json5",
2998
- path23.join("AppScope", "app.json5"),
2999
- path23.join(defaultModuleName, "src", "main", "module.json5"),
3000
- path23.join(defaultModuleName, "oh-package.json5"),
3137
+ path25.join("AppScope", "app.json5"),
3138
+ path25.join(defaultModuleName, "src", "main", "module.json5"),
3139
+ path25.join(defaultModuleName, "oh-package.json5"),
3001
3140
  "hvigorfile.ts"
3002
3141
  ];
3003
- return required.filter((rel) => !fs23.existsSync(path23.join(templateDir, rel)));
3142
+ return required.filter((rel) => !fs25.existsSync(path25.join(templateDir, rel)));
3004
3143
  }
3005
3144
 
3006
3145
  // src/project/createScaffold.ts
3007
- import * as fs25 from "fs";
3008
- import * as path24 from "path";
3146
+ import * as fs27 from "fs";
3147
+ import * as path26 from "path";
3009
3148
 
3010
3149
  // src/project/jsonHelpers.ts
3011
- import * as fs24 from "fs";
3012
- import JSON54 from "json5";
3150
+ import * as fs26 from "fs";
3151
+ import JSON55 from "json5";
3013
3152
  function readJson5File(filePath) {
3014
- return JSON54.parse(fs24.readFileSync(filePath, "utf8"));
3153
+ return JSON55.parse(fs26.readFileSync(filePath, "utf8"));
3015
3154
  }
3016
3155
  function writeJson5File(filePath, value) {
3017
- fs24.writeFileSync(filePath, JSON.stringify(value, null, 2) + "\n", "utf8");
3156
+ fs26.writeFileSync(filePath, JSON.stringify(value, null, 2) + "\n", "utf8");
3018
3157
  }
3019
3158
  function readJsonFile(filePath) {
3020
- return JSON.parse(fs24.readFileSync(filePath, "utf8"));
3159
+ return JSON.parse(fs26.readFileSync(filePath, "utf8"));
3021
3160
  }
3022
3161
  function writeJsonFile(filePath, value) {
3023
- fs24.writeFileSync(filePath, JSON.stringify(value, null, 2) + "\n", "utf8");
3162
+ fs26.writeFileSync(filePath, JSON.stringify(value, null, 2) + "\n", "utf8");
3024
3163
  }
3025
3164
 
3026
3165
  // src/project/createScaffold.ts
3027
3166
  var IGNORED_DIRS = /* @__PURE__ */ new Set(["oh_modules", "node_modules", "build", ".hvigor"]);
3028
3167
  async function pathExists(p) {
3029
3168
  try {
3030
- await fs25.promises.access(p);
3169
+ await fs27.promises.access(p);
3031
3170
  return true;
3032
3171
  } catch {
3033
3172
  return false;
3034
3173
  }
3035
3174
  }
3036
3175
  async function copyDirRecursive(srcDir, destDir) {
3037
- await fs25.promises.mkdir(destDir, { recursive: true });
3038
- const entries = await fs25.promises.readdir(srcDir, { withFileTypes: true });
3176
+ await fs27.promises.mkdir(destDir, { recursive: true });
3177
+ const entries = await fs27.promises.readdir(srcDir, { withFileTypes: true });
3039
3178
  for (const entry of entries) {
3040
- const src = path24.join(srcDir, entry.name);
3041
- const dest = path24.join(destDir, entry.name);
3179
+ const src = path26.join(srcDir, entry.name);
3180
+ const dest = path26.join(destDir, entry.name);
3042
3181
  if (entry.isDirectory()) {
3043
3182
  await copyDirRecursive(src, dest);
3044
3183
  } else if (entry.isSymbolicLink()) {
3045
3184
  continue;
3046
3185
  } else if (entry.isFile()) {
3047
3186
  if (entry.name === "template.json") continue;
3048
- await fs25.promises.copyFile(src, dest);
3187
+ await fs27.promises.copyFile(src, dest);
3049
3188
  }
3050
3189
  }
3051
3190
  }
@@ -3053,9 +3192,9 @@ async function normalizeJson5ToJson(projectDir, logger) {
3053
3192
  const stack = [projectDir];
3054
3193
  while (stack.length > 0) {
3055
3194
  const currentDir = stack.pop();
3056
- const entries = await fs25.promises.readdir(currentDir, { withFileTypes: true });
3195
+ const entries = await fs27.promises.readdir(currentDir, { withFileTypes: true });
3057
3196
  for (const entry of entries) {
3058
- const fullPath = path24.join(currentDir, entry.name);
3197
+ const fullPath = path26.join(currentDir, entry.name);
3059
3198
  if (entry.isDirectory()) {
3060
3199
  if (IGNORED_DIRS.has(entry.name)) continue;
3061
3200
  stack.push(fullPath);
@@ -3077,23 +3216,23 @@ function toJavaPropertiesPath(p) {
3077
3216
  function createOrUpdateLocalProperties(projectDir, sdkDir) {
3078
3217
  const content = `sdk.dir=${toJavaPropertiesPath(sdkDir)}
3079
3218
  `;
3080
- fs25.writeFileSync(path24.join(projectDir, "local.properties"), content, "utf8");
3219
+ fs27.writeFileSync(path26.join(projectDir, "local.properties"), content, "utf8");
3081
3220
  }
3082
3221
  function renameIfExists(fromPath, toPath) {
3083
- if (fs25.existsSync(fromPath) && fromPath !== toPath) {
3084
- fs25.renameSync(fromPath, toPath);
3222
+ if (fs27.existsSync(fromPath) && fromPath !== toPath) {
3223
+ fs27.renameSync(fromPath, toPath);
3085
3224
  }
3086
3225
  }
3087
3226
  function updateTemplateConfigs(projectDir, args, sdkBaseDir) {
3088
- const appJsonPath = path24.join(projectDir, "AppScope", "app.json5");
3089
- if (fs25.existsSync(appJsonPath)) {
3227
+ const appJsonPath = path26.join(projectDir, "AppScope", "app.json5");
3228
+ if (fs27.existsSync(appJsonPath)) {
3090
3229
  const appJson = readJson5File(appJsonPath);
3091
3230
  appJson.app = appJson.app ?? {};
3092
3231
  appJson.app.bundleName = args.bundleName;
3093
3232
  writeJson5File(appJsonPath, appJson);
3094
3233
  }
3095
- const stringsPath = path24.join(projectDir, "AppScope", "resources", "base", "element", "string.json");
3096
- if (fs25.existsSync(stringsPath)) {
3234
+ const stringsPath = path26.join(projectDir, "AppScope", "resources", "base", "element", "string.json");
3235
+ if (fs27.existsSync(stringsPath)) {
3097
3236
  const strings = readJsonFile(stringsPath);
3098
3237
  if (Array.isArray(strings.string)) {
3099
3238
  const appName = strings.string.find((s) => s.name === "app_name");
@@ -3101,13 +3240,13 @@ function updateTemplateConfigs(projectDir, args, sdkBaseDir) {
3101
3240
  }
3102
3241
  writeJsonFile(stringsPath, strings);
3103
3242
  }
3104
- const buildProfilePath = path24.join(projectDir, "build-profile.json5");
3105
- if (fs25.existsSync(buildProfilePath)) {
3243
+ const buildProfilePath = path26.join(projectDir, "build-profile.json5");
3244
+ if (fs27.existsSync(buildProfilePath)) {
3106
3245
  const buildProfile = readJson5File(buildProfilePath);
3107
3246
  buildProfile.app = buildProfile.app ?? {};
3108
3247
  if (Array.isArray(buildProfile.app.products) && buildProfile.app.products.length > 0) {
3109
- buildProfile.app.products[0].compileSdkVersion = args.sdkApi;
3110
- buildProfile.app.products[0].compatibleSdkVersion = args.sdkApi;
3248
+ buildProfile.app.products[0].compileSdkVersion = args.sdkVersion;
3249
+ buildProfile.app.products[0].compatibleSdkVersion = args.sdkVersion;
3111
3250
  }
3112
3251
  if (Array.isArray(buildProfile.modules) && buildProfile.modules.length > 0) {
3113
3252
  buildProfile.modules[0].name = args.moduleName;
@@ -3115,23 +3254,23 @@ function updateTemplateConfigs(projectDir, args, sdkBaseDir) {
3115
3254
  }
3116
3255
  writeJson5File(buildProfilePath, buildProfile);
3117
3256
  }
3118
- const moduleJsonPath = path24.join(projectDir, args.moduleName, "src", "main", "module.json5");
3119
- if (fs25.existsSync(moduleJsonPath)) {
3257
+ const moduleJsonPath = path26.join(projectDir, args.moduleName, "src", "main", "module.json5");
3258
+ if (fs27.existsSync(moduleJsonPath)) {
3120
3259
  const moduleJson = readJson5File(moduleJsonPath);
3121
3260
  moduleJson.module = moduleJson.module ?? {};
3122
3261
  moduleJson.module.name = args.moduleName;
3123
3262
  writeJson5File(moduleJsonPath, moduleJson);
3124
3263
  }
3125
- const moduleOhPackagePath = path24.join(projectDir, args.moduleName, "oh-package.json5");
3126
- if (fs25.existsSync(moduleOhPackagePath)) {
3264
+ const moduleOhPackagePath = path26.join(projectDir, args.moduleName, "oh-package.json5");
3265
+ if (fs27.existsSync(moduleOhPackagePath)) {
3127
3266
  const pkg = readJson5File(moduleOhPackagePath);
3128
3267
  pkg.name = args.moduleName;
3129
3268
  writeJson5File(moduleOhPackagePath, pkg);
3130
3269
  }
3131
- const vscodeDir = path24.join(projectDir, ".vscode");
3132
- fs25.mkdirSync(vscodeDir, { recursive: true });
3270
+ const vscodeDir = path26.join(projectDir, ".vscode");
3271
+ fs27.mkdirSync(vscodeDir, { recursive: true });
3133
3272
  const hapPath = `${args.moduleName}/build/default/outputs/default/${args.moduleName}-default-signed.hap`;
3134
- writeJsonFile(path24.join(vscodeDir, "settings.json"), {
3273
+ writeJsonFile(path26.join(vscodeDir, "settings.json"), {
3135
3274
  "oniro.hapPath": hapPath,
3136
3275
  "files.associations": { "*.json5": "jsonc" }
3137
3276
  });
@@ -3148,7 +3287,7 @@ async function createScaffold(opts) {
3148
3287
  if (!opts.location || !await pathExists(opts.location)) {
3149
3288
  throw new OniroError(`Location does not exist: ${opts.location}`);
3150
3289
  }
3151
- const templateDir = path24.join(opts.templateRoot, opts.templateId);
3290
+ const templateDir = path26.join(opts.templateRoot, opts.templateId);
3152
3291
  if (!await pathExists(templateDir)) {
3153
3292
  throw new OniroError(`Template not found: ${templateDir}`);
3154
3293
  }
@@ -3161,38 +3300,60 @@ async function createScaffold(opts) {
3161
3300
  throw new OniroError(`Template '${opts.templateId}' is missing required files:
3162
3301
  - ${missing.join("\n- ")}`);
3163
3302
  }
3164
- const projectDir = path24.join(opts.location, opts.projectName);
3303
+ let sdkVersion = opts.sdkApi;
3304
+ let sdkBaseDir = getOhosBaseSdkHome(opts.config);
3305
+ if (detectRuntimeOs(templateDir) === "HarmonyOS") {
3306
+ const harmonySdk = findHarmonyOsSdk({ config: opts.config, logger });
3307
+ const label = harmonySdk?.version?.endsWith(`(${opts.sdkApi})`) ? harmonySdk.version : harmonyOsSdkLabelForApi(opts.sdkApi);
3308
+ if (!label) {
3309
+ throw new OniroError(
3310
+ `No HarmonyOS release is known for API ${opts.sdkApi}. Known: ${[...HARMONYOS_SDK_VERSIONS.keys()].join(", ")}` + (harmonySdk?.version ? `, and the installed SDK's ${harmonySdk.version}.` : ".")
3311
+ );
3312
+ }
3313
+ sdkVersion = label;
3314
+ if (!harmonySdk) {
3315
+ logger.warn("[create] No HarmonyOS SDK found; set ONIRO_HARMONYOS_SDK_PATH before building.");
3316
+ } else {
3317
+ sdkBaseDir = harmonySdk.sdkPath;
3318
+ if (harmonySdk.version && harmonySdk.version !== label) {
3319
+ logger.warn(
3320
+ `[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.`
3321
+ );
3322
+ }
3323
+ }
3324
+ }
3325
+ const projectDir = path26.join(opts.location, opts.projectName);
3165
3326
  if (await pathExists(projectDir)) {
3166
3327
  if (!opts.overwrite) {
3167
3328
  throw new OniroError(`Destination already exists: ${projectDir}. Pass overwrite: true to replace it.`);
3168
3329
  }
3169
- await fs25.promises.rm(projectDir, { recursive: true, force: true });
3330
+ await fs27.promises.rm(projectDir, { recursive: true, force: true });
3170
3331
  }
3171
3332
  logger.info(`[create] Scaffolding ${opts.templateId} at ${projectDir}`);
3172
3333
  await copyDirRecursive(templateDir, projectDir);
3173
3334
  if (moduleName !== defaultModuleName) {
3174
- renameIfExists(path24.join(projectDir, defaultModuleName), path24.join(projectDir, moduleName));
3335
+ renameIfExists(path26.join(projectDir, defaultModuleName), path26.join(projectDir, moduleName));
3175
3336
  }
3176
3337
  updateTemplateConfigs(
3177
3338
  projectDir,
3178
3339
  {
3179
3340
  projectName: opts.projectName,
3180
3341
  bundleName: opts.bundleName,
3181
- sdkApi: opts.sdkApi,
3342
+ sdkVersion,
3182
3343
  moduleName
3183
3344
  },
3184
- getOhosBaseSdkHome(opts.config)
3345
+ sdkBaseDir
3185
3346
  );
3186
3347
  await normalizeJson5ToJson(projectDir, logger);
3187
3348
  return { projectDir };
3188
3349
  }
3189
3350
 
3190
3351
  // src/project/listModules.ts
3191
- import * as fs26 from "fs";
3192
- import * as path25 from "path";
3352
+ import * as fs28 from "fs";
3353
+ import * as path27 from "path";
3193
3354
  function listModules(opts) {
3194
- const buildProfile = path25.join(opts.projectDir, "build-profile.json5");
3195
- if (!fs26.existsSync(buildProfile)) {
3355
+ const buildProfile = path27.join(opts.projectDir, "build-profile.json5");
3356
+ if (!fs28.existsSync(buildProfile)) {
3196
3357
  throw new OniroError(`build-profile.json5 not found at ${buildProfile}`);
3197
3358
  }
3198
3359
  const parsed = readJson5File(buildProfile);
@@ -3204,12 +3365,12 @@ function listModules(opts) {
3204
3365
  }
3205
3366
 
3206
3367
  // src/project/abilities.ts
3207
- import * as fs27 from "fs";
3208
- import * as path26 from "path";
3368
+ import * as fs29 from "fs";
3369
+ import * as path28 from "path";
3209
3370
  function listAbilities(opts) {
3210
3371
  const moduleName = opts.moduleName ?? "entry";
3211
- const moduleJsonPath = path26.join(opts.projectDir, moduleName, "src", "main", "module.json5");
3212
- if (!fs27.existsSync(moduleJsonPath)) {
3372
+ const moduleJsonPath = path28.join(opts.projectDir, moduleName, "src", "main", "module.json5");
3373
+ if (!fs29.existsSync(moduleJsonPath)) {
3213
3374
  throw new OniroError(`Could not find module.json5 at ${moduleJsonPath}`);
3214
3375
  }
3215
3376
  const parsed = readJson5File(moduleJsonPath);
@@ -3217,12 +3378,12 @@ function listAbilities(opts) {
3217
3378
  }
3218
3379
 
3219
3380
  // src/project/prepareSigning.ts
3220
- import * as fs28 from "fs";
3221
- import * as path27 from "path";
3381
+ import * as fs30 from "fs";
3382
+ import * as path29 from "path";
3222
3383
  function hasSigningArtifacts(dir) {
3223
- if (!fs28.existsSync(path27.join(dir, "signatures"))) return false;
3224
- const buildProfile = path27.join(dir, "build-profile.json5");
3225
- if (!fs28.existsSync(buildProfile)) return false;
3384
+ if (!fs30.existsSync(path29.join(dir, "signatures"))) return false;
3385
+ const buildProfile = path29.join(dir, "build-profile.json5");
3386
+ if (!fs30.existsSync(buildProfile)) return false;
3226
3387
  try {
3227
3388
  const parsed = readJson5File(buildProfile);
3228
3389
  return Array.isArray(parsed.app?.signingConfigs) && parsed.app.signingConfigs.length > 0;
@@ -3312,8 +3473,8 @@ async function diffHapAssets(opts) {
3312
3473
  }
3313
3474
 
3314
3475
  // src/install/apply.ts
3315
- import * as fs29 from "fs";
3316
- import * as path28 from "path";
3476
+ import * as fs31 from "fs";
3477
+ import * as path30 from "path";
3317
3478
  function decideInstallMethod(s) {
3318
3479
  if (!s.signInfoInconsistent) return "replace";
3319
3480
  if (s.isSystemBundle && !s.allowUninstall) return "refuse";
@@ -3334,7 +3495,7 @@ async function pidOf(config, bundle, deviceSerial) {
3334
3495
  async function resolveHapPath(opts) {
3335
3496
  let hapPath = opts.hapPath;
3336
3497
  if (hapPath) {
3337
- if (!path28.isAbsolute(hapPath) && opts.projectDir) hapPath = path28.join(opts.projectDir, hapPath);
3498
+ if (!path30.isAbsolute(hapPath) && opts.projectDir) hapPath = path30.join(opts.projectDir, hapPath);
3338
3499
  } else {
3339
3500
  if (!opts.projectDir) throw new OniroError("applyChanges requires hapPath or projectDir.");
3340
3501
  const haps = await discoverHaps({ projectDir: opts.projectDir });
@@ -3353,7 +3514,7 @@ async function resolveHapPath(opts) {
3353
3514
  hapPath = withSigned[0][1].signed[0];
3354
3515
  }
3355
3516
  }
3356
- if (!fs29.existsSync(hapPath)) throw new OniroError(`HAP not found at ${hapPath}.`);
3517
+ if (!fs31.existsSync(hapPath)) throw new OniroError(`HAP not found at ${hapPath}.`);
3357
3518
  return hapPath;
3358
3519
  }
3359
3520
  async function applyChanges(opts) {
@@ -3363,7 +3524,7 @@ async function applyChanges(opts) {
3363
3524
  const allowUninstall = opts.allowUninstall ?? false;
3364
3525
  const hapPath = await resolveHapPath(opts);
3365
3526
  let assetsChanged = false;
3366
- if (opts.installedHapPath && fs29.existsSync(opts.installedHapPath)) {
3527
+ if (opts.installedHapPath && fs31.existsSync(opts.installedHapPath)) {
3367
3528
  try {
3368
3529
  const diff = await diffHapAssets({ installedHap: opts.installedHapPath, newHap: hapPath });
3369
3530
  assetsChanged = diff.addedAssetPaths.length > 0 || diff.removedAssetPaths.length > 0;
@@ -3404,20 +3565,1201 @@ async function applyChanges(opts) {
3404
3565
  return { method, preInstallPid, postInstallPid, replaced: installMethod === "replace", cacheCleared };
3405
3566
  }
3406
3567
 
3568
+ // src/harmonyos/auth/endpoints.ts
3569
+ var HUAWEI_APP_ID = "1009";
3570
+ var HUAWEI_API_VERSION = "1.0.0";
3571
+ var LOGIN_TIMEOUT_MS = 6e5;
3572
+ var HUAWEI_SITES = {
3573
+ CN: {
3574
+ code: "CN",
3575
+ siteId: "1",
3576
+ siteParam: "CN",
3577
+ loginUrl: "https://cn.devecostudio.huawei.com",
3578
+ agcBaseUrl: "https://connect-api.cloud.huawei.com",
3579
+ language: "zh_CN"
3580
+ },
3581
+ SG: {
3582
+ code: "SG",
3583
+ siteId: "5",
3584
+ siteParam: "SG",
3585
+ loginUrl: "https://sg.devecostudio.huawei.com",
3586
+ agcBaseUrl: "https://connect-api-dra.cloud.huawei.com",
3587
+ language: "zh_CN"
3588
+ },
3589
+ EU: {
3590
+ code: "EU",
3591
+ siteId: "7",
3592
+ siteParam: "DE",
3593
+ loginUrl: "https://de.devecostudio.huawei.com",
3594
+ agcBaseUrl: "https://connect-api-dre.cloud.huawei.com",
3595
+ language: "de_DE"
3596
+ },
3597
+ RU: {
3598
+ code: "RU",
3599
+ siteId: "8",
3600
+ siteParam: "RU",
3601
+ loginUrl: "https://ru.devecostudio.huawei.com",
3602
+ agcBaseUrl: "https://connect-api-drru.cloud.huawei.com",
3603
+ language: "ru_RU"
3604
+ }
3605
+ };
3606
+ var AGC_TEAMS_URL = "https://connect-api.cloud.huawei.com/api/ups/user-permission-service/v1/user-team-list";
3607
+ function siteForId(siteId) {
3608
+ return Object.values(HUAWEI_SITES).find((s) => s.siteId === siteId) ?? null;
3609
+ }
3610
+ var AUTH_PATHS = {
3611
+ /** The browser authenticates here, then redirects to the loopback server. */
3612
+ AUTH_APPLY: "console/DevEcoIDE/apply",
3613
+ /** Exchanges the browser's temp token for a JWT. */
3614
+ TEMP_TOKEN_CHECK: "authrouter/auth/api/temptoken/check",
3615
+ /** Validates a JWT and, with `refresh`, mints an AGC access token. */
3616
+ JWT_TOKEN_CHECK: "authrouter/auth/api/jwToken/check",
3617
+ LOGIN_SUCCESS: "console/DevEcoCLI/loginSuccess",
3618
+ LOGIN_FAILED: "console/DevEcoCLI/loginFailed",
3619
+ LOGOUT: "authrouter/auth/api/logout",
3620
+ /**
3621
+ * Whether an account without real-name verification accepted the developer
3622
+ * agreement, which is what lets it sign. From DevEco Studio (`space.agr.url`).
3623
+ */
3624
+ UNREALNAME_AGREEMENT: "authrouter/unrealname/agreement"
3625
+ };
3626
+
3627
+ // src/harmonyos/auth/tokenStore.ts
3628
+ import * as fs32 from "fs";
3629
+ import * as os11 from "os";
3630
+ import * as path31 from "path";
3631
+ import * as crypto3 from "crypto";
3632
+ var ALGORITHM = "aes-256-gcm";
3633
+ var KEY_LENGTH = 32;
3634
+ var IV_LENGTH = 12;
3635
+ var TOKEN_FILE = "token.enc";
3636
+ var KEY_FILE = "kek.bin";
3637
+ var DIR_MODE = 448;
3638
+ var FILE_MODE = 384;
3639
+ function encrypt2(key, plaintext) {
3640
+ const iv = crypto3.randomBytes(IV_LENGTH);
3641
+ const cipher = crypto3.createCipheriv(ALGORITHM, key, iv);
3642
+ const ciphertext = Buffer.concat([cipher.update(plaintext), cipher.final()]);
3643
+ return {
3644
+ version: 1,
3645
+ algorithm: ALGORITHM,
3646
+ ciphertext: ciphertext.toString("base64"),
3647
+ iv: iv.toString("base64"),
3648
+ authTag: cipher.getAuthTag().toString("base64")
3649
+ };
3650
+ }
3651
+ function decrypt2(key, blob) {
3652
+ const decipher = crypto3.createDecipheriv(ALGORITHM, key, Buffer.from(blob.iv, "base64"));
3653
+ decipher.setAuthTag(Buffer.from(blob.authTag, "base64"));
3654
+ return Buffer.concat([decipher.update(Buffer.from(blob.ciphertext, "base64")), decipher.final()]);
3655
+ }
3656
+ function isEncryptedBlob(value) {
3657
+ if (!value || typeof value !== "object") return false;
3658
+ const b = value;
3659
+ return b.algorithm === ALGORITHM && typeof b.ciphertext === "string" && typeof b.iv === "string" && typeof b.authTag === "string";
3660
+ }
3661
+ function permissionHint(dir) {
3662
+ return process.platform === "win32" ? `Permission denied writing ${dir}. Grant write permission to that directory.` : `Permission denied writing ${dir}. Try: chown -R "$(whoami)" ${dir}`;
3663
+ }
3664
+ function ensureDir(dir) {
3665
+ try {
3666
+ fs32.mkdirSync(dir, { recursive: true, mode: DIR_MODE });
3667
+ } catch (err) {
3668
+ if (err.code === "EACCES") {
3669
+ throw new OniroError(permissionHint(dir), err);
3670
+ }
3671
+ throw err;
3672
+ }
3673
+ }
3674
+ function createTokenStore(opts) {
3675
+ const logger = opts.logger ?? noopLogger;
3676
+ const authDir = opts.config.get("harmonyosAuthDir", defaultPaths.harmonyosAuthDir());
3677
+ const tokenPath = path31.join(authDir, TOKEN_FILE);
3678
+ const keyPath = path31.join(os11.homedir(), ".local", "share", "oniro-app", "keys", KEY_FILE);
3679
+ function loadOrCreateKey() {
3680
+ if (fs32.existsSync(keyPath)) {
3681
+ const existing = fs32.readFileSync(keyPath);
3682
+ if (existing.length === KEY_LENGTH) return existing;
3683
+ logger.warn("[harmonyos] The token key file was malformed; regenerating (you will need to log in again).");
3684
+ }
3685
+ ensureDir(path31.dirname(keyPath));
3686
+ const key = crypto3.randomBytes(KEY_LENGTH);
3687
+ fs32.writeFileSync(keyPath, key, { mode: FILE_MODE });
3688
+ return key;
3689
+ }
3690
+ return {
3691
+ tokenPath,
3692
+ save(session) {
3693
+ if (!session.jwtToken) throw new OniroError("Refusing to store an empty token.");
3694
+ const payload = Buffer.from(JSON.stringify(session), "utf8");
3695
+ const blob = encrypt2(loadOrCreateKey(), payload);
3696
+ ensureDir(authDir);
3697
+ fs32.writeFileSync(tokenPath, JSON.stringify(blob, null, 2), { mode: FILE_MODE });
3698
+ },
3699
+ load() {
3700
+ if (!fs32.existsSync(tokenPath)) return null;
3701
+ try {
3702
+ const blob = JSON.parse(fs32.readFileSync(tokenPath, "utf8"));
3703
+ if (!isEncryptedBlob(blob)) return null;
3704
+ const { jwtToken, siteId } = JSON.parse(decrypt2(loadOrCreateKey(), blob).toString("utf8"));
3705
+ return typeof jwtToken === "string" && jwtToken && typeof siteId === "string" ? { jwtToken, siteId } : null;
3706
+ } catch (err) {
3707
+ const code = err.code;
3708
+ if (code === "EACCES" || code === "EPERM") {
3709
+ logger.warn(`[harmonyos] Cannot read ${tokenPath}: ${permissionHint(authDir)}`);
3710
+ return null;
3711
+ }
3712
+ logger.debug(`[harmonyos] Discarding unreadable token at ${tokenPath}.`);
3713
+ try {
3714
+ fs32.rmSync(tokenPath, { force: true });
3715
+ } catch {
3716
+ }
3717
+ return null;
3718
+ }
3719
+ },
3720
+ clear() {
3721
+ try {
3722
+ fs32.rmSync(tokenPath, { force: true });
3723
+ } catch (err) {
3724
+ throw new OniroError(`Failed to remove ${tokenPath}.`, err);
3725
+ }
3726
+ }
3727
+ };
3728
+ }
3729
+
3730
+ // src/harmonyos/auth/session.ts
3731
+ import * as crypto5 from "crypto";
3732
+
3733
+ // src/harmonyos/http.ts
3734
+ import { EnvHttpProxyAgent, fetch } from "undici";
3735
+ var DEFAULT_HEADERS = {
3736
+ "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",
3737
+ "accept-language": "zh-CN",
3738
+ accept: "application/json, text/plain, */*"
3739
+ };
3740
+ var DEFAULT_TIMEOUT_MS = 2e4;
3741
+ var SENSITIVE_PARAMS = /* @__PURE__ */ new Set(["temptoken", "jwttoken", "code", "oauth2token", "accesstoken"]);
3742
+ function redactUrl(url) {
3743
+ let parsed;
3744
+ try {
3745
+ parsed = new URL(url);
3746
+ } catch {
3747
+ return url;
3748
+ }
3749
+ for (const key of [...parsed.searchParams.keys()]) {
3750
+ if (SENSITIVE_PARAMS.has(key.toLowerCase())) parsed.searchParams.set(key, "***");
3751
+ }
3752
+ return parsed.toString();
3753
+ }
3754
+ function previewBody(body, limit = 300) {
3755
+ const trimmed = body.trim();
3756
+ if (!trimmed) return "(empty body)";
3757
+ if (/^[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+$/.test(trimmed)) return "(a token-shaped value)";
3758
+ const collapsed = trimmed.replace(/\s+/g, " ");
3759
+ return collapsed.length > limit ? `${collapsed.slice(0, limit)}\u2026` : collapsed;
3760
+ }
3761
+ function describeResponse(response) {
3762
+ const status = `HTTP ${response.statusCode}${response.statusText ? ` ${response.statusText}` : ""}`;
3763
+ return `${status}: ${previewBody(response.data)}`;
3764
+ }
3765
+ function buildUrl(url, query) {
3766
+ if (!query) return url;
3767
+ const parsed = new URL(url);
3768
+ for (const [key, value] of Object.entries(query)) {
3769
+ if (value !== void 0 && value !== null) parsed.searchParams.set(key, String(value));
3770
+ }
3771
+ return parsed.toString();
3772
+ }
3773
+ function createHttpClient(opts = {}) {
3774
+ const logger = opts.logger ?? noopLogger;
3775
+ const dispatcher = new EnvHttpProxyAgent();
3776
+ async function send(method, url, options) {
3777
+ const target = buildUrl(url, options.query);
3778
+ const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
3779
+ const headers = { ...DEFAULT_HEADERS, ...options.headers };
3780
+ let body;
3781
+ if (options.body !== void 0) {
3782
+ body = JSON.stringify(options.body);
3783
+ headers["content-type"] = "application/json";
3784
+ }
3785
+ logger.debug(`[harmonyos] ${method} ${redactUrl(target)}`);
3786
+ try {
3787
+ return await fetch(target, {
3788
+ method,
3789
+ headers,
3790
+ body,
3791
+ signal: AbortSignal.timeout(timeoutMs),
3792
+ dispatcher
3793
+ });
3794
+ } catch (err) {
3795
+ const reason = err instanceof Error && err.name === "TimeoutError" ? `timed out after ${timeoutMs}ms` : err instanceof Error ? err.message : String(err);
3796
+ throw new OniroError(
3797
+ `${method} ${redactUrl(target)} failed: ${reason}. Check the network connection and any proxy configuration (HTTPS_PROXY / HTTP_PROXY).`,
3798
+ err
3799
+ );
3800
+ }
3801
+ }
3802
+ async function request(method, url, options = {}) {
3803
+ const response = await send(method, url, options);
3804
+ return { data: await response.text(), statusCode: response.status, statusText: response.statusText };
3805
+ }
3806
+ return {
3807
+ get: (url, o) => request("GET", url, o),
3808
+ post: (url, o) => request("POST", url, o),
3809
+ delete: (url, o) => request("DELETE", url, o),
3810
+ async getBinary(url, o = {}) {
3811
+ const response = await send("GET", url, o);
3812
+ if (!response.ok) {
3813
+ throw new OniroError(`Download of ${redactUrl(response.url || url)} failed (HTTP ${response.status}).`);
3814
+ }
3815
+ return Buffer.from(await response.arrayBuffer());
3816
+ }
3817
+ };
3818
+ }
3819
+
3820
+ // src/harmonyos/auth/callbackServer.ts
3821
+ import * as http2 from "http";
3822
+ import * as crypto4 from "crypto";
3823
+ var CALLBACK_PATH = "/callback";
3824
+ var MAX_BODY_BYTES = 65536;
3825
+ function codeMatches(code, secret) {
3826
+ const a = Buffer.from(code ?? "", "utf8");
3827
+ const b = Buffer.from(secret, "utf8");
3828
+ return a.length === b.length && crypto4.timingSafeEqual(a, b);
3829
+ }
3830
+ function isQuit(quit) {
3831
+ return quit === "true" || quit === "access_denied" || quit === "quit";
3832
+ }
3833
+ async function startCallbackServer(opts) {
3834
+ let resolveCallback;
3835
+ let rejectCallback;
3836
+ const callback = new Promise((resolve6, reject) => {
3837
+ resolveCallback = resolve6;
3838
+ rejectCallback = reject;
3839
+ });
3840
+ callback.catch(() => {
3841
+ });
3842
+ const server = http2.createServer((req, res) => {
3843
+ const host = (req.headers.host ?? "").toLowerCase();
3844
+ const port2 = server.address()?.port;
3845
+ if (host !== `127.0.0.1:${port2}` && host !== `localhost:${port2}`) {
3846
+ res.writeHead(400);
3847
+ res.end("Bad Host");
3848
+ return;
3849
+ }
3850
+ const url = new URL(req.url ?? "", `http://${host}`);
3851
+ if (url.pathname !== CALLBACK_PATH) {
3852
+ res.writeHead(404);
3853
+ res.end("Not Found");
3854
+ return;
3855
+ }
3856
+ const finish = (params) => {
3857
+ if (!codeMatches(params.get("code"), opts.clientSecret)) {
3858
+ res.writeHead(400);
3859
+ res.end("Bad Request");
3860
+ return;
3861
+ }
3862
+ if (isQuit(params.get("quit"))) {
3863
+ res.writeHead(302, { Location: `${opts.baseUrl}/${opts.failedPath}` });
3864
+ res.end();
3865
+ rejectCallback(new CancelledError("Login was cancelled in the browser."));
3866
+ return;
3867
+ }
3868
+ const tempToken = params.get("tempToken");
3869
+ const siteId = params.get("siteId");
3870
+ if (!tempToken || !siteId) {
3871
+ res.writeHead(400);
3872
+ res.end("Bad Request");
3873
+ return;
3874
+ }
3875
+ res.writeHead(302, { Location: `${opts.baseUrl}/${opts.successPath}` });
3876
+ res.end();
3877
+ resolveCallback({ tempToken, siteId });
3878
+ };
3879
+ if (req.method === "POST") {
3880
+ let body = "";
3881
+ let size = 0;
3882
+ req.on("data", (chunk) => {
3883
+ size += chunk.length;
3884
+ if (size > MAX_BODY_BYTES) {
3885
+ req.destroy(new Error("Request body too large"));
3886
+ return;
3887
+ }
3888
+ body += chunk.toString();
3889
+ });
3890
+ req.on("end", () => finish(body.trim() ? new URLSearchParams(body) : url.searchParams));
3891
+ } else {
3892
+ finish(url.searchParams);
3893
+ }
3894
+ });
3895
+ server.keepAliveTimeout = 1;
3896
+ await new Promise((resolve6, reject) => {
3897
+ server.once("error", (err) => reject(new OniroError("Failed to start the local login callback server.", err)));
3898
+ server.listen(0, "127.0.0.1", resolve6);
3899
+ });
3900
+ const port = server.address().port;
3901
+ return {
3902
+ port,
3903
+ waitForCallback(timeoutMs) {
3904
+ let timer;
3905
+ const timeout = new Promise((_, reject) => {
3906
+ timer = setTimeout(
3907
+ () => reject(new OniroError(`Timed out after ${Math.round(timeoutMs / 1e3)}s waiting for the browser login to complete.`)),
3908
+ timeoutMs
3909
+ );
3910
+ });
3911
+ return Promise.race([callback, timeout]).finally(() => clearTimeout(timer));
3912
+ },
3913
+ stop() {
3914
+ return new Promise((resolve6) => {
3915
+ server.closeAllConnections?.();
3916
+ const timer = setTimeout(resolve6, 100);
3917
+ server.close(() => {
3918
+ clearTimeout(timer);
3919
+ resolve6();
3920
+ });
3921
+ });
3922
+ }
3923
+ };
3924
+ }
3925
+
3926
+ // src/harmonyos/auth/browser.ts
3927
+ import { spawn as spawn3 } from "child_process";
3928
+ function isSafeUrl(url) {
3929
+ try {
3930
+ const parsed = new URL(url);
3931
+ return ["http:", "https:"].includes(parsed.protocol) && parsed.hostname !== "" && !url.includes('"');
3932
+ } catch {
3933
+ return false;
3934
+ }
3935
+ }
3936
+ function escapeForCmd(s) {
3937
+ return s.replace(/[&|<>()^%!]/g, (c) => `^${c}`);
3938
+ }
3939
+ function openBrowser(url) {
3940
+ if (!isSafeUrl(url)) {
3941
+ return Promise.reject(new OniroError(`Refusing to open unsafe URL: ${JSON.stringify(url)}`));
3942
+ }
3943
+ const [command, args] = process.platform === "win32" ? ["cmd", ["/c", "start", '""', escapeForCmd(url)]] : process.platform === "darwin" ? ["open", [url]] : ["xdg-open", [url]];
3944
+ const child = spawn3(command, args, {
3945
+ stdio: "ignore",
3946
+ shell: false,
3947
+ detached: true,
3948
+ windowsHide: true
3949
+ });
3950
+ child.unref();
3951
+ return new Promise((resolve6, reject) => {
3952
+ child.on("error", (err) => reject(new OniroError(`Failed to open a browser (${command}).`, err)));
3953
+ child.on(
3954
+ "exit",
3955
+ (code) => code === 0 ? resolve6() : reject(new OniroError(`Browser launcher '${command}' exited with code ${code}.`))
3956
+ );
3957
+ });
3958
+ }
3959
+
3960
+ // src/harmonyos/auth/session.ts
3961
+ var NOT_SIGNED_IN = "Not signed in. Run `oniro-app auth login` first.";
3962
+ function createHarmonyOsSession(opts) {
3963
+ const logger = opts.logger ?? noopLogger;
3964
+ const http3 = opts.http ?? createHttpClient({ logger });
3965
+ const store = opts.tokenStore ?? createTokenStore({ config: opts.config, logger });
3966
+ async function exchangeTempToken(site, tempToken) {
3967
+ const response = await http3.get(`${site.loginUrl}/${AUTH_PATHS.TEMP_TOKEN_CHECK}`, {
3968
+ query: {
3969
+ // The page sometimes appends extra `&`-joined parameters to the token.
3970
+ tempToken: tempToken.split("&")[0],
3971
+ site: site.siteParam,
3972
+ version: HUAWEI_API_VERSION,
3973
+ appid: HUAWEI_APP_ID
3974
+ }
3975
+ });
3976
+ const jwtToken = response.data.trim();
3977
+ if (response.statusCode === 200 && /^[^.]+\.[^.]+\.[^.]+$/.test(jwtToken)) return jwtToken;
3978
+ throw new OniroError(
3979
+ `${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.`
3980
+ );
3981
+ }
3982
+ async function userFromJwt(site, jwtToken, refresh) {
3983
+ const response = await http3.get(`${site.loginUrl}/${AUTH_PATHS.JWT_TOKEN_CHECK}`, {
3984
+ headers: { refresh: String(refresh), jwtToken }
3985
+ });
3986
+ let info;
3987
+ try {
3988
+ if (response.statusCode !== 200) throw new Error();
3989
+ info = JSON.parse(response.data);
3990
+ } catch {
3991
+ throw new OniroError(`Could not validate the session with ${site.loginUrl} (${describeResponse(response)}).`);
3992
+ }
3993
+ const user = info.userInfo;
3994
+ if (!info.status || !user?.accessToken) return null;
3995
+ return {
3996
+ userId: user.userId ?? "",
3997
+ userName: user.name ?? "",
3998
+ accessToken: user.accessToken,
3999
+ countryCode: user.nationalCode ?? "",
4000
+ siteCode: site.code,
4001
+ isRealName: String(user.realName) === "true"
4002
+ };
4003
+ }
4004
+ async function getUserInfo(o = {}) {
4005
+ const stored = store.load();
4006
+ const site = stored && siteForId(stored.siteId);
4007
+ const user = site ? await userFromJwt(site, stored.jwtToken, o.refresh ?? false) : null;
4008
+ if (stored && !user) {
4009
+ logger.debug("[harmonyos] The stored session is no longer valid; clearing it.");
4010
+ store.clear();
4011
+ }
4012
+ return user;
4013
+ }
4014
+ async function requireUser() {
4015
+ const user = await getUserInfo({ refresh: true });
4016
+ if (!user) throw new OniroError(NOT_SIGNED_IN);
4017
+ return user;
4018
+ }
4019
+ async function getDeveloperAgreement(user) {
4020
+ try {
4021
+ const response = await http3.get(`${HUAWEI_SITES[user.siteCode].loginUrl}/${AUTH_PATHS.UNREALNAME_AGREEMENT}`, {
4022
+ headers: { accessToken: user.accessToken }
4023
+ });
4024
+ const payload = JSON.parse(response.data);
4025
+ if (response.statusCode !== 200 || String(payload.success) !== "true" || !payload.body) return null;
4026
+ return { signed: payload.body.signedFlag === true, latest: payload.body.signLastestFlag === true };
4027
+ } catch {
4028
+ return null;
4029
+ }
4030
+ }
4031
+ return {
4032
+ tokenPath: store.tokenPath,
4033
+ getUserInfo,
4034
+ getDeveloperAgreement,
4035
+ async login(o = {}) {
4036
+ const start = HUAWEI_SITES.CN.loginUrl;
4037
+ const clientSecret = crypto5.randomUUID().replace(/-/g, "");
4038
+ const server = await startCallbackServer({
4039
+ clientSecret,
4040
+ baseUrl: start,
4041
+ successPath: AUTH_PATHS.LOGIN_SUCCESS,
4042
+ failedPath: AUTH_PATHS.LOGIN_FAILED
4043
+ });
4044
+ try {
4045
+ const loginUrl = `${start}/${AUTH_PATHS.AUTH_APPLY}?port=${server.port}&appid=${HUAWEI_APP_ID}&code=${clientSecret}`;
4046
+ opts.onLoginUrl?.(loginUrl);
4047
+ openBrowser(loginUrl).catch((err) => {
4048
+ logger.warn(`[harmonyos] Could not open a browser automatically: ${err.message}`);
4049
+ });
4050
+ const callback = await server.waitForCallback(o.timeoutMs ?? LOGIN_TIMEOUT_MS);
4051
+ const site = siteForId(callback.siteId);
4052
+ if (!site) {
4053
+ throw new OniroError(`The account belongs to a Huawei region oniro-app does not know (siteId ${callback.siteId}).`);
4054
+ }
4055
+ const jwtToken = await exchangeTempToken(site, callback.tempToken);
4056
+ const user = await userFromJwt(site, jwtToken, false);
4057
+ if (!user) throw new OniroError("Login did not yield a usable session. Try again.");
4058
+ store.save({ jwtToken, siteId: site.siteId });
4059
+ logger.info(`[harmonyos] Signed in as ${user.userName || user.userId} (${site.code}).`);
4060
+ return user;
4061
+ } finally {
4062
+ await server.stop();
4063
+ }
4064
+ },
4065
+ async logout() {
4066
+ const stored = store.load();
4067
+ if (!stored) return false;
4068
+ const site = siteForId(stored.siteId);
4069
+ try {
4070
+ if (site) {
4071
+ await http3.post(`${site.loginUrl}/${AUTH_PATHS.LOGOUT}`, {
4072
+ query: { jwtToken: stored.jwtToken },
4073
+ timeoutMs: 5e3
4074
+ });
4075
+ }
4076
+ } catch {
4077
+ } finally {
4078
+ store.clear();
4079
+ }
4080
+ return true;
4081
+ },
4082
+ async listTeams() {
4083
+ const user = await requireUser();
4084
+ const response = await http3.get(AGC_TEAMS_URL, {
4085
+ headers: {
4086
+ oauth2Token: user.accessToken,
4087
+ uid: user.userId,
4088
+ source: "cli",
4089
+ lang: HUAWEI_SITES[user.siteCode].language
4090
+ },
4091
+ timeoutMs: 15e3
4092
+ });
4093
+ if (response.statusCode === 401) {
4094
+ throw new OniroError("The session has expired. Run `oniro-app auth login` again.");
4095
+ }
4096
+ if (response.statusCode !== 200) {
4097
+ throw new OniroError(`Could not list AGC teams (${describeResponse(response)}).`);
4098
+ }
4099
+ return parseTeamList(response.data);
4100
+ },
4101
+ async resolveAgcAuth(o = {}) {
4102
+ const user = await requireUser();
4103
+ if (!user.isRealName) {
4104
+ const agreement = await getDeveloperAgreement(user);
4105
+ if (agreement && !(agreement.signed && agreement.latest)) {
4106
+ throw new OniroError(
4107
+ "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."
4108
+ );
4109
+ }
4110
+ }
4111
+ return {
4112
+ uid: user.userId,
4113
+ // Personal accounts sign under their own user id; `--team-id` picks another team.
4114
+ teamId: o.teamId ?? user.userId,
4115
+ accessToken: user.accessToken,
4116
+ baseUrl: HUAWEI_SITES[user.siteCode].agcBaseUrl
4117
+ };
4118
+ }
4119
+ };
4120
+ }
4121
+ function parseTeamList(body) {
4122
+ let payload;
4123
+ try {
4124
+ payload = JSON.parse(body);
4125
+ } catch {
4126
+ return [];
4127
+ }
4128
+ if (payload.ret && payload.ret.code !== 0) {
4129
+ throw new OniroError(
4130
+ `AppGallery Connect refused the team list (code ${payload.ret.code}${payload.ret.msg ? `: ${payload.ret.msg}` : ""}).`
4131
+ );
4132
+ }
4133
+ if (!Array.isArray(payload.teams)) return [];
4134
+ return payload.teams.map((t) => ({ id: String(t?.id ?? ""), name: String(t?.name ?? "") })).filter((t) => t.id);
4135
+ }
4136
+
4137
+ // src/harmonyos/signing/errors.ts
4138
+ var AgcError = class extends OniroError {
4139
+ constructor(message, statusCode, retCode) {
4140
+ super(message);
4141
+ this.statusCode = statusCode;
4142
+ this.retCode = retCode;
4143
+ this.name = "AgcError";
4144
+ }
4145
+ statusCode;
4146
+ retCode;
4147
+ };
4148
+ var SIGNING_ERRORS = {
4149
+ 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`).",
4150
+ UNAUTHORIZED: "AppGallery Connect rejected the access token. Run `oniro-app auth login` again.",
4151
+ NETWORK: "Could not reach AppGallery Connect. Check the network connection and any proxy configuration (HTTPS_PROXY / HTTP_PROXY).",
4152
+ USER_NOT_HARMONY: "This Huawei account is not enrolled for HarmonyOS development. Request HarmonyOS access for it in AppGallery Connect.",
4153
+ CERT_REQUEST: "AppGallery Connect did not issue the debug certificate.",
4154
+ CERT_LIMIT: "This team has reached its debug-certificate limit. Delete an unused certificate in AppGallery Connect and retry.",
4155
+ DEVICE_ADD: "AppGallery Connect did not register the device.",
4156
+ DEVICE_LIMIT: "This team has reached its device limit. Remove an unused device in AppGallery Connect and retry.",
4157
+ DEVICE_NAME_REPEAT: "A device with that name is already registered. Retry.",
4158
+ 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.",
4159
+ PROFILE_ADD: "AppGallery Connect did not issue the provisioning profile.",
4160
+ PROFILE_NAME_REPEAT: "A provisioning profile with that name already exists in AppGallery Connect. Retry.",
4161
+ PROVISION_LIMIT: "This team has reached its debug-profile limit. Delete unused profiles in AppGallery Connect and retry.",
4162
+ CERT_INVALID: "AppGallery Connect returned an unreadable certificate. Retry with --force.",
4163
+ CERT_EXPIRED: "The signing certificate is outside its validity window. Check the system clock, then retry with --force.",
4164
+ CERT_PROFILE_MISMATCH: "The provisioning profile was not issued for the downloaded certificate. Retry with --force.",
4165
+ KEYSTORE_CERT_MISMATCH: "The local keystore does not hold the key the downloaded certificate was issued for. Retry with --force.",
4166
+ 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)."
4167
+ };
4168
+ var RET_CODE_ERRORS = {
4169
+ "205389872": SIGNING_ERRORS.CERT_LIMIT,
4170
+ "205389904": SIGNING_ERRORS.USER_NOT_HARMONY,
4171
+ "205389859": SIGNING_ERRORS.DEVICE_LIMIT,
4172
+ "205389857": SIGNING_ERRORS.DEVICE_NAME_REPEAT,
4173
+ "205389938": SIGNING_ERRORS.PROVISION_LIMIT,
4174
+ "205389830": SIGNING_ERRORS.PROFILE_NAME_REPEAT
4175
+ };
4176
+ var PROXY_BLOCKED = "Openproxy_Blocked_URL_list";
4177
+ function readRet(body) {
4178
+ try {
4179
+ let ret = JSON.parse(body).ret;
4180
+ if (typeof ret === "string") ret = JSON.parse(ret);
4181
+ return ret && typeof ret === "object" ? ret : null;
4182
+ } catch {
4183
+ return null;
4184
+ }
4185
+ }
4186
+ var FALLBACK = {
4187
+ cert: SIGNING_ERRORS.CERT_REQUEST,
4188
+ device: SIGNING_ERRORS.DEVICE_ADD,
4189
+ profile: SIGNING_ERRORS.PROFILE_ADD
4190
+ };
4191
+ function mapAgcError(operation, statusCode, reasonPhrase, body) {
4192
+ if (statusCode === 403) {
4193
+ return new AgcError(reasonPhrase === PROXY_BLOCKED ? SIGNING_ERRORS.NETWORK : SIGNING_ERRORS.FORBIDDEN, 403);
4194
+ }
4195
+ if (statusCode === 401) return new AgcError(SIGNING_ERRORS.UNAUTHORIZED, 401);
4196
+ const ret = readRet(body);
4197
+ const retCode = ret?.code === void 0 ? void 0 : String(ret.code);
4198
+ const msg = typeof ret?.msg === "string" && ret.msg.trim() ? ret.msg : void 0;
4199
+ return new AgcError(retCode && RET_CODE_ERRORS[retCode] || msg || FALLBACK[operation], statusCode, retCode);
4200
+ }
4201
+
4202
+ // src/harmonyos/signing/profile.ts
4203
+ import * as fs33 from "fs";
4204
+ import { X509Certificate } from "crypto";
4205
+ import forge from "node-forge";
4206
+ function extractProfileJson(buffer) {
4207
+ const text = buffer.toString("latin1");
4208
+ for (let start = text.indexOf("{"); start !== -1; start = text.indexOf("{", start + 1)) {
4209
+ let depth = 0;
4210
+ let inString = false;
4211
+ let escaped = false;
4212
+ for (let i = start; i < text.length; i++) {
4213
+ const ch = text[i];
4214
+ if (escaped) escaped = false;
4215
+ else if (inString) {
4216
+ if (ch === "\\") escaped = true;
4217
+ else if (ch === '"') inString = false;
4218
+ } else if (ch === '"') inString = true;
4219
+ else if (ch === "{") depth++;
4220
+ else if (ch === "}" && --depth === 0) {
4221
+ const json = Buffer.from(text.slice(start, i + 1), "latin1").toString("utf8");
4222
+ try {
4223
+ if ("bundle-info" in JSON.parse(json)) return json;
4224
+ } catch {
4225
+ }
4226
+ break;
4227
+ }
4228
+ }
4229
+ }
4230
+ return null;
4231
+ }
4232
+ function readProvisionProfile(profilePath) {
4233
+ let raw;
4234
+ try {
4235
+ const json = extractProfileJson(fs33.readFileSync(profilePath));
4236
+ if (!json) return null;
4237
+ raw = JSON.parse(json);
4238
+ } catch {
4239
+ return null;
4240
+ }
4241
+ const str = (v) => typeof v === "string" ? v : void 0;
4242
+ const strings = (v) => Array.isArray(v) ? v.filter((x2) => typeof x2 === "string") : [];
4243
+ const notAfter = raw.validity?.["not-after"];
4244
+ const bundle = raw["bundle-info"] ?? {};
4245
+ return {
4246
+ bundleName: str(bundle["bundle-name"]),
4247
+ developerId: str(bundle["developer-id"]),
4248
+ // Epoch seconds.
4249
+ notAfter: typeof notAfter === "number" ? new Date(notAfter * 1e3) : void 0,
4250
+ deviceIds: strings(raw["debug-info"]?.["device-ids"]).map((d) => d.toUpperCase()),
4251
+ aclPermissions: strings(raw.acls?.["allowed-acls"]),
4252
+ developmentCertificate: str(bundle["development-certificate"])
4253
+ };
4254
+ }
4255
+ function parseCertificate(pem) {
4256
+ try {
4257
+ return new X509Certificate(pem);
4258
+ } catch {
4259
+ throw new AgcError(SIGNING_ERRORS.CERT_INVALID);
4260
+ }
4261
+ }
4262
+ function keystoreHoldsKey(p12Path, keyAlias, keyPwd, certificate) {
4263
+ try {
4264
+ const der = forge.util.createBuffer(fs33.readFileSync(p12Path).toString("binary"));
4265
+ const p12 = forge.pkcs12.pkcs12FromAsn1(forge.asn1.fromDer(der), keyPwd);
4266
+ const certBag = forge.pki.oids.certBag ?? "1.2.840.113549.1.12.10.1.3";
4267
+ const wanted = certificate.publicKey.export({ type: "spki", format: "der" });
4268
+ return (p12.getBags({ bagType: certBag })[certBag] ?? []).some((bag) => {
4269
+ if (bag.attributes?.friendlyName?.[0]?.toLowerCase() !== keyAlias.toLowerCase()) return false;
4270
+ const asn1 = bag.cert ? forge.pki.certificateToAsn1(bag.cert) : bag.asn1;
4271
+ if (!asn1) return false;
4272
+ const bagCert = new X509Certificate(Buffer.from(forge.asn1.toDer(asn1).getBytes(), "binary"));
4273
+ return bagCert.publicKey.export({ type: "spki", format: "der" }).equals(wanted);
4274
+ });
4275
+ } catch {
4276
+ return false;
4277
+ }
4278
+ }
4279
+ function verifySigningMaterial(opts) {
4280
+ const pems = fs33.readFileSync(opts.cerPath, "utf8").match(/-----BEGIN CERTIFICATE-----[\s\S]*?-----END CERTIFICATE-----/g);
4281
+ const chain = (pems ?? []).map(parseCertificate);
4282
+ const leaf = chain.find((c) => !c.ca) ?? chain.at(-1);
4283
+ if (!leaf) throw new AgcError(SIGNING_ERRORS.CERT_INVALID);
4284
+ const now = opts.now ?? /* @__PURE__ */ new Date();
4285
+ if (chain.some((c) => now < new Date(c.validFrom) || now > new Date(c.validTo))) {
4286
+ throw new AgcError(SIGNING_ERRORS.CERT_EXPIRED);
4287
+ }
4288
+ const issuedFor = readProvisionProfile(opts.profilePath)?.developmentCertificate;
4289
+ if (!issuedFor || parseCertificate(issuedFor).fingerprint256 !== leaf.fingerprint256) {
4290
+ throw new AgcError(SIGNING_ERRORS.CERT_PROFILE_MISMATCH);
4291
+ }
4292
+ if (!keystoreHoldsKey(opts.p12Path, opts.keyAlias, opts.keyPwd, leaf)) {
4293
+ throw new AgcError(SIGNING_ERRORS.KEYSTORE_CERT_MISMATCH);
4294
+ }
4295
+ }
4296
+
4297
+ // src/harmonyos/signing/autoSign.ts
4298
+ import * as crypto9 from "crypto";
4299
+ import * as fs38 from "fs";
4300
+ import * as path37 from "path";
4301
+ import JSON57 from "json5";
4302
+
4303
+ // src/harmonyos/signing/agc.ts
4304
+ import * as crypto6 from "crypto";
4305
+ import * as fs34 from "fs";
4306
+ import * as path32 from "path";
4307
+ var ENDPOINTS = {
4308
+ CERT_LIST: "/api/cps/harmony-cert-manage/v1/cert/list",
4309
+ CERT_ADD: "/api/cps/harmony-cert-manage/v1/cert/add",
4310
+ CERT_DELETE: "/api/cps/harmony-cert-manage/v1/cert/delete",
4311
+ /** Exchanges an object id for a short-lived signed download URL. */
4312
+ OBJECT_URL: "/api/amis/app-manage/v1/objects/url/reapply",
4313
+ DEVICE_LIST: "/api/cps/device-manage/v1/device/list",
4314
+ DEVICE_ADD: "/api/cps/device-manage/v1/device/add",
4315
+ /** Debug ("test") provisioning profiles — the only kind oniro-app issues. */
4316
+ PROVISION_ADD: "/api/cps/provision-manage/v1/ide/test/provision/add",
4317
+ PROVISION_DELETE: "/api/cps/provision-manage/v1/provision/delete"
4318
+ };
4319
+ var DEVICE_TYPE_CODES = { liteWearable: 1, wearable: 2, tv: 3, phone: 4 };
4320
+ function createAgcClient(http3, auth) {
4321
+ async function call(operation, method, endpoint, opts = {}) {
4322
+ const response = await http3[method](`${auth.baseUrl}${endpoint}`, {
4323
+ ...opts,
4324
+ headers: { uid: auth.uid, teamId: auth.teamId, oauth2Token: auth.accessToken }
4325
+ });
4326
+ if (response.statusCode !== 200 || readRet(response.data)?.code !== 0) {
4327
+ throw mapAgcError(operation, response.statusCode, response.statusText, response.data);
4328
+ }
4329
+ return JSON.parse(response.data);
4330
+ }
4331
+ return {
4332
+ async listCertificates() {
4333
+ return (await call("cert", "post", ENDPOINTS.CERT_LIST)).certList ?? [];
4334
+ },
4335
+ async addCertificate(certName, csr) {
4336
+ await call("cert", "post", ENDPOINTS.CERT_ADD, { body: { csr, certName, certType: "1" } });
4337
+ },
4338
+ async deleteCertificate(certId) {
4339
+ await call("cert", "delete", ENDPOINTS.CERT_DELETE, { body: { certIds: [certId] } });
4340
+ },
4341
+ /** Every device registered to the team, across pages. */
4342
+ async listDevices() {
4343
+ const pageSize = 100;
4344
+ const devices = [];
4345
+ for (let start = 1; ; start++) {
4346
+ const page = await call("device", "get", ENDPOINTS.DEVICE_LIST, {
4347
+ query: { encodeFlag: 0, start, pageSize }
4348
+ });
4349
+ devices.push(...page.list ?? []);
4350
+ if (!page.list?.length || devices.length >= (page.totalCount ?? 0)) return devices;
4351
+ }
4352
+ },
4353
+ async addDevice(udid, deviceKind) {
4354
+ const deviceName = `oniro_app_device_${crypto6.randomBytes(4).toString("hex")}`;
4355
+ await call("device", "post", ENDPOINTS.DEVICE_ADD, {
4356
+ body: { deviceName, udid, deviceType: String(DEVICE_TYPE_CODES[deviceKind] ?? DEVICE_TYPE_CODES.phone) }
4357
+ });
4358
+ },
4359
+ /** Issue a debug profile; returns its id and object handle. */
4360
+ async addDebugProfile(opts) {
4361
+ return call("profile", "post", ENDPOINTS.PROVISION_ADD, {
4362
+ body: {
4363
+ provisionName: opts.provisionName,
4364
+ packageName: opts.bundleName,
4365
+ certList: [opts.certId],
4366
+ deviceList: opts.deviceIds,
4367
+ ...opts.aclPermissions.length ? { aclPermissionList: opts.aclPermissions } : {}
4368
+ }
4369
+ });
4370
+ },
4371
+ async deleteProfile(id) {
4372
+ await call("profile", "delete", ENDPOINTS.PROVISION_DELETE, { query: { id } });
4373
+ },
4374
+ /**
4375
+ * Download a certificate or profile object to `dest`, checking the digest AGC
4376
+ * supplies with the URL. Nothing is written unless it matches.
4377
+ */
4378
+ async download(operation, objectId, dest) {
4379
+ const { urlsInfo } = await call(
4380
+ operation,
4381
+ "post",
4382
+ ENDPOINTS.OBJECT_URL,
4383
+ { body: { sourceUrls: objectId } }
4384
+ );
4385
+ const target = urlsInfo?.[0];
4386
+ if (!target) throw mapAgcError(operation, 200, "", "");
4387
+ const buffer = await http3.getBinary(target.newUrl, { timeoutMs: 6e4 });
4388
+ const actual = crypto6.createHash("sha256").update(buffer).digest("hex");
4389
+ if (target.sha256 && actual !== target.sha256.toLowerCase()) {
4390
+ throw new ChecksumMismatchError(target.sha256, actual);
4391
+ }
4392
+ fs34.mkdirSync(path32.dirname(dest), { recursive: true, mode: 448 });
4393
+ fs34.writeFileSync(dest, buffer, { mode: 384 });
4394
+ }
4395
+ };
4396
+ }
4397
+
4398
+ // src/harmonyos/signing/devices.ts
4399
+ function parseUdid(stdout) {
4400
+ return stdout.match(/\b[A-Fa-f0-9]{64}\b/)?.[0].toUpperCase() ?? "";
4401
+ }
4402
+ function parseDeviceKind(stdout) {
4403
+ return ["liteWearable", "wearable", "tv"].find((kind) => stdout.includes(kind)) ?? "phone";
4404
+ }
4405
+ async function collectLocalDevices(config, logger) {
4406
+ const devices = [];
4407
+ for (const target of await listDevices(config, { logger, timeoutMs: 15e3 })) {
4408
+ if (target.status !== "Connected") continue;
4409
+ const shell2 = async (...args) => (await hdcExec({ config, deviceSerial: target.serial, args: ["shell", ...args], timeoutMs: 15e3, logger })).stdout;
4410
+ try {
4411
+ const udid = parseUdid(await shell2("bm", "get", "-u"));
4412
+ if (!udid) {
4413
+ logger.warn(`[harmonyos] ${target.serial} did not report a UDID; skipping it.`);
4414
+ continue;
4415
+ }
4416
+ devices.push({ serial: target.serial, udid, kind: parseDeviceKind(await shell2("getprop", "hw_sc.build.os.deviceType")) });
4417
+ } catch (err) {
4418
+ logger.warn(`[harmonyos] Could not read ${target.serial}: ${err.message}`);
4419
+ }
4420
+ }
4421
+ return devices;
4422
+ }
4423
+ async function registerDevices(agc, connected, logger) {
4424
+ let devices = await agc.listDevices();
4425
+ const registered = new Set(devices.map((d) => d.udid.toUpperCase()));
4426
+ const unregistered = connected.filter((d) => !registered.has(d.udid));
4427
+ for (const device of unregistered) {
4428
+ logger.info(`[harmonyos] Registering ${device.serial} with AppGallery Connect...`);
4429
+ await agc.addDevice(device.udid, device.kind);
4430
+ }
4431
+ if (unregistered.length > 0) devices = await agc.listDevices();
4432
+ const deviceIds = devices.map((d) => d.id);
4433
+ if (deviceIds.length === 0) throw new AgcError(SIGNING_ERRORS.DEVICE_NONE);
4434
+ if (connected.length === 0) {
4435
+ logger.warn(
4436
+ `[harmonyos] No device is connected; the profile will name the ${deviceIds.length} device(s) already registered to this team.`
4437
+ );
4438
+ }
4439
+ return deviceIds;
4440
+ }
4441
+
4442
+ // src/harmonyos/signing/keystore.ts
4443
+ import * as crypto7 from "crypto";
4444
+ import * as fs36 from "fs";
4445
+ import * as path34 from "path";
4446
+
4447
+ // src/harmonyos/java.ts
4448
+ import * as fs35 from "fs";
4449
+ import * as path33 from "path";
4450
+ var JAVA_BIN = process.platform === "win32" ? "java.exe" : "java";
4451
+ function findJava(sdk, env = process.env) {
4452
+ const candidates = [
4453
+ env.JAVA_HOME && path33.join(env.JAVA_HOME, "bin", JAVA_BIN),
4454
+ sdk && path33.join(sdk.installRoot, "jbr", "bin", JAVA_BIN),
4455
+ // macOS: the JBR is itself a bundle inside Studio's Contents.
4456
+ sdk && path33.join(sdk.installRoot, "jbr", "Contents", "Home", "bin", JAVA_BIN),
4457
+ ...(env.PATH ?? "").split(path33.delimiter).map((dir) => dir && path33.join(dir, JAVA_BIN))
4458
+ ];
4459
+ const found = candidates.find((c) => !!c && fs35.existsSync(c));
4460
+ if (found) return found;
4461
+ throw new OniroError(
4462
+ "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."
4463
+ );
4464
+ }
4465
+
4466
+ // src/harmonyos/signing/keystore.ts
4467
+ var KEY_ALIAS = "debugKey";
4468
+ var SIGN_ALG = "SHA256withECDSA";
4469
+ function redactArgs(args) {
4470
+ return args.map((arg, i) => args[i - 1] === "-keyPwd" || args[i - 1] === "-keystorePwd" ? "******" : arg);
4471
+ }
4472
+ async function generateKeystoreAndCsr(opts) {
4473
+ const jar = path34.join(opts.sdk.sdkPath, "default", "openharmony", "toolchains", "lib", "hap-sign-tool.jar");
4474
+ if (!fs36.existsSync(jar)) {
4475
+ throw new OniroError(`hap-sign-tool.jar not found at ${jar}; the HarmonyOS SDK looks incomplete.`);
4476
+ }
4477
+ const java = findJava(opts.sdk);
4478
+ const keyPwd = crypto7.randomBytes(12).toString("base64url").replaceAll(/[-_]/g, "");
4479
+ fs36.mkdirSync(path34.dirname(opts.p12Path), { recursive: true, mode: 448 });
4480
+ const run = async (what, args) => {
4481
+ const argv = ["-jar", jar, ...args, "-keyAlias", KEY_ALIAS, "-keystoreFile", opts.p12Path, "-keystorePwd", keyPwd, "-keyPwd", keyPwd];
4482
+ opts.logger.debug(`[harmonyos] ${java} ${redactArgs(argv).join(" ")}`);
4483
+ const result = await runProcess({ command: java, args: argv, timeoutMs: 12e4, logger: opts.logger });
4484
+ if (result.code !== 0) {
4485
+ const detail = (result.stderr || result.stdout || "").trim();
4486
+ throw new OniroError(`hap-sign-tool failed to ${what} (exit ${result.code}).${detail ? `
4487
+ ${detail}` : ""}`);
4488
+ }
4489
+ };
4490
+ opts.logger.info("[harmonyos] Generating the local keystore and certificate signing request...");
4491
+ await run("generate the key pair", ["generate-keypair", "-keyAlg", "ECC", "-keySize", "NIST-P-256"]);
4492
+ await run("generate the CSR", ["generate-csr", "-subject", "CN=DebugKey", "-signAlg", SIGN_ALG, "-outFile", opts.csrPath]);
4493
+ return { keyPwd, csr: fs36.readFileSync(opts.csrPath, "utf8") };
4494
+ }
4495
+
4496
+ // src/harmonyos/signing/paths.ts
4497
+ import * as crypto8 from "crypto";
4498
+ import * as path35 from "path";
4499
+ function sanitizeNamePart(raw) {
4500
+ const safe = (raw?.trim() ?? "").replace(/[\\/:*?"<>|=.-]+/g, "_");
4501
+ return safe.slice(0, 64) || "default";
4502
+ }
4503
+ function resolveSigningMaterialPaths(config, productName, projectRoot) {
4504
+ const hash = crypto8.createHash("sha256").update(projectRoot, "utf8").digest("base64url").replaceAll(/[-_]/g, "");
4505
+ const base = path35.join(
4506
+ config.get("harmonyosSigningDir", defaultPaths.harmonyosSigningDir()),
4507
+ `${sanitizeNamePart(productName)}_${path35.basename(projectRoot)}_${hash}=`
4508
+ );
4509
+ return { p12Path: `${base}.p12`, csrPath: `${base}.csr`, cerPath: `${base}.cer`, profilePath: `${base}.p7b` };
4510
+ }
4511
+
4512
+ // src/harmonyos/signing/project.ts
4513
+ import * as fs37 from "fs";
4514
+ import * as path36 from "path";
4515
+ import JSON56 from "json5";
4516
+
4517
+ // src/harmonyos/signing/aclPermissionList.ts
4518
+ var ACL_PERMISSIONS = /* @__PURE__ */ new Set([
4519
+ "ohos.permission.ACCESS_DDK_HID",
4520
+ "ohos.permission.ACCESS_DDK_SCSI_PERIPHERAL",
4521
+ "ohos.permission.ACCESS_DDK_USB",
4522
+ "ohos.permission.ACCESS_DDK_USB_SERIAL",
4523
+ "ohos.permission.ACCESS_DISK_PHY_INFO",
4524
+ "ohos.permission.ACCESS_FIDO2_ONLINEAUTH",
4525
+ "ohos.permission.ACCESS_NET_TRACE_INFO",
4526
+ "ohos.permission.ACCESS_USER_FULL_DISK",
4527
+ "ohos.permission.ACCESS_VIRTUAL_SCREEN",
4528
+ "ohos.permission.CUSTOMIZE_SAVE_BUTTON",
4529
+ "ohos.permission.CUSTOM_SCREEN_RECORDING",
4530
+ "ohos.permission.DLP_GET_HIDE_STATUS",
4531
+ "ohos.permission.FILE_ACCESS_PERSIST",
4532
+ "ohos.permission.GET_ABILITY_INFO",
4533
+ "ohos.permission.GET_ETHERNET_LOCAL_MAC",
4534
+ "ohos.permission.GET_IP_MAC_INFO",
4535
+ "ohos.permission.GET_WIFI_LOCAL_MAC",
4536
+ "ohos.permission.GET_WIFI_PEERS_MAC",
4537
+ "ohos.permission.HOOK_KEY_EVENT",
4538
+ "ohos.permission.INPUT_MONITORING",
4539
+ "ohos.permission.INTERCEPT_INPUT_EVENT",
4540
+ "ohos.permission.KEEP_BACKGROUND_RUNNING_SYSTEM",
4541
+ "ohos.permission.LINKTURBO",
4542
+ "ohos.permission.MANAGE_APN_SETTING",
4543
+ "ohos.permission.MANAGE_PASTEBOARD_APP_SHARE_OPTION",
4544
+ "ohos.permission.MANAGE_SCREEN_TIME_GUARD",
4545
+ "ohos.permission.MANAGE_UDMF_APP_SHARE_OPTION",
4546
+ "ohos.permission.PERSISTENT_BLUETOOTH_PEERS_MAC",
4547
+ "ohos.permission.PERSONAL_MANAGE_RESTRICTIONS",
4548
+ "ohos.permission.PRELOAD_FILE",
4549
+ "ohos.permission.READ_AUDIO",
4550
+ "ohos.permission.READ_CONTACTS",
4551
+ "ohos.permission.READ_IMAGEVIDEO",
4552
+ "ohos.permission.READ_LOCAL_DEVICE_NAME",
4553
+ "ohos.permission.READ_PASTEBOARD",
4554
+ "ohos.permission.READ_WRITE_DESKTOP_DIRECTORY",
4555
+ "ohos.permission.READ_WRITE_DOCUMENTS_DIRECTORY",
4556
+ "ohos.permission.READ_WRITE_DOWNLOAD_DIRECTORY",
4557
+ "ohos.permission.READ_WRITE_USB_DEV",
4558
+ "ohos.permission.READ_WRITE_USER_FILE",
4559
+ "ohos.permission.SET_PAC_URL",
4560
+ "ohos.permission.SET_SYSTEMSHARE_APPLAUNCHTRUSTLIST",
4561
+ "ohos.permission.SHORT_TERM_WRITE_IMAGEVIDEO",
4562
+ "ohos.permission.START_PROVISIONING_MESSAGE",
4563
+ "ohos.permission.SUBSCRIBE_NOTIFICATION",
4564
+ "ohos.permission.SYSTEM_FLOAT_WINDOW",
4565
+ "ohos.permission.USE_FLOAT_BALL",
4566
+ "ohos.permission.USE_FRAUD_APP_PICKER",
4567
+ "ohos.permission.USE_FRAUD_CALL_LOG_PICKER",
4568
+ "ohos.permission.USE_FRAUD_MESSAGES_PICKER",
4569
+ "ohos.permission.WEB_NATIVE_MESSAGING",
4570
+ "ohos.permission.WRITE_AUDIO",
4571
+ "ohos.permission.WRITE_CONTACTS",
4572
+ "ohos.permission.WRITE_IMAGEVIDEO",
4573
+ "ohos.permission.atomicService.MANAGE_STORAGE",
4574
+ "ohos.permission.kernel.ALLOW_EXECUTABLE_FORT_MEMORY",
4575
+ "ohos.permission.kernel.ALLOW_WRITABLE_CODE_MEMORY",
4576
+ "ohos.permission.kernel.DISABLE_CODE_MEMORY_PROTECTION",
4577
+ "ohos.permission.kernel.DISABLE_GOTPLT_RO_PROTECTION"
4578
+ ]);
4579
+
4580
+ // src/harmonyos/signing/project.ts
4581
+ function readRequestedPermissions(moduleConfigPath) {
4582
+ if (!fs37.existsSync(moduleConfigPath)) return [];
4583
+ try {
4584
+ const parsed = JSON56.parse(fs37.readFileSync(moduleConfigPath, "utf-8"));
4585
+ return (parsed.module?.requestPermissions ?? []).map((p) => p?.name).filter((n) => typeof n === "string" && n.length > 0);
4586
+ } catch {
4587
+ return [];
4588
+ }
4589
+ }
4590
+ function collectAclPermissions(projectDir, logger = noopLogger) {
4591
+ const requested = /* @__PURE__ */ new Set();
4592
+ for (const module of listModules({ projectDir })) {
4593
+ const moduleRoot = path36.resolve(projectDir, module.srcPath);
4594
+ for (const sourceSet of ["main", "ohosTest"]) {
4595
+ for (const name of readRequestedPermissions(
4596
+ path36.join(moduleRoot, "src", sourceSet, "module.json5")
4597
+ )) {
4598
+ requested.add(name);
4599
+ }
4600
+ }
4601
+ }
4602
+ const acls = [...requested].filter((p) => ACL_PERMISSIONS.has(p)).sort();
4603
+ if (acls.length > 0) {
4604
+ logger.info(`[harmonyos] Project requests ${acls.length} ACL permission(s): ${acls.join(", ")}`);
4605
+ }
4606
+ return acls;
4607
+ }
4608
+
4609
+ // src/harmonyos/signing/autoSign.ts
4610
+ function shouldRegenerate(input) {
4611
+ if (input.force) return "--force was passed";
4612
+ const missing = Object.values(input.paths).find((file) => !fs38.existsSync(file));
4613
+ if (missing) return `${missing} is missing`;
4614
+ const config = input.buildProfile.app?.signingConfigs?.find((c) => c.name === input.productName);
4615
+ if (config?.type !== "HarmonyOS" || config.material?.profile !== input.paths.profilePath) {
4616
+ return "build-profile.json5 has no signingConfig for this material";
4617
+ }
4618
+ const profile = readProvisionProfile(input.paths.profilePath);
4619
+ if (!profile) return "the provisioning profile could not be read";
4620
+ if (profile.bundleName !== input.bundleName) return `the profile is for bundle '${profile.bundleName}'`;
4621
+ if (profile.developerId && profile.developerId !== input.teamId) {
4622
+ return `the profile was issued to team '${profile.developerId}'`;
4623
+ }
4624
+ if (profile.notAfter && profile.notAfter <= (input.now ?? /* @__PURE__ */ new Date())) return "the profile has expired";
4625
+ const unnamed = input.connectedUdids.filter((udid) => !profile.deviceIds.includes(udid.toUpperCase()));
4626
+ if (unnamed.length > 0) return `${unnamed.length} connected device(s) are not named in the profile`;
4627
+ const acls = input.aclPermissions.filter((p) => !profile.aclPermissions.includes(p));
4628
+ if (acls.length > 0) return `the profile lacks ACL permission(s) ${acls.join(", ")}`;
4629
+ return null;
4630
+ }
4631
+ function writeSigningConfig(opts) {
4632
+ const app = opts.buildProfile.app ??= {};
4633
+ const entry = {
4634
+ name: opts.productName,
4635
+ // `type` is what makes hvigor treat this as HarmonyOS signing material.
4636
+ type: "HarmonyOS",
4637
+ material: {
4638
+ certpath: opts.paths.cerPath,
4639
+ keyAlias: KEY_ALIAS,
4640
+ keyPassword: opts.encryptedPassword,
4641
+ profile: opts.paths.profilePath,
4642
+ signAlg: SIGN_ALG,
4643
+ storeFile: opts.paths.p12Path,
4644
+ storePassword: opts.encryptedPassword
4645
+ }
4646
+ };
4647
+ app.signingConfigs = [...(app.signingConfigs ?? []).filter((c) => c.name !== opts.productName), entry];
4648
+ for (const product of app.products ?? []) {
4649
+ if (product.name === opts.productName) product.signingConfig = opts.productName;
4650
+ }
4651
+ fs38.writeFileSync(opts.buildProfilePath, JSON.stringify(opts.buildProfile, null, 2));
4652
+ }
4653
+ async function harmonyOsAutoSign(opts) {
4654
+ const logger = opts.logger ?? noopLogger;
4655
+ const productName = opts.productName ?? "default";
4656
+ const projectDir = path37.resolve(opts.projectDir);
4657
+ const buildProfilePath = path37.join(projectDir, "build-profile.json5");
4658
+ let buildProfile;
4659
+ try {
4660
+ buildProfile = JSON57.parse(fs38.readFileSync(buildProfilePath, "utf-8"));
4661
+ } catch (err) {
4662
+ throw new OniroError(`Could not read ${buildProfilePath}: ${err.message}`, err);
4663
+ }
4664
+ const product = buildProfile.app?.products?.find((p) => p.name === productName);
4665
+ if (!product) throw new OniroError(`${buildProfilePath} declares no product named '${productName}'.`);
4666
+ const bundleName = product.bundleName || getBundleName(projectDir);
4667
+ if (!isValidBundleName(bundleName) || bundleName.split(".").length < 3 || bundleName.length < 7 || bundleName.length > 128) {
4668
+ throw new AgcError(SIGNING_ERRORS.BUNDLE_NAME_INVALID);
4669
+ }
4670
+ const sdk = requireHarmonyOsSdk({ config: opts.config, logger });
4671
+ const auth = await opts.session.resolveAgcAuth({ teamId: opts.teamId });
4672
+ const aclPermissions = collectAclPermissions(projectDir, logger);
4673
+ const paths = resolveSigningMaterialPaths(opts.config, productName, projectDir);
4674
+ logger.info(`[harmonyos] Signing ${bundleName} (product '${productName}', team ${auth.teamId}).`);
4675
+ const connected = await collectLocalDevices(opts.config, logger);
4676
+ const result = (regenerated) => ({
4677
+ bundleName,
4678
+ teamId: auth.teamId,
4679
+ paths,
4680
+ regenerated,
4681
+ deviceIds: readProvisionProfile(paths.profilePath)?.deviceIds ?? []
4682
+ });
4683
+ const reason = shouldRegenerate({
4684
+ paths,
4685
+ buildProfile,
4686
+ productName,
4687
+ bundleName,
4688
+ teamId: auth.teamId,
4689
+ connectedUdids: connected.map((d) => d.udid),
4690
+ aclPermissions,
4691
+ force: opts.force
4692
+ });
4693
+ if (!reason) {
4694
+ logger.info("[harmonyos] Reusing the existing signing material, which is still valid.");
4695
+ return result(false);
4696
+ }
4697
+ logger.info(`[harmonyos] Regenerating signing material: ${reason}.`);
4698
+ const agc = createAgcClient(opts.http ?? createHttpClient({ logger }), auth);
4699
+ const deviceIds = await registerDevices(agc, connected, logger);
4700
+ for (const file of Object.values(paths)) fs38.rmSync(file, { force: true });
4701
+ const certName = `oniro_debug_${auth.teamId.replace(/[\\/.:]/g, "")}.cer`;
4702
+ const stale = (await agc.listCertificates()).find((c) => c.certName === certName);
4703
+ if (stale) {
4704
+ logger.info(`[harmonyos] Replacing the previous '${certName}' certificate in AppGallery Connect...`);
4705
+ await agc.deleteCertificate(stale.id);
4706
+ }
4707
+ const { keyPwd, csr } = await generateKeystoreAndCsr({ sdk, p12Path: paths.p12Path, csrPath: paths.csrPath, logger });
4708
+ logger.info("[harmonyos] Requesting a debug certificate from AppGallery Connect...");
4709
+ await agc.addCertificate(certName, csr);
4710
+ const cert = (await agc.listCertificates()).find((c) => c.certName === certName);
4711
+ if (!cert) throw new AgcError(SIGNING_ERRORS.CERT_REQUEST);
4712
+ await agc.download("cert", cert.certObjectId, paths.cerPath);
4713
+ logger.info("[harmonyos] Issuing the debug provisioning profile...");
4714
+ const profile = await agc.addDebugProfile({
4715
+ provisionName: `oniro_${crypto9.randomBytes(6).toString("hex")}`,
4716
+ bundleName,
4717
+ certId: cert.id,
4718
+ deviceIds,
4719
+ aclPermissions
4720
+ });
4721
+ try {
4722
+ await agc.download("profile", profile.provisionFileUrl, paths.profilePath);
4723
+ } finally {
4724
+ await agc.deleteProfile(profile.id).catch((err) => {
4725
+ logger.debug(`[harmonyos] Could not delete profile ${profile.id} from AppGallery Connect: ${err.message}`);
4726
+ });
4727
+ }
4728
+ try {
4729
+ verifySigningMaterial({ ...paths, keyAlias: KEY_ALIAS, keyPwd });
4730
+ } catch (err) {
4731
+ fs38.rmSync(paths.profilePath, { force: true });
4732
+ throw err;
4733
+ }
4734
+ const materialDir = path37.join(path37.dirname(paths.p12Path), "material");
4735
+ try {
4736
+ getKey(materialDir);
4737
+ } catch {
4738
+ createMaterial(materialDir);
4739
+ }
4740
+ writeSigningConfig({ buildProfilePath, buildProfile, productName, paths, encryptedPassword: encryptPwd(keyPwd, materialDir) });
4741
+ logger.info(`[harmonyos] Wrote signingConfigs['${productName}'] to ${buildProfilePath}.`);
4742
+ return result(true);
4743
+ }
4744
+
3407
4745
  // src/index.ts
3408
- var VERSION = "0.9.0";
4746
+ var VERSION = "0.11.0";
3409
4747
  export {
3410
4748
  ALL_SDKS,
3411
4749
  APL_VALUES,
3412
4750
  APP_FEATURE_VALUES,
4751
+ AgcError,
3413
4752
  CancelledError,
3414
4753
  ChecksumMismatchError,
3415
4754
  CmdToolsNotInstalledError,
3416
4755
  CommandFailedError,
4756
+ HARMONYOS_SDK_VERSIONS,
4757
+ HUAWEI_SITES,
3417
4758
  INSTALL_TMP_DIRNAME,
3418
4759
  InsufficientSpaceError,
3419
4760
  OHOS_URL_BASE,
3420
4761
  OniroError,
4762
+ SIGNING_ERRORS,
3421
4763
  SdkNotInstalledError,
3422
4764
  TMP_DIR_HINT,
3423
4765
  UnsupportedPlatformError,
@@ -3433,10 +4775,12 @@ export {
3433
4775
  buildRawTouchCommand,
3434
4776
  captureBurst,
3435
4777
  consoleLogger,
4778
+ createHarmonyOsSession,
3436
4779
  createInstallTempDir,
3437
4780
  createMaterial,
3438
4781
  createScaffold,
3439
4782
  createTempWorkDir,
4783
+ createTokenStore,
3440
4784
  decideInstallMethod,
3441
4785
  decideReboot,
3442
4786
  decryptPwd,
@@ -3445,6 +4789,7 @@ export {
3445
4789
  defaultPaths,
3446
4790
  detectAaStartFailure,
3447
4791
  detectProjectSdkVersion,
4792
+ detectRuntimeOs,
3448
4793
  detectSigningConfigNames,
3449
4794
  diffEntryNames,
3450
4795
  diffHapAssets,
@@ -3467,6 +4812,7 @@ export {
3467
4812
  extractZipWithProgress,
3468
4813
  findAppProcessId,
3469
4814
  findCmdToolsSourceDir,
4815
+ findHarmonyOsSdk,
3470
4816
  findRunningProcess,
3471
4817
  forceStop,
3472
4818
  formatBytes,
@@ -3491,6 +4837,8 @@ export {
3491
4837
  getSdkFilename,
3492
4838
  getSdkRootDir,
3493
4839
  getSupportedSdksForUi,
4840
+ harmonyOsAutoSign,
4841
+ harmonyOsSdkLabelForApi,
3494
4842
  hdcExec,
3495
4843
  installApp,
3496
4844
  installCmdTools,
@@ -3525,12 +4873,14 @@ export {
3525
4873
  pruneLayout,
3526
4874
  readJson5File,
3527
4875
  readJsonFile,
4876
+ readProvisionProfile,
3528
4877
  reboot,
3529
4878
  recvFile,
3530
4879
  removeCmdTools,
3531
4880
  removeEmulator,
3532
4881
  removeSdk,
3533
4882
  removeTempWorkDir,
4883
+ requireHarmonyOsSdk,
3534
4884
  resolveTmpRoot,
3535
4885
  runCodelinter,
3536
4886
  runHvigorw,