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