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