@oniroproject/core 0.7.0 → 0.8.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
@@ -105,9 +105,9 @@ var CmdToolsNotInstalledError = class extends OniroError {
105
105
  expectedPath;
106
106
  };
107
107
  var UnsupportedPlatformError = class extends OniroError {
108
- constructor(platform6) {
109
- super(`Unsupported platform: ${platform6}.`);
110
- this.platform = platform6;
108
+ constructor(platform7) {
109
+ super(`Unsupported platform: ${platform7}.`);
110
+ this.platform = platform7;
111
111
  this.name = "UnsupportedPlatformError";
112
112
  }
113
113
  platform;
@@ -122,6 +122,14 @@ var ChecksumMismatchError = class extends OniroError {
122
122
  expected;
123
123
  actual;
124
124
  };
125
+ var InsufficientSpaceError = class extends OniroError {
126
+ constructor(message, dir, cause) {
127
+ super(message, cause);
128
+ this.dir = dir;
129
+ this.name = "InsufficientSpaceError";
130
+ }
131
+ dir;
132
+ };
125
133
  var CancelledError = class extends OniroError {
126
134
  constructor(message = "Operation cancelled.") {
127
135
  super(message);
@@ -168,29 +176,29 @@ var OHOS_URL_BASE = "https://repo.huaweicloud.com/openharmony/os";
168
176
  // src/sdk/platform.ts
169
177
  import * as os2 from "os";
170
178
  function getOsFolder() {
171
- const platform6 = os2.platform();
172
- if (platform6 === "linux") return "linux";
173
- if (platform6 === "darwin") return "darwin";
174
- if (platform6 === "win32") return "windows";
175
- throw new UnsupportedPlatformError(platform6);
179
+ const platform7 = os2.platform();
180
+ if (platform7 === "linux") return "linux";
181
+ if (platform7 === "darwin") return "darwin";
182
+ if (platform7 === "win32") return "windows";
183
+ throw new UnsupportedPlatformError(platform7);
176
184
  }
177
185
  function getSdkFilename(version) {
178
- const platform6 = os2.platform();
186
+ const platform7 = os2.platform();
179
187
  const v = version ?? ALL_SDKS[ALL_SDKS.length - 1].version;
180
188
  const strips = ALL_SDKS.find((r) => r.version === v)?.tarballStrip ?? { linuxWindows: 1, darwin: 3 };
181
- if (platform6 === "linux") {
189
+ if (platform7 === "linux") {
182
190
  return { filename: "ohos-sdk-windows_linux-public.tar.gz", osFolder: "linux", strip: strips.linuxWindows };
183
191
  }
184
- if (platform6 === "darwin") {
192
+ if (platform7 === "darwin") {
185
193
  if (os2.arch() === "arm64") {
186
194
  return { filename: "L2-SDK-MAC-M1-PUBLIC.tar.gz", osFolder: "darwin", strip: strips.darwin };
187
195
  }
188
196
  return { filename: "ohos-sdk-mac-public.tar.gz", osFolder: "darwin", strip: strips.darwin };
189
197
  }
190
- if (platform6 === "win32") {
198
+ if (platform7 === "win32") {
191
199
  return { filename: "ohos-sdk-windows_linux-public.tar.gz", osFolder: "windows", strip: strips.linuxWindows };
192
200
  }
193
- throw new UnsupportedPlatformError(platform6);
201
+ throw new UnsupportedPlatformError(platform7);
194
202
  }
195
203
 
196
204
  // src/sdk/paths.ts
@@ -231,6 +239,8 @@ function getOhpmPath(config) {
231
239
  return getCmdToolsBin(config, "ohpm");
232
240
  }
233
241
  function getHdcPath(config) {
242
+ const override = config.get("hdcPath", "");
243
+ if (override) return override;
234
244
  const base = path2.join(getCmdToolsPath(config), "sdk", "default", "openharmony", "toolchains");
235
245
  if (os3.platform() === "win32") {
236
246
  return pickExisting([
@@ -281,8 +291,164 @@ function detectProjectSdkVersion(projectRoot, logger = noopLogger) {
281
291
  }
282
292
  }
283
293
 
284
- // src/sdk/download.ts
294
+ // src/sdk/tmp.ts
285
295
  import * as fs3 from "fs";
296
+ import * as os4 from "os";
297
+ import * as path4 from "path";
298
+ var INSTALL_TMP_DIRNAME = ".oniro-tmp";
299
+ var TMPFS_MAGIC = 16914836;
300
+ var RAMFS_MAGIC = 2240043254;
301
+ var HINT_EXAMPLE = os4.platform() === "win32" ? "D:\\oniro-tmp" : "~/.cache/oniro-tmp";
302
+ 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}.`;
303
+ function resolveTmpRoot(config, override, installRoot) {
304
+ const explicit = (override ?? "").trim();
305
+ if (explicit) return path4.resolve(explicit);
306
+ const configured = config.get("tmpDir", "").trim();
307
+ if (configured) return path4.resolve(configured);
308
+ if (installRoot) return path4.join(path4.resolve(installRoot), INSTALL_TMP_DIRNAME);
309
+ return os4.tmpdir();
310
+ }
311
+ function createTempWorkDir(root, prefix) {
312
+ try {
313
+ fs3.mkdirSync(root, { recursive: true });
314
+ return fs3.mkdtempSync(path4.join(root, prefix));
315
+ } catch (err) {
316
+ if (isOutOfSpaceError(err)) throw toSpaceError(err, root, "a temporary directory");
317
+ throw new OniroError(
318
+ `Cannot create a temporary directory in '${root}': ${err instanceof Error ? err.message : String(err)}
319
+ ${TMP_DIR_HINT}`,
320
+ err
321
+ );
322
+ }
323
+ }
324
+ function removeTempWorkDir(dir, root) {
325
+ fs3.rmSync(dir, { recursive: true, force: true });
326
+ if (!root || path4.basename(root) !== INSTALL_TMP_DIRNAME) return;
327
+ try {
328
+ fs3.rmdirSync(root);
329
+ } catch {
330
+ }
331
+ }
332
+ function createInstallTempDir(opts) {
333
+ const requested = (opts.override ?? "").trim() || opts.config.get("tmpDir", "").trim();
334
+ const root = resolveTmpRoot(opts.config, opts.override, opts.installRoot);
335
+ try {
336
+ return { dir: createTempWorkDir(root, opts.prefix), root };
337
+ } catch (err) {
338
+ const fallback = os4.tmpdir();
339
+ if (requested || isOutOfSpaceError(err) || path4.resolve(fallback) === path4.resolve(root)) throw err;
340
+ const reason = err instanceof Error ? err.message.split("\n")[0] : String(err);
341
+ (opts.logger ?? noopLogger).warn(`${reason} Falling back to '${fallback}'.`);
342
+ return { dir: createTempWorkDir(fallback, opts.prefix), root: fallback };
343
+ }
344
+ }
345
+ function nearestExisting(dir) {
346
+ let current = path4.resolve(dir);
347
+ for (; ; ) {
348
+ if (fs3.existsSync(current)) return current;
349
+ const parent = path4.dirname(current);
350
+ if (parent === current) return null;
351
+ current = parent;
352
+ }
353
+ }
354
+ function getFreeSpaceBytes(dir) {
355
+ const target = nearestExisting(dir);
356
+ if (!target) return null;
357
+ try {
358
+ const stats = fs3.statfsSync(target);
359
+ return Number(stats.bsize) * Number(stats.bavail);
360
+ } catch {
361
+ return null;
362
+ }
363
+ }
364
+ function isRamBackedPath(dir) {
365
+ const target = nearestExisting(dir);
366
+ if (!target) return false;
367
+ try {
368
+ const type = Number(fs3.statfsSync(target).type);
369
+ return type === TMPFS_MAGIC || type === RAMFS_MAGIC;
370
+ } catch {
371
+ return false;
372
+ }
373
+ }
374
+ function formatBytes(bytes) {
375
+ const units = ["B", "KiB", "MiB", "GiB", "TiB"];
376
+ let value = Math.max(0, bytes);
377
+ let unit = 0;
378
+ while (value >= 1024 && unit < units.length - 1) {
379
+ value /= 1024;
380
+ unit++;
381
+ }
382
+ return `${value >= 10 || unit === 0 ? Math.round(value) : value.toFixed(1)} ${units[unit]}`;
383
+ }
384
+ function isOutOfSpaceError(err) {
385
+ if (err instanceof InsufficientSpaceError) return true;
386
+ const seen = /* @__PURE__ */ new Set();
387
+ let current = err;
388
+ while (current && typeof current === "object" && !seen.has(current)) {
389
+ seen.add(current);
390
+ const e = current;
391
+ if (e.code === "ENOSPC" || e.code === "EDQUOT") return true;
392
+ if (typeof e.message === "string" && /ENOSPC|EDQUOT|no space left on device/i.test(e.message)) return true;
393
+ current = e.cause;
394
+ }
395
+ return false;
396
+ }
397
+ function spaceMessage(dir, what, opts) {
398
+ const need = typeof opts.requiredBytes === "number" ? `needs about ${formatBytes(opts.requiredBytes)}` : null;
399
+ const free = typeof opts.freeBytes === "number" ? `${formatBytes(opts.freeBytes)} available` : null;
400
+ const amounts = [need, free].filter(Boolean).join(", ");
401
+ const lines = [`Not enough free space in '${dir}' for ${what}${amounts ? ` (${amounts})` : ""}.`];
402
+ if (isRamBackedPath(dir)) {
403
+ lines.push(
404
+ `'${dir}' is on a RAM-backed filesystem (tmpfs), so its capacity is limited by free memory, not by free disk.`
405
+ );
406
+ }
407
+ if (opts.tmpDirHint) lines.push(TMP_DIR_HINT);
408
+ return lines.join("\n");
409
+ }
410
+ function ensureFreeSpace(dir, requiredBytes, what) {
411
+ if (!Number.isFinite(requiredBytes) || requiredBytes <= 0) return;
412
+ const freeBytes = getFreeSpaceBytes(dir);
413
+ if (freeBytes === null || freeBytes >= requiredBytes) return;
414
+ throw new InsufficientSpaceError(
415
+ spaceMessage(dir, what, { requiredBytes, freeBytes, tmpDirHint: true }),
416
+ dir
417
+ );
418
+ }
419
+ function errorPath(err) {
420
+ const seen = /* @__PURE__ */ new Set();
421
+ let current = err;
422
+ while (current && typeof current === "object" && !seen.has(current)) {
423
+ seen.add(current);
424
+ const e = current;
425
+ if (typeof e.path === "string" && e.path) return e.path;
426
+ if (typeof e.dest === "string" && e.dest) return e.dest;
427
+ current = e.cause;
428
+ }
429
+ return null;
430
+ }
431
+ function isInside(child, parent) {
432
+ const rel = path4.relative(path4.resolve(parent), path4.resolve(child));
433
+ return rel === "" || !rel.startsWith("..") && !path4.isAbsolute(rel);
434
+ }
435
+ function toSpaceError(err, tmpRoot, what) {
436
+ if (err instanceof InsufficientSpaceError) return err;
437
+ if (!isOutOfSpaceError(err)) return err;
438
+ const failed = errorPath(err);
439
+ const failedDir = failed ? path4.dirname(path4.resolve(failed)) : path4.resolve(tmpRoot);
440
+ const inTmp = isInside(failedDir, tmpRoot);
441
+ const dir = inTmp ? path4.resolve(tmpRoot) : failedDir;
442
+ return new InsufficientSpaceError(
443
+ spaceMessage(dir, what, { freeBytes: getFreeSpaceBytes(dir), tmpDirHint: inTmp }),
444
+ dir,
445
+ err
446
+ );
447
+ }
448
+
449
+ // src/sdk/download.ts
450
+ import * as fs4 from "fs";
451
+ import * as path5 from "path";
286
452
  import * as crypto from "crypto";
287
453
  import { pipeline } from "stream";
288
454
  import { promisify } from "util";
@@ -292,36 +458,57 @@ var pipelineAsync = promisify(pipeline);
292
458
  async function downloadFile(opts) {
293
459
  const { url, dest, progress, abortSignal } = opts;
294
460
  const proto = url.startsWith("https") ? https : http;
295
- return new Promise((resolve2, reject) => {
461
+ return new Promise((resolve4, reject) => {
296
462
  const s = Math.max(0, Math.min(100, opts.start ?? 0));
297
463
  const r = Math.max(0, Math.min(100 - s, opts.range ?? 100));
298
464
  let settled = false;
299
465
  const done = (err) => {
300
466
  if (settled) return;
301
467
  settled = true;
302
- err ? reject(err) : resolve2();
468
+ err ? reject(err) : resolve4();
303
469
  };
304
470
  if (abortSignal?.aborted) {
305
471
  done(new CancelledError("Download cancelled before start."));
306
472
  return;
307
473
  }
308
- const file = fs3.createWriteStream(dest);
474
+ const destDir = path5.dirname(path5.resolve(dest));
475
+ const what = opts.what ?? `the download of '${url}'`;
476
+ const writeFailed = (err) => isOutOfSpaceError(err) ? toSpaceError(err, opts.tmpRoot ?? destDir, what) : new OniroError(`Error writing '${dest}': ${err instanceof Error ? err.message : String(err)}`, err);
477
+ const file = fs4.createWriteStream(dest);
478
+ let activeResponse;
479
+ const failWith = (err) => {
480
+ file.close(() => fs4.rm(dest, { force: true }, () => done(err)));
481
+ };
482
+ file.on("error", (err) => {
483
+ try {
484
+ activeResponse?.destroy?.();
485
+ } catch {
486
+ }
487
+ failWith(writeFailed(err));
488
+ });
309
489
  const req = proto.get(url, (response) => {
490
+ activeResponse = response;
310
491
  if (response.statusCode !== 200) {
311
492
  try {
312
493
  response.destroy();
313
494
  } catch {
314
495
  }
315
- try {
316
- file.close();
317
- } catch {
318
- }
319
- fs3.unlink(dest, () => {
320
- });
321
- done(new OniroError(`Failed to download '${url}' (HTTP ${response.statusCode})`));
496
+ failWith(new OniroError(`Failed to download '${url}' (HTTP ${response.statusCode})`));
322
497
  return;
323
498
  }
324
499
  const total = parseInt(response.headers["content-length"] || "0", 10);
500
+ if (total > 0) {
501
+ try {
502
+ ensureFreeSpace(opts.tmpRoot ?? destDir, total, what);
503
+ } catch (err) {
504
+ try {
505
+ response.destroy();
506
+ } catch {
507
+ }
508
+ failWith(err);
509
+ return;
510
+ }
511
+ }
325
512
  let downloaded = 0;
326
513
  let lastOverall = Math.round(s);
327
514
  response.on("data", (chunk) => {
@@ -345,38 +532,26 @@ async function downloadFile(opts) {
345
532
  const inc = endOverall - lastOverall;
346
533
  if (inc > 0) progress.report({ message: "Downloading: 100%", increment: inc });
347
534
  }
348
- file.close((err) => err ? done(err) : done());
535
+ file.close((err) => err ? done(writeFailed(err)) : done());
349
536
  });
350
537
  abortSignal?.addEventListener("abort", () => {
351
538
  response.destroy();
352
- file.close();
353
- fs3.unlink(dest, () => {
354
- });
355
- done(new CancelledError("Download cancelled."));
539
+ failWith(new CancelledError("Download cancelled."));
356
540
  });
357
541
  });
358
542
  req.on("error", (err) => {
359
- try {
360
- file.close();
361
- } catch {
362
- }
363
- fs3.unlink(dest, () => {
364
- });
365
- done(new OniroError(`Error downloading '${url}': ${err.message}`, err));
543
+ failWith(new OniroError(`Error downloading '${url}': ${err.message}`, err));
366
544
  });
367
545
  abortSignal?.addEventListener("abort", () => {
368
546
  req.destroy();
369
- file.close();
370
- fs3.unlink(dest, () => {
371
- });
372
- done(new CancelledError("Download cancelled."));
547
+ failWith(new CancelledError("Download cancelled."));
373
548
  });
374
549
  });
375
550
  }
376
551
  async function verifySha256(filePath, sha256Path) {
377
- const expected = fs3.readFileSync(sha256Path, "utf8").split(/\s+/)[0];
552
+ const expected = fs4.readFileSync(sha256Path, "utf8").split(/\s+/)[0];
378
553
  const hash = crypto.createHash("sha256");
379
- await pipelineAsync(fs3.createReadStream(filePath), hash);
554
+ await pipelineAsync(fs4.createReadStream(filePath), hash);
380
555
  const actual = hash.digest("hex");
381
556
  if (actual !== expected) {
382
557
  throw new ChecksumMismatchError(expected, actual);
@@ -384,9 +559,9 @@ async function verifySha256(filePath, sha256Path) {
384
559
  }
385
560
 
386
561
  // src/sdk/extract.ts
387
- import * as fs4 from "fs";
388
- import * as os4 from "os";
389
- import * as path4 from "path";
562
+ import * as fs5 from "fs";
563
+ import * as os5 from "os";
564
+ import * as path6 from "path";
390
565
  import { pipeline as pipeline2 } from "stream";
391
566
  import { promisify as promisify2 } from "util";
392
567
  import * as tar from "tar";
@@ -403,12 +578,12 @@ async function extractZipWithProgress(opts) {
403
578
  const r = Math.max(0, Math.min(100 - s, opts.range ?? 100));
404
579
  let processed = 0;
405
580
  let lastOverall = Math.round(s);
406
- await fs4.promises.mkdir(dest, { recursive: true });
407
- const destRoot = path4.resolve(dest);
581
+ await fs5.promises.mkdir(dest, { recursive: true });
582
+ const destRoot = path6.resolve(dest);
408
583
  const safeResolveTarget = (entryName) => {
409
- const resolved = path4.resolve(destRoot, entryName);
410
- const rel = path4.relative(destRoot, resolved);
411
- if (rel.startsWith("..") || path4.isAbsolute(rel)) {
584
+ const resolved = path6.resolve(destRoot, entryName);
585
+ const rel = path6.relative(destRoot, resolved);
586
+ if (rel.startsWith("..") || path6.isAbsolute(rel)) {
412
587
  throw new OniroError(`Blocked ZIP entry with illegal path: ${entryName}`);
413
588
  }
414
589
  return resolved;
@@ -420,25 +595,25 @@ async function extractZipWithProgress(opts) {
420
595
  const attr = file.attr ? file.attr >>> 16 : 0;
421
596
  const isSymlink = "isSymbolicLink" in file ? Boolean(file.isSymbolicLink) : (attr & 61440) === 40960;
422
597
  if (file.isDirectory) {
423
- await fs4.promises.mkdir(targetPath, { recursive: true });
598
+ await fs5.promises.mkdir(targetPath, { recursive: true });
424
599
  } else if (isSymlink) {
425
- await fs4.promises.mkdir(path4.dirname(targetPath), { recursive: true });
600
+ await fs5.promises.mkdir(path6.dirname(targetPath), { recursive: true });
426
601
  const linkTargetBuffer = await zip.entryData(file);
427
602
  const linkTarget = linkTargetBuffer.toString("utf8");
428
603
  try {
429
- const isWin = os4.platform() === "win32";
430
- await fs4.promises.symlink(linkTarget, targetPath, isWin ? "file" : void 0);
604
+ const isWin = os5.platform() === "win32";
605
+ await fs5.promises.symlink(linkTarget, targetPath, isWin ? "file" : void 0);
431
606
  } catch (e) {
432
607
  logger.warn(`Failed to create symlink at ${targetPath}: ${String(e)}`);
433
608
  }
434
609
  } else {
435
- await fs4.promises.mkdir(path4.dirname(targetPath), { recursive: true });
610
+ await fs5.promises.mkdir(path6.dirname(targetPath), { recursive: true });
436
611
  const readStream = await zip.stream(file);
437
- const writeStream = fs4.createWriteStream(targetPath);
612
+ const writeStream = fs5.createWriteStream(targetPath);
438
613
  await pipelineAsync2(readStream, writeStream);
439
614
  if (attr > 0) {
440
615
  try {
441
- await fs4.promises.chmod(targetPath, attr & 511);
616
+ await fs5.promises.chmod(targetPath, attr & 511);
442
617
  } catch {
443
618
  }
444
619
  }
@@ -470,41 +645,41 @@ async function extractTarball(tarPath, dest, strip = 0) {
470
645
  }
471
646
 
472
647
  // src/sdk/list.ts
473
- import * as fs5 from "fs";
474
- import * as path5 from "path";
648
+ import * as fs6 from "fs";
649
+ import * as path7 from "path";
475
650
  function getSupportedSdksForUi(config) {
476
- const base = path5.join(getSdkRootDir(config), getOsFolder());
651
+ const base = path7.join(getSdkRootDir(config), getOsFolder());
477
652
  return ALL_SDKS.map((sdk) => ({
478
653
  version: sdk.version,
479
654
  api: sdk.api,
480
- installed: fs5.existsSync(path5.join(base, sdk.api))
655
+ installed: fs6.existsSync(path7.join(base, sdk.api))
481
656
  })).sort((a, b) => Number(b.api) - Number(a.api));
482
657
  }
483
658
  function getInstalledSdks(config) {
484
659
  const sdkRoot = getSdkRootDir(config);
485
660
  const versions = /* @__PURE__ */ new Set();
486
- if (!fs5.existsSync(sdkRoot)) return [];
661
+ if (!fs6.existsSync(sdkRoot)) return [];
487
662
  for (const osFolder of ["linux", "darwin", "windows"]) {
488
- const osPath = path5.join(sdkRoot, osFolder);
489
- if (!fs5.existsSync(osPath) || !fs5.statSync(osPath).isDirectory()) continue;
490
- for (const api of fs5.readdirSync(osPath)) {
491
- const apiPath = path5.join(osPath, api);
492
- if (fs5.statSync(apiPath).isDirectory()) versions.add(api);
663
+ const osPath = path7.join(sdkRoot, osFolder);
664
+ if (!fs6.existsSync(osPath) || !fs6.statSync(osPath).isDirectory()) continue;
665
+ for (const api of fs6.readdirSync(osPath)) {
666
+ const apiPath = path7.join(osPath, api);
667
+ if (fs6.statSync(apiPath).isDirectory()) versions.add(api);
493
668
  }
494
669
  }
495
670
  return ALL_SDKS.filter((sdk) => versions.has(sdk.api)).map((sdk) => sdk.version);
496
671
  }
497
672
 
498
673
  // src/sdk/remove.ts
499
- import * as fs6 from "fs";
500
- import * as path6 from "path";
674
+ import * as fs7 from "fs";
675
+ import * as path8 from "path";
501
676
  function removeSdk(config, api) {
502
677
  const sdkRoot = getSdkRootDir(config);
503
678
  let removed = false;
504
679
  for (const osFolder of ["linux", "darwin", "windows"]) {
505
- const sdkPath = path6.join(sdkRoot, osFolder, api);
506
- if (fs6.existsSync(sdkPath)) {
507
- fs6.rmSync(sdkPath, { recursive: true, force: true });
680
+ const sdkPath = path8.join(sdkRoot, osFolder, api);
681
+ if (fs7.existsSync(sdkPath)) {
682
+ fs7.rmSync(sdkPath, { recursive: true, force: true });
508
683
  removed = true;
509
684
  }
510
685
  }
@@ -512,19 +687,18 @@ function removeSdk(config, api) {
512
687
  }
513
688
 
514
689
  // src/sdk/install.ts
515
- import * as fs8 from "fs";
516
- import * as os5 from "os";
517
- import * as path7 from "path";
690
+ import * as fs9 from "fs";
691
+ import * as path9 from "path";
518
692
 
519
693
  // src/sdk/move.ts
520
- import * as fs7 from "fs";
694
+ import * as fs8 from "fs";
521
695
  function movePath(src, dest) {
522
696
  try {
523
- fs7.renameSync(src, dest);
697
+ fs8.renameSync(src, dest);
524
698
  } catch (err) {
525
699
  if (err.code !== "EXDEV") throw err;
526
- fs7.cpSync(src, dest, { recursive: true });
527
- fs7.rmSync(src, { recursive: true, force: true });
700
+ fs8.cpSync(src, dest, { recursive: true });
701
+ fs8.rmSync(src, { recursive: true, force: true });
528
702
  }
529
703
  }
530
704
 
@@ -536,21 +710,29 @@ async function downloadAndInstallSdk(opts) {
536
710
  const urlBase = config.get("sdkUrlBase", OHOS_URL_BASE);
537
711
  const downloadUrl = `${urlBase}/${version}-Release/${filename}`;
538
712
  const sha256Url = `${downloadUrl}.sha256`;
539
- const tmpDir = fs8.mkdtempSync(path7.join(os5.tmpdir(), "oniro-sdk-"));
540
- const tarPath = path7.join(tmpDir, filename);
541
- const sha256Path = path7.join(tmpDir, `${filename}.sha256`);
542
- const extractDir = path7.join(tmpDir, "extract");
543
- fs8.mkdirSync(extractDir);
544
- const sdkInstallDir = path7.join(getSdkRootDir(config), osFolder, api);
545
- fs8.mkdirSync(path7.dirname(sdkInstallDir), { recursive: true });
713
+ const what = `the SDK ${version} install`;
714
+ const sdkRoot = getSdkRootDir(config);
715
+ const { dir: tmpDir, root: tmpRoot } = createInstallTempDir({
716
+ config,
717
+ override: opts.tmpDir,
718
+ installRoot: sdkRoot,
719
+ prefix: "oniro-sdk-",
720
+ logger
721
+ });
722
+ const tarPath = path9.join(tmpDir, filename);
723
+ const sha256Path = path9.join(tmpDir, `${filename}.sha256`);
724
+ const extractDir = path9.join(tmpDir, "extract");
725
+ fs9.mkdirSync(extractDir);
726
+ const sdkInstallDir = path9.join(sdkRoot, osFolder, api);
727
+ fs9.mkdirSync(path9.dirname(sdkInstallDir), { recursive: true });
546
728
  const checkCancelled = () => {
547
729
  if (abortSignal?.aborted) throw new CancelledError();
548
730
  };
549
731
  try {
550
732
  progress?.report({ message: "Downloading SDK archive...", increment: 0 });
551
- await downloadFile({ url: downloadUrl, dest: tarPath, progress, abortSignal, start: 0, range: 35 });
733
+ await downloadFile({ url: downloadUrl, dest: tarPath, progress, abortSignal, start: 0, range: 35, what, tmpRoot });
552
734
  progress?.report({ message: "Downloading checksum...", increment: 0 });
553
- await downloadFile({ url: sha256Url, dest: sha256Path, progress, abortSignal, start: 35, range: 10 });
735
+ await downloadFile({ url: sha256Url, dest: sha256Path, progress, abortSignal, start: 35, range: 10, what, tmpRoot });
554
736
  progress?.report({ message: "Verifying checksum...", increment: 0 });
555
737
  await verifySha256(tarPath, sha256Path);
556
738
  progress?.report({ message: "Verifying checksum...", increment: 5 });
@@ -559,13 +741,13 @@ async function downloadAndInstallSdk(opts) {
559
741
  await extractTarball(tarPath, extractDir, strip);
560
742
  progress?.report({ message: "Extracting SDK (this may take a while)...", increment: 10 });
561
743
  checkCancelled();
562
- const osContentPath = path7.join(extractDir, osFolder);
563
- if (!fs8.existsSync(osContentPath)) {
744
+ const osContentPath = path9.join(extractDir, osFolder);
745
+ if (!fs9.existsSync(osContentPath)) {
564
746
  throw new OniroError(
565
747
  `Expected folder '${osFolder}' not found in extracted SDK. Tarball layout may have changed.`
566
748
  );
567
749
  }
568
- const zipFiles = fs8.readdirSync(osContentPath).filter((n2) => n2.endsWith(".zip"));
750
+ const zipFiles = fs9.readdirSync(osContentPath).filter((n2) => n2.endsWith(".zip"));
569
751
  const componentsStart = 60;
570
752
  const componentsBudget = 35;
571
753
  const n = zipFiles.length;
@@ -580,39 +762,40 @@ async function downloadAndInstallSdk(opts) {
580
762
  for (const zipFile of zipFiles) {
581
763
  checkCancelled();
582
764
  logger.info(`Extracting component ${zipFile}`);
583
- const zipPath = path7.join(osContentPath, zipFile);
765
+ const zipPath = path9.join(osContentPath, zipFile);
584
766
  const thisBudget = base + (rem > 0 ? 1 : 0);
585
767
  if (rem > 0) rem--;
586
768
  await extractZipWithProgress({ zipPath, dest: osContentPath, progress, start: cursor, range: thisBudget, logger });
587
769
  cursor += thisBudget;
588
- fs8.unlinkSync(zipPath);
770
+ fs9.unlinkSync(zipPath);
589
771
  }
590
772
  progress?.report({ message: "Finalizing installation...", increment: 0 });
591
- if (fs8.existsSync(sdkInstallDir)) {
592
- fs8.rmSync(sdkInstallDir, { recursive: true, force: true });
773
+ if (fs9.existsSync(sdkInstallDir)) {
774
+ fs9.rmSync(sdkInstallDir, { recursive: true, force: true });
593
775
  }
594
776
  movePath(osContentPath, sdkInstallDir);
595
777
  progress?.report({ message: "Finalizing installation...", increment: 3 });
596
778
  progress?.report({ message: "Cleaning up...", increment: 0 });
597
- fs8.rmSync(tmpDir, { recursive: true, force: true });
779
+ removeTempWorkDir(tmpDir, tmpRoot);
598
780
  progress?.report({ message: "Cleaning up...", increment: 2 });
599
781
  } catch (err) {
600
- logger.error(`SDK install failed: ${err instanceof Error ? err.message : String(err)}`);
601
- fs8.rmSync(tmpDir, { recursive: true, force: true });
602
- throw err;
782
+ const wrapped = toSpaceError(err, tmpRoot, what);
783
+ logger.error(`SDK install failed: ${wrapped instanceof Error ? wrapped.message : String(wrapped)}`);
784
+ removeTempWorkDir(tmpDir, tmpRoot);
785
+ throw wrapped;
603
786
  }
604
787
  }
605
788
 
606
789
  // src/cmdTools/install.ts
607
- import * as fs9 from "fs";
790
+ import * as fs10 from "fs";
608
791
  import * as os6 from "os";
609
- import * as path8 from "path";
792
+ import * as path10 from "path";
610
793
  import { execFileSync } from "child_process";
611
- function getCmdToolsDownloadUrl(config, platform6 = os6.platform()) {
612
- if (platform6 === "linux") {
794
+ function getCmdToolsDownloadUrl(config, platform7 = os6.platform()) {
795
+ if (platform7 === "linux") {
613
796
  return config.get("cmdToolsUrlLinux", defaultPaths.cmdToolsUrlLinux);
614
797
  }
615
- if (platform6 === "win32") {
798
+ if (platform7 === "win32") {
616
799
  const url = config.get("cmdToolsUrlWindows", "");
617
800
  if (!url) {
618
801
  throw new OniroError(
@@ -621,7 +804,7 @@ function getCmdToolsDownloadUrl(config, platform6 = os6.platform()) {
621
804
  }
622
805
  return url;
623
806
  }
624
- if (platform6 === "darwin") {
807
+ if (platform7 === "darwin") {
625
808
  const url = config.get("cmdToolsUrlMac", "");
626
809
  if (!url) {
627
810
  throw new OniroError(
@@ -630,20 +813,20 @@ function getCmdToolsDownloadUrl(config, platform6 = os6.platform()) {
630
813
  }
631
814
  return url;
632
815
  }
633
- throw new UnsupportedPlatformError(platform6);
816
+ throw new UnsupportedPlatformError(platform7);
634
817
  }
635
818
  function findCmdToolsSourceDir(extractPath) {
636
819
  const known = [
637
- path8.join(extractPath, "command-line-tools"),
638
- path8.join(extractPath, "oh-command-line-tools"),
639
- path8.join(extractPath, "commandline-tools")
820
+ path10.join(extractPath, "command-line-tools"),
821
+ path10.join(extractPath, "oh-command-line-tools"),
822
+ path10.join(extractPath, "commandline-tools")
640
823
  ];
641
824
  for (const c of known) {
642
- if (fs9.existsSync(c)) return c;
825
+ if (fs10.existsSync(c)) return c;
643
826
  }
644
- const entries = fs9.readdirSync(extractPath, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => path8.join(extractPath, e.name));
827
+ const entries = fs10.readdirSync(extractPath, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => path10.join(extractPath, e.name));
645
828
  for (const dir of entries) {
646
- if (fs9.existsSync(path8.join(dir, "bin"))) return dir;
829
+ if (fs10.existsSync(path10.join(dir, "bin"))) return dir;
647
830
  }
648
831
  throw new OniroError("Could not locate command line tools folder in the extracted archive.");
649
832
  }
@@ -651,53 +834,62 @@ async function installCmdTools(opts) {
651
834
  const { config, progress, abortSignal } = opts;
652
835
  const logger = opts.logger ?? noopLogger;
653
836
  const CMD_PATH = getCmdToolsPath(config);
654
- const tmpDir = fs9.mkdtempSync(path8.join(os6.tmpdir(), "oniro-cmdtools-"));
655
- const zipPath = opts.localZipPath ?? path8.join(tmpDir, "oh-command-line-tools.zip");
656
- const extractPath = path8.join(tmpDir, "oh-command-line-tools");
837
+ const what = "the command-line tools install";
838
+ const { dir: tmpDir, root: tmpRoot } = createInstallTempDir({
839
+ config,
840
+ override: opts.tmpDir,
841
+ installRoot: path10.dirname(CMD_PATH),
842
+ prefix: "oniro-cmdtools-",
843
+ logger
844
+ });
845
+ const zipPath = opts.localZipPath ?? path10.join(tmpDir, "oh-command-line-tools.zip");
846
+ const extractPath = path10.join(tmpDir, "oh-command-line-tools");
657
847
  try {
658
848
  if (!opts.localZipPath) {
659
849
  const url = getCmdToolsDownloadUrl(config);
660
850
  progress?.report({ message: "Downloading command line tools...", increment: 0 });
661
- await downloadFile({ url, dest: zipPath, progress, abortSignal, start: 0, range: 50 });
851
+ await downloadFile({ url, dest: zipPath, progress, abortSignal, start: 0, range: 50, what, tmpRoot });
662
852
  }
663
853
  progress?.report({ message: "Extracting tools...", increment: 0 });
664
854
  await extractZipWithProgress({ zipPath, dest: extractPath, progress, start: 50, range: 45, logger });
665
- fs9.mkdirSync(CMD_PATH, { recursive: true });
855
+ fs10.mkdirSync(CMD_PATH, { recursive: true });
666
856
  const srcDir = findCmdToolsSourceDir(extractPath);
667
- for (const entry of fs9.readdirSync(srcDir)) {
668
- const src = path8.join(srcDir, entry);
669
- const dest = path8.join(CMD_PATH, entry);
670
- if (fs9.statSync(src).isDirectory()) {
671
- if (fs9.existsSync(dest)) fs9.rmSync(dest, { recursive: true, force: true });
857
+ for (const entry of fs10.readdirSync(srcDir)) {
858
+ const src = path10.join(srcDir, entry);
859
+ const dest = path10.join(CMD_PATH, entry);
860
+ if (fs10.statSync(src).isDirectory()) {
861
+ if (fs10.existsSync(dest)) fs10.rmSync(dest, { recursive: true, force: true });
672
862
  movePath(src, dest);
673
863
  } else {
674
- fs9.copyFileSync(src, dest);
864
+ fs10.copyFileSync(src, dest);
675
865
  }
676
866
  }
677
- const binDir = path8.join(CMD_PATH, "bin");
678
- if (fs9.existsSync(binDir) && os6.platform() !== "win32") {
679
- for (const file of fs9.readdirSync(binDir)) {
680
- fs9.chmodSync(path8.join(binDir, file), 493);
867
+ const binDir = path10.join(CMD_PATH, "bin");
868
+ if (fs10.existsSync(binDir) && os6.platform() !== "win32") {
869
+ for (const file of fs10.readdirSync(binDir)) {
870
+ fs10.chmodSync(path10.join(binDir, file), 493);
681
871
  }
682
872
  }
683
873
  progress?.report({ message: "Finalizing installation...", increment: 5 });
684
874
  progress?.report({ message: "Cleaning up...", increment: 0 });
875
+ } catch (err) {
876
+ throw toSpaceError(err, tmpRoot, what);
685
877
  } finally {
686
- fs9.rmSync(tmpDir, { recursive: true, force: true });
878
+ removeTempWorkDir(tmpDir, tmpRoot);
687
879
  }
688
880
  }
689
881
  function getCmdToolsStatus(config) {
690
882
  const cmdPath = getCmdToolsPath(config);
691
- const binDir = path8.join(cmdPath, "bin");
883
+ const binDir = path10.join(cmdPath, "bin");
692
884
  const candidates = os6.platform() === "win32" ? ["ohpm.exe", "ohpm.cmd", "ohpm.bat", "ohpm"] : ["ohpm"];
693
- const bin = candidates.map((c) => path8.join(binDir, c)).find((p) => fs9.existsSync(p));
885
+ const bin = candidates.map((c) => path10.join(binDir, c)).find((p) => fs10.existsSync(p));
694
886
  if (!bin) {
695
887
  return { installed: false, status: "Not installed" };
696
888
  }
697
889
  try {
698
- const versionFile = path8.join(cmdPath, "version.txt");
699
- if (fs9.existsSync(versionFile)) {
700
- const content = fs9.readFileSync(versionFile, "utf8");
890
+ const versionFile = path10.join(cmdPath, "version.txt");
891
+ if (fs10.existsSync(versionFile)) {
892
+ const content = fs10.readFileSync(versionFile, "utf8");
701
893
  for (const line of content.split(/\r?\n/)) {
702
894
  const m = line.match(/^\s*#\s*Version:\s*(.+)$/);
703
895
  if (m?.[1]) return { installed: true, status: `Installed (${m[1].trim()})` };
@@ -717,14 +909,14 @@ function isCmdToolsInstalled(config) {
717
909
  }
718
910
  function removeCmdTools(config) {
719
911
  const cmdPath = getCmdToolsPath(config);
720
- if (fs9.existsSync(cmdPath)) {
721
- fs9.rmSync(cmdPath, { recursive: true, force: true });
912
+ if (fs10.existsSync(cmdPath)) {
913
+ fs10.rmSync(cmdPath, { recursive: true, force: true });
722
914
  }
723
915
  }
724
916
 
725
917
  // src/sign/encryptKey.ts
726
- import * as fs10 from "fs";
727
- import * as path9 from "path";
918
+ import * as fs11 from "fs";
919
+ import * as path11 from "path";
728
920
  import * as crypto2 from "crypto";
729
921
  var DAEMON_ROOT_KEY_COMPONENT_LENGTH = 16;
730
922
  var DAEMON_SALT_KEY_LENGTH = 16;
@@ -796,33 +988,33 @@ function getRootKey(fdComponents, salt) {
796
988
  function createAndStoreKey(dir, keyLength) {
797
989
  const key = crypto2.randomBytes(keyLength);
798
990
  const hash = crypto2.createHash("sha256").update(key).digest("hex");
799
- const filePath = path9.join(dir, hash);
800
- fs10.writeFileSync(filePath, key);
801
- fs10.chmodSync(filePath, KEY_FILE_PERMISSIONS);
991
+ const filePath = path11.join(dir, hash);
992
+ fs11.writeFileSync(filePath, key);
993
+ fs11.chmodSync(filePath, KEY_FILE_PERMISSIONS);
802
994
  return key;
803
995
  }
804
996
  function createAndStoreEnKey(rootKey, ceDir) {
805
997
  const workKey = crypto2.randomBytes(DAEMON_WORK_KEY_LENGTH);
806
998
  const encrypted = encrypt(rootKey, workKey);
807
999
  const hash = crypto2.createHash("sha256").update(encrypted).digest("hex");
808
- const filePath = path9.join(ceDir, hash);
809
- fs10.writeFileSync(filePath, encrypted);
810
- fs10.chmodSync(filePath, KEY_FILE_PERMISSIONS);
1000
+ const filePath = path11.join(ceDir, hash);
1001
+ fs11.writeFileSync(filePath, encrypted);
1002
+ fs11.chmodSync(filePath, KEY_FILE_PERMISSIONS);
811
1003
  }
812
1004
  function createMaterial(materialPath) {
813
- fs10.mkdirSync(materialPath, { recursive: true });
814
- fs10.chmodSync(materialPath, KEY_FILE_DIRECTORY_PERMISSIONS);
815
- const fdDir = path9.join(materialPath, "fd");
816
- const acDir = path9.join(materialPath, "ac");
817
- const ceDir = path9.join(materialPath, "ce");
818
- fs10.mkdirSync(fdDir, { recursive: true });
819
- fs10.mkdirSync(acDir, { recursive: true });
820
- fs10.mkdirSync(ceDir, { recursive: true });
1005
+ fs11.mkdirSync(materialPath, { recursive: true });
1006
+ fs11.chmodSync(materialPath, KEY_FILE_DIRECTORY_PERMISSIONS);
1007
+ const fdDir = path11.join(materialPath, "fd");
1008
+ const acDir = path11.join(materialPath, "ac");
1009
+ const ceDir = path11.join(materialPath, "ce");
1010
+ fs11.mkdirSync(fdDir, { recursive: true });
1011
+ fs11.mkdirSync(acDir, { recursive: true });
1012
+ fs11.mkdirSync(ceDir, { recursive: true });
821
1013
  const fdSubDirs = ["0", "1", "2"];
822
1014
  const fdComponents = [];
823
1015
  for (const sub of fdSubDirs) {
824
- const subDir = path9.join(fdDir, sub);
825
- fs10.mkdirSync(subDir, { recursive: true });
1016
+ const subDir = path11.join(fdDir, sub);
1017
+ fs11.mkdirSync(subDir, { recursive: true });
826
1018
  const comp = createAndStoreKey(subDir, DAEMON_ROOT_KEY_COMPONENT_LENGTH);
827
1019
  fdComponents.push(comp);
828
1020
  }
@@ -831,24 +1023,24 @@ function createMaterial(materialPath) {
831
1023
  createAndStoreEnKey(rootKey, ceDir);
832
1024
  }
833
1025
  function readSingleFile(dir) {
834
- if (!fs10.existsSync(dir) || !fs10.statSync(dir).isDirectory()) {
1026
+ if (!fs11.existsSync(dir) || !fs11.statSync(dir).isDirectory()) {
835
1027
  throw new Error(`Missing signing material directory: ${dir}`);
836
1028
  }
837
- const files = fs10.readdirSync(dir).filter((f) => f !== ".DS_Store");
1029
+ const files = fs11.readdirSync(dir).filter((f) => f !== ".DS_Store");
838
1030
  if (files.length !== 1) {
839
1031
  throw new Error(`Signing material in ${dir} is illegal (expected exactly one file).`);
840
1032
  }
841
- return fs10.readFileSync(path9.join(dir, files[0]));
1033
+ return fs11.readFileSync(path11.join(dir, files[0]));
842
1034
  }
843
1035
  function getKey(materialPath) {
844
- if (!fs10.existsSync(materialPath) || !fs10.statSync(materialPath).isDirectory()) {
1036
+ if (!fs11.existsSync(materialPath) || !fs11.statSync(materialPath).isDirectory()) {
845
1037
  throw new Error("Material directory does not exist.");
846
1038
  }
847
- const fdDir = path9.join(materialPath, "fd");
848
- const fdComponents = ["0", "1", "2"].map((sub) => readSingleFile(path9.join(fdDir, sub)));
849
- const salt = readSingleFile(path9.join(materialPath, "ac"));
1039
+ const fdDir = path11.join(materialPath, "fd");
1040
+ const fdComponents = ["0", "1", "2"].map((sub) => readSingleFile(path11.join(fdDir, sub)));
1041
+ const salt = readSingleFile(path11.join(materialPath, "ac"));
850
1042
  const rootKey = getRootKey(fdComponents, salt);
851
- const workMaterial = readSingleFile(path9.join(materialPath, "ce"));
1043
+ const workMaterial = readSingleFile(path11.join(materialPath, "ce"));
852
1044
  return decrypt(rootKey, workMaterial);
853
1045
  }
854
1046
  function encryptPwd(password, materialPath) {
@@ -863,8 +1055,8 @@ function decryptPwd(encryptedHex, materialPath) {
863
1055
  }
864
1056
 
865
1057
  // src/sign/generateSigningConfigs.ts
866
- import * as fs11 from "fs";
867
- import * as path10 from "path";
1058
+ import * as fs12 from "fs";
1059
+ import * as path12 from "path";
868
1060
  import { execFileSync as execFileSync2 } from "child_process";
869
1061
  import JSON52 from "json5";
870
1062
 
@@ -934,17 +1126,17 @@ var SIGNING_KIND_CONFIG = {
934
1126
  };
935
1127
  function copyFilesToProject(projectDir, paths, kind, logger, applicationCertPath) {
936
1128
  logger.info("[sign] Copying signing material into project...");
937
- const signaturesDir = path10.join(projectDir, "signatures");
938
- fs11.mkdirSync(signaturesDir, { recursive: true });
939
- fs11.copyFileSync(paths.keystore, path10.join(signaturesDir, "OpenHarmony.p12"));
940
- fs11.copyFileSync(paths.profileCert, path10.join(signaturesDir, "OpenHarmonyProfileRelease.pem"));
941
- fs11.copyFileSync(paths.unsignedProfileTemplate, path10.join(signaturesDir, "UnsgnedReleasedProfileTemplate.json"));
1129
+ const signaturesDir = path12.join(projectDir, "signatures");
1130
+ fs12.mkdirSync(signaturesDir, { recursive: true });
1131
+ fs12.copyFileSync(paths.keystore, path12.join(signaturesDir, "OpenHarmony.p12"));
1132
+ fs12.copyFileSync(paths.profileCert, path12.join(signaturesDir, "OpenHarmonyProfileRelease.pem"));
1133
+ fs12.copyFileSync(paths.unsignedProfileTemplate, path12.join(signaturesDir, "UnsgnedReleasedProfileTemplate.json"));
942
1134
  if (kind === "application-release") {
943
- const dest = path10.join(signaturesDir, "OpenHarmonyApplication.cer");
1135
+ const dest = path12.join(signaturesDir, "OpenHarmonyApplication.cer");
944
1136
  if (applicationCertPath) {
945
- fs11.copyFileSync(applicationCertPath, dest);
1137
+ fs12.copyFileSync(applicationCertPath, dest);
946
1138
  } else {
947
- fs11.writeFileSync(dest, OPEN_HARMONY_APPLICATION_CERT);
1139
+ fs12.writeFileSync(dest, OPEN_HARMONY_APPLICATION_CERT);
948
1140
  }
949
1141
  }
950
1142
  }
@@ -953,7 +1145,7 @@ function resolveAppFeature(apl, override) {
953
1145
  return apl === "normal" ? "hos_normal_app" : "hos_system_app";
954
1146
  }
955
1147
  function extractLeafCertificate(chainPath) {
956
- const certContent = fs11.readFileSync(chainPath, "utf-8");
1148
+ const certContent = fs12.readFileSync(chainPath, "utf-8");
957
1149
  const parts = certContent.split("-----END CERTIFICATE-----");
958
1150
  if (parts.length < 3) {
959
1151
  throw new OniroError(`${chainPath} does not contain enough certificates.`);
@@ -963,20 +1155,20 @@ function extractLeafCertificate(chainPath) {
963
1155
  function modifyProfileTemplate(projectDir, apl, appFeature, logger, options = {}) {
964
1156
  const kind = options.kind ?? pickSigningKind(apl);
965
1157
  logger.info(`[sign] Modifying profile template (apl=${apl}, app-feature=${appFeature}, kind=${kind})...`);
966
- const appJsonPath = path10.join(projectDir, "AppScope/app.json5");
967
- const profileTemplatePath = path10.join(projectDir, "signatures/UnsgnedReleasedProfileTemplate.json");
968
- if (!fs11.existsSync(appJsonPath)) {
1158
+ const appJsonPath = path12.join(projectDir, "AppScope/app.json5");
1159
+ const profileTemplatePath = path12.join(projectDir, "signatures/UnsgnedReleasedProfileTemplate.json");
1160
+ if (!fs12.existsSync(appJsonPath)) {
969
1161
  throw new OniroError(`${appJsonPath} does not exist.`);
970
1162
  }
971
1163
  let appJson;
972
1164
  try {
973
- appJson = JSON52.parse(fs11.readFileSync(appJsonPath, "utf-8"));
1165
+ appJson = JSON52.parse(fs12.readFileSync(appJsonPath, "utf-8"));
974
1166
  } catch (e) {
975
1167
  throw new OniroError(`Error parsing ${appJsonPath}: ${e.message}`, e);
976
1168
  }
977
1169
  let profileTemplate;
978
1170
  try {
979
- profileTemplate = JSON.parse(fs11.readFileSync(profileTemplatePath, "utf-8"));
1171
+ profileTemplate = JSON.parse(fs12.readFileSync(profileTemplatePath, "utf-8"));
980
1172
  } catch (e) {
981
1173
  throw new OniroError(`Error parsing ${profileTemplatePath}: ${e.message}`, e);
982
1174
  }
@@ -989,7 +1181,7 @@ function modifyProfileTemplate(projectDir, apl, appFeature, logger, options = {}
989
1181
  profileTemplate["bundle-info"]["bundle-name"] = appJson.app.bundleName;
990
1182
  profileTemplate["bundle-info"]["apl"] = apl;
991
1183
  profileTemplate["bundle-info"]["app-feature"] = appFeature;
992
- const distCertChainPath = path10.join(
1184
+ const distCertChainPath = path12.join(
993
1185
  projectDir,
994
1186
  "signatures",
995
1187
  SIGNING_KIND_CONFIG[kind].certBasename
@@ -999,13 +1191,13 @@ function modifyProfileTemplate(projectDir, apl, appFeature, logger, options = {}
999
1191
  profileTemplate.acls = profileTemplate.acls ?? {};
1000
1192
  profileTemplate.acls["allowed-acls"] = options.acls;
1001
1193
  }
1002
- fs11.writeFileSync(profileTemplatePath, JSON.stringify(profileTemplate, null, 2));
1194
+ fs12.writeFileSync(profileTemplatePath, JSON.stringify(profileTemplate, null, 2));
1003
1195
  }
1004
1196
  function generateP7bFile(projectDir, paths, logger, passwords) {
1005
1197
  logger.info("[sign] Generating P7b profile via hap-sign-tool...");
1006
- const signaturesDir = path10.join(projectDir, "signatures");
1007
- const profileTemplatePath = path10.join(signaturesDir, "UnsgnedReleasedProfileTemplate.json");
1008
- const outputProfilePath = path10.join(signaturesDir, "app1-profile.p7b");
1198
+ const signaturesDir = path12.join(projectDir, "signatures");
1199
+ const profileTemplatePath = path12.join(signaturesDir, "UnsgnedReleasedProfileTemplate.json");
1200
+ const outputProfilePath = path12.join(signaturesDir, "app1-profile.p7b");
1009
1201
  const args = [
1010
1202
  "-jar",
1011
1203
  paths.signTool,
@@ -1039,11 +1231,11 @@ function generateP7bFile(projectDir, paths, logger, passwords) {
1039
1231
  }
1040
1232
  }
1041
1233
  function detectSigningConfigNames(projectDir) {
1042
- const buildProfilePath = path10.join(projectDir, "build-profile.json5");
1043
- if (!fs11.existsSync(buildProfilePath)) return ["default"];
1234
+ const buildProfilePath = path12.join(projectDir, "build-profile.json5");
1235
+ if (!fs12.existsSync(buildProfilePath)) return ["default"];
1044
1236
  let parsed;
1045
1237
  try {
1046
- parsed = JSON52.parse(fs11.readFileSync(buildProfilePath, "utf-8"));
1238
+ parsed = JSON52.parse(fs12.readFileSync(buildProfilePath, "utf-8"));
1047
1239
  } catch {
1048
1240
  return ["default"];
1049
1241
  }
@@ -1060,14 +1252,14 @@ function detectSigningConfigNames(projectDir) {
1060
1252
  }
1061
1253
  function updateBuildProfile(projectDir, kind, logger, passwords) {
1062
1254
  logger.info("[sign] Writing signing configs into build-profile.json5...");
1063
- const materialDir = path10.join(projectDir, "signatures", "material");
1064
- const buildProfilePath = path10.join(projectDir, "build-profile.json5");
1255
+ const materialDir = path12.join(projectDir, "signatures", "material");
1256
+ const buildProfilePath = path12.join(projectDir, "build-profile.json5");
1065
1257
  const encryptedStorePassword = encryptPwd(passwords.store, materialDir);
1066
1258
  const encryptedKeyPassword = encryptPwd(passwords.key, materialDir);
1067
1259
  let buildProfile = { app: {} };
1068
- if (fs11.existsSync(buildProfilePath)) {
1260
+ if (fs12.existsSync(buildProfilePath)) {
1069
1261
  try {
1070
- buildProfile = JSON52.parse(fs11.readFileSync(buildProfilePath, "utf-8"));
1262
+ buildProfile = JSON52.parse(fs12.readFileSync(buildProfilePath, "utf-8"));
1071
1263
  } catch (e) {
1072
1264
  throw new OniroError(`Error parsing ${buildProfilePath}: ${e.message}`, e);
1073
1265
  }
@@ -1088,13 +1280,13 @@ function updateBuildProfile(projectDir, kind, logger, passwords) {
1088
1280
  storeFile: "./signatures/OpenHarmony.p12"
1089
1281
  }
1090
1282
  }));
1091
- fs11.writeFileSync(buildProfilePath, JSON.stringify(buildProfile, null, 2));
1283
+ fs12.writeFileSync(buildProfilePath, JSON.stringify(buildProfile, null, 2));
1092
1284
  }
1093
1285
  function prepareMaterialDirectory(projectDir, logger) {
1094
1286
  logger.info("[sign] Generating fresh signing material...");
1095
- const materialDir = path10.join(projectDir, "signatures", "material");
1096
- if (fs11.existsSync(materialDir)) {
1097
- fs11.rmSync(materialDir, { recursive: true, force: true });
1287
+ const materialDir = path12.join(projectDir, "signatures", "material");
1288
+ if (fs12.existsSync(materialDir)) {
1289
+ fs12.rmSync(materialDir, { recursive: true, force: true });
1098
1290
  }
1099
1291
  createMaterial(materialDir);
1100
1292
  }
@@ -1117,15 +1309,15 @@ function generateSigningConfigs(options) {
1117
1309
  "Could not detect project SDK version (compileSdkVersion missing in build-profile.json5)."
1118
1310
  );
1119
1311
  }
1120
- const sdkPath = path10.join(sdkHome, String(sdkVersion));
1121
- if (!fs11.existsSync(sdkPath)) {
1312
+ const sdkPath = path12.join(sdkHome, String(sdkVersion));
1313
+ if (!fs12.existsSync(sdkPath)) {
1122
1314
  throw new OniroError(`SDK path does not exist: ${sdkPath}`);
1123
1315
  }
1124
1316
  const paths = {
1125
- signTool: path10.join(sdkPath, "toolchains/lib/hap-sign-tool.jar"),
1126
- keystore: path10.join(sdkPath, "toolchains/lib/OpenHarmony.p12"),
1127
- profileCert: path10.join(sdkPath, "toolchains/lib/OpenHarmonyProfileRelease.pem"),
1128
- unsignedProfileTemplate: path10.join(sdkPath, "toolchains/lib/UnsgnedReleasedProfileTemplate.json")
1317
+ signTool: path12.join(sdkPath, "toolchains/lib/hap-sign-tool.jar"),
1318
+ keystore: path12.join(sdkPath, "toolchains/lib/OpenHarmony.p12"),
1319
+ profileCert: path12.join(sdkPath, "toolchains/lib/OpenHarmonyProfileRelease.pem"),
1320
+ unsignedProfileTemplate: path12.join(sdkPath, "toolchains/lib/UnsgnedReleasedProfileTemplate.json")
1129
1321
  };
1130
1322
  const kind = pickSigningKind(apl);
1131
1323
  const passwords = options.passwords ?? DEFAULT_PASSWORDS;
@@ -1139,46 +1331,54 @@ function generateSigningConfigs(options) {
1139
1331
  }
1140
1332
 
1141
1333
  // src/emulator/install.ts
1142
- import * as fs12 from "fs";
1143
- import * as os7 from "os";
1144
- import * as path11 from "path";
1334
+ import * as fs13 from "fs";
1335
+ import * as path13 from "path";
1145
1336
  async function installEmulator(opts) {
1146
1337
  const { config, progress, abortSignal } = opts;
1147
1338
  const logger = opts.logger ?? noopLogger;
1148
1339
  const url = config.get("emulatorUrl", defaultPaths.emulatorUrl);
1149
1340
  const emulatorDir = getEmulatorDir(config);
1150
- const tmpDir = fs12.mkdtempSync(path11.join(os7.tmpdir(), "oniro-emulator-"));
1151
- const tmpZip = path11.join(tmpDir, "oniro_emulator.zip");
1341
+ const what = "the emulator install";
1342
+ const { dir: tmpDir, root: tmpRoot } = createInstallTempDir({
1343
+ config,
1344
+ override: opts.tmpDir,
1345
+ installRoot: path13.dirname(emulatorDir),
1346
+ prefix: "oniro-emulator-",
1347
+ logger
1348
+ });
1349
+ const tmpZip = path13.join(tmpDir, "oniro_emulator.zip");
1152
1350
  try {
1153
- fs12.mkdirSync(emulatorDir, { recursive: true });
1351
+ fs13.mkdirSync(emulatorDir, { recursive: true });
1154
1352
  progress?.report({ message: "Downloading emulator...", increment: 0 });
1155
- await downloadFile({ url, dest: tmpZip, progress, abortSignal, start: 0, range: 50 });
1353
+ await downloadFile({ url, dest: tmpZip, progress, abortSignal, start: 0, range: 50, what, tmpRoot });
1156
1354
  progress?.report({ message: "Extracting emulator...", increment: 0 });
1157
1355
  await extractZipWithProgress({ zipPath: tmpZip, dest: emulatorDir, progress, start: 50, range: 45, logger });
1158
- const runSh = path11.join(emulatorDir, "images", "run.sh");
1159
- if (fs12.existsSync(runSh)) {
1160
- fs12.chmodSync(runSh, 493);
1356
+ const runSh = path13.join(emulatorDir, "images", "run.sh");
1357
+ if (fs13.existsSync(runSh)) {
1358
+ fs13.chmodSync(runSh, 493);
1161
1359
  }
1162
1360
  progress?.report({ message: "Finalizing installation...", increment: 5 });
1361
+ } catch (err) {
1362
+ throw toSpaceError(err, tmpRoot, what);
1163
1363
  } finally {
1164
- fs12.rmSync(tmpDir, { recursive: true, force: true });
1364
+ removeTempWorkDir(tmpDir, tmpRoot);
1165
1365
  }
1166
1366
  }
1167
1367
  function isEmulatorInstalled(config) {
1168
- const imagesDir = path11.join(getEmulatorDir(config), "images");
1169
- return fs12.existsSync(path11.join(imagesDir, "run.sh")) || fs12.existsSync(path11.join(imagesDir, "run.bat"));
1368
+ const imagesDir = path13.join(getEmulatorDir(config), "images");
1369
+ return fs13.existsSync(path13.join(imagesDir, "run.sh")) || fs13.existsSync(path13.join(imagesDir, "run.bat"));
1170
1370
  }
1171
1371
  function removeEmulator(config) {
1172
1372
  const emulatorDir = getEmulatorDir(config);
1173
- if (fs12.existsSync(emulatorDir)) {
1174
- fs12.rmSync(emulatorDir, { recursive: true, force: true });
1373
+ if (fs13.existsSync(emulatorDir)) {
1374
+ fs13.rmSync(emulatorDir, { recursive: true, force: true });
1175
1375
  }
1176
1376
  }
1177
1377
 
1178
1378
  // src/emulator/lifecycle.ts
1179
- import * as fs13 from "fs";
1180
- import * as os8 from "os";
1181
- import * as path12 from "path";
1379
+ import * as fs14 from "fs";
1380
+ import * as os7 from "os";
1381
+ import * as path14 from "path";
1182
1382
  import { spawn as spawn2 } from "child_process";
1183
1383
 
1184
1384
  // src/hdc/exec.ts
@@ -1187,7 +1387,7 @@ function runProcess(opts) {
1187
1387
  const { command, args, cwd, env, onOutput } = opts;
1188
1388
  const timeoutMs = opts.timeoutMs ?? 6e4;
1189
1389
  const { abortSignal } = opts;
1190
- return new Promise((resolve2, reject) => {
1390
+ return new Promise((resolve4, reject) => {
1191
1391
  if (abortSignal?.aborted) {
1192
1392
  reject(new CancelledError(`Command aborted before start: ${command}`));
1193
1393
  return;
@@ -1248,7 +1448,7 @@ ${stderr.trim()}` : "")
1248
1448
  );
1249
1449
  return;
1250
1450
  }
1251
- resolve2({ code: code ?? -1, stdout, stderr });
1451
+ resolve4({ code: code ?? -1, stdout, stderr });
1252
1452
  });
1253
1453
  });
1254
1454
  }
@@ -1274,9 +1474,29 @@ function ensureOk(result, command) {
1274
1474
  }
1275
1475
  return result;
1276
1476
  }
1477
+ var REMOTE_RC_RE = /__ONIRO_RC=(-?\d+)__/g;
1478
+ function parseRemoteExit(stdout) {
1479
+ let code = null;
1480
+ for (const m of stdout.matchAll(REMOTE_RC_RE)) code = Number(m[1]);
1481
+ if (code === null) return null;
1482
+ return { code, stdout: stdout.replace(REMOTE_RC_RE, "").replace(/[ \t\r\n]+$/, "") };
1483
+ }
1484
+ async function shellChecked(opts) {
1485
+ const { command, ...rest } = opts;
1486
+ const res = await hdcExec({ ...rest, args: ["shell", `${command}; echo "__ONIRO_RC=$?__"`] });
1487
+ const parsed = parseRemoteExit(res.stdout);
1488
+ if (!parsed) return res;
1489
+ return { code: parsed.code, stdout: parsed.stdout, stderr: res.stderr };
1490
+ }
1491
+ function ensureRemoteOk(result, command) {
1492
+ if (result.code !== 0) {
1493
+ throw new CommandFailedError(command, result.code, result.stderr.trim() || result.stdout.trim());
1494
+ }
1495
+ return result;
1496
+ }
1277
1497
 
1278
1498
  // src/emulator/lifecycle.ts
1279
- var PID_FILE = path12.join(os8.tmpdir(), "oniro_emulator.pid");
1499
+ var PID_FILE = path14.join(os7.tmpdir(), "oniro_emulator.pid");
1280
1500
  async function attemptHdcConnection(config, address = "127.0.0.1:55555", logger = noopLogger) {
1281
1501
  try {
1282
1502
  await hdcExec({ config, args: ["start", "-r"], logger });
@@ -1289,24 +1509,24 @@ async function attemptHdcConnection(config, address = "127.0.0.1:55555", logger
1289
1509
  }
1290
1510
  }
1291
1511
  function resolveLauncher(imagesPath, headless, connect) {
1292
- const runSh = path12.join(imagesPath, "run.sh");
1293
- const runBat = path12.join(imagesPath, "run.bat");
1512
+ const runSh = path14.join(imagesPath, "run.sh");
1513
+ const runBat = path14.join(imagesPath, "run.bat");
1294
1514
  const extra = [];
1295
1515
  if (headless) extra.push("--headless");
1296
1516
  if (connect) extra.push("--connect", connect);
1297
- if (os8.platform() === "win32") {
1298
- if (fs13.existsSync(runBat)) {
1517
+ if (os7.platform() === "win32") {
1518
+ if (fs14.existsSync(runBat)) {
1299
1519
  return { command: "cmd.exe", args: ["/c", runBat, ...extra], cwd: imagesPath };
1300
1520
  }
1301
- if (fs13.existsSync(runSh)) {
1521
+ if (fs14.existsSync(runSh)) {
1302
1522
  return { command: "bash", args: [runSh, ...extra], cwd: imagesPath };
1303
1523
  }
1304
1524
  throw new OniroError(`No emulator launcher (run.bat or run.sh) found in ${imagesPath}.`);
1305
1525
  }
1306
- if (fs13.existsSync(runSh)) {
1526
+ if (fs14.existsSync(runSh)) {
1307
1527
  return { command: "bash", args: [runSh, ...extra], cwd: imagesPath };
1308
1528
  }
1309
- if (fs13.existsSync(runBat)) {
1529
+ if (fs14.existsSync(runBat)) {
1310
1530
  return { command: "bash", args: [runBat, ...extra], cwd: imagesPath };
1311
1531
  }
1312
1532
  throw new OniroError(`No emulator launcher (run.sh or run.bat) found in ${imagesPath}.`);
@@ -1314,9 +1534,9 @@ function resolveLauncher(imagesPath, headless, connect) {
1314
1534
  async function startEmulator(opts) {
1315
1535
  const { config } = opts;
1316
1536
  const logger = opts.logger ?? noopLogger;
1317
- if (os8.platform() !== "win32" && fs13.existsSync(PID_FILE)) {
1537
+ if (os7.platform() !== "win32" && fs14.existsSync(PID_FILE)) {
1318
1538
  try {
1319
- const pid = Number(fs13.readFileSync(PID_FILE, "utf8").trim());
1539
+ const pid = Number(fs14.readFileSync(PID_FILE, "utf8").trim());
1320
1540
  if (Number.isFinite(pid) && pid > 0) {
1321
1541
  try {
1322
1542
  process.kill(pid, 0);
@@ -1332,13 +1552,13 @@ async function startEmulator(opts) {
1332
1552
  logger.warn(`Could not read PID file: ${err.message}`);
1333
1553
  }
1334
1554
  }
1335
- const imagesPath = path12.join(getEmulatorDir(config), "images");
1336
- if (!fs13.existsSync(imagesPath)) {
1555
+ const imagesPath = path14.join(getEmulatorDir(config), "images");
1556
+ if (!fs14.existsSync(imagesPath)) {
1337
1557
  throw new OniroError(`Emulator images directory not found at ${imagesPath}. Run \`oniro-app emulator install\` first.`);
1338
1558
  }
1339
1559
  const launcher = resolveLauncher(imagesPath, opts.headless === true, opts.connect);
1340
- const logPath = opts.logFile ?? (os8.platform() === "win32" ? "NUL" : "/dev/null");
1341
- const logFd = fs13.openSync(logPath, "a");
1560
+ const logPath = opts.logFile ?? (os7.platform() === "win32" ? "NUL" : "/dev/null");
1561
+ const logFd = fs14.openSync(logPath, "a");
1342
1562
  let child;
1343
1563
  try {
1344
1564
  child = spawn2(launcher.command, [...launcher.args], {
@@ -1348,19 +1568,19 @@ async function startEmulator(opts) {
1348
1568
  windowsHide: true
1349
1569
  });
1350
1570
  } finally {
1351
- fs13.closeSync(logFd);
1571
+ fs14.closeSync(logFd);
1352
1572
  }
1353
1573
  if (!child.pid) {
1354
1574
  throw new OniroError(`Failed to spawn launcher (${launcher.command} ${launcher.args.join(" ")}).`);
1355
1575
  }
1356
- fs13.writeFileSync(PID_FILE, String(child.pid));
1576
+ fs14.writeFileSync(PID_FILE, String(child.pid));
1357
1577
  child.unref();
1358
1578
  logger.info(`Emulator launched (PID ${child.pid}). Logs: ${logPath}`);
1359
- const earlyExit = new Promise((resolve2) => {
1360
- child.once("exit", (code) => resolve2(code));
1361
- child.once("error", () => resolve2(-1));
1579
+ const earlyExit = new Promise((resolve4) => {
1580
+ child.once("exit", (code) => resolve4(code));
1581
+ child.once("error", () => resolve4(-1));
1362
1582
  });
1363
- const liveness = new Promise((resolve2) => setTimeout(() => resolve2("alive"), 2e3));
1583
+ const liveness = new Promise((resolve4) => setTimeout(() => resolve4("alive"), 2e3));
1364
1584
  const outcome = await Promise.race([earlyExit, liveness]);
1365
1585
  if (outcome !== "alive") {
1366
1586
  const tail = readTail(logPath, 4e3);
@@ -1400,24 +1620,24 @@ ${tail}` : ""}`);
1400
1620
  }
1401
1621
  function readTail(filePath, bytes) {
1402
1622
  try {
1403
- const stat = fs13.statSync(filePath);
1623
+ const stat = fs14.statSync(filePath);
1404
1624
  const start = Math.max(0, stat.size - bytes);
1405
- const fd = fs13.openSync(filePath, "r");
1625
+ const fd = fs14.openSync(filePath, "r");
1406
1626
  try {
1407
1627
  const buf = Buffer.alloc(stat.size - start);
1408
- fs13.readSync(fd, buf, 0, buf.length, start);
1628
+ fs14.readSync(fd, buf, 0, buf.length, start);
1409
1629
  return buf.toString("utf8");
1410
1630
  } finally {
1411
- fs13.closeSync(fd);
1631
+ fs14.closeSync(fd);
1412
1632
  }
1413
1633
  } catch {
1414
1634
  return "";
1415
1635
  }
1416
1636
  }
1417
1637
  async function stopEmulator(logger = noopLogger) {
1418
- if (fs13.existsSync(PID_FILE)) {
1638
+ if (fs14.existsSync(PID_FILE)) {
1419
1639
  try {
1420
- const pid = Number(fs13.readFileSync(PID_FILE, "utf8").trim());
1640
+ const pid = Number(fs14.readFileSync(PID_FILE, "utf8").trim());
1421
1641
  if (Number.isFinite(pid) && pid > 0) {
1422
1642
  try {
1423
1643
  process.kill(pid, "SIGTERM");
@@ -1430,13 +1650,13 @@ async function stopEmulator(logger = noopLogger) {
1430
1650
  logger.warn(`Could not read PID file: ${err.message}`);
1431
1651
  }
1432
1652
  }
1433
- const [killCmd, ...killArgs] = os8.platform() === "win32" ? ["taskkill", "/IM", "qemu-system-x86_64.exe", "/F"] : ["pkill", "-f", "qemu-system-x86_64"];
1653
+ const [killCmd, ...killArgs] = os7.platform() === "win32" ? ["taskkill", "/IM", "qemu-system-x86_64.exe", "/F"] : ["pkill", "-f", "qemu-system-x86_64"];
1434
1654
  try {
1435
1655
  await runProcess({ command: killCmd, args: killArgs, logger });
1436
1656
  } catch (err) {
1437
1657
  logger.warn(`[emulator] could not run ${killCmd}: ${err.message}`);
1438
1658
  }
1439
- fs13.rm(PID_FILE, { force: true }, (err) => {
1659
+ fs14.rm(PID_FILE, { force: true }, (err) => {
1440
1660
  if (err) logger.warn(`Could not remove PID file: ${err.message}`);
1441
1661
  });
1442
1662
  }
@@ -1559,7 +1779,7 @@ async function getDisplaySize(config, opts = {}) {
1559
1779
 
1560
1780
  // src/hdc/wait.ts
1561
1781
  function delay(ms, signal) {
1562
- return new Promise((resolve2, reject) => {
1782
+ return new Promise((resolve4, reject) => {
1563
1783
  if (signal?.aborted) {
1564
1784
  reject(new CancelledError("Wait cancelled."));
1565
1785
  return;
@@ -1574,7 +1794,7 @@ function delay(ms, signal) {
1574
1794
  };
1575
1795
  const timer = setTimeout(() => {
1576
1796
  cleanup();
1577
- resolve2();
1797
+ resolve4();
1578
1798
  }, ms);
1579
1799
  signal?.addEventListener("abort", onAbort, { once: true });
1580
1800
  });
@@ -1603,21 +1823,21 @@ async function waitForCondition(opts) {
1603
1823
  }
1604
1824
 
1605
1825
  // src/hdc/app.ts
1606
- import * as fs15 from "fs";
1607
- import * as path14 from "path";
1826
+ import * as fs16 from "fs";
1827
+ import * as path16 from "path";
1608
1828
  import { spawn as spawn3 } from "child_process";
1609
1829
 
1610
1830
  // src/hdc/project.ts
1611
- import * as fs14 from "fs";
1612
- import * as path13 from "path";
1831
+ import * as fs15 from "fs";
1832
+ import * as path15 from "path";
1613
1833
  import JSON53 from "json5";
1614
1834
  function readJson5(filePath) {
1615
- const content = fs14.readFileSync(filePath, "utf-8");
1835
+ const content = fs15.readFileSync(filePath, "utf-8");
1616
1836
  return JSON53.parse(content);
1617
1837
  }
1618
1838
  function getBundleName(projectDir) {
1619
- const appJsonPath = path13.join(projectDir, "AppScope", "app.json5");
1620
- if (!fs14.existsSync(appJsonPath)) {
1839
+ const appJsonPath = path15.join(projectDir, "AppScope", "app.json5");
1840
+ if (!fs15.existsSync(appJsonPath)) {
1621
1841
  throw new OniroError(`Could not find app.json5 at ${appJsonPath}`);
1622
1842
  }
1623
1843
  const appJson = readJson5(appJsonPath);
@@ -1627,8 +1847,8 @@ function getBundleName(projectDir) {
1627
1847
  return appJson.app.bundleName;
1628
1848
  }
1629
1849
  function getMainAbility(projectDir, moduleName = "entry", abilityName) {
1630
- const moduleJsonPath = path13.join(projectDir, moduleName, "src", "main", "module.json5");
1631
- if (!fs14.existsSync(moduleJsonPath)) {
1850
+ const moduleJsonPath = path15.join(projectDir, moduleName, "src", "main", "module.json5");
1851
+ if (!fs15.existsSync(moduleJsonPath)) {
1632
1852
  throw new OniroError(`Could not find module.json5 at ${moduleJsonPath}`);
1633
1853
  }
1634
1854
  const moduleJson = readJson5(moduleJsonPath);
@@ -1651,8 +1871,8 @@ function getMainAbility(projectDir, moduleName = "entry", abilityName) {
1651
1871
  async function installApp(opts) {
1652
1872
  const logger = scopedLogger(opts.logger ?? noopLogger, "hdc");
1653
1873
  const relativeHapPath = opts.hapPath ?? opts.config.get("hapPath", defaultPaths.hapPath);
1654
- const hapPath = path14.isAbsolute(relativeHapPath) ? relativeHapPath : path14.join(opts.projectDir, relativeHapPath);
1655
- if (!fs15.existsSync(hapPath)) {
1874
+ const hapPath = path16.isAbsolute(relativeHapPath) ? relativeHapPath : path16.join(opts.projectDir, relativeHapPath);
1875
+ if (!fs16.existsSync(hapPath)) {
1656
1876
  throw new OniroError(`HAP file not found at: ${hapPath}. Build and sign the app first.`);
1657
1877
  }
1658
1878
  const result = await hdcExec({ config: opts.config, args: ["install", hapPath], timeoutMs: 3e5 });
@@ -1669,13 +1889,59 @@ async function installApp(opts) {
1669
1889
  function detectAaStartFailure(output) {
1670
1890
  return /failed to start ability|Error Code:\s*\d|Error Message:/i.test(output) ? output.trim() : null;
1671
1891
  }
1892
+ function parseMainAbilityFromBmDump(stdout) {
1893
+ const start = stdout.indexOf("{");
1894
+ if (start < 0) return null;
1895
+ let info;
1896
+ try {
1897
+ info = JSON.parse(stdout.slice(start));
1898
+ } catch {
1899
+ return null;
1900
+ }
1901
+ const mods = Array.isArray(info?.hapModuleInfos) ? info.hapModuleInfos : [];
1902
+ const abilityOf = (m) => m?.mainElementName || m?.mainAbility || void 0;
1903
+ const isEntry = (m) => m?.moduleType === 1 || m?.moduleType === "1" || m?.moduleType === "entry";
1904
+ const entry = mods.find((m) => isEntry(m) && abilityOf(m));
1905
+ const any = mods.find((m) => abilityOf(m));
1906
+ return entry && abilityOf(entry) || any && abilityOf(any) || null;
1907
+ }
1908
+ async function resolveInstalledMainAbility(opts) {
1909
+ const res = await shell({
1910
+ config: opts.config,
1911
+ command: `bm dump -n ${opts.bundle}`,
1912
+ deviceSerial: opts.deviceSerial,
1913
+ timeoutMs: 15e3,
1914
+ logger: opts.logger
1915
+ });
1916
+ const ability = parseMainAbilityFromBmDump(res.stdout);
1917
+ if (!ability) {
1918
+ throw new OniroError(
1919
+ `Could not determine the main ability for installed bundle ${opts.bundle} (is it installed?). Pass an explicit ability instead.`
1920
+ );
1921
+ }
1922
+ return ability;
1923
+ }
1672
1924
  async function launchApp(opts) {
1673
1925
  const logger = scopedLogger(opts.logger ?? noopLogger, "hdc");
1674
- const bundleName = getBundleName(opts.projectDir);
1675
- const mainAbility = getMainAbility(opts.projectDir, opts.moduleName, opts.abilityName);
1926
+ let bundleName;
1927
+ let mainAbility;
1928
+ if (opts.bundleName) {
1929
+ bundleName = opts.bundleName;
1930
+ mainAbility = opts.abilityName ?? await resolveInstalledMainAbility({
1931
+ config: opts.config,
1932
+ bundle: bundleName,
1933
+ deviceSerial: opts.deviceSerial,
1934
+ logger: opts.logger
1935
+ });
1936
+ } else {
1937
+ if (!opts.projectDir) throw new OniroError("launchApp requires either bundleName or projectDir.");
1938
+ bundleName = getBundleName(opts.projectDir);
1939
+ mainAbility = getMainAbility(opts.projectDir, opts.moduleName, opts.abilityName);
1940
+ }
1676
1941
  const result = await hdcExec({
1677
1942
  config: opts.config,
1678
- args: ["shell", "aa", "start", "-a", mainAbility, "-b", bundleName]
1943
+ args: ["shell", "aa", "start", "-a", mainAbility, "-b", bundleName],
1944
+ deviceSerial: opts.deviceSerial
1679
1945
  });
1680
1946
  if (result.stdout.trim()) logger.info(result.stdout.trim());
1681
1947
  if (result.stderr.trim()) logger.warn(result.stderr.trim());
@@ -1690,7 +1956,7 @@ function listRunningProcesses(config, options = {}) {
1690
1956
  const logger = options.logger ?? noopLogger;
1691
1957
  const timeoutMs = options.timeoutMs ?? 1e3;
1692
1958
  const hdc = getHdcPath(config);
1693
- return new Promise((resolve2, reject) => {
1959
+ return new Promise((resolve4, reject) => {
1694
1960
  const proc = spawn3(hdc, ["track-jpid"]);
1695
1961
  const processes = [];
1696
1962
  let resolved = false;
@@ -1704,7 +1970,7 @@ function listRunningProcesses(config, options = {}) {
1704
1970
  if (options.targetProcessName && name === options.targetProcessName && !resolved) {
1705
1971
  resolved = true;
1706
1972
  proc.kill();
1707
- resolve2(pid);
1973
+ resolve4(pid);
1708
1974
  return;
1709
1975
  }
1710
1976
  }
@@ -1725,7 +1991,7 @@ function listRunningProcesses(config, options = {}) {
1725
1991
  if (options.targetProcessName) {
1726
1992
  reject(new OniroError(`Could not find process for bundle: ${options.targetProcessName}`));
1727
1993
  } else {
1728
- resolve2(processes);
1994
+ resolve4(processes);
1729
1995
  }
1730
1996
  }, timeoutMs);
1731
1997
  proc.on("close", () => {
@@ -1735,7 +2001,7 @@ function listRunningProcesses(config, options = {}) {
1735
2001
  if (options.targetProcessName) {
1736
2002
  reject(new OniroError(`Could not find process for bundle: ${options.targetProcessName}`));
1737
2003
  } else {
1738
- resolve2(processes);
2004
+ resolve4(processes);
1739
2005
  }
1740
2006
  });
1741
2007
  });
@@ -1755,8 +2021,17 @@ async function findRunningProcess(opts) {
1755
2021
  logger: opts.logger
1756
2022
  });
1757
2023
  const pid = res.stdout.trim().split(/\s+/).filter(Boolean)[0];
1758
- if (res.code !== 0 || !pid || !/^\d+$/.test(pid)) return null;
1759
- return { pid, name: opts.bundle };
2024
+ if (res.code === 0 && pid && /^\d+$/.test(pid)) return { pid, name: opts.bundle };
2025
+ try {
2026
+ const list = await listRunningProcesses(opts.config, { timeoutMs: 1200, logger: opts.logger });
2027
+ if (Array.isArray(list)) {
2028
+ const mine = (name) => name === opts.bundle || name.startsWith(`${opts.bundle}/`) || name.startsWith(`${opts.bundle}:`);
2029
+ const match = list.find((p) => p.name === opts.bundle) ?? list.find((p) => mine(p.name));
2030
+ if (match) return match;
2031
+ }
2032
+ } catch {
2033
+ }
2034
+ return null;
1760
2035
  }
1761
2036
  async function uninstallApp(opts) {
1762
2037
  const logger = scopedLogger(opts.logger ?? noopLogger, "hdc");
@@ -1870,12 +2145,12 @@ async function reboot(opts) {
1870
2145
  }
1871
2146
 
1872
2147
  // src/hdc/screen.ts
1873
- import * as fs16 from "fs";
1874
- import * as os9 from "os";
1875
- import * as path15 from "path";
2148
+ import * as fs17 from "fs";
2149
+ import * as os8 from "os";
2150
+ import * as path17 from "path";
1876
2151
  import { randomBytes as randomBytes2 } from "crypto";
1877
2152
  function tmpName(ext) {
1878
- return path15.join(os9.tmpdir(), `oniro_screen_${randomBytes2(6).toString("hex")}.${ext}`);
2153
+ return path17.join(os8.tmpdir(), `oniro_screen_${randomBytes2(6).toString("hex")}.${ext}`);
1879
2154
  }
1880
2155
  async function takeScreenshot(opts) {
1881
2156
  const remote = "/data/local/tmp/oniro_screenshot.jpeg";
@@ -1885,11 +2160,11 @@ async function takeScreenshot(opts) {
1885
2160
  const local = tmpName("jpeg");
1886
2161
  try {
1887
2162
  await recvFile({ config: opts.config, remote, local, deviceSerial: opts.deviceSerial, timeoutMs });
1888
- const pixels = await fs16.promises.readFile(local);
2163
+ const pixels = await fs17.promises.readFile(local);
1889
2164
  const size = await getDisplaySize(opts.config, { deviceSerial: opts.deviceSerial }) ?? { width: 0, height: 0 };
1890
2165
  return { pixels, width: size.width, height: size.height };
1891
2166
  } finally {
1892
- fs16.promises.unlink(local).catch(() => {
2167
+ fs17.promises.unlink(local).catch(() => {
1893
2168
  });
1894
2169
  void shell({ config: opts.config, command: `rm -f ${remote}`, deviceSerial: opts.deviceSerial }).catch(() => {
1895
2170
  });
@@ -1908,9 +2183,9 @@ async function captureBurst(opts) {
1908
2183
  const local = tmpName(`burst_${i}.jpeg`);
1909
2184
  try {
1910
2185
  await recvFile({ config: opts.config, remote: `${remoteDir}/f${i}.jpeg`, local, deviceSerial: opts.deviceSerial });
1911
- frames.push(await fs16.promises.readFile(local));
2186
+ frames.push(await fs17.promises.readFile(local));
1912
2187
  } finally {
1913
- fs16.promises.unlink(local).catch(() => {
2188
+ fs17.promises.unlink(local).catch(() => {
1914
2189
  });
1915
2190
  }
1916
2191
  }
@@ -1954,8 +2229,8 @@ function buildInputCommand(opts) {
1954
2229
  }
1955
2230
  async function sendInput(opts) {
1956
2231
  const cmd = buildInputCommand(opts);
1957
- const res = await shell({ config: opts.config, command: cmd, deviceSerial: opts.deviceSerial, timeoutMs: 3e4, logger: opts.logger });
1958
- ensureOk(res, `hdc shell ${cmd}`);
2232
+ const res = await shellChecked({ config: opts.config, command: cmd, deviceSerial: opts.deviceSerial, timeoutMs: 3e4, logger: opts.logger });
2233
+ ensureRemoteOk(res, `hdc shell ${cmd}`);
1959
2234
  }
1960
2235
  function buildGestureCommands(waypoints) {
1961
2236
  if (waypoints.length < 2) {
@@ -1998,32 +2273,47 @@ function waypointsToRawEvents(waypoints, holdStartMs = 0, holdEndMs = 0) {
1998
2273
  events.push({ type: "up", x: last.x, y: last.y, t: last.t + holdStartMs + holdEndMs });
1999
2274
  return events;
2000
2275
  }
2276
+ var UITEST_VELOCITY_MIN = 200;
2277
+ var UITEST_VELOCITY_MAX = 4e4;
2278
+ function buildGestureUitestCommand(waypoints, opts = {}) {
2279
+ if (waypoints.length < 2) throw new OniroError("sendGesture requires at least two waypoints.");
2280
+ const a = waypoints[0];
2281
+ const b = waypoints[waypoints.length - 1];
2282
+ const dist = Math.hypot(b.x - a.x, b.y - a.y);
2283
+ const dtMs = b.t - a.t;
2284
+ const op = (opts.holdStartMs ?? 0) > 0 ? "drag" : "swipe";
2285
+ let cmd = `uitest uiInput ${op} ${a.x} ${a.y} ${b.x} ${b.y}`;
2286
+ if (dtMs > 0 && dist > 0) {
2287
+ const v = Math.round(dist / (dtMs / 1e3));
2288
+ cmd += ` ${Math.max(UITEST_VELOCITY_MIN, Math.min(UITEST_VELOCITY_MAX, v))}`;
2289
+ }
2290
+ return cmd;
2291
+ }
2001
2292
  async function sendGesture(opts) {
2002
- const wantsHold = (opts.holdStartMs ?? 0) > 0 || (opts.holdEndMs ?? 0) > 0;
2003
- if (wantsHold) {
2004
- await sendRawTouch({
2005
- config: opts.config,
2006
- events: waypointsToRawEvents(opts.waypoints, opts.holdStartMs, opts.holdEndMs),
2007
- deviceSerial: opts.deviceSerial,
2008
- logger: opts.logger
2009
- });
2010
- return;
2293
+ if ((opts.holdEndMs ?? 0) > 0) {
2294
+ opts.logger?.warn(
2295
+ "sendGesture: hold-end has no uitest equivalent and is ignored; use sendRawTouch for a trailing hold."
2296
+ );
2011
2297
  }
2012
- for (const cmd of buildGestureCommands(opts.waypoints)) {
2013
- const res = await shell({ config: opts.config, command: cmd, deviceSerial: opts.deviceSerial, timeoutMs: 3e4, logger: opts.logger });
2014
- ensureOk(res, `hdc shell ${cmd}`);
2298
+ if (opts.waypoints.length > 2) {
2299
+ opts.logger?.debug(
2300
+ `sendGesture: ${opts.waypoints.length} waypoints collapsed to first\u2192last (uitest has no polyline path).`
2301
+ );
2015
2302
  }
2303
+ const cmd = buildGestureUitestCommand(opts.waypoints, { holdStartMs: opts.holdStartMs });
2304
+ const res = await shellChecked({ config: opts.config, command: cmd, deviceSerial: opts.deviceSerial, timeoutMs: 3e4, logger: opts.logger });
2305
+ ensureRemoteOk(res, `hdc shell ${cmd}`);
2016
2306
  }
2017
2307
  async function sendRawTouch(opts) {
2018
2308
  const cmd = buildRawTouchCommand(opts.events);
2019
- const res = await shell({ config: opts.config, command: cmd, deviceSerial: opts.deviceSerial, timeoutMs: 3e4, logger: opts.logger });
2020
- ensureOk(res, `hdc shell ${cmd}`);
2309
+ const res = await shellChecked({ config: opts.config, command: cmd, deviceSerial: opts.deviceSerial, timeoutMs: 3e4, logger: opts.logger });
2310
+ ensureRemoteOk(res, `hdc shell ${cmd}`);
2021
2311
  }
2022
2312
 
2023
2313
  // src/hdc/dump.ts
2024
- import * as fs17 from "fs";
2025
- import * as os10 from "os";
2026
- import * as path16 from "path";
2314
+ import * as fs18 from "fs";
2315
+ import * as os9 from "os";
2316
+ import * as path18 from "path";
2027
2317
  import { randomBytes as randomBytes3 } from "crypto";
2028
2318
  function parseBounds(s) {
2029
2319
  const m = /^\[(-?\d+),(-?\d+)\]\[(-?\d+),(-?\d+)\]$/.exec(s ?? "");
@@ -2108,10 +2398,10 @@ async function dumpLayout(opts) {
2108
2398
  if (!m) throw new OniroError(`Could not parse dumpLayout output:
2109
2399
  ${dump.stdout}`);
2110
2400
  const remote = m[1];
2111
- const local = path16.join(os10.tmpdir(), `oniro_layout_${randomBytes3(6).toString("hex")}.json`);
2401
+ const local = path18.join(os9.tmpdir(), `oniro_layout_${randomBytes3(6).toString("hex")}.json`);
2112
2402
  try {
2113
2403
  await recvFile({ config: opts.config, remote, local, deviceSerial: opts.deviceSerial, timeoutMs });
2114
- const parsed = JSON.parse(await fs17.promises.readFile(local, "utf8"));
2404
+ const parsed = JSON.parse(await fs18.promises.readFile(local, "utf8"));
2115
2405
  const display = await getDisplaySize(opts.config, { deviceSerial: opts.deviceSerial });
2116
2406
  if (opts.prune === false) {
2117
2407
  return { display, tree: parsed };
@@ -2120,7 +2410,7 @@ ${dump.stdout}`);
2120
2410
  const tree = Array.isArray(prunedRaw) ? { children: prunedRaw } : prunedRaw ?? { children: [] };
2121
2411
  return { display, tree };
2122
2412
  } finally {
2123
- fs17.promises.unlink(local).catch(() => {
2413
+ fs18.promises.unlink(local).catch(() => {
2124
2414
  });
2125
2415
  void shell({ config: opts.config, command: `rm -f ${remote}`, deviceSerial: opts.deviceSerial }).catch(() => {
2126
2416
  });
@@ -2132,7 +2422,7 @@ import { spawn as spawn4 } from "child_process";
2132
2422
  function setHilogLevel(opts) {
2133
2423
  const logger = opts.logger ?? noopLogger;
2134
2424
  const hdc = getHdcPath(opts.config);
2135
- return new Promise((resolve2, reject) => {
2425
+ return new Promise((resolve4, reject) => {
2136
2426
  const child = spawn4(hdc, ["shell", "hilog", "-b", opts.level]);
2137
2427
  let stderr = "";
2138
2428
  child.stderr.on("data", (chunk) => {
@@ -2144,7 +2434,7 @@ function setHilogLevel(opts) {
2144
2434
  });
2145
2435
  child.once("close", (code) => {
2146
2436
  if (code === 0) {
2147
- resolve2();
2437
+ resolve4();
2148
2438
  } else {
2149
2439
  const msg = stderr.trim() || `hdc shell hilog -b ${opts.level} exited with code ${code}`;
2150
2440
  logger.error(`[hilog] ${msg}`);
@@ -2199,7 +2489,7 @@ async function resolveProcessId(config, bundle, deviceSerial) {
2199
2489
  return proc?.pid;
2200
2490
  }
2201
2491
  function waitForLog(opts) {
2202
- return new Promise((resolve2, reject) => {
2492
+ return new Promise((resolve4, reject) => {
2203
2493
  const deadline = Date.now() + opts.timeoutMs;
2204
2494
  let settled = false;
2205
2495
  let child = null;
@@ -2221,7 +2511,7 @@ function waitForLog(opts) {
2221
2511
  if (settled) return;
2222
2512
  settled = true;
2223
2513
  cleanup();
2224
- resolve2(entry);
2514
+ resolve4(entry);
2225
2515
  };
2226
2516
  const fail = (err) => {
2227
2517
  if (settled) return;
@@ -2268,7 +2558,7 @@ function waitForLog(opts) {
2268
2558
  });
2269
2559
  }
2270
2560
  function watchLog(opts) {
2271
- return new Promise((resolve2, reject) => {
2561
+ return new Promise((resolve4, reject) => {
2272
2562
  const matches = [];
2273
2563
  let settled = false;
2274
2564
  let child = null;
@@ -2288,7 +2578,7 @@ function watchLog(opts) {
2288
2578
  if (settled) return;
2289
2579
  settled = true;
2290
2580
  cleanup();
2291
- resolve2(opts.dedup === false ? matches : dedupEntries(matches));
2581
+ resolve4(opts.dedup === false ? matches : dedupEntries(matches));
2292
2582
  };
2293
2583
  const onAbort = () => {
2294
2584
  if (settled) return;
@@ -2340,8 +2630,8 @@ async function dumpLog(opts) {
2340
2630
  }
2341
2631
 
2342
2632
  // src/build/runHvigorw.ts
2343
- import * as fs18 from "fs";
2344
- import * as path17 from "path";
2633
+ import * as fs19 from "fs";
2634
+ import * as path19 from "path";
2345
2635
  import { spawn as spawn5 } from "child_process";
2346
2636
  function buildHvigorwArgs(opts) {
2347
2637
  const args = [opts.task ?? "assembleHap", "--mode", "module"];
@@ -2356,17 +2646,17 @@ function buildHvigorwArgs(opts) {
2356
2646
  function runHvigorw(opts) {
2357
2647
  const { config, projectDir } = opts;
2358
2648
  const logger = opts.logger ?? noopLogger;
2359
- if (!fs18.existsSync(path17.join(projectDir, "build-profile.json5"))) {
2649
+ if (!fs19.existsSync(path19.join(projectDir, "build-profile.json5"))) {
2360
2650
  throw new OniroError(`Not an OpenHarmony project: ${projectDir} (build-profile.json5 not found).`);
2361
2651
  }
2362
- if (!fs18.existsSync(getCmdToolsPath(config))) {
2652
+ if (!fs19.existsSync(getCmdToolsPath(config))) {
2363
2653
  throw new CmdToolsNotInstalledError(getCmdToolsPath(config));
2364
2654
  }
2365
2655
  const hvigorw = getHvigorwPath(config, projectDir);
2366
2656
  const args = buildHvigorwArgs(opts);
2367
2657
  if (process.platform !== "win32") {
2368
2658
  try {
2369
- fs18.chmodSync(hvigorw, 493);
2659
+ fs19.chmodSync(hvigorw, 493);
2370
2660
  } catch {
2371
2661
  }
2372
2662
  }
@@ -2374,7 +2664,7 @@ function runHvigorw(opts) {
2374
2664
  ...process.env,
2375
2665
  OHOS_BASE_SDK_HOME: getOhosBaseSdkHome(config)
2376
2666
  };
2377
- return new Promise((resolve2, reject) => {
2667
+ return new Promise((resolve4, reject) => {
2378
2668
  if (opts.abortSignal?.aborted) {
2379
2669
  reject(new CancelledError("hvigorw build cancelled."));
2380
2670
  return;
@@ -2415,19 +2705,18 @@ function runHvigorw(opts) {
2415
2705
  settled = true;
2416
2706
  cleanup();
2417
2707
  const exitCode = code ?? 1;
2418
- if (exitCode === 0) resolve2({ exitCode });
2708
+ if (exitCode === 0) resolve4({ exitCode });
2419
2709
  else reject(new OniroError(`hvigorw exited with code ${exitCode}`));
2420
2710
  });
2421
2711
  });
2422
2712
  }
2423
2713
 
2424
2714
  // src/build/discoverHaps.ts
2425
- import * as fs19 from "fs";
2426
- import * as path18 from "path";
2427
- function moduleOf(projectDir, hapPath) {
2428
- const rel = path18.relative(projectDir, hapPath);
2429
- const first = rel.split(path18.sep)[0];
2430
- return first && first !== ".." ? first : "(root)";
2715
+ import * as fs20 from "fs";
2716
+ import * as path20 from "path";
2717
+ function moduleOfHap(hapFileName) {
2718
+ const dash = hapFileName.indexOf("-");
2719
+ return dash > 0 ? hapFileName.slice(0, dash) : hapFileName.replace(/\.hap$/i, "");
2431
2720
  }
2432
2721
  async function discoverHaps(opts) {
2433
2722
  const { projectDir } = opts;
@@ -2435,14 +2724,14 @@ async function discoverHaps(opts) {
2435
2724
  const result = {};
2436
2725
  const walk = async (dir, depth) => {
2437
2726
  if (depth > maxDepth) return;
2438
- const entries = await fs19.promises.readdir(dir, { withFileTypes: true }).catch(() => []);
2727
+ const entries = await fs20.promises.readdir(dir, { withFileTypes: true }).catch(() => []);
2439
2728
  for (const e of entries) {
2440
- const full = path18.join(dir, e.name);
2729
+ const full = path20.join(dir, e.name);
2441
2730
  if (e.isDirectory()) {
2442
2731
  if (e.name === "node_modules" || e.name === "oh_modules" || e.name === ".git") continue;
2443
2732
  await walk(full, depth + 1);
2444
2733
  } else if (e.isFile() && e.name.endsWith(".hap")) {
2445
- const moduleName = moduleOf(projectDir, full);
2734
+ const moduleName = moduleOfHap(e.name);
2446
2735
  const bucket = result[moduleName] ??= { signed: [], unsigned: [] };
2447
2736
  if (e.name.includes("unsigned")) bucket.unsigned.push(full);
2448
2737
  else if (e.name.includes("signed")) bucket.signed.push(full);
@@ -2454,8 +2743,8 @@ async function discoverHaps(opts) {
2454
2743
  }
2455
2744
 
2456
2745
  // src/build/ohpm.ts
2457
- import * as fs20 from "fs";
2458
- import * as path19 from "path";
2746
+ import * as fs21 from "fs";
2747
+ import * as path21 from "path";
2459
2748
  function runOhpm(opts) {
2460
2749
  return runProcess({
2461
2750
  command: getOhpmPath(opts.config),
@@ -2468,7 +2757,7 @@ function runOhpm(opts) {
2468
2757
  });
2469
2758
  }
2470
2759
  async function ensureOhModules(opts) {
2471
- if (fs20.existsSync(path19.join(opts.projectDir, "oh_modules"))) {
2760
+ if (fs21.existsSync(path21.join(opts.projectDir, "oh_modules"))) {
2472
2761
  return { installed: false };
2473
2762
  }
2474
2763
  const res = await runOhpm({
@@ -2563,21 +2852,21 @@ function isValidBundleName(bundleName) {
2563
2852
  }
2564
2853
 
2565
2854
  // src/project/templates.ts
2566
- import * as fs21 from "fs";
2567
- import * as path20 from "path";
2855
+ import * as fs22 from "fs";
2856
+ import * as path22 from "path";
2568
2857
  function toHumanTemplateName(folderName) {
2569
2858
  return folderName.replace(/([a-z])([A-Z])/g, "$1 $2");
2570
2859
  }
2571
2860
  function listTemplates(templateRoot) {
2572
- if (!fs21.existsSync(templateRoot)) return [];
2573
- const entries = fs21.readdirSync(templateRoot, { withFileTypes: true });
2861
+ if (!fs22.existsSync(templateRoot)) return [];
2862
+ const entries = fs22.readdirSync(templateRoot, { withFileTypes: true });
2574
2863
  return entries.filter((e) => e.isDirectory()).map((e) => {
2575
- const dir = path20.join(templateRoot, e.name);
2576
- const metaPath = path20.join(dir, "template.json");
2864
+ const dir = path22.join(templateRoot, e.name);
2865
+ const metaPath = path22.join(dir, "template.json");
2577
2866
  let meta = {};
2578
- if (fs21.existsSync(metaPath)) {
2867
+ if (fs22.existsSync(metaPath)) {
2579
2868
  try {
2580
- meta = JSON.parse(fs21.readFileSync(metaPath, "utf8"));
2869
+ meta = JSON.parse(fs22.readFileSync(metaPath, "utf8"));
2581
2870
  } catch {
2582
2871
  }
2583
2872
  }
@@ -2592,57 +2881,57 @@ function listTemplates(templateRoot) {
2592
2881
  function validateTemplateLayout(templateDir, defaultModuleName) {
2593
2882
  const required = [
2594
2883
  "build-profile.json5",
2595
- path20.join("AppScope", "app.json5"),
2596
- path20.join(defaultModuleName, "src", "main", "module.json5"),
2597
- path20.join(defaultModuleName, "oh-package.json5"),
2884
+ path22.join("AppScope", "app.json5"),
2885
+ path22.join(defaultModuleName, "src", "main", "module.json5"),
2886
+ path22.join(defaultModuleName, "oh-package.json5"),
2598
2887
  "hvigorfile.ts"
2599
2888
  ];
2600
- return required.filter((rel) => !fs21.existsSync(path20.join(templateDir, rel)));
2889
+ return required.filter((rel) => !fs22.existsSync(path22.join(templateDir, rel)));
2601
2890
  }
2602
2891
 
2603
2892
  // src/project/createScaffold.ts
2604
- import * as fs23 from "fs";
2605
- import * as path21 from "path";
2893
+ import * as fs24 from "fs";
2894
+ import * as path23 from "path";
2606
2895
 
2607
2896
  // src/project/jsonHelpers.ts
2608
- import * as fs22 from "fs";
2897
+ import * as fs23 from "fs";
2609
2898
  import JSON54 from "json5";
2610
2899
  function readJson5File(filePath) {
2611
- return JSON54.parse(fs22.readFileSync(filePath, "utf8"));
2900
+ return JSON54.parse(fs23.readFileSync(filePath, "utf8"));
2612
2901
  }
2613
2902
  function writeJson5File(filePath, value) {
2614
- fs22.writeFileSync(filePath, JSON.stringify(value, null, 2) + "\n", "utf8");
2903
+ fs23.writeFileSync(filePath, JSON.stringify(value, null, 2) + "\n", "utf8");
2615
2904
  }
2616
2905
  function readJsonFile(filePath) {
2617
- return JSON.parse(fs22.readFileSync(filePath, "utf8"));
2906
+ return JSON.parse(fs23.readFileSync(filePath, "utf8"));
2618
2907
  }
2619
2908
  function writeJsonFile(filePath, value) {
2620
- fs22.writeFileSync(filePath, JSON.stringify(value, null, 2) + "\n", "utf8");
2909
+ fs23.writeFileSync(filePath, JSON.stringify(value, null, 2) + "\n", "utf8");
2621
2910
  }
2622
2911
 
2623
2912
  // src/project/createScaffold.ts
2624
2913
  var IGNORED_DIRS = /* @__PURE__ */ new Set(["oh_modules", "node_modules", "build", ".hvigor"]);
2625
2914
  async function pathExists(p) {
2626
2915
  try {
2627
- await fs23.promises.access(p);
2916
+ await fs24.promises.access(p);
2628
2917
  return true;
2629
2918
  } catch {
2630
2919
  return false;
2631
2920
  }
2632
2921
  }
2633
2922
  async function copyDirRecursive(srcDir, destDir) {
2634
- await fs23.promises.mkdir(destDir, { recursive: true });
2635
- const entries = await fs23.promises.readdir(srcDir, { withFileTypes: true });
2923
+ await fs24.promises.mkdir(destDir, { recursive: true });
2924
+ const entries = await fs24.promises.readdir(srcDir, { withFileTypes: true });
2636
2925
  for (const entry of entries) {
2637
- const src = path21.join(srcDir, entry.name);
2638
- const dest = path21.join(destDir, entry.name);
2926
+ const src = path23.join(srcDir, entry.name);
2927
+ const dest = path23.join(destDir, entry.name);
2639
2928
  if (entry.isDirectory()) {
2640
2929
  await copyDirRecursive(src, dest);
2641
2930
  } else if (entry.isSymbolicLink()) {
2642
2931
  continue;
2643
2932
  } else if (entry.isFile()) {
2644
2933
  if (entry.name === "template.json") continue;
2645
- await fs23.promises.copyFile(src, dest);
2934
+ await fs24.promises.copyFile(src, dest);
2646
2935
  }
2647
2936
  }
2648
2937
  }
@@ -2650,9 +2939,9 @@ async function normalizeJson5ToJson(projectDir, logger) {
2650
2939
  const stack = [projectDir];
2651
2940
  while (stack.length > 0) {
2652
2941
  const currentDir = stack.pop();
2653
- const entries = await fs23.promises.readdir(currentDir, { withFileTypes: true });
2942
+ const entries = await fs24.promises.readdir(currentDir, { withFileTypes: true });
2654
2943
  for (const entry of entries) {
2655
- const fullPath = path21.join(currentDir, entry.name);
2944
+ const fullPath = path23.join(currentDir, entry.name);
2656
2945
  if (entry.isDirectory()) {
2657
2946
  if (IGNORED_DIRS.has(entry.name)) continue;
2658
2947
  stack.push(fullPath);
@@ -2674,23 +2963,23 @@ function toJavaPropertiesPath(p) {
2674
2963
  function createOrUpdateLocalProperties(projectDir, sdkDir) {
2675
2964
  const content = `sdk.dir=${toJavaPropertiesPath(sdkDir)}
2676
2965
  `;
2677
- fs23.writeFileSync(path21.join(projectDir, "local.properties"), content, "utf8");
2966
+ fs24.writeFileSync(path23.join(projectDir, "local.properties"), content, "utf8");
2678
2967
  }
2679
2968
  function renameIfExists(fromPath, toPath) {
2680
- if (fs23.existsSync(fromPath) && fromPath !== toPath) {
2681
- fs23.renameSync(fromPath, toPath);
2969
+ if (fs24.existsSync(fromPath) && fromPath !== toPath) {
2970
+ fs24.renameSync(fromPath, toPath);
2682
2971
  }
2683
2972
  }
2684
2973
  function updateTemplateConfigs(projectDir, args, sdkBaseDir) {
2685
- const appJsonPath = path21.join(projectDir, "AppScope", "app.json5");
2686
- if (fs23.existsSync(appJsonPath)) {
2974
+ const appJsonPath = path23.join(projectDir, "AppScope", "app.json5");
2975
+ if (fs24.existsSync(appJsonPath)) {
2687
2976
  const appJson = readJson5File(appJsonPath);
2688
2977
  appJson.app = appJson.app ?? {};
2689
2978
  appJson.app.bundleName = args.bundleName;
2690
2979
  writeJson5File(appJsonPath, appJson);
2691
2980
  }
2692
- const stringsPath = path21.join(projectDir, "AppScope", "resources", "base", "element", "string.json");
2693
- if (fs23.existsSync(stringsPath)) {
2981
+ const stringsPath = path23.join(projectDir, "AppScope", "resources", "base", "element", "string.json");
2982
+ if (fs24.existsSync(stringsPath)) {
2694
2983
  const strings = readJsonFile(stringsPath);
2695
2984
  if (Array.isArray(strings.string)) {
2696
2985
  const appName = strings.string.find((s) => s.name === "app_name");
@@ -2698,8 +2987,8 @@ function updateTemplateConfigs(projectDir, args, sdkBaseDir) {
2698
2987
  }
2699
2988
  writeJsonFile(stringsPath, strings);
2700
2989
  }
2701
- const buildProfilePath = path21.join(projectDir, "build-profile.json5");
2702
- if (fs23.existsSync(buildProfilePath)) {
2990
+ const buildProfilePath = path23.join(projectDir, "build-profile.json5");
2991
+ if (fs24.existsSync(buildProfilePath)) {
2703
2992
  const buildProfile = readJson5File(buildProfilePath);
2704
2993
  buildProfile.app = buildProfile.app ?? {};
2705
2994
  if (Array.isArray(buildProfile.app.products) && buildProfile.app.products.length > 0) {
@@ -2712,23 +3001,23 @@ function updateTemplateConfigs(projectDir, args, sdkBaseDir) {
2712
3001
  }
2713
3002
  writeJson5File(buildProfilePath, buildProfile);
2714
3003
  }
2715
- const moduleJsonPath = path21.join(projectDir, args.moduleName, "src", "main", "module.json5");
2716
- if (fs23.existsSync(moduleJsonPath)) {
3004
+ const moduleJsonPath = path23.join(projectDir, args.moduleName, "src", "main", "module.json5");
3005
+ if (fs24.existsSync(moduleJsonPath)) {
2717
3006
  const moduleJson = readJson5File(moduleJsonPath);
2718
3007
  moduleJson.module = moduleJson.module ?? {};
2719
3008
  moduleJson.module.name = args.moduleName;
2720
3009
  writeJson5File(moduleJsonPath, moduleJson);
2721
3010
  }
2722
- const moduleOhPackagePath = path21.join(projectDir, args.moduleName, "oh-package.json5");
2723
- if (fs23.existsSync(moduleOhPackagePath)) {
3011
+ const moduleOhPackagePath = path23.join(projectDir, args.moduleName, "oh-package.json5");
3012
+ if (fs24.existsSync(moduleOhPackagePath)) {
2724
3013
  const pkg = readJson5File(moduleOhPackagePath);
2725
3014
  pkg.name = args.moduleName;
2726
3015
  writeJson5File(moduleOhPackagePath, pkg);
2727
3016
  }
2728
- const vscodeDir = path21.join(projectDir, ".vscode");
2729
- fs23.mkdirSync(vscodeDir, { recursive: true });
3017
+ const vscodeDir = path23.join(projectDir, ".vscode");
3018
+ fs24.mkdirSync(vscodeDir, { recursive: true });
2730
3019
  const hapPath = `${args.moduleName}/build/default/outputs/default/${args.moduleName}-default-signed.hap`;
2731
- writeJsonFile(path21.join(vscodeDir, "settings.json"), {
3020
+ writeJsonFile(path23.join(vscodeDir, "settings.json"), {
2732
3021
  "oniro.hapPath": hapPath,
2733
3022
  "files.associations": { "*.json5": "jsonc" }
2734
3023
  });
@@ -2745,7 +3034,7 @@ async function createScaffold(opts) {
2745
3034
  if (!opts.location || !await pathExists(opts.location)) {
2746
3035
  throw new OniroError(`Location does not exist: ${opts.location}`);
2747
3036
  }
2748
- const templateDir = path21.join(opts.templateRoot, opts.templateId);
3037
+ const templateDir = path23.join(opts.templateRoot, opts.templateId);
2749
3038
  if (!await pathExists(templateDir)) {
2750
3039
  throw new OniroError(`Template not found: ${templateDir}`);
2751
3040
  }
@@ -2758,17 +3047,17 @@ async function createScaffold(opts) {
2758
3047
  throw new OniroError(`Template '${opts.templateId}' is missing required files:
2759
3048
  - ${missing.join("\n- ")}`);
2760
3049
  }
2761
- const projectDir = path21.join(opts.location, opts.projectName);
3050
+ const projectDir = path23.join(opts.location, opts.projectName);
2762
3051
  if (await pathExists(projectDir)) {
2763
3052
  if (!opts.overwrite) {
2764
3053
  throw new OniroError(`Destination already exists: ${projectDir}. Pass overwrite: true to replace it.`);
2765
3054
  }
2766
- await fs23.promises.rm(projectDir, { recursive: true, force: true });
3055
+ await fs24.promises.rm(projectDir, { recursive: true, force: true });
2767
3056
  }
2768
3057
  logger.info(`[create] Scaffolding ${opts.templateId} at ${projectDir}`);
2769
3058
  await copyDirRecursive(templateDir, projectDir);
2770
3059
  if (moduleName !== defaultModuleName) {
2771
- renameIfExists(path21.join(projectDir, defaultModuleName), path21.join(projectDir, moduleName));
3060
+ renameIfExists(path23.join(projectDir, defaultModuleName), path23.join(projectDir, moduleName));
2772
3061
  }
2773
3062
  updateTemplateConfigs(
2774
3063
  projectDir,
@@ -2785,11 +3074,11 @@ async function createScaffold(opts) {
2785
3074
  }
2786
3075
 
2787
3076
  // src/project/listModules.ts
2788
- import * as fs24 from "fs";
2789
- import * as path22 from "path";
3077
+ import * as fs25 from "fs";
3078
+ import * as path24 from "path";
2790
3079
  function listModules(opts) {
2791
- const buildProfile = path22.join(opts.projectDir, "build-profile.json5");
2792
- if (!fs24.existsSync(buildProfile)) {
3080
+ const buildProfile = path24.join(opts.projectDir, "build-profile.json5");
3081
+ if (!fs25.existsSync(buildProfile)) {
2793
3082
  throw new OniroError(`build-profile.json5 not found at ${buildProfile}`);
2794
3083
  }
2795
3084
  const parsed = readJson5File(buildProfile);
@@ -2801,12 +3090,12 @@ function listModules(opts) {
2801
3090
  }
2802
3091
 
2803
3092
  // src/project/abilities.ts
2804
- import * as fs25 from "fs";
2805
- import * as path23 from "path";
3093
+ import * as fs26 from "fs";
3094
+ import * as path25 from "path";
2806
3095
  function listAbilities(opts) {
2807
3096
  const moduleName = opts.moduleName ?? "entry";
2808
- const moduleJsonPath = path23.join(opts.projectDir, moduleName, "src", "main", "module.json5");
2809
- if (!fs25.existsSync(moduleJsonPath)) {
3097
+ const moduleJsonPath = path25.join(opts.projectDir, moduleName, "src", "main", "module.json5");
3098
+ if (!fs26.existsSync(moduleJsonPath)) {
2810
3099
  throw new OniroError(`Could not find module.json5 at ${moduleJsonPath}`);
2811
3100
  }
2812
3101
  const parsed = readJson5File(moduleJsonPath);
@@ -2814,12 +3103,12 @@ function listAbilities(opts) {
2814
3103
  }
2815
3104
 
2816
3105
  // src/project/prepareSigning.ts
2817
- import * as fs26 from "fs";
2818
- import * as path24 from "path";
3106
+ import * as fs27 from "fs";
3107
+ import * as path26 from "path";
2819
3108
  function hasSigningArtifacts(dir) {
2820
- if (!fs26.existsSync(path24.join(dir, "signatures"))) return false;
2821
- const buildProfile = path24.join(dir, "build-profile.json5");
2822
- if (!fs26.existsSync(buildProfile)) return false;
3109
+ if (!fs27.existsSync(path26.join(dir, "signatures"))) return false;
3110
+ const buildProfile = path26.join(dir, "build-profile.json5");
3111
+ if (!fs27.existsSync(buildProfile)) return false;
2823
3112
  try {
2824
3113
  const parsed = readJson5File(buildProfile);
2825
3114
  return Array.isArray(parsed.app?.signingConfigs) && parsed.app.signingConfigs.length > 0;
@@ -2909,8 +3198,8 @@ async function diffHapAssets(opts) {
2909
3198
  }
2910
3199
 
2911
3200
  // src/install/apply.ts
2912
- import * as fs27 from "fs";
2913
- import * as path25 from "path";
3201
+ import * as fs28 from "fs";
3202
+ import * as path27 from "path";
2914
3203
  function decideInstallMethod(s) {
2915
3204
  if (!s.signInfoInconsistent) return "replace";
2916
3205
  if (s.isSystemBundle && !s.allowUninstall) return "refuse";
@@ -2931,7 +3220,7 @@ async function pidOf(config, bundle, deviceSerial) {
2931
3220
  async function resolveHapPath(opts) {
2932
3221
  let hapPath = opts.hapPath;
2933
3222
  if (hapPath) {
2934
- if (!path25.isAbsolute(hapPath) && opts.projectDir) hapPath = path25.join(opts.projectDir, hapPath);
3223
+ if (!path27.isAbsolute(hapPath) && opts.projectDir) hapPath = path27.join(opts.projectDir, hapPath);
2935
3224
  } else {
2936
3225
  if (!opts.projectDir) throw new OniroError("applyChanges requires hapPath or projectDir.");
2937
3226
  const haps = await discoverHaps({ projectDir: opts.projectDir });
@@ -2950,7 +3239,7 @@ async function resolveHapPath(opts) {
2950
3239
  hapPath = withSigned[0][1].signed[0];
2951
3240
  }
2952
3241
  }
2953
- if (!fs27.existsSync(hapPath)) throw new OniroError(`HAP not found at ${hapPath}.`);
3242
+ if (!fs28.existsSync(hapPath)) throw new OniroError(`HAP not found at ${hapPath}.`);
2954
3243
  return hapPath;
2955
3244
  }
2956
3245
  async function applyChanges(opts) {
@@ -2960,7 +3249,7 @@ async function applyChanges(opts) {
2960
3249
  const allowUninstall = opts.allowUninstall ?? false;
2961
3250
  const hapPath = await resolveHapPath(opts);
2962
3251
  let assetsChanged = false;
2963
- if (opts.installedHapPath && fs27.existsSync(opts.installedHapPath)) {
3252
+ if (opts.installedHapPath && fs28.existsSync(opts.installedHapPath)) {
2964
3253
  try {
2965
3254
  const diff = await diffHapAssets({ installedHap: opts.installedHapPath, newHap: hapPath });
2966
3255
  assetsChanged = diff.addedAssetPaths.length > 0 || diff.removedAssetPaths.length > 0;
@@ -3002,7 +3291,7 @@ async function applyChanges(opts) {
3002
3291
  }
3003
3292
 
3004
3293
  // src/index.ts
3005
- var VERSION = "0.7.0";
3294
+ var VERSION = "0.8.0";
3006
3295
  export {
3007
3296
  ALL_SDKS,
3008
3297
  APL_VALUES,
@@ -3011,22 +3300,28 @@ export {
3011
3300
  ChecksumMismatchError,
3012
3301
  CmdToolsNotInstalledError,
3013
3302
  CommandFailedError,
3303
+ INSTALL_TMP_DIRNAME,
3304
+ InsufficientSpaceError,
3014
3305
  OHOS_URL_BASE,
3015
3306
  OniroError,
3016
3307
  SdkNotInstalledError,
3308
+ TMP_DIR_HINT,
3017
3309
  UnsupportedPlatformError,
3018
3310
  VERSION,
3019
3311
  applyChanges,
3020
3312
  attemptHdcConnection,
3021
3313
  buildGestureCommands,
3314
+ buildGestureUitestCommand,
3022
3315
  buildHap,
3023
3316
  buildHvigorwArgs,
3024
3317
  buildInputCommand,
3025
3318
  buildRawTouchCommand,
3026
3319
  captureBurst,
3027
3320
  consoleLogger,
3321
+ createInstallTempDir,
3028
3322
  createMaterial,
3029
3323
  createScaffold,
3324
+ createTempWorkDir,
3030
3325
  decideInstallMethod,
3031
3326
  decideReboot,
3032
3327
  decryptPwd,
@@ -3047,14 +3342,17 @@ export {
3047
3342
  dumpScreen,
3048
3343
  dumpWindow,
3049
3344
  encryptPwd,
3345
+ ensureFreeSpace,
3050
3346
  ensureOhModules,
3051
3347
  ensureOk,
3348
+ ensureRemoteOk,
3052
3349
  extractTarball,
3053
3350
  extractZipWithProgress,
3054
3351
  findAppProcessId,
3055
3352
  findCmdToolsSourceDir,
3056
3353
  findRunningProcess,
3057
3354
  forceStop,
3355
+ formatBytes,
3058
3356
  generateSigningConfigs,
3059
3357
  getBundleName,
3060
3358
  getCmdToolsBin,
@@ -3064,6 +3362,7 @@ export {
3064
3362
  getDeviceInfo,
3065
3363
  getDisplaySize,
3066
3364
  getEmulatorDir,
3365
+ getFreeSpaceBytes,
3067
3366
  getHdcPath,
3068
3367
  getHvigorwPath,
3069
3368
  getInstalledSdks,
@@ -3081,6 +3380,8 @@ export {
3081
3380
  installEmulator,
3082
3381
  isCmdToolsInstalled,
3083
3382
  isEmulatorInstalled,
3383
+ isOutOfSpaceError,
3384
+ isRamBackedPath,
3084
3385
  isValidBundleName,
3085
3386
  isValidProjectName,
3086
3387
  launchApp,
@@ -3098,6 +3399,8 @@ export {
3098
3399
  parseCodelinterFindings,
3099
3400
  parseDeviceList,
3100
3401
  parseHilogLine,
3402
+ parseMainAbilityFromBmDump,
3403
+ parseRemoteExit,
3101
3404
  pickSigningKind,
3102
3405
  prepareSigning,
3103
3406
  pruneLayout,
@@ -3108,6 +3411,8 @@ export {
3108
3411
  removeCmdTools,
3109
3412
  removeEmulator,
3110
3413
  removeSdk,
3414
+ removeTempWorkDir,
3415
+ resolveTmpRoot,
3111
3416
  runCodelinter,
3112
3417
  runHvigorw,
3113
3418
  runOhpm,
@@ -3120,11 +3425,13 @@ export {
3120
3425
  sendRawTouch,
3121
3426
  setHilogLevel,
3122
3427
  shell,
3428
+ shellChecked,
3123
3429
  startEmulator,
3124
3430
  staticConfig,
3125
3431
  stopEmulator,
3126
3432
  streamHilog,
3127
3433
  takeScreenshot,
3434
+ toSpaceError,
3128
3435
  uninstallApp,
3129
3436
  validateTemplateLayout,
3130
3437
  verifySha256,
@@ -3133,6 +3440,7 @@ export {
3133
3440
  waitForCondition,
3134
3441
  waitForLog,
3135
3442
  watchLog,
3443
+ waypointsToRawEvents,
3136
3444
  writeJson5File,
3137
3445
  writeJsonFile
3138
3446
  };