@oniroproject/core 0.12.0 → 0.14.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 +26 -13
- package/dist/index.cjs +1227 -311
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +483 -27
- package/dist/index.d.ts +483 -27
- package/dist/index.js +1187 -311
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -47,9 +47,13 @@ __export(index_exports, {
|
|
|
47
47
|
SIGNING_ERRORS: () => SIGNING_ERRORS,
|
|
48
48
|
SdkNotInstalledError: () => SdkNotInstalledError,
|
|
49
49
|
TMP_DIR_HINT: () => TMP_DIR_HINT,
|
|
50
|
+
TargetNotFoundError: () => TargetNotFoundError,
|
|
50
51
|
UnsupportedPlatformError: () => UnsupportedPlatformError,
|
|
51
52
|
VERSION: () => VERSION,
|
|
53
|
+
appSandboxDir: () => appSandboxDir,
|
|
52
54
|
applyChanges: () => applyChanges,
|
|
55
|
+
assertDevicePath: () => assertDevicePath,
|
|
56
|
+
assertLayoutSelector: () => assertLayoutSelector,
|
|
53
57
|
attemptHdcConnection: () => attemptHdcConnection,
|
|
54
58
|
buildCmdWrapper: () => buildCmdWrapper,
|
|
55
59
|
buildGestureCommands: () => buildGestureCommands,
|
|
@@ -59,6 +63,8 @@ __export(index_exports, {
|
|
|
59
63
|
buildInputCommand: () => buildInputCommand,
|
|
60
64
|
buildRawTouchCommand: () => buildRawTouchCommand,
|
|
61
65
|
captureBurst: () => captureBurst,
|
|
66
|
+
captureLog: () => captureLog,
|
|
67
|
+
cleanCodelinterOutput: () => cleanCodelinterOutput,
|
|
62
68
|
consoleLogger: () => consoleLogger,
|
|
63
69
|
createHarmonyOsSession: () => createHarmonyOsSession,
|
|
64
70
|
createInstallTempDir: () => createInstallTempDir,
|
|
@@ -72,10 +78,14 @@ __export(index_exports, {
|
|
|
72
78
|
dedupEntries: () => dedupEntries,
|
|
73
79
|
defaultContext: () => defaultContext,
|
|
74
80
|
defaultPaths: () => defaultPaths,
|
|
81
|
+
describeLayoutNode: () => describeLayoutNode,
|
|
75
82
|
detectAaStartFailure: () => detectAaStartFailure,
|
|
83
|
+
detectCmdToolsFlavour: () => detectCmdToolsFlavour,
|
|
84
|
+
detectInstallFailure: () => detectInstallFailure,
|
|
76
85
|
detectProjectSdkVersion: () => detectProjectSdkVersion,
|
|
77
86
|
detectRuntimeOs: () => detectRuntimeOs,
|
|
78
87
|
detectSigningConfigNames: () => detectSigningConfigNames,
|
|
88
|
+
diagnoseBuildFailure: () => diagnoseBuildFailure,
|
|
79
89
|
diffEntryNames: () => diffEntryNames,
|
|
80
90
|
diffHapAssets: () => diffHapAssets,
|
|
81
91
|
discoverHaps: () => discoverHaps,
|
|
@@ -88,17 +98,24 @@ __export(index_exports, {
|
|
|
88
98
|
dumpWindow: () => dumpWindow,
|
|
89
99
|
encryptPwd: () => encryptPwd,
|
|
90
100
|
ensureFreeSpace: () => ensureFreeSpace,
|
|
101
|
+
ensureInstallOk: () => ensureInstallOk,
|
|
91
102
|
ensureOhModules: () => ensureOhModules,
|
|
92
103
|
ensureOk: () => ensureOk,
|
|
93
104
|
ensureRemoteOk: () => ensureRemoteOk,
|
|
94
105
|
escapeCmdArgument: () => escapeCmdArgument,
|
|
95
106
|
escapeCmdCommand: () => escapeCmdCommand,
|
|
107
|
+
existingHapOrUnsigned: () => existingHapOrUnsigned,
|
|
96
108
|
extractTarball: () => extractTarball,
|
|
97
109
|
extractZipWithProgress: () => extractZipWithProgress,
|
|
98
110
|
findAppProcessId: () => findAppProcessId,
|
|
99
111
|
findCmdToolsSourceDir: () => findCmdToolsSourceDir,
|
|
112
|
+
findCoveredNodes: () => findCoveredNodes,
|
|
100
113
|
findHarmonyOsSdk: () => findHarmonyOsSdk,
|
|
114
|
+
findKeyboardWindow: () => findKeyboardWindow,
|
|
115
|
+
findLayoutNodes: () => findLayoutNodes,
|
|
116
|
+
findMissingProjectSdk: () => findMissingProjectSdk,
|
|
101
117
|
findRunningProcess: () => findRunningProcess,
|
|
118
|
+
flattenLayout: () => flattenLayout,
|
|
102
119
|
forceStop: () => forceStop,
|
|
103
120
|
formatBytes: () => formatBytes,
|
|
104
121
|
generateSigningConfigs: () => generateSigningConfigs,
|
|
@@ -110,9 +127,11 @@ __export(index_exports, {
|
|
|
110
127
|
getDeviceInfo: () => getDeviceInfo,
|
|
111
128
|
getDisplaySize: () => getDisplaySize,
|
|
112
129
|
getEmulatorDir: () => getEmulatorDir,
|
|
130
|
+
getEmulatorStatus: () => getEmulatorStatus,
|
|
113
131
|
getFreeSpaceBytes: () => getFreeSpaceBytes,
|
|
114
132
|
getHdcPath: () => getHdcPath,
|
|
115
133
|
getHvigorwPath: () => getHvigorwPath,
|
|
134
|
+
getImeBundles: () => getImeBundles,
|
|
116
135
|
getInstalledSdks: () => getInstalledSdks,
|
|
117
136
|
getKey: () => getKey,
|
|
118
137
|
getMainAbility: () => getMainAbility,
|
|
@@ -124,14 +143,21 @@ __export(index_exports, {
|
|
|
124
143
|
getSupportedSdksForUi: () => getSupportedSdksForUi,
|
|
125
144
|
harmonyOsAutoSign: () => harmonyOsAutoSign,
|
|
126
145
|
harmonyOsSdkLabelForApi: () => harmonyOsSdkLabelForApi,
|
|
146
|
+
hdcClientFailure: () => hdcClientFailure,
|
|
127
147
|
hdcExec: () => hdcExec,
|
|
148
|
+
hdcTargetArgs: () => hdcTargetArgs,
|
|
149
|
+
hdcTargetArgsFor: () => hdcTargetArgsFor,
|
|
150
|
+
hideKeyboard: () => hideKeyboard,
|
|
128
151
|
installApp: () => installApp,
|
|
129
152
|
installCmdTools: () => installCmdTools,
|
|
130
153
|
installEmulator: () => installEmulator,
|
|
154
|
+
installFailureReason: () => installFailureReason,
|
|
155
|
+
isBundleInstalled: () => isBundleInstalled,
|
|
131
156
|
isCmdToolsInstalled: () => isCmdToolsInstalled,
|
|
132
157
|
isEmulatorInstalled: () => isEmulatorInstalled,
|
|
133
158
|
isOutOfSpaceError: () => isOutOfSpaceError,
|
|
134
159
|
isRamBackedPath: () => isRamBackedPath,
|
|
160
|
+
isUnsignedHap: () => isUnsignedHap,
|
|
135
161
|
isValidBundleName: () => isValidBundleName,
|
|
136
162
|
isValidProjectName: () => isValidProjectName,
|
|
137
163
|
killProcessTree: () => killProcessTree,
|
|
@@ -141,19 +167,28 @@ __export(index_exports, {
|
|
|
141
167
|
listModules: () => listModules,
|
|
142
168
|
listRunningProcesses: () => listRunningProcesses,
|
|
143
169
|
listTemplates: () => listTemplates,
|
|
170
|
+
locateTarget: () => locateTarget,
|
|
144
171
|
needsCmdWrapper: () => needsCmdWrapper,
|
|
145
172
|
nonInteractivePrompter: () => nonInteractivePrompter,
|
|
146
173
|
noopLogger: () => noopLogger,
|
|
147
174
|
noopProgress: () => noopProgress,
|
|
175
|
+
normaliseLocalPath: () => normaliseLocalPath,
|
|
148
176
|
paramGet: () => paramGet,
|
|
177
|
+
paramGetMany: () => paramGetMany,
|
|
149
178
|
paramSet: () => paramSet,
|
|
150
179
|
parseBounds: () => parseBounds,
|
|
151
180
|
parseCodelinterFindings: () => parseCodelinterFindings,
|
|
181
|
+
parseCodelinterReport: () => parseCodelinterReport,
|
|
152
182
|
parseDeviceList: () => parseDeviceList,
|
|
153
183
|
parseHilogLine: () => parseHilogLine,
|
|
184
|
+
parseImeBundles: () => parseImeBundles,
|
|
154
185
|
parseMainAbilityFromBmDump: () => parseMainAbilityFromBmDump,
|
|
186
|
+
parseParamValues: () => parseParamValues,
|
|
155
187
|
parseRemoteExit: () => parseRemoteExit,
|
|
188
|
+
performInput: () => performInput,
|
|
189
|
+
pickHap: () => pickHap,
|
|
156
190
|
pickSigningKind: () => pickSigningKind,
|
|
191
|
+
pickTarget: () => pickTarget,
|
|
157
192
|
prepareSigning: () => prepareSigning,
|
|
158
193
|
pruneLayout: () => pruneLayout,
|
|
159
194
|
readJson5File: () => readJson5File,
|
|
@@ -166,6 +201,8 @@ __export(index_exports, {
|
|
|
166
201
|
removeSdk: () => removeSdk,
|
|
167
202
|
removeTempWorkDir: () => removeTempWorkDir,
|
|
168
203
|
requireHarmonyOsSdk: () => requireHarmonyOsSdk,
|
|
204
|
+
resolveDeviceSerial: () => resolveDeviceSerial,
|
|
205
|
+
resolveKeyEvent: () => resolveKeyEvent,
|
|
169
206
|
resolveTmpRoot: () => resolveTmpRoot,
|
|
170
207
|
runCodelinter: () => runCodelinter,
|
|
171
208
|
runHvigorw: () => runHvigorw,
|
|
@@ -189,14 +226,17 @@ __export(index_exports, {
|
|
|
189
226
|
toLongPath: () => toLongPath,
|
|
190
227
|
toSpaceError: () => toSpaceError,
|
|
191
228
|
uninstallApp: () => uninstallApp,
|
|
229
|
+
unsignedHapRejection: () => unsignedHapRejection,
|
|
192
230
|
validateTemplateLayout: () => validateTemplateLayout,
|
|
193
231
|
verifySha256: () => verifySha256,
|
|
194
232
|
waitForBoot: () => waitForBoot,
|
|
195
233
|
waitForBundle: () => waitForBundle,
|
|
196
234
|
waitForCondition: () => waitForCondition,
|
|
197
235
|
waitForLog: () => waitForLog,
|
|
236
|
+
waitForTarget: () => waitForTarget,
|
|
198
237
|
watchLog: () => watchLog,
|
|
199
238
|
waypointsToRawEvents: () => waypointsToRawEvents,
|
|
239
|
+
withoutWindow: () => withoutWindow,
|
|
200
240
|
writeJson5File: () => writeJson5File,
|
|
201
241
|
writeJsonFile: () => writeJsonFile
|
|
202
242
|
});
|
|
@@ -293,14 +333,19 @@ var OniroError = class extends Error {
|
|
|
293
333
|
cause;
|
|
294
334
|
};
|
|
295
335
|
var SdkNotInstalledError = class extends OniroError {
|
|
296
|
-
|
|
297
|
-
|
|
336
|
+
/** `version` is the release that ships `api` (e.g. `6.0` for API 20), when one is known. */
|
|
337
|
+
constructor(api, expectedPath, version) {
|
|
338
|
+
super(
|
|
339
|
+
`SDK API ${api} is not installed at ${expectedPath}.` + (version ? ` Install it with \`oniro-app sdk install ${version}\`.` : "")
|
|
340
|
+
);
|
|
298
341
|
this.api = api;
|
|
299
342
|
this.expectedPath = expectedPath;
|
|
343
|
+
this.version = version;
|
|
300
344
|
this.name = "SdkNotInstalledError";
|
|
301
345
|
}
|
|
302
346
|
api;
|
|
303
347
|
expectedPath;
|
|
348
|
+
version;
|
|
304
349
|
};
|
|
305
350
|
var CmdToolsNotInstalledError = class extends OniroError {
|
|
306
351
|
constructor(expectedPath) {
|
|
@@ -576,6 +621,24 @@ function isProjectLocalHvigorwWorking(projectDir) {
|
|
|
576
621
|
var fs3 = __toESM(require("fs"), 1);
|
|
577
622
|
var path4 = __toESM(require("path"), 1);
|
|
578
623
|
var import_json5 = __toESM(require("json5"), 1);
|
|
624
|
+
function localPropertiesSdkDir(projectRoot) {
|
|
625
|
+
try {
|
|
626
|
+
const content = fs3.readFileSync(path4.join(projectRoot, "local.properties"), "utf-8");
|
|
627
|
+
const m = /^\s*sdk\.dir\s*[=:]\s*(.+?)\s*$/m.exec(content);
|
|
628
|
+
return m ? m[1].replace(/\\(.)/g, "$1") : void 0;
|
|
629
|
+
} catch {
|
|
630
|
+
return void 0;
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
function findMissingProjectSdk(config, projectRoot) {
|
|
634
|
+
const api = detectProjectSdkVersion(projectRoot);
|
|
635
|
+
if (api === void 0) return null;
|
|
636
|
+
const expected = path4.join(getOhosBaseSdkHome(config), String(api));
|
|
637
|
+
if (fs3.existsSync(expected)) return null;
|
|
638
|
+
const localSdkDir = localPropertiesSdkDir(projectRoot);
|
|
639
|
+
if (localSdkDir && fs3.existsSync(path4.join(localSdkDir, String(api)))) return null;
|
|
640
|
+
return new SdkNotInstalledError(String(api), expected, ALL_SDKS.find((s) => s.api === String(api))?.version);
|
|
641
|
+
}
|
|
579
642
|
function detectProjectSdkVersion(projectRoot, logger = noopLogger) {
|
|
580
643
|
const buildProfilePath = path4.join(projectRoot, "build-profile.json5");
|
|
581
644
|
if (!fs3.existsSync(buildProfilePath)) {
|
|
@@ -761,14 +824,14 @@ var pipelineAsync = (0, import_node_util.promisify)(import_node_stream.pipeline)
|
|
|
761
824
|
async function downloadFile(opts) {
|
|
762
825
|
const { url, dest, progress, abortSignal } = opts;
|
|
763
826
|
const proto = url.startsWith("https") ? https : http;
|
|
764
|
-
return new Promise((
|
|
827
|
+
return new Promise((resolve7, reject) => {
|
|
765
828
|
const s = Math.max(0, Math.min(100, opts.start ?? 0));
|
|
766
829
|
const r = Math.max(0, Math.min(100 - s, opts.range ?? 100));
|
|
767
830
|
let settled = false;
|
|
768
831
|
const done = (err) => {
|
|
769
832
|
if (settled) return;
|
|
770
833
|
settled = true;
|
|
771
|
-
err ? reject(err) :
|
|
834
|
+
err ? reject(err) : resolve7();
|
|
772
835
|
};
|
|
773
836
|
if (abortSignal?.aborted) {
|
|
774
837
|
done(new CancelledError("Download cancelled before start."));
|
|
@@ -1255,21 +1318,27 @@ async function installCmdTools(opts) {
|
|
|
1255
1318
|
removeTempWorkDir(tmpDir, tmpRoot);
|
|
1256
1319
|
}
|
|
1257
1320
|
}
|
|
1321
|
+
function detectCmdToolsFlavour(cmdToolsPath) {
|
|
1322
|
+
return fs11.existsSync(path12.join(cmdToolsPath, "sdk", "default", "hms")) ? "HarmonyOS" : "OpenHarmony";
|
|
1323
|
+
}
|
|
1258
1324
|
function getCmdToolsStatus(config) {
|
|
1259
1325
|
const cmdPath = getCmdToolsPath(config);
|
|
1326
|
+
const hdcPath = getHdcPath(config);
|
|
1327
|
+
const base = { path: cmdPath, hdc: { path: hdcPath, exists: fs11.existsSync(hdcPath) } };
|
|
1260
1328
|
const binDir = path12.join(cmdPath, "bin");
|
|
1261
1329
|
const candidates = os7.platform() === "win32" ? ["ohpm.exe", "ohpm.cmd", "ohpm.bat", "ohpm"] : ["ohpm"];
|
|
1262
1330
|
const bin = candidates.map((c) => path12.join(binDir, c)).find((p) => fs11.existsSync(p));
|
|
1263
1331
|
if (!bin) {
|
|
1264
|
-
return { installed: false, status: "Not installed" };
|
|
1332
|
+
return { installed: false, status: "Not installed", ...base };
|
|
1265
1333
|
}
|
|
1334
|
+
const installed = { installed: true, ...base, flavour: detectCmdToolsFlavour(cmdPath) };
|
|
1266
1335
|
try {
|
|
1267
1336
|
const versionFile = path12.join(cmdPath, "version.txt");
|
|
1268
1337
|
if (fs11.existsSync(versionFile)) {
|
|
1269
1338
|
const content = fs11.readFileSync(versionFile, "utf8");
|
|
1270
1339
|
for (const line of content.split(/\r?\n/)) {
|
|
1271
1340
|
const m = line.match(/^\s*#\s*Version:\s*(.+)$/);
|
|
1272
|
-
if (m?.[1]) return { installed
|
|
1341
|
+
if (m?.[1]) return { ...installed, status: `Installed (${m[1].trim()})` };
|
|
1273
1342
|
}
|
|
1274
1343
|
}
|
|
1275
1344
|
} catch {
|
|
@@ -1283,9 +1352,9 @@ function getCmdToolsStatus(config) {
|
|
|
1283
1352
|
});
|
|
1284
1353
|
if (res.error || res.status !== 0) throw res.error ?? new Error(`exit ${String(res.status)}`);
|
|
1285
1354
|
const version = res.stdout.trim();
|
|
1286
|
-
return { installed
|
|
1355
|
+
return { ...installed, status: `Installed (${version})` };
|
|
1287
1356
|
} catch {
|
|
1288
|
-
return { installed
|
|
1357
|
+
return { ...installed, status: "Installed (version unknown)" };
|
|
1289
1358
|
}
|
|
1290
1359
|
}
|
|
1291
1360
|
function isCmdToolsInstalled(config) {
|
|
@@ -1575,6 +1644,9 @@ function modifyProfileTemplate(projectDir, apl, appFeature, logger, options = {}
|
|
|
1575
1644
|
profileTemplate.acls = profileTemplate.acls ?? {};
|
|
1576
1645
|
profileTemplate.acls["allowed-acls"] = options.acls;
|
|
1577
1646
|
}
|
|
1647
|
+
if (options.appPrivilegeCapabilities !== void 0) {
|
|
1648
|
+
profileTemplate["app-privilege-capabilities"] = options.appPrivilegeCapabilities;
|
|
1649
|
+
}
|
|
1578
1650
|
fs13.writeFileSync(profileTemplatePath, JSON.stringify(profileTemplate, null, 2));
|
|
1579
1651
|
}
|
|
1580
1652
|
function generateP7bFile(projectDir, paths, logger, passwords) {
|
|
@@ -1664,6 +1736,12 @@ function updateBuildProfile(projectDir, kind, logger, passwords) {
|
|
|
1664
1736
|
storeFile: "./signatures/OpenHarmony.p12"
|
|
1665
1737
|
}
|
|
1666
1738
|
}));
|
|
1739
|
+
for (const product of buildProfile.app.products ?? []) {
|
|
1740
|
+
if (typeof product.signingConfig !== "string" || product.signingConfig === "") {
|
|
1741
|
+
product.signingConfig = configNames[0];
|
|
1742
|
+
logger.info(`[sign] Pointing a product with no signingConfig at "${configNames[0]}".`);
|
|
1743
|
+
}
|
|
1744
|
+
}
|
|
1667
1745
|
fs13.writeFileSync(buildProfilePath, JSON.stringify(buildProfile, null, 2));
|
|
1668
1746
|
}
|
|
1669
1747
|
function prepareMaterialDirectory(projectDir, logger) {
|
|
@@ -1695,7 +1773,7 @@ function generateSigningConfigs(options) {
|
|
|
1695
1773
|
}
|
|
1696
1774
|
const sdkPath = path14.join(sdkHome, String(sdkVersion));
|
|
1697
1775
|
if (!fs13.existsSync(sdkPath)) {
|
|
1698
|
-
throw new
|
|
1776
|
+
throw new SdkNotInstalledError(String(sdkVersion), sdkPath, ALL_SDKS.find((s) => s.api === String(sdkVersion))?.version);
|
|
1699
1777
|
}
|
|
1700
1778
|
const paths = {
|
|
1701
1779
|
signTool: path14.join(sdkPath, "toolchains/lib/hap-sign-tool.jar"),
|
|
@@ -1707,7 +1785,11 @@ function generateSigningConfigs(options) {
|
|
|
1707
1785
|
const passwords = options.passwords ?? DEFAULT_PASSWORDS;
|
|
1708
1786
|
logger.info("[sign] Starting signing configuration generation...");
|
|
1709
1787
|
copyFilesToProject(projectDir, paths, kind, logger, options.applicationCertPath);
|
|
1710
|
-
modifyProfileTemplate(projectDir, apl, appFeature, logger, {
|
|
1788
|
+
modifyProfileTemplate(projectDir, apl, appFeature, logger, {
|
|
1789
|
+
kind,
|
|
1790
|
+
acls: options.acls,
|
|
1791
|
+
appPrivilegeCapabilities: options.appPrivilegeCapabilities
|
|
1792
|
+
});
|
|
1711
1793
|
generateP7bFile(projectDir, paths, logger, passwords);
|
|
1712
1794
|
prepareMaterialDirectory(projectDir, logger);
|
|
1713
1795
|
updateBuildProfile(projectDir, kind, logger, passwords);
|
|
@@ -1774,7 +1856,7 @@ function runProcess(opts) {
|
|
|
1774
1856
|
const { command, args, cwd, env, onOutput } = opts;
|
|
1775
1857
|
const timeoutMs = opts.timeoutMs ?? 6e4;
|
|
1776
1858
|
const { abortSignal } = opts;
|
|
1777
|
-
return new Promise((
|
|
1859
|
+
return new Promise((resolve7, reject) => {
|
|
1778
1860
|
if (abortSignal?.aborted) {
|
|
1779
1861
|
reject(new CancelledError(`Command aborted before start: ${command}`));
|
|
1780
1862
|
return;
|
|
@@ -1839,21 +1921,41 @@ ${stderr.trim()}` : "")
|
|
|
1839
1921
|
);
|
|
1840
1922
|
return;
|
|
1841
1923
|
}
|
|
1842
|
-
|
|
1924
|
+
resolve7({ code: code ?? -1, stdout, stderr });
|
|
1843
1925
|
});
|
|
1844
1926
|
});
|
|
1845
1927
|
}
|
|
1846
|
-
function
|
|
1928
|
+
function resolveDeviceSerial(explicit, env = process.env) {
|
|
1929
|
+
if (explicit && explicit !== "auto") return explicit;
|
|
1930
|
+
const fromEnv = env.ONIRO_DEVICE_SERIAL || env.DEVICE_SERIAL;
|
|
1931
|
+
return fromEnv && fromEnv !== "auto" ? fromEnv : void 0;
|
|
1932
|
+
}
|
|
1933
|
+
function hdcTargetArgs(explicit) {
|
|
1934
|
+
const serial = resolveDeviceSerial(explicit);
|
|
1935
|
+
return serial ? ["-t", serial] : [];
|
|
1936
|
+
}
|
|
1937
|
+
var HOST_COMMANDS = /* @__PURE__ */ new Set(["list", "tconn", "start", "kill", "version", "checkserver"]);
|
|
1938
|
+
function hdcTargetArgsFor(args, explicit) {
|
|
1939
|
+
if (HOST_COMMANDS.has(args[0] ?? "")) return explicit ? ["-t", explicit] : [];
|
|
1940
|
+
return hdcTargetArgs(explicit);
|
|
1941
|
+
}
|
|
1942
|
+
function hdcClientFailure(output) {
|
|
1943
|
+
const first = output.split(/\r?\n/).find((l) => l.trim() !== "")?.trim() ?? "";
|
|
1944
|
+
return first.startsWith("[Fail]") ? first : null;
|
|
1945
|
+
}
|
|
1946
|
+
async function hdcExec(opts) {
|
|
1847
1947
|
const hdc = getHdcPath(opts.config);
|
|
1848
|
-
const
|
|
1849
|
-
return runProcess({
|
|
1948
|
+
const res = await runProcess({
|
|
1850
1949
|
command: hdc,
|
|
1851
|
-
args:
|
|
1950
|
+
args: [...hdcTargetArgsFor(opts.args, opts.deviceSerial), ...opts.args],
|
|
1852
1951
|
timeoutMs: opts.timeoutMs,
|
|
1853
1952
|
abortSignal: opts.abortSignal,
|
|
1854
1953
|
onOutput: opts.onOutput,
|
|
1855
1954
|
logger: opts.logger
|
|
1856
1955
|
});
|
|
1956
|
+
if (res.code === 0 && hdcClientFailure(`${res.stdout}
|
|
1957
|
+
${res.stderr}`)) return { ...res, code: 1 };
|
|
1958
|
+
return res;
|
|
1857
1959
|
}
|
|
1858
1960
|
function shell(opts) {
|
|
1859
1961
|
const { command, ...rest } = opts;
|
|
@@ -1861,10 +1963,15 @@ function shell(opts) {
|
|
|
1861
1963
|
}
|
|
1862
1964
|
function ensureOk(result, command) {
|
|
1863
1965
|
if (result.code !== 0) {
|
|
1864
|
-
throw new CommandFailedError(command, result.code, result.stderr.trim());
|
|
1966
|
+
throw new CommandFailedError(command, result.code, withTargetHint(result.stderr.trim() || result.stdout.trim()));
|
|
1865
1967
|
}
|
|
1866
1968
|
return result;
|
|
1867
1969
|
}
|
|
1970
|
+
function withTargetHint(detail) {
|
|
1971
|
+
if (!/need connect-key|Not match target/i.test(detail)) return detail;
|
|
1972
|
+
return `${detail}
|
|
1973
|
+
More than one target is connected, or the serial is unknown: pass --device <serial> or set ONIRO_DEVICE_SERIAL to one listed by \`oniro-app devices\`.`;
|
|
1974
|
+
}
|
|
1868
1975
|
var REMOTE_RC_RE = /__ONIRO_RC=(-?\d+)__/g;
|
|
1869
1976
|
function parseRemoteExit(stdout) {
|
|
1870
1977
|
let code = null;
|
|
@@ -1881,7 +1988,7 @@ async function shellChecked(opts) {
|
|
|
1881
1988
|
}
|
|
1882
1989
|
function ensureRemoteOk(result, command) {
|
|
1883
1990
|
if (result.code !== 0) {
|
|
1884
|
-
throw new CommandFailedError(command, result.code, result.stderr.trim() || result.stdout.trim());
|
|
1991
|
+
throw new CommandFailedError(command, result.code, withTargetHint(result.stderr.trim() || result.stdout.trim()));
|
|
1885
1992
|
}
|
|
1886
1993
|
return result;
|
|
1887
1994
|
}
|
|
@@ -1967,11 +2074,11 @@ async function startEmulator(opts) {
|
|
|
1967
2074
|
fs16.writeFileSync(PID_FILE, String(child.pid));
|
|
1968
2075
|
child.unref();
|
|
1969
2076
|
logger.info(`Emulator launched (PID ${child.pid}). Logs: ${logPath}`);
|
|
1970
|
-
const earlyExit = new Promise((
|
|
1971
|
-
child.once("exit", (code) =>
|
|
1972
|
-
child.once("error", () =>
|
|
2077
|
+
const earlyExit = new Promise((resolve7) => {
|
|
2078
|
+
child.once("exit", (code) => resolve7(code));
|
|
2079
|
+
child.once("error", () => resolve7(-1));
|
|
1973
2080
|
});
|
|
1974
|
-
const liveness = new Promise((
|
|
2081
|
+
const liveness = new Promise((resolve7) => setTimeout(() => resolve7("alive"), 2e3));
|
|
1975
2082
|
const outcome = await Promise.race([earlyExit, liveness]);
|
|
1976
2083
|
if (outcome !== "alive") {
|
|
1977
2084
|
const tail = readTail(logPath, 4e3);
|
|
@@ -2051,6 +2158,43 @@ async function stopEmulator(logger = noopLogger) {
|
|
|
2051
2158
|
if (err) logger.warn(`Could not remove PID file: ${err.message}`);
|
|
2052
2159
|
});
|
|
2053
2160
|
}
|
|
2161
|
+
async function isQemuRunning(logger) {
|
|
2162
|
+
if (os8.platform() !== "win32") {
|
|
2163
|
+
try {
|
|
2164
|
+
const pid = Number(fs16.readFileSync(PID_FILE, "utf8").trim());
|
|
2165
|
+
if (Number.isFinite(pid) && pid > 0) {
|
|
2166
|
+
process.kill(pid, 0);
|
|
2167
|
+
return true;
|
|
2168
|
+
}
|
|
2169
|
+
} catch {
|
|
2170
|
+
}
|
|
2171
|
+
}
|
|
2172
|
+
const [cmd, ...args] = os8.platform() === "win32" ? ["tasklist", "/FI", "IMAGENAME eq qemu-system-x86_64.exe", "/NH"] : ["pgrep", "-f", "qemu-system-x86_64"];
|
|
2173
|
+
try {
|
|
2174
|
+
const res = await runProcess({ command: cmd, args, timeoutMs: 1e4, logger });
|
|
2175
|
+
return os8.platform() === "win32" ? /qemu-system-x86_64/i.test(res.stdout) : res.code === 0;
|
|
2176
|
+
} catch {
|
|
2177
|
+
return false;
|
|
2178
|
+
}
|
|
2179
|
+
}
|
|
2180
|
+
async function getEmulatorStatus(config, opts = {}) {
|
|
2181
|
+
const logger = opts.logger ?? noopLogger;
|
|
2182
|
+
const address = opts.address ?? "127.0.0.1:55555";
|
|
2183
|
+
const dir = getEmulatorDir(config);
|
|
2184
|
+
let connected = false;
|
|
2185
|
+
try {
|
|
2186
|
+
const targets = await hdcExec({ config, args: ["list", "targets", "-v"], timeoutMs: 1e4, logger });
|
|
2187
|
+
connected = targets.stdout.split("\n").some((l) => l.trim().startsWith(address) && /\bConnected\b/.test(l));
|
|
2188
|
+
} catch {
|
|
2189
|
+
}
|
|
2190
|
+
return {
|
|
2191
|
+
installed: isEmulatorInstalled(config),
|
|
2192
|
+
dir,
|
|
2193
|
+
running: await isQemuRunning(logger),
|
|
2194
|
+
address,
|
|
2195
|
+
connected
|
|
2196
|
+
};
|
|
2197
|
+
}
|
|
2054
2198
|
|
|
2055
2199
|
// src/hdc/devices.ts
|
|
2056
2200
|
var STATUS_TOKENS = /* @__PURE__ */ new Set([
|
|
@@ -2089,9 +2233,8 @@ async function listDevices(config, opts = {}) {
|
|
|
2089
2233
|
return parseDeviceList(res.stdout);
|
|
2090
2234
|
}
|
|
2091
2235
|
async function selectDevice(config, serial, opts = {}) {
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
if (envSerial && envSerial !== "auto") return envSerial;
|
|
2236
|
+
const resolved = resolveDeviceSerial(serial);
|
|
2237
|
+
if (resolved) return resolved;
|
|
2095
2238
|
const usable = (await listDevices(config, opts)).filter(
|
|
2096
2239
|
(d) => d.status === "Connected" || d.status === "Unknown"
|
|
2097
2240
|
);
|
|
@@ -2105,6 +2248,7 @@ async function selectDevice(config, serial, opts = {}) {
|
|
|
2105
2248
|
}
|
|
2106
2249
|
|
|
2107
2250
|
// src/hdc/param.ts
|
|
2251
|
+
var PARAM_MISSING_RE = /^Get parameter ".*" fail!/;
|
|
2108
2252
|
async function paramGet(config, key, opts = {}) {
|
|
2109
2253
|
const res = await shell({
|
|
2110
2254
|
config,
|
|
@@ -2114,7 +2258,30 @@ async function paramGet(config, key, opts = {}) {
|
|
|
2114
2258
|
logger: opts.logger
|
|
2115
2259
|
});
|
|
2116
2260
|
ensureOk(res, `hdc shell param get ${key}`);
|
|
2117
|
-
|
|
2261
|
+
const value = res.stdout.trim();
|
|
2262
|
+
if (PARAM_MISSING_RE.test(value)) throw new OniroError(`System parameter ${key} is not set: ${value}`);
|
|
2263
|
+
return value;
|
|
2264
|
+
}
|
|
2265
|
+
var PARAM_SEPARATOR = "__ONIRO_PARAM__";
|
|
2266
|
+
function parseParamValues(keys, stdout) {
|
|
2267
|
+
const parts = stdout.split(PARAM_SEPARATOR);
|
|
2268
|
+
const values = {};
|
|
2269
|
+
keys.forEach((key, i) => {
|
|
2270
|
+
const value = parts[i]?.trim();
|
|
2271
|
+
values[key] = value && !PARAM_MISSING_RE.test(value) ? value : void 0;
|
|
2272
|
+
});
|
|
2273
|
+
return values;
|
|
2274
|
+
}
|
|
2275
|
+
async function paramGetMany(config, keys, opts = {}) {
|
|
2276
|
+
const res = await shell({
|
|
2277
|
+
config,
|
|
2278
|
+
command: keys.map((k) => `param get ${k}; echo ${PARAM_SEPARATOR}`).join("; "),
|
|
2279
|
+
deviceSerial: opts.deviceSerial,
|
|
2280
|
+
timeoutMs: opts.timeoutMs ?? 1e4,
|
|
2281
|
+
logger: opts.logger
|
|
2282
|
+
});
|
|
2283
|
+
ensureOk(res, "hdc shell param get \u2026");
|
|
2284
|
+
return parseParamValues(keys, res.stdout);
|
|
2118
2285
|
}
|
|
2119
2286
|
async function paramSet(config, key, value, opts = {}) {
|
|
2120
2287
|
const res = await shell({
|
|
@@ -2128,29 +2295,66 @@ async function paramSet(config, key, value, opts = {}) {
|
|
|
2128
2295
|
}
|
|
2129
2296
|
|
|
2130
2297
|
// src/hdc/files.ts
|
|
2298
|
+
var path17 = __toESM(require("path"), 1);
|
|
2299
|
+
function normaliseLocalPath(local) {
|
|
2300
|
+
const abs = path17.resolve(local);
|
|
2301
|
+
const long = toLongPath(abs);
|
|
2302
|
+
return long !== abs ? long : path17.join(toLongPath(path17.dirname(abs)), path17.basename(abs));
|
|
2303
|
+
}
|
|
2304
|
+
function assertDevicePath(remote) {
|
|
2305
|
+
if (/^[A-Za-z]:[\\/]/.test(remote)) {
|
|
2306
|
+
throw new OniroError(
|
|
2307
|
+
`The device path ${remote} is a Windows path. Git Bash rewrites arguments that start with /: run the command with MSYS_NO_PATHCONV=1, or from PowerShell or cmd.`
|
|
2308
|
+
);
|
|
2309
|
+
}
|
|
2310
|
+
}
|
|
2311
|
+
function appSandboxDir(bundle) {
|
|
2312
|
+
return `/data/app/el2/100/base/${bundle}`;
|
|
2313
|
+
}
|
|
2314
|
+
function shQuote(s) {
|
|
2315
|
+
return `'${s.replace(/'/g, `'\\''`)}'`;
|
|
2316
|
+
}
|
|
2131
2317
|
async function sendFile(opts) {
|
|
2318
|
+
assertDevicePath(opts.remote);
|
|
2319
|
+
const local = normaliseLocalPath(opts.local);
|
|
2320
|
+
const sandbox = opts.bundle ? appSandboxDir(opts.bundle) : void 0;
|
|
2321
|
+
const remote = sandbox && !opts.remote.startsWith("/") ? path17.posix.join(sandbox, opts.remote) : opts.remote;
|
|
2322
|
+
if (sandbox && !remote.startsWith(`${sandbox}/`)) {
|
|
2323
|
+
throw new OniroError(`${remote} is outside the sandbox of ${opts.bundle} (${sandbox}).`);
|
|
2324
|
+
}
|
|
2325
|
+
const device = { config: opts.config, deviceSerial: opts.deviceSerial, logger: opts.logger };
|
|
2326
|
+
if (sandbox) {
|
|
2327
|
+
const mkdir = `mkdir -p ${shQuote(path17.posix.dirname(remote))}`;
|
|
2328
|
+
ensureRemoteOk(await shellChecked({ ...device, command: mkdir }), `hdc shell ${mkdir}`);
|
|
2329
|
+
}
|
|
2132
2330
|
const res = await hdcExec({
|
|
2133
|
-
|
|
2134
|
-
args: ["file", "send",
|
|
2135
|
-
deviceSerial: opts.deviceSerial,
|
|
2331
|
+
...device,
|
|
2332
|
+
args: ["file", "send", local, remote],
|
|
2136
2333
|
timeoutMs: opts.timeoutMs ?? 6e5,
|
|
2137
2334
|
abortSignal: opts.abortSignal,
|
|
2138
|
-
onOutput: opts.onOutput
|
|
2139
|
-
logger: opts.logger
|
|
2335
|
+
onOutput: opts.onOutput
|
|
2140
2336
|
});
|
|
2141
|
-
ensureOk(res, `hdc file send ${
|
|
2337
|
+
ensureOk(res, `hdc file send ${local} ${remote}`);
|
|
2338
|
+
if (sandbox) {
|
|
2339
|
+
const top = path17.posix.join(sandbox, path17.posix.relative(sandbox, remote).split("/")[0]);
|
|
2340
|
+
const chown = `chown -R "$(stat -c %u:%g ${shQuote(sandbox)})" ${shQuote(top)}`;
|
|
2341
|
+
ensureRemoteOk(await shellChecked({ ...device, command: chown }), `hdc shell ${chown}`);
|
|
2342
|
+
}
|
|
2343
|
+
return remote;
|
|
2142
2344
|
}
|
|
2143
2345
|
async function recvFile(opts) {
|
|
2346
|
+
assertDevicePath(opts.remote);
|
|
2347
|
+
const local = normaliseLocalPath(opts.local);
|
|
2144
2348
|
const res = await hdcExec({
|
|
2145
2349
|
config: opts.config,
|
|
2146
|
-
args: ["file", "recv", opts.remote,
|
|
2350
|
+
args: ["file", "recv", opts.remote, local],
|
|
2147
2351
|
deviceSerial: opts.deviceSerial,
|
|
2148
2352
|
timeoutMs: opts.timeoutMs ?? 6e5,
|
|
2149
2353
|
abortSignal: opts.abortSignal,
|
|
2150
2354
|
onOutput: opts.onOutput,
|
|
2151
2355
|
logger: opts.logger
|
|
2152
2356
|
});
|
|
2153
|
-
ensureOk(res, `hdc file recv ${opts.remote} ${
|
|
2357
|
+
ensureOk(res, `hdc file recv ${opts.remote} ${local}`);
|
|
2154
2358
|
}
|
|
2155
2359
|
|
|
2156
2360
|
// src/hdc/display.ts
|
|
@@ -2170,7 +2374,7 @@ async function getDisplaySize(config, opts = {}) {
|
|
|
2170
2374
|
|
|
2171
2375
|
// src/hdc/wait.ts
|
|
2172
2376
|
function delay(ms, signal) {
|
|
2173
|
-
return new Promise((
|
|
2377
|
+
return new Promise((resolve7, reject) => {
|
|
2174
2378
|
if (signal?.aborted) {
|
|
2175
2379
|
reject(new CancelledError("Wait cancelled."));
|
|
2176
2380
|
return;
|
|
@@ -2185,7 +2389,7 @@ function delay(ms, signal) {
|
|
|
2185
2389
|
};
|
|
2186
2390
|
const timer = setTimeout(() => {
|
|
2187
2391
|
cleanup();
|
|
2188
|
-
|
|
2392
|
+
resolve7();
|
|
2189
2393
|
}, ms);
|
|
2190
2394
|
signal?.addEventListener("abort", onAbort, { once: true });
|
|
2191
2395
|
});
|
|
@@ -2214,20 +2418,73 @@ async function waitForCondition(opts) {
|
|
|
2214
2418
|
}
|
|
2215
2419
|
|
|
2216
2420
|
// src/hdc/app.ts
|
|
2217
|
-
var
|
|
2421
|
+
var fs19 = __toESM(require("fs"), 1);
|
|
2422
|
+
var path20 = __toESM(require("path"), 1);
|
|
2423
|
+
|
|
2424
|
+
// src/build/discoverHaps.ts
|
|
2425
|
+
var fs17 = __toESM(require("fs"), 1);
|
|
2218
2426
|
var path18 = __toESM(require("path"), 1);
|
|
2427
|
+
function moduleOfHap(hapFileName) {
|
|
2428
|
+
const dash = hapFileName.indexOf("-");
|
|
2429
|
+
return dash > 0 ? hapFileName.slice(0, dash) : hapFileName.replace(/\.hap$/i, "");
|
|
2430
|
+
}
|
|
2431
|
+
async function discoverHaps(opts) {
|
|
2432
|
+
const { projectDir } = opts;
|
|
2433
|
+
const maxDepth = opts.maxDepth ?? 8;
|
|
2434
|
+
const result = {};
|
|
2435
|
+
const walk = async (dir, depth) => {
|
|
2436
|
+
if (depth > maxDepth) return;
|
|
2437
|
+
const entries = await fs17.promises.readdir(dir, { withFileTypes: true }).catch(() => []);
|
|
2438
|
+
for (const e of entries) {
|
|
2439
|
+
const full = path18.join(dir, e.name);
|
|
2440
|
+
if (e.isDirectory()) {
|
|
2441
|
+
if (e.name === "node_modules" || e.name === "oh_modules" || e.name === ".git") continue;
|
|
2442
|
+
await walk(full, depth + 1);
|
|
2443
|
+
} else if (e.isFile() && e.name.endsWith(".hap")) {
|
|
2444
|
+
const moduleName = moduleOfHap(e.name);
|
|
2445
|
+
const bucket = result[moduleName] ??= { signed: [], unsigned: [] };
|
|
2446
|
+
if (e.name.includes("unsigned")) bucket.unsigned.push(full);
|
|
2447
|
+
else if (e.name.includes("signed")) bucket.signed.push(full);
|
|
2448
|
+
}
|
|
2449
|
+
}
|
|
2450
|
+
};
|
|
2451
|
+
await walk(projectDir, 0);
|
|
2452
|
+
const mtime = (p) => fs17.statSync(p).mtimeMs;
|
|
2453
|
+
for (const bucket of Object.values(result)) {
|
|
2454
|
+
bucket.signed.sort((a, b) => mtime(b) - mtime(a));
|
|
2455
|
+
bucket.unsigned.sort((a, b) => mtime(b) - mtime(a));
|
|
2456
|
+
}
|
|
2457
|
+
return result;
|
|
2458
|
+
}
|
|
2459
|
+
function pickHap(haps, module2) {
|
|
2460
|
+
const pick = (name) => {
|
|
2461
|
+
const signed = haps[name]?.signed[0];
|
|
2462
|
+
if (signed) return { module: name, path: signed, signed: true };
|
|
2463
|
+
const unsigned = haps[name]?.unsigned[0];
|
|
2464
|
+
return unsigned ? { module: name, path: unsigned, signed: false } : null;
|
|
2465
|
+
};
|
|
2466
|
+
if (module2) return pick(module2);
|
|
2467
|
+
const names = Object.keys(haps);
|
|
2468
|
+
const withSigned = names.filter((n) => haps[n].signed.length > 0);
|
|
2469
|
+
const candidates = withSigned.length > 0 ? withSigned : names.filter((n) => haps[n].unsigned.length > 0);
|
|
2470
|
+
if (candidates.length === 0) return null;
|
|
2471
|
+
if (candidates.length > 1) {
|
|
2472
|
+
throw new OniroError(`Multiple modules have built HAPs (${candidates.join(", ")}). Pass a module to choose.`);
|
|
2473
|
+
}
|
|
2474
|
+
return pick(candidates[0]);
|
|
2475
|
+
}
|
|
2219
2476
|
|
|
2220
2477
|
// src/hdc/project.ts
|
|
2221
|
-
var
|
|
2222
|
-
var
|
|
2478
|
+
var fs18 = __toESM(require("fs"), 1);
|
|
2479
|
+
var path19 = __toESM(require("path"), 1);
|
|
2223
2480
|
var import_json53 = __toESM(require("json5"), 1);
|
|
2224
2481
|
function readJson5(filePath) {
|
|
2225
|
-
const content =
|
|
2482
|
+
const content = fs18.readFileSync(filePath, "utf-8");
|
|
2226
2483
|
return import_json53.default.parse(content);
|
|
2227
2484
|
}
|
|
2228
2485
|
function getBundleName(projectDir) {
|
|
2229
|
-
const appJsonPath =
|
|
2230
|
-
if (!
|
|
2486
|
+
const appJsonPath = path19.join(projectDir, "AppScope", "app.json5");
|
|
2487
|
+
if (!fs18.existsSync(appJsonPath)) {
|
|
2231
2488
|
throw new OniroError(`Could not find app.json5 at ${appJsonPath}`);
|
|
2232
2489
|
}
|
|
2233
2490
|
const appJson = readJson5(appJsonPath);
|
|
@@ -2237,8 +2494,8 @@ function getBundleName(projectDir) {
|
|
|
2237
2494
|
return appJson.app.bundleName;
|
|
2238
2495
|
}
|
|
2239
2496
|
function getMainAbility(projectDir, moduleName = "entry", abilityName) {
|
|
2240
|
-
const moduleJsonPath =
|
|
2241
|
-
if (!
|
|
2497
|
+
const moduleJsonPath = path19.join(projectDir, moduleName, "src", "main", "module.json5");
|
|
2498
|
+
if (!fs18.existsSync(moduleJsonPath)) {
|
|
2242
2499
|
throw new OniroError(`Could not find module.json5 at ${moduleJsonPath}`);
|
|
2243
2500
|
}
|
|
2244
2501
|
const moduleJson = readJson5(moduleJsonPath);
|
|
@@ -2258,13 +2515,67 @@ function getMainAbility(projectDir, moduleName = "entry", abilityName) {
|
|
|
2258
2515
|
}
|
|
2259
2516
|
|
|
2260
2517
|
// src/hdc/app.ts
|
|
2518
|
+
function detectInstallFailure(output) {
|
|
2519
|
+
return /\[Fail\]|msg:\s*error|error:\s*failed to install|code:\s*95\d{5}/i.test(output) ? output.trim() : null;
|
|
2520
|
+
}
|
|
2521
|
+
function isUnsignedHap(hapPath) {
|
|
2522
|
+
return path20.basename(hapPath).includes("unsigned");
|
|
2523
|
+
}
|
|
2524
|
+
function installFailureReason(output) {
|
|
2525
|
+
const code = /code:\s*(\d+)/i.exec(output);
|
|
2526
|
+
if (!code) return output.trim().split(/\r?\n/)[0] ?? "";
|
|
2527
|
+
const message = /error:\s*([^\r\n]+)/i.exec(output.slice(code.index + code[0].length))?.[1]?.trim().replace(/\.$/, "");
|
|
2528
|
+
return message ? `code ${code[1]}: ${message}` : `code ${code[1]}`;
|
|
2529
|
+
}
|
|
2530
|
+
function unsignedHapRejection(output, hap) {
|
|
2531
|
+
const name = path20.basename(hap.path);
|
|
2532
|
+
const reason = installFailureReason(output);
|
|
2533
|
+
const what = hap.fallback ? `No signed HAP was found, so the unsigned ${name} was tried, and the target rejected it (${reason}).` : `The target rejected the unsigned ${name} (${reason}).`;
|
|
2534
|
+
if (!/9568320|signature|pkcs7/i.test(reason)) return what;
|
|
2535
|
+
return `${what} This target installs only signed HAPs. Sign the project for it, build again, and install the signed HAP: \`oniro-app sign\` for an OpenHarmony device or the Oniro emulator, \`oniro-app sign --harmonyos\` for a HarmonyOS device.`;
|
|
2536
|
+
}
|
|
2537
|
+
function ensureInstallOk(result, command, hap) {
|
|
2538
|
+
const output = `${result.stdout}
|
|
2539
|
+
${result.stderr}`;
|
|
2540
|
+
if (hap && isUnsignedHap(hap.path) && /code:\s*95\d{5}/i.test(output)) {
|
|
2541
|
+
throw new OniroError(unsignedHapRejection(output, hap), new CommandFailedError(command, result.code || 1, output.trim()));
|
|
2542
|
+
}
|
|
2543
|
+
ensureOk(result, command);
|
|
2544
|
+
const failure = detectInstallFailure(output);
|
|
2545
|
+
if (failure) throw new CommandFailedError(command, 1, failure);
|
|
2546
|
+
return result;
|
|
2547
|
+
}
|
|
2548
|
+
function existingHapOrUnsigned(hapPath) {
|
|
2549
|
+
if (fs19.existsSync(hapPath)) return { path: hapPath };
|
|
2550
|
+
const sibling = hapPath.replace(/-signed\.hap$/i, "-unsigned.hap");
|
|
2551
|
+
return sibling !== hapPath && fs19.existsSync(sibling) ? { path: sibling, fallback: true } : void 0;
|
|
2552
|
+
}
|
|
2553
|
+
async function resolveInstallHap(opts, logger) {
|
|
2554
|
+
const warnIfFallback = (hap) => {
|
|
2555
|
+
if (hap.fallback) logger.warn(`No signed HAP found; installing the unsigned ${path20.basename(hap.path)}.`);
|
|
2556
|
+
return hap;
|
|
2557
|
+
};
|
|
2558
|
+
const configured = opts.hapPath ?? opts.config.get("hapPath", "");
|
|
2559
|
+
const relative3 = configured || defaultPaths.hapPath;
|
|
2560
|
+
const hapPath = path20.isAbsolute(relative3) ? relative3 : path20.join(opts.projectDir, relative3);
|
|
2561
|
+
if (configured) {
|
|
2562
|
+
const hap = existingHapOrUnsigned(hapPath);
|
|
2563
|
+
if (!hap) throw new OniroError(`HAP file not found at: ${hapPath}. Build the app first.`);
|
|
2564
|
+
return warnIfFallback(hap);
|
|
2565
|
+
}
|
|
2566
|
+
if (fs19.existsSync(hapPath)) return { path: hapPath };
|
|
2567
|
+
const haps = await discoverHaps({ projectDir: opts.projectDir });
|
|
2568
|
+
const anySigned = Object.values(haps).some((m) => m.signed.length > 0);
|
|
2569
|
+
const entryUnsigned = anySigned ? void 0 : existingHapOrUnsigned(hapPath);
|
|
2570
|
+
if (entryUnsigned) return warnIfFallback(entryUnsigned);
|
|
2571
|
+
const picked = pickHap(haps);
|
|
2572
|
+
if (!picked) throw new OniroError(`No HAP found under ${opts.projectDir}. Build the app first (\`oniro-app build\`).`);
|
|
2573
|
+
return warnIfFallback({ path: picked.path, fallback: !picked.signed });
|
|
2574
|
+
}
|
|
2261
2575
|
async function installApp(opts) {
|
|
2262
2576
|
const logger = scopedLogger(opts.logger ?? noopLogger, "hdc");
|
|
2263
|
-
const
|
|
2264
|
-
const hapPath =
|
|
2265
|
-
if (!fs18.existsSync(hapPath)) {
|
|
2266
|
-
throw new OniroError(`HAP file not found at: ${hapPath}. Build and sign the app first.`);
|
|
2267
|
-
}
|
|
2577
|
+
const hap = await resolveInstallHap(opts, logger);
|
|
2578
|
+
const hapPath = hap.path;
|
|
2268
2579
|
const result = await hdcExec({
|
|
2269
2580
|
config: opts.config,
|
|
2270
2581
|
args: ["install", hapPath],
|
|
@@ -2273,7 +2584,7 @@ async function installApp(opts) {
|
|
|
2273
2584
|
});
|
|
2274
2585
|
if (result.stdout.trim()) logger.info(result.stdout.trim());
|
|
2275
2586
|
if (result.stderr.trim()) logger.warn(result.stderr.trim());
|
|
2276
|
-
|
|
2587
|
+
ensureInstallOk(result, `hdc install ${hapPath}`, hap);
|
|
2277
2588
|
let bundleName = "";
|
|
2278
2589
|
try {
|
|
2279
2590
|
bundleName = getBundleName(opts.projectDir);
|
|
@@ -2281,6 +2592,18 @@ async function installApp(opts) {
|
|
|
2281
2592
|
}
|
|
2282
2593
|
return { installed: true, bundleName, hapPath, output: `${result.stdout}${result.stderr}`.trim() };
|
|
2283
2594
|
}
|
|
2595
|
+
async function isBundleInstalled(opts) {
|
|
2596
|
+
const safe = opts.bundle.replace(/'/g, `'\\''`);
|
|
2597
|
+
const res = await shell({
|
|
2598
|
+
config: opts.config,
|
|
2599
|
+
command: `bm dump -n '${safe}'`,
|
|
2600
|
+
deviceSerial: opts.deviceSerial,
|
|
2601
|
+
timeoutMs: opts.timeoutMs ?? 15e3,
|
|
2602
|
+
logger: opts.logger
|
|
2603
|
+
});
|
|
2604
|
+
ensureOk(res, `hdc shell bm dump -n ${opts.bundle}`);
|
|
2605
|
+
return res.stdout.includes("{") && !/failed to get information/i.test(res.stdout);
|
|
2606
|
+
}
|
|
2284
2607
|
function detectAaStartFailure(output) {
|
|
2285
2608
|
return /failed to start ability|Error Code:\s*\d|Error Message:/i.test(output) ? output.trim() : null;
|
|
2286
2609
|
}
|
|
@@ -2351,10 +2674,10 @@ function listRunningProcesses(config, options = {}) {
|
|
|
2351
2674
|
const logger = options.logger ?? noopLogger;
|
|
2352
2675
|
const timeoutMs = options.timeoutMs ?? 1e3;
|
|
2353
2676
|
const hdc = getHdcPath(config);
|
|
2354
|
-
return new Promise((
|
|
2677
|
+
return new Promise((resolve7, reject) => {
|
|
2355
2678
|
let proc;
|
|
2356
2679
|
try {
|
|
2357
|
-
proc = spawnCompat(hdc, ["track-jpid"]);
|
|
2680
|
+
proc = spawnCompat(hdc, [...hdcTargetArgs(options.deviceSerial), "track-jpid"]);
|
|
2358
2681
|
} catch (err) {
|
|
2359
2682
|
reject(new OniroError(`Failed to spawn ${hdc}: ${err instanceof Error ? err.message : String(err)}`, err));
|
|
2360
2683
|
return;
|
|
@@ -2371,7 +2694,7 @@ function listRunningProcesses(config, options = {}) {
|
|
|
2371
2694
|
if (options.targetProcessName && name === options.targetProcessName && !resolved) {
|
|
2372
2695
|
resolved = true;
|
|
2373
2696
|
killProcessTree(proc);
|
|
2374
|
-
|
|
2697
|
+
resolve7(pid);
|
|
2375
2698
|
return;
|
|
2376
2699
|
}
|
|
2377
2700
|
}
|
|
@@ -2392,7 +2715,7 @@ function listRunningProcesses(config, options = {}) {
|
|
|
2392
2715
|
if (options.targetProcessName) {
|
|
2393
2716
|
reject(new OniroError(`Could not find process for bundle: ${options.targetProcessName}`));
|
|
2394
2717
|
} else {
|
|
2395
|
-
|
|
2718
|
+
resolve7(processes);
|
|
2396
2719
|
}
|
|
2397
2720
|
}, timeoutMs);
|
|
2398
2721
|
proc.on("close", () => {
|
|
@@ -2402,7 +2725,7 @@ function listRunningProcesses(config, options = {}) {
|
|
|
2402
2725
|
if (options.targetProcessName) {
|
|
2403
2726
|
reject(new OniroError(`Could not find process for bundle: ${options.targetProcessName}`));
|
|
2404
2727
|
} else {
|
|
2405
|
-
|
|
2728
|
+
resolve7(processes);
|
|
2406
2729
|
}
|
|
2407
2730
|
});
|
|
2408
2731
|
});
|
|
@@ -2424,7 +2747,11 @@ async function findRunningProcess(opts) {
|
|
|
2424
2747
|
const pid = res.stdout.trim().split(/\s+/).filter(Boolean)[0];
|
|
2425
2748
|
if (res.code === 0 && pid && /^\d+$/.test(pid)) return { pid, name: opts.bundle };
|
|
2426
2749
|
try {
|
|
2427
|
-
const list = await listRunningProcesses(opts.config, {
|
|
2750
|
+
const list = await listRunningProcesses(opts.config, {
|
|
2751
|
+
timeoutMs: 1200,
|
|
2752
|
+
deviceSerial: opts.deviceSerial,
|
|
2753
|
+
logger: opts.logger
|
|
2754
|
+
});
|
|
2428
2755
|
if (Array.isArray(list)) {
|
|
2429
2756
|
const mine = (name) => name === opts.bundle || name.startsWith(`${opts.bundle}/`) || name.startsWith(`${opts.bundle}:`);
|
|
2430
2757
|
const match = list.find((p) => p.name === opts.bundle) ?? list.find((p) => mine(p.name));
|
|
@@ -2567,12 +2894,12 @@ async function reboot(opts) {
|
|
|
2567
2894
|
}
|
|
2568
2895
|
|
|
2569
2896
|
// src/hdc/screen.ts
|
|
2570
|
-
var
|
|
2897
|
+
var fs20 = __toESM(require("fs"), 1);
|
|
2571
2898
|
var os9 = __toESM(require("os"), 1);
|
|
2572
|
-
var
|
|
2899
|
+
var path21 = __toESM(require("path"), 1);
|
|
2573
2900
|
var import_node_crypto = require("crypto");
|
|
2574
2901
|
function tmpName(ext) {
|
|
2575
|
-
return
|
|
2902
|
+
return path21.join(os9.tmpdir(), `oniro_screen_${(0, import_node_crypto.randomBytes)(6).toString("hex")}.${ext}`);
|
|
2576
2903
|
}
|
|
2577
2904
|
async function takeScreenshot(opts) {
|
|
2578
2905
|
const remote = "/data/local/tmp/oniro_screenshot.jpeg";
|
|
@@ -2582,11 +2909,11 @@ async function takeScreenshot(opts) {
|
|
|
2582
2909
|
const local = tmpName("jpeg");
|
|
2583
2910
|
try {
|
|
2584
2911
|
await recvFile({ config: opts.config, remote, local, deviceSerial: opts.deviceSerial, timeoutMs });
|
|
2585
|
-
const pixels = await
|
|
2912
|
+
const pixels = await fs20.promises.readFile(local);
|
|
2586
2913
|
const size = await getDisplaySize(opts.config, { deviceSerial: opts.deviceSerial }) ?? { width: 0, height: 0 };
|
|
2587
2914
|
return { pixels, width: size.width, height: size.height };
|
|
2588
2915
|
} finally {
|
|
2589
|
-
|
|
2916
|
+
fs20.promises.unlink(local).catch(() => {
|
|
2590
2917
|
});
|
|
2591
2918
|
void shell({ config: opts.config, command: `rm -f ${remote}`, deviceSerial: opts.deviceSerial }).catch(() => {
|
|
2592
2919
|
});
|
|
@@ -2605,9 +2932,9 @@ async function captureBurst(opts) {
|
|
|
2605
2932
|
const local = tmpName(`burst_${i}.jpeg`);
|
|
2606
2933
|
try {
|
|
2607
2934
|
await recvFile({ config: opts.config, remote: `${remoteDir}/f${i}.jpeg`, local, deviceSerial: opts.deviceSerial });
|
|
2608
|
-
frames.push(await
|
|
2935
|
+
frames.push(await fs20.promises.readFile(local));
|
|
2609
2936
|
} finally {
|
|
2610
|
-
|
|
2937
|
+
fs20.promises.unlink(local).catch(() => {
|
|
2611
2938
|
});
|
|
2612
2939
|
}
|
|
2613
2940
|
}
|
|
@@ -2619,6 +2946,55 @@ async function captureBurst(opts) {
|
|
|
2619
2946
|
}
|
|
2620
2947
|
|
|
2621
2948
|
// src/hdc/input.ts
|
|
2949
|
+
var UITEST_KEY_NAMES = ["Back", "Home", "Power"];
|
|
2950
|
+
var KEY_IDS = {
|
|
2951
|
+
back: 2,
|
|
2952
|
+
home: 1,
|
|
2953
|
+
power: 18,
|
|
2954
|
+
volumeup: 16,
|
|
2955
|
+
volumedown: 17,
|
|
2956
|
+
up: 2012,
|
|
2957
|
+
down: 2013,
|
|
2958
|
+
left: 2014,
|
|
2959
|
+
right: 2015,
|
|
2960
|
+
alt: 2045,
|
|
2961
|
+
shift: 2047,
|
|
2962
|
+
tab: 2049,
|
|
2963
|
+
space: 2050,
|
|
2964
|
+
enter: 2054,
|
|
2965
|
+
delete: 2055,
|
|
2966
|
+
backspace: 2055,
|
|
2967
|
+
pageup: 2068,
|
|
2968
|
+
pagedown: 2069,
|
|
2969
|
+
escape: 2070,
|
|
2970
|
+
esc: 2070,
|
|
2971
|
+
forwarddelete: 2071,
|
|
2972
|
+
ctrl: 2072,
|
|
2973
|
+
meta: 2076,
|
|
2974
|
+
movehome: 2081,
|
|
2975
|
+
moveend: 2082
|
|
2976
|
+
};
|
|
2977
|
+
function keyId(name) {
|
|
2978
|
+
const k = name.trim().toLowerCase();
|
|
2979
|
+
if (/^\d+$/.test(k)) return Number(k);
|
|
2980
|
+
if (/^[a-z]$/.test(k)) return 2017 + (k.charCodeAt(0) - 97);
|
|
2981
|
+
const id = KEY_IDS[k];
|
|
2982
|
+
if (id === void 0) {
|
|
2983
|
+
throw new OniroError(
|
|
2984
|
+
`Unknown key "${name}". Use a numeric KeyCode id, a letter, or one of: ${Object.keys(KEY_IDS).join(", ")}. Combine up to three with "+", e.g. Ctrl+A.`
|
|
2985
|
+
);
|
|
2986
|
+
}
|
|
2987
|
+
return id;
|
|
2988
|
+
}
|
|
2989
|
+
function resolveKeyEvent(key) {
|
|
2990
|
+
const spec = String(key).trim();
|
|
2991
|
+
if (spec === "") throw new OniroError('send input "keyEvent" requires a key.');
|
|
2992
|
+
const native = UITEST_KEY_NAMES.find((n) => n.toLowerCase() === spec.toLowerCase());
|
|
2993
|
+
if (native) return native;
|
|
2994
|
+
const parts = spec.includes("+") ? spec.split("+") : spec.split(/\s+/);
|
|
2995
|
+
if (parts.length > 3) throw new OniroError(`uitest injects at most three keys at once; got "${spec}".`);
|
|
2996
|
+
return parts.map(keyId).join(" ");
|
|
2997
|
+
}
|
|
2622
2998
|
function reqNum(value, name, type) {
|
|
2623
2999
|
if (value === void 0 || Number.isNaN(value)) {
|
|
2624
3000
|
throw new OniroError(`send input "${type}" requires a numeric ${name}.`);
|
|
@@ -2639,7 +3015,7 @@ function buildInputCommand(opts) {
|
|
|
2639
3015
|
}
|
|
2640
3016
|
case "keyEvent":
|
|
2641
3017
|
if (opts.key === void 0 || opts.key === "") throw new OniroError('send input "keyEvent" requires a key.');
|
|
2642
|
-
return `uitest uiInput keyEvent ${opts.key}`;
|
|
3018
|
+
return `uitest uiInput keyEvent ${resolveKeyEvent(opts.key)}`;
|
|
2643
3019
|
case "inputText": {
|
|
2644
3020
|
const t = String(opts.text ?? "").replace(/'/g, `'\\''`);
|
|
2645
3021
|
if (opts.pxX !== void 0 && opts.pxY !== void 0) {
|
|
@@ -2733,10 +3109,52 @@ async function sendRawTouch(opts) {
|
|
|
2733
3109
|
}
|
|
2734
3110
|
|
|
2735
3111
|
// src/hdc/dump.ts
|
|
2736
|
-
var
|
|
3112
|
+
var fs21 = __toESM(require("fs"), 1);
|
|
2737
3113
|
var os10 = __toESM(require("os"), 1);
|
|
2738
|
-
var
|
|
3114
|
+
var path22 = __toESM(require("path"), 1);
|
|
2739
3115
|
var import_node_crypto2 = require("crypto");
|
|
3116
|
+
|
|
3117
|
+
// src/hdc/ime.ts
|
|
3118
|
+
var INPUT_METHOD_SA_ID = 3703;
|
|
3119
|
+
var IME_BUNDLE_FALLBACK = /inputmethod|keyboard|(^|\.)ime(\.|$)/i;
|
|
3120
|
+
function parseImeBundles(output) {
|
|
3121
|
+
const current = [];
|
|
3122
|
+
const others = [];
|
|
3123
|
+
const entry = /\{[^{}]*"ime"\s*:\s*"([^"/]+)\/[^"]*"[^{}]*\}/g;
|
|
3124
|
+
for (const m of output.matchAll(entry)) {
|
|
3125
|
+
const bundle = m[1];
|
|
3126
|
+
const list = /"isCurrentIme"\s*:\s*"true"/.test(m[0]) ? current : others;
|
|
3127
|
+
if (!current.includes(bundle) && !others.includes(bundle)) list.push(bundle);
|
|
3128
|
+
}
|
|
3129
|
+
return [...current, ...others];
|
|
3130
|
+
}
|
|
3131
|
+
async function getImeBundles(config, opts = {}) {
|
|
3132
|
+
try {
|
|
3133
|
+
const res = await shell({
|
|
3134
|
+
config,
|
|
3135
|
+
command: `hidumper -s ${INPUT_METHOD_SA_ID} -a '-a'`,
|
|
3136
|
+
deviceSerial: opts.deviceSerial,
|
|
3137
|
+
timeoutMs: 1e4,
|
|
3138
|
+
logger: opts.logger
|
|
3139
|
+
});
|
|
3140
|
+
if (res.code === 0) return parseImeBundles(res.stdout);
|
|
3141
|
+
} catch (err) {
|
|
3142
|
+
opts.logger?.debug(`could not list input methods: ${err instanceof Error ? err.message : String(err)}`);
|
|
3143
|
+
}
|
|
3144
|
+
return [];
|
|
3145
|
+
}
|
|
3146
|
+
function findKeyboardWindow(tree, imeBundles) {
|
|
3147
|
+
const isIme = (bundle) => imeBundles.length > 0 ? imeBundles.includes(bundle) : IME_BUNDLE_FALLBACK.test(bundle);
|
|
3148
|
+
const stack = [tree];
|
|
3149
|
+
while (stack.length > 0) {
|
|
3150
|
+
const n = stack.shift();
|
|
3151
|
+
if (n.bundle && isIme(n.bundle)) return n;
|
|
3152
|
+
stack.push(...n.children ?? []);
|
|
3153
|
+
}
|
|
3154
|
+
return null;
|
|
3155
|
+
}
|
|
3156
|
+
|
|
3157
|
+
// src/hdc/dump.ts
|
|
2740
3158
|
function parseBounds(s) {
|
|
2741
3159
|
const m = /^\[(-?\d+),(-?\d+)\]\[(-?\d+),(-?\d+)\]$/.exec(s ?? "");
|
|
2742
3160
|
if (!m) return null;
|
|
@@ -2771,7 +3189,46 @@ var FLAG_MAP = [
|
|
|
2771
3189
|
["scrollable", "scroll"],
|
|
2772
3190
|
["longClickable", "longClick"]
|
|
2773
3191
|
];
|
|
2774
|
-
function
|
|
3192
|
+
function colorAlpha(color) {
|
|
3193
|
+
const m = /^#([0-9a-f]{2})?[0-9a-f]{6}$/i.exec(color ?? "");
|
|
3194
|
+
if (!m) return 0;
|
|
3195
|
+
return m[1] === void 0 ? 255 : parseInt(m[1], 16);
|
|
3196
|
+
}
|
|
3197
|
+
function paintsFill(a) {
|
|
3198
|
+
if (a.visible === "false") return false;
|
|
3199
|
+
if (a.opacity !== void 0 && a.opacity !== "" && !(Number(a.opacity) > 0)) return false;
|
|
3200
|
+
return colorAlpha(a.backgroundColor) > 0;
|
|
3201
|
+
}
|
|
3202
|
+
function findCoveredNodes(root) {
|
|
3203
|
+
const order = [];
|
|
3204
|
+
const fills = [];
|
|
3205
|
+
const visit = (n) => {
|
|
3206
|
+
const a = n.attributes ?? {};
|
|
3207
|
+
const entry = { node: n, bounds: parseBounds(a.bounds), end: 0 };
|
|
3208
|
+
if (entry.bounds && paintsFill(a)) fills.push(order.length);
|
|
3209
|
+
order.push(entry);
|
|
3210
|
+
const kids = (n.children ?? []).map((c, i) => ({ c, i, z: Number(c.attributes?.zIndex) || 0 })).sort((p, q) => p.z - q.z || p.i - q.i);
|
|
3211
|
+
for (const k of kids) visit(k.c);
|
|
3212
|
+
entry.end = order.length;
|
|
3213
|
+
};
|
|
3214
|
+
visit(root);
|
|
3215
|
+
const covered = /* @__PURE__ */ new Set();
|
|
3216
|
+
for (const p of order) {
|
|
3217
|
+
if (!p.bounds) continue;
|
|
3218
|
+
const cx = (p.bounds[0] + p.bounds[2]) / 2;
|
|
3219
|
+
const cy = (p.bounds[1] + p.bounds[3]) / 2;
|
|
3220
|
+
for (const j of fills) {
|
|
3221
|
+
if (j < p.end) continue;
|
|
3222
|
+
const q = order[j].bounds;
|
|
3223
|
+
if (cx >= q[0] && cx < q[2] && cy >= q[1] && cy < q[3]) {
|
|
3224
|
+
covered.add(p.node);
|
|
3225
|
+
break;
|
|
3226
|
+
}
|
|
3227
|
+
}
|
|
3228
|
+
}
|
|
3229
|
+
return covered;
|
|
3230
|
+
}
|
|
3231
|
+
function pruneLayout(node, display, covered) {
|
|
2775
3232
|
const a = node.attributes ?? {};
|
|
2776
3233
|
const px = parseBounds(a.bounds);
|
|
2777
3234
|
const out = {};
|
|
@@ -2789,28 +3246,125 @@ function pruneLayout(node, display) {
|
|
|
2789
3246
|
if (a.type && !STRUCTURAL_TYPES.has(a.type)) out.type = a.type;
|
|
2790
3247
|
if (a.text) out.text = a.text;
|
|
2791
3248
|
if (a.description) out.desc = a.description;
|
|
3249
|
+
if (a.hint) out.hint = a.hint;
|
|
2792
3250
|
if (a.id) out.id = a.id;
|
|
2793
3251
|
if (a.key && a.key !== a.id) out.key = a.key;
|
|
2794
3252
|
if (a.bundleName) out.bundle = a.bundleName;
|
|
2795
3253
|
for (const [attr, short] of FLAG_MAP) {
|
|
2796
3254
|
if (a[attr] === "true") out[short] = true;
|
|
2797
3255
|
}
|
|
3256
|
+
if (a.enabled === "false") out.disabled = true;
|
|
3257
|
+
if (covered?.has(node)) out.covered = true;
|
|
2798
3258
|
const children = [];
|
|
2799
3259
|
for (const c of node.children ?? []) {
|
|
2800
|
-
const pc = pruneLayout(c, display);
|
|
3260
|
+
const pc = pruneLayout(c, display, covered);
|
|
2801
3261
|
if (pc) {
|
|
2802
3262
|
if (Array.isArray(pc)) children.push(...pc);
|
|
2803
3263
|
else children.push(pc);
|
|
2804
3264
|
}
|
|
2805
3265
|
}
|
|
2806
3266
|
const hasSignal = Boolean(
|
|
2807
|
-
out.text || out.desc || out.id || out.key || out.click || out.scroll || out.check || out.longClick || out.bundle || out.type && out.type !== "root"
|
|
3267
|
+
out.text || out.desc || out.hint || out.id || out.key || out.click || out.scroll || out.check || out.longClick || out.bundle || out.type && out.type !== "root"
|
|
2808
3268
|
);
|
|
2809
3269
|
if (!hasSignal && children.length === 0) return null;
|
|
2810
3270
|
if (!hasSignal) return children.length === 1 ? children[0] : children;
|
|
2811
3271
|
if (children.length) out.children = children;
|
|
2812
3272
|
return out;
|
|
2813
3273
|
}
|
|
3274
|
+
function flattenLayout(tree) {
|
|
3275
|
+
const out = [];
|
|
3276
|
+
const visit = (n, window) => {
|
|
3277
|
+
const { children, ...rest } = n;
|
|
3278
|
+
const win = n.bundle ?? window;
|
|
3279
|
+
if (Object.keys(rest).length > 0) out.push({ ...rest, ...win ? { window: win } : {} });
|
|
3280
|
+
for (const c of children ?? []) visit(c, win);
|
|
3281
|
+
};
|
|
3282
|
+
visit(tree, void 0);
|
|
3283
|
+
return out;
|
|
3284
|
+
}
|
|
3285
|
+
function assertLayoutSelector(selector) {
|
|
3286
|
+
if (selector.text === void 0 && selector.id === void 0 && selector.type === void 0) {
|
|
3287
|
+
throw new OniroError("A layout selector needs a text, id, or type.");
|
|
3288
|
+
}
|
|
3289
|
+
if (selector.text !== void 0 && selector.text.trim() === "") {
|
|
3290
|
+
throw new OniroError("A layout selector text must not be blank.");
|
|
3291
|
+
}
|
|
3292
|
+
}
|
|
3293
|
+
function withoutWindow(tree, bundle) {
|
|
3294
|
+
const children = (tree.children ?? []).filter((c) => c.bundle !== bundle).map((c) => withoutWindow(c, bundle));
|
|
3295
|
+
return tree.children ? { ...tree, children } : tree;
|
|
3296
|
+
}
|
|
3297
|
+
function findLayoutNodes(tree, selector) {
|
|
3298
|
+
assertLayoutSelector(selector);
|
|
3299
|
+
const type = selector.type?.toLowerCase();
|
|
3300
|
+
const nodes = flattenLayout(tree).filter(
|
|
3301
|
+
(n) => (type === void 0 || n.type?.toLowerCase() === type) && (selector.id === void 0 || n.id === selector.id || n.key === selector.id)
|
|
3302
|
+
);
|
|
3303
|
+
if (selector.text === void 0) return nodes;
|
|
3304
|
+
const want = selector.text.trim().toLowerCase();
|
|
3305
|
+
const labels = (n) => [n.text, n.desc, n.hint].filter((s) => !!s).map((s) => s.trim().toLowerCase());
|
|
3306
|
+
const exact = nodes.filter((n) => labels(n).some((l) => l === want));
|
|
3307
|
+
if (exact.length > 0) return exact;
|
|
3308
|
+
return nodes.filter((n) => labels(n).some((l) => l.includes(want)));
|
|
3309
|
+
}
|
|
3310
|
+
function describeSelector(s) {
|
|
3311
|
+
const parts = [];
|
|
3312
|
+
if (s.text !== void 0) parts.push(`text "${s.text}"`);
|
|
3313
|
+
if (s.id !== void 0) parts.push(`id "${s.id}"`);
|
|
3314
|
+
if (s.type !== void 0) parts.push(`type ${s.type}`);
|
|
3315
|
+
return parts.join(", ");
|
|
3316
|
+
}
|
|
3317
|
+
function describeLayoutNode(n) {
|
|
3318
|
+
const label = n.text ?? n.desc ?? (n.hint !== void 0 ? `hint: ${n.hint}` : void 0);
|
|
3319
|
+
const at = n.c ? ` at ${n.c[0].toFixed(3)},${n.c[1].toFixed(3)}` : "";
|
|
3320
|
+
return `${n.type ?? "node"}${label !== void 0 ? ` "${label}"` : ""}${n.id ? ` #${n.id}` : ""}${at}`;
|
|
3321
|
+
}
|
|
3322
|
+
var area = (b) => b ? (b[2] - b[0]) * (b[3] - b[1]) : 0;
|
|
3323
|
+
function enclosesTarget(outer, inner) {
|
|
3324
|
+
const b = outer.b;
|
|
3325
|
+
const c = inner.c;
|
|
3326
|
+
if (!b || !c) return false;
|
|
3327
|
+
return c[0] >= b[0] && c[0] <= b[2] && c[1] >= b[1] && c[1] <= b[3] && area(b) > area(inner.b);
|
|
3328
|
+
}
|
|
3329
|
+
var TargetNotFoundError = class extends OniroError {
|
|
3330
|
+
constructor(message, reason) {
|
|
3331
|
+
super(message);
|
|
3332
|
+
this.reason = reason;
|
|
3333
|
+
this.name = "TargetNotFoundError";
|
|
3334
|
+
}
|
|
3335
|
+
reason;
|
|
3336
|
+
};
|
|
3337
|
+
function pickTarget(tree, selector, index) {
|
|
3338
|
+
const matches = findLayoutNodes(tree, selector).filter((n) => n.c !== void 0);
|
|
3339
|
+
const what = describeSelector(selector);
|
|
3340
|
+
if (matches.length === 0) {
|
|
3341
|
+
throw new TargetNotFoundError(`No on-screen element matches ${what}. Run \`oniro-app dump layout --flat\` to see what is there.`, "none");
|
|
3342
|
+
}
|
|
3343
|
+
const visible = matches.filter((n) => !n.covered);
|
|
3344
|
+
if (visible.length === 0) {
|
|
3345
|
+
throw new TargetNotFoundError(
|
|
3346
|
+
`Found ${matches.length} element(s) matching ${what}, but each is covered by an overlay (dialog, keyboard, or another layer): ${matches.map(describeLayoutNode).join("; ")}.`,
|
|
3347
|
+
"covered"
|
|
3348
|
+
);
|
|
3349
|
+
}
|
|
3350
|
+
const clickable = visible.filter((n) => n.click);
|
|
3351
|
+
const preferred = clickable.length > 0 ? clickable : visible;
|
|
3352
|
+
const candidates = preferred.filter((n) => !preferred.some((o) => o !== n && o.click && enclosesTarget(o, n)));
|
|
3353
|
+
if (index !== void 0) {
|
|
3354
|
+
const picked = candidates[index];
|
|
3355
|
+
if (!picked) {
|
|
3356
|
+
throw new TargetNotFoundError(`Index ${index} is out of range: ${candidates.length} element(s) match ${what}.`, "ambiguous");
|
|
3357
|
+
}
|
|
3358
|
+
return picked;
|
|
3359
|
+
}
|
|
3360
|
+
if (candidates.length > 1) {
|
|
3361
|
+
throw new TargetNotFoundError(
|
|
3362
|
+
`${candidates.length} elements match ${what}; pass an index to choose: ` + candidates.map((n, i) => `[${i}] ${describeLayoutNode(n)}`).join("; "),
|
|
3363
|
+
"ambiguous"
|
|
3364
|
+
);
|
|
3365
|
+
}
|
|
3366
|
+
return candidates[0];
|
|
3367
|
+
}
|
|
2814
3368
|
async function dumpLayout(opts) {
|
|
2815
3369
|
const timeoutMs = opts.timeoutMs ?? 3e4;
|
|
2816
3370
|
const bundleArg = opts.bundleName ? ` -b '${opts.bundleName.replace(/'/g, `'\\''`)}'` : "";
|
|
@@ -2820,33 +3374,193 @@ async function dumpLayout(opts) {
|
|
|
2820
3374
|
if (!m) throw new OniroError(`Could not parse dumpLayout output:
|
|
2821
3375
|
${dump.stdout}`);
|
|
2822
3376
|
const remote = m[1];
|
|
2823
|
-
const local =
|
|
3377
|
+
const local = path22.join(os10.tmpdir(), `oniro_layout_${(0, import_node_crypto2.randomBytes)(6).toString("hex")}.json`);
|
|
2824
3378
|
try {
|
|
2825
3379
|
await recvFile({ config: opts.config, remote, local, deviceSerial: opts.deviceSerial, timeoutMs });
|
|
2826
|
-
const parsed = JSON.parse(await
|
|
3380
|
+
const parsed = JSON.parse(await fs21.promises.readFile(local, "utf8"));
|
|
2827
3381
|
const display = await getDisplaySize(opts.config, { deviceSerial: opts.deviceSerial });
|
|
2828
3382
|
if (opts.prune === false) {
|
|
2829
3383
|
return { display, tree: parsed };
|
|
2830
3384
|
}
|
|
2831
|
-
const prunedRaw = pruneLayout(parsed, display);
|
|
3385
|
+
const prunedRaw = pruneLayout(parsed, display, findCoveredNodes(parsed));
|
|
2832
3386
|
const tree = Array.isArray(prunedRaw) ? { children: prunedRaw } : prunedRaw ?? { children: [] };
|
|
2833
|
-
return { display, tree };
|
|
3387
|
+
if (!opts.detectKeyboard || opts.bundleName) return { display, tree };
|
|
3388
|
+
const imeBundles = await getImeBundles(opts.config, { deviceSerial: opts.deviceSerial, logger: opts.logger });
|
|
3389
|
+
const kb = findKeyboardWindow(tree, imeBundles);
|
|
3390
|
+
const keyboard = kb ? { shown: true, bundle: kb.bundle, ...kb.b ? { b: kb.b } : {} } : { shown: false };
|
|
3391
|
+
return { display, tree, keyboard };
|
|
2834
3392
|
} finally {
|
|
2835
|
-
|
|
3393
|
+
fs21.promises.unlink(local).catch(() => {
|
|
2836
3394
|
});
|
|
2837
3395
|
void shell({ config: opts.config, command: `rm -f ${remote}`, deviceSerial: opts.deviceSerial }).catch(() => {
|
|
2838
3396
|
});
|
|
2839
3397
|
}
|
|
2840
3398
|
}
|
|
2841
3399
|
|
|
3400
|
+
// src/hdc/uiActions.ts
|
|
3401
|
+
var FOCUS_SETTLE_MS = 400;
|
|
3402
|
+
var KEYBOARD_SETTLE_MS = 400;
|
|
3403
|
+
var sleep = (ms) => new Promise((resolve7) => setTimeout(resolve7, ms));
|
|
3404
|
+
function targetTree(layout) {
|
|
3405
|
+
const kb = layout.keyboard;
|
|
3406
|
+
return kb?.shown && kb.bundle ? withoutWindow(layout.tree, kb.bundle) : layout.tree;
|
|
3407
|
+
}
|
|
3408
|
+
function fractionToPixel(f, size) {
|
|
3409
|
+
return Math.min(size - 1, Math.round(f * size));
|
|
3410
|
+
}
|
|
3411
|
+
function pressBack(opts) {
|
|
3412
|
+
return sendInput({ config: opts.config, type: "keyEvent", key: "Back", deviceSerial: opts.deviceSerial, logger: opts.logger });
|
|
3413
|
+
}
|
|
3414
|
+
async function hideKeyboard(opts) {
|
|
3415
|
+
const { keyboard } = await dumpLayout({ ...opts, detectKeyboard: true });
|
|
3416
|
+
if (!keyboard?.shown) return false;
|
|
3417
|
+
await pressBack(opts);
|
|
3418
|
+
await sleep(KEYBOARD_SETTLE_MS);
|
|
3419
|
+
return true;
|
|
3420
|
+
}
|
|
3421
|
+
async function locateTarget(opts) {
|
|
3422
|
+
assertLayoutSelector(opts.selector);
|
|
3423
|
+
const dumpOpts = { config: opts.config, detectKeyboard: true, deviceSerial: opts.deviceSerial, logger: opts.logger };
|
|
3424
|
+
let layout = await dumpLayout(dumpOpts);
|
|
3425
|
+
if (!layout.display) throw new OniroError("Could not read the display size to locate the element.");
|
|
3426
|
+
let keyboardHidden = false;
|
|
3427
|
+
let node;
|
|
3428
|
+
let failure;
|
|
3429
|
+
try {
|
|
3430
|
+
node = pickTarget(targetTree(layout), opts.selector, opts.index);
|
|
3431
|
+
} catch (err) {
|
|
3432
|
+
if (!(err instanceof TargetNotFoundError) || err.reason === "ambiguous") throw err;
|
|
3433
|
+
failure = err;
|
|
3434
|
+
}
|
|
3435
|
+
if ((!node || !node.click) && layout.keyboard?.shown && opts.hideKeyboard !== false) {
|
|
3436
|
+
opts.logger?.debug("target not reachable while the keyboard is up; dismissing it and looking again");
|
|
3437
|
+
await pressBack(opts);
|
|
3438
|
+
await sleep(KEYBOARD_SETTLE_MS);
|
|
3439
|
+
keyboardHidden = true;
|
|
3440
|
+
layout = await dumpLayout(dumpOpts);
|
|
3441
|
+
if (!layout.display) throw new OniroError("Could not read the display size to locate the element.");
|
|
3442
|
+
node = pickTarget(targetTree(layout), opts.selector, opts.index);
|
|
3443
|
+
}
|
|
3444
|
+
if (!node) throw failure;
|
|
3445
|
+
if (!node.c) throw new OniroError("The element has no on-screen position.");
|
|
3446
|
+
const x2 = fractionToPixel(node.c[0], layout.display.width);
|
|
3447
|
+
const y = fractionToPixel(node.c[1], layout.display.height);
|
|
3448
|
+
return { node, x: x2, y, display: layout.display, keyboardHidden };
|
|
3449
|
+
}
|
|
3450
|
+
function fraction(value, name) {
|
|
3451
|
+
if (value === void 0) return void 0;
|
|
3452
|
+
if (!(value >= 0 && value <= 1)) throw new OniroError(`${name} must be a fraction between 0 and 1; got ${value}.`);
|
|
3453
|
+
return value;
|
|
3454
|
+
}
|
|
3455
|
+
function pair(a, b, names) {
|
|
3456
|
+
if (a === void 0 && b === void 0) return void 0;
|
|
3457
|
+
if (a === void 0 || b === void 0) throw new OniroError(`Give both ${names}.`);
|
|
3458
|
+
return [a, b];
|
|
3459
|
+
}
|
|
3460
|
+
async function performInput(opts) {
|
|
3461
|
+
const device = { config: opts.config, deviceSerial: opts.deviceSerial, logger: opts.logger };
|
|
3462
|
+
const startFx = pair(fraction(opts.fx, "--fx"), fraction(opts.fy, "--fy"), "--fx and --fy");
|
|
3463
|
+
const startPx = pair(opts.pxX, opts.pxY, "--x and --y");
|
|
3464
|
+
const endFx = pair(fraction(opts.fx2, "--fx2"), fraction(opts.fy2, "--fy2"), "--fx2 and --fy2");
|
|
3465
|
+
const endPx = pair(opts.pxX2, opts.pxY2, "--x2 and --y2");
|
|
3466
|
+
if ([opts.target, startFx, startPx].filter((s) => s !== void 0).length > 1) {
|
|
3467
|
+
throw new OniroError("Give the start point one way: a target, fractions (--fx/--fy), or pixels (--x/--y).");
|
|
3468
|
+
}
|
|
3469
|
+
if (endFx && endPx) throw new OniroError("Give the end point one way: fractions (--fx2/--fy2) or pixels (--x2/--y2).");
|
|
3470
|
+
let display;
|
|
3471
|
+
const toPx = async ([fx, fy]) => {
|
|
3472
|
+
display ??= await getDisplaySize(opts.config, { deviceSerial: opts.deviceSerial, logger: opts.logger });
|
|
3473
|
+
if (!display) throw new OniroError("Could not read the display size to convert fractions to pixels.");
|
|
3474
|
+
return [fractionToPixel(fx, display.width), fractionToPixel(fy, display.height)];
|
|
3475
|
+
};
|
|
3476
|
+
const result = { keyboardHidden: false };
|
|
3477
|
+
let start = startPx;
|
|
3478
|
+
if (opts.target) {
|
|
3479
|
+
const located = await locateTarget({ ...device, selector: opts.target, index: opts.targetIndex });
|
|
3480
|
+
display = located.display;
|
|
3481
|
+
start = [located.x, located.y];
|
|
3482
|
+
result.target = located.node;
|
|
3483
|
+
result.keyboardHidden = located.keyboardHidden;
|
|
3484
|
+
} else if (startFx) {
|
|
3485
|
+
start = await toPx(startFx);
|
|
3486
|
+
}
|
|
3487
|
+
const end = endFx ? await toPx(endFx) : endPx;
|
|
3488
|
+
if (start) [result.x, result.y] = start;
|
|
3489
|
+
const tapToFocus = async () => {
|
|
3490
|
+
if (!start) return;
|
|
3491
|
+
await sendInput({ ...device, type: "click", pxX: start[0], pxY: start[1] });
|
|
3492
|
+
await sleep(FOCUS_SETTLE_MS);
|
|
3493
|
+
};
|
|
3494
|
+
const clearFocused = async () => {
|
|
3495
|
+
await sendInput({ ...device, type: "keyEvent", key: "Ctrl+A" });
|
|
3496
|
+
await sendInput({ ...device, type: "keyEvent", key: "Delete" });
|
|
3497
|
+
};
|
|
3498
|
+
switch (opts.type) {
|
|
3499
|
+
case "hideKeyboard":
|
|
3500
|
+
result.keyboardHidden = await hideKeyboard(device) || result.keyboardHidden;
|
|
3501
|
+
return result;
|
|
3502
|
+
case "clearText":
|
|
3503
|
+
await tapToFocus();
|
|
3504
|
+
await clearFocused();
|
|
3505
|
+
break;
|
|
3506
|
+
case "inputText":
|
|
3507
|
+
if (opts.clear) {
|
|
3508
|
+
await tapToFocus();
|
|
3509
|
+
await clearFocused();
|
|
3510
|
+
await sendInput({ ...device, type: "inputText", text: opts.text });
|
|
3511
|
+
} else {
|
|
3512
|
+
await sendInput({ ...device, type: "inputText", pxX: start?.[0], pxY: start?.[1], text: opts.text });
|
|
3513
|
+
}
|
|
3514
|
+
break;
|
|
3515
|
+
default:
|
|
3516
|
+
await sendInput({
|
|
3517
|
+
...device,
|
|
3518
|
+
type: opts.type,
|
|
3519
|
+
pxX: start?.[0],
|
|
3520
|
+
pxY: start?.[1],
|
|
3521
|
+
pxX2: end?.[0],
|
|
3522
|
+
pxY2: end?.[1],
|
|
3523
|
+
speed: opts.speed,
|
|
3524
|
+
key: opts.key,
|
|
3525
|
+
text: opts.text
|
|
3526
|
+
});
|
|
3527
|
+
}
|
|
3528
|
+
if (opts.hideKeyboard) result.keyboardHidden = await hideKeyboard(device) || result.keyboardHidden;
|
|
3529
|
+
return result;
|
|
3530
|
+
}
|
|
3531
|
+
async function waitForTarget(opts) {
|
|
3532
|
+
const { selector } = opts;
|
|
3533
|
+
assertLayoutSelector(selector);
|
|
3534
|
+
let found = null;
|
|
3535
|
+
await waitForCondition({
|
|
3536
|
+
probe: async () => {
|
|
3537
|
+
const layout = await dumpLayout({
|
|
3538
|
+
config: opts.config,
|
|
3539
|
+
detectKeyboard: true,
|
|
3540
|
+
deviceSerial: opts.deviceSerial,
|
|
3541
|
+
logger: opts.logger
|
|
3542
|
+
});
|
|
3543
|
+
found = findLayoutNodes(targetTree(layout), selector).find((n) => !n.covered) ?? null;
|
|
3544
|
+
return opts.gone ? found === null : found !== null;
|
|
3545
|
+
},
|
|
3546
|
+
timeoutMs: opts.timeoutMs,
|
|
3547
|
+
pollMs: opts.pollMs ?? 500,
|
|
3548
|
+
abortSignal: opts.abortSignal,
|
|
3549
|
+
logger: opts.logger,
|
|
3550
|
+
timeoutMessage: opts.gone ? `An element matching the selector was still on screen after ${opts.timeoutMs}ms.` : `No element matching the selector appeared within ${opts.timeoutMs}ms.`
|
|
3551
|
+
});
|
|
3552
|
+
return opts.gone ? null : found;
|
|
3553
|
+
}
|
|
3554
|
+
|
|
2842
3555
|
// src/hdc/hilog.ts
|
|
3556
|
+
var fs22 = __toESM(require("fs"), 1);
|
|
2843
3557
|
function setHilogLevel(opts) {
|
|
2844
3558
|
const logger = opts.logger ?? noopLogger;
|
|
2845
3559
|
const hdc = getHdcPath(opts.config);
|
|
2846
|
-
return new Promise((
|
|
3560
|
+
return new Promise((resolve7, reject) => {
|
|
2847
3561
|
let child;
|
|
2848
3562
|
try {
|
|
2849
|
-
child = spawnCompat(hdc, ["shell", "hilog", "-b", opts.level]);
|
|
3563
|
+
child = spawnCompat(hdc, [...hdcTargetArgs(opts.deviceSerial), "shell", "hilog", "-b", opts.level]);
|
|
2850
3564
|
} catch (err) {
|
|
2851
3565
|
const msg = err instanceof Error ? err.message : String(err);
|
|
2852
3566
|
logger.error(`[hilog] failed to spawn hdc: ${msg}`);
|
|
@@ -2863,7 +3577,7 @@ function setHilogLevel(opts) {
|
|
|
2863
3577
|
});
|
|
2864
3578
|
child.once("close", (code) => {
|
|
2865
3579
|
if (code === 0) {
|
|
2866
|
-
|
|
3580
|
+
resolve7();
|
|
2867
3581
|
} else {
|
|
2868
3582
|
const msg = stderr.trim() || `hdc shell hilog -b ${opts.level} exited with code ${code}`;
|
|
2869
3583
|
logger.error(`[hilog] ${msg}`);
|
|
@@ -2874,8 +3588,7 @@ function setHilogLevel(opts) {
|
|
|
2874
3588
|
}
|
|
2875
3589
|
function streamHilog(opts) {
|
|
2876
3590
|
const hdc = getHdcPath(opts.config);
|
|
2877
|
-
const args = [];
|
|
2878
|
-
if (opts.deviceSerial) args.push("-t", opts.deviceSerial);
|
|
3591
|
+
const args = [...hdcTargetArgs(opts.deviceSerial)];
|
|
2879
3592
|
args.push("shell", "hilog");
|
|
2880
3593
|
if (opts.processId && opts.processId.trim() !== "") {
|
|
2881
3594
|
args.push("-P", opts.processId.trim());
|
|
@@ -2918,7 +3631,7 @@ async function resolveProcessId(config, bundle, deviceSerial) {
|
|
|
2918
3631
|
return proc?.pid;
|
|
2919
3632
|
}
|
|
2920
3633
|
function waitForLog(opts) {
|
|
2921
|
-
return new Promise((
|
|
3634
|
+
return new Promise((resolve7, reject) => {
|
|
2922
3635
|
const deadline = Date.now() + opts.timeoutMs;
|
|
2923
3636
|
let settled = false;
|
|
2924
3637
|
let child = null;
|
|
@@ -2937,7 +3650,7 @@ function waitForLog(opts) {
|
|
|
2937
3650
|
if (settled) return;
|
|
2938
3651
|
settled = true;
|
|
2939
3652
|
cleanup();
|
|
2940
|
-
|
|
3653
|
+
resolve7(entry);
|
|
2941
3654
|
};
|
|
2942
3655
|
const fail = (err) => {
|
|
2943
3656
|
if (settled) return;
|
|
@@ -2984,7 +3697,7 @@ function waitForLog(opts) {
|
|
|
2984
3697
|
});
|
|
2985
3698
|
}
|
|
2986
3699
|
function watchLog(opts) {
|
|
2987
|
-
return new Promise((
|
|
3700
|
+
return new Promise((resolve7, reject) => {
|
|
2988
3701
|
const matches = [];
|
|
2989
3702
|
let settled = false;
|
|
2990
3703
|
let child = null;
|
|
@@ -3001,7 +3714,7 @@ function watchLog(opts) {
|
|
|
3001
3714
|
if (settled) return;
|
|
3002
3715
|
settled = true;
|
|
3003
3716
|
cleanup();
|
|
3004
|
-
|
|
3717
|
+
resolve7(opts.dedup === false ? matches : dedupEntries(matches));
|
|
3005
3718
|
};
|
|
3006
3719
|
const onAbort = () => {
|
|
3007
3720
|
if (settled) return;
|
|
@@ -3033,6 +3746,94 @@ function watchLog(opts) {
|
|
|
3033
3746
|
});
|
|
3034
3747
|
});
|
|
3035
3748
|
}
|
|
3749
|
+
async function captureLog(opts) {
|
|
3750
|
+
const logger = opts.logger ?? noopLogger;
|
|
3751
|
+
if (opts.clear) {
|
|
3752
|
+
const res = await shell({ config: opts.config, command: "hilog -r", deviceSerial: opts.deviceSerial, timeoutMs: 1e4, logger });
|
|
3753
|
+
ensureOk(res, "hdc shell hilog -r");
|
|
3754
|
+
}
|
|
3755
|
+
const fd = fs22.openSync(opts.file, "a");
|
|
3756
|
+
let lines = 0;
|
|
3757
|
+
let reconnects = 0;
|
|
3758
|
+
try {
|
|
3759
|
+
await new Promise((resolve7, reject) => {
|
|
3760
|
+
const deadline = opts.durationMs !== void 0 ? Date.now() + opts.durationMs : Infinity;
|
|
3761
|
+
let settled = false;
|
|
3762
|
+
let child = null;
|
|
3763
|
+
let reconnectTimer;
|
|
3764
|
+
let buf = "";
|
|
3765
|
+
let open = true;
|
|
3766
|
+
const write = (line) => {
|
|
3767
|
+
if (!open || opts.pattern && !opts.pattern.test(line)) return;
|
|
3768
|
+
fs22.writeSync(fd, `${line}
|
|
3769
|
+
`);
|
|
3770
|
+
lines++;
|
|
3771
|
+
};
|
|
3772
|
+
const cleanup = () => {
|
|
3773
|
+
if (timer) clearTimeout(timer);
|
|
3774
|
+
if (reconnectTimer) clearTimeout(reconnectTimer);
|
|
3775
|
+
opts.abortSignal?.removeEventListener("abort", finish);
|
|
3776
|
+
if (buf.trim()) write(buf.trimEnd());
|
|
3777
|
+
buf = "";
|
|
3778
|
+
open = false;
|
|
3779
|
+
if (child) {
|
|
3780
|
+
child.stdout.removeAllListeners("data");
|
|
3781
|
+
killProcessTree(child);
|
|
3782
|
+
child = null;
|
|
3783
|
+
}
|
|
3784
|
+
};
|
|
3785
|
+
function finish() {
|
|
3786
|
+
if (settled) return;
|
|
3787
|
+
settled = true;
|
|
3788
|
+
cleanup();
|
|
3789
|
+
resolve7();
|
|
3790
|
+
}
|
|
3791
|
+
const fail = (err) => {
|
|
3792
|
+
if (settled) return;
|
|
3793
|
+
settled = true;
|
|
3794
|
+
cleanup();
|
|
3795
|
+
reject(err instanceof Error ? err : new Error(String(err)));
|
|
3796
|
+
};
|
|
3797
|
+
const timer = opts.durationMs !== void 0 ? setTimeout(finish, opts.durationMs) : void 0;
|
|
3798
|
+
if (opts.abortSignal?.aborted) {
|
|
3799
|
+
finish();
|
|
3800
|
+
return;
|
|
3801
|
+
}
|
|
3802
|
+
opts.abortSignal?.addEventListener("abort", finish, { once: true });
|
|
3803
|
+
const spawnStream = () => {
|
|
3804
|
+
if (settled) return;
|
|
3805
|
+
const stream = streamHilog({ config: opts.config, domain: opts.domain, deviceSerial: opts.deviceSerial });
|
|
3806
|
+
child = stream;
|
|
3807
|
+
stream.stdout.on("data", (d) => {
|
|
3808
|
+
buf += d.toString();
|
|
3809
|
+
const parts = buf.split(/\r?\n/);
|
|
3810
|
+
buf = parts.pop() ?? "";
|
|
3811
|
+
for (const line of parts) write(line);
|
|
3812
|
+
});
|
|
3813
|
+
const onEnd = () => {
|
|
3814
|
+
if (settled || child !== stream) return;
|
|
3815
|
+
child = null;
|
|
3816
|
+
if (Date.now() >= deadline) return;
|
|
3817
|
+
reconnects++;
|
|
3818
|
+
logger.warn(`[hilog] stream ended; reconnecting (${reconnects}).`);
|
|
3819
|
+
reconnectTimer = setTimeout(() => {
|
|
3820
|
+
try {
|
|
3821
|
+
spawnStream();
|
|
3822
|
+
} catch (err) {
|
|
3823
|
+
fail(err);
|
|
3824
|
+
}
|
|
3825
|
+
}, 2e3);
|
|
3826
|
+
};
|
|
3827
|
+
stream.once("close", onEnd);
|
|
3828
|
+
stream.once("error", onEnd);
|
|
3829
|
+
};
|
|
3830
|
+
spawnStream();
|
|
3831
|
+
});
|
|
3832
|
+
} finally {
|
|
3833
|
+
fs22.closeSync(fd);
|
|
3834
|
+
}
|
|
3835
|
+
return { file: opts.file, lines, reconnects };
|
|
3836
|
+
}
|
|
3036
3837
|
async function dumpLog(opts) {
|
|
3037
3838
|
let pipe = "hilog -x";
|
|
3038
3839
|
if (opts.domain !== void 0 && `${opts.domain}` !== "") pipe += ` -D ${opts.domain}`;
|
|
@@ -3057,17 +3858,17 @@ async function dumpLog(opts) {
|
|
|
3057
3858
|
}
|
|
3058
3859
|
|
|
3059
3860
|
// src/build/runHvigorw.ts
|
|
3060
|
-
var
|
|
3061
|
-
var
|
|
3861
|
+
var fs24 = __toESM(require("fs"), 1);
|
|
3862
|
+
var path24 = __toESM(require("path"), 1);
|
|
3062
3863
|
|
|
3063
3864
|
// src/harmonyos/target.ts
|
|
3064
|
-
var
|
|
3065
|
-
var
|
|
3865
|
+
var fs23 = __toESM(require("fs"), 1);
|
|
3866
|
+
var path23 = __toESM(require("path"), 1);
|
|
3066
3867
|
var import_json54 = __toESM(require("json5"), 1);
|
|
3067
3868
|
function detectRuntimeOs(projectDir, productName) {
|
|
3068
3869
|
let products = [];
|
|
3069
3870
|
try {
|
|
3070
|
-
const parsed = import_json54.default.parse(
|
|
3871
|
+
const parsed = import_json54.default.parse(fs23.readFileSync(path23.join(projectDir, "build-profile.json5"), "utf-8"));
|
|
3071
3872
|
if (Array.isArray(parsed.app?.products)) products = parsed.app.products;
|
|
3072
3873
|
} catch {
|
|
3073
3874
|
return "OpenHarmony";
|
|
@@ -3108,7 +3909,7 @@ function runHvigorw(opts) {
|
|
|
3108
3909
|
const { config } = opts;
|
|
3109
3910
|
const projectDir = toLongPath(opts.projectDir);
|
|
3110
3911
|
const logger = opts.logger ?? noopLogger;
|
|
3111
|
-
if (!
|
|
3912
|
+
if (!fs24.existsSync(path24.join(projectDir, "build-profile.json5"))) {
|
|
3112
3913
|
throw new OniroError(`Not an OpenHarmony project: ${projectDir} (build-profile.json5 not found).`);
|
|
3113
3914
|
}
|
|
3114
3915
|
let hvigorw;
|
|
@@ -3120,24 +3921,24 @@ function runHvigorw(opts) {
|
|
|
3120
3921
|
hvigorw = resolved.startsWith(projectDir) ? resolved : findHarmonyOsTool(sdk, "hvigorw") ?? resolved;
|
|
3121
3922
|
logger.info(`[build] Building for HarmonyOS against the SDK at ${sdk.sdkPath}.`);
|
|
3122
3923
|
} else {
|
|
3123
|
-
if (!
|
|
3924
|
+
if (!fs24.existsSync(getCmdToolsPath(config))) {
|
|
3124
3925
|
throw new CmdToolsNotInstalledError(getCmdToolsPath(config));
|
|
3125
3926
|
}
|
|
3126
3927
|
sdkEnv = { OHOS_BASE_SDK_HOME: getOhosBaseSdkHome(config) };
|
|
3127
3928
|
hvigorw = getHvigorwPath(config, projectDir);
|
|
3128
3929
|
}
|
|
3129
|
-
if (!
|
|
3930
|
+
if (!fs24.existsSync(hvigorw)) {
|
|
3130
3931
|
throw new OniroError(`hvigorw wrapper not found at ${hvigorw}.`);
|
|
3131
3932
|
}
|
|
3132
3933
|
const args = buildHvigorwArgs(opts);
|
|
3133
3934
|
if (process.platform !== "win32") {
|
|
3134
3935
|
try {
|
|
3135
|
-
|
|
3936
|
+
fs24.chmodSync(hvigorw, 493);
|
|
3136
3937
|
} catch {
|
|
3137
3938
|
}
|
|
3138
3939
|
}
|
|
3139
3940
|
const env = { ...process.env, ...sdkEnv };
|
|
3140
|
-
return new Promise((
|
|
3941
|
+
return new Promise((resolve7, reject) => {
|
|
3141
3942
|
if (opts.abortSignal?.aborted) {
|
|
3142
3943
|
reject(new CancelledError("hvigorw build cancelled."));
|
|
3143
3944
|
return;
|
|
@@ -3181,46 +3982,15 @@ function runHvigorw(opts) {
|
|
|
3181
3982
|
settled = true;
|
|
3182
3983
|
cleanup();
|
|
3183
3984
|
const exitCode = code ?? 1;
|
|
3184
|
-
if (exitCode === 0)
|
|
3985
|
+
if (exitCode === 0) resolve7({ exitCode });
|
|
3185
3986
|
else reject(new OniroError(`hvigorw exited with code ${exitCode}`));
|
|
3186
3987
|
});
|
|
3187
3988
|
});
|
|
3188
3989
|
}
|
|
3189
3990
|
|
|
3190
|
-
// src/build/discoverHaps.ts
|
|
3191
|
-
var fs23 = __toESM(require("fs"), 1);
|
|
3192
|
-
var path23 = __toESM(require("path"), 1);
|
|
3193
|
-
function moduleOfHap(hapFileName) {
|
|
3194
|
-
const dash = hapFileName.indexOf("-");
|
|
3195
|
-
return dash > 0 ? hapFileName.slice(0, dash) : hapFileName.replace(/\.hap$/i, "");
|
|
3196
|
-
}
|
|
3197
|
-
async function discoverHaps(opts) {
|
|
3198
|
-
const { projectDir } = opts;
|
|
3199
|
-
const maxDepth = opts.maxDepth ?? 8;
|
|
3200
|
-
const result = {};
|
|
3201
|
-
const walk = async (dir, depth) => {
|
|
3202
|
-
if (depth > maxDepth) return;
|
|
3203
|
-
const entries = await fs23.promises.readdir(dir, { withFileTypes: true }).catch(() => []);
|
|
3204
|
-
for (const e of entries) {
|
|
3205
|
-
const full = path23.join(dir, e.name);
|
|
3206
|
-
if (e.isDirectory()) {
|
|
3207
|
-
if (e.name === "node_modules" || e.name === "oh_modules" || e.name === ".git") continue;
|
|
3208
|
-
await walk(full, depth + 1);
|
|
3209
|
-
} else if (e.isFile() && e.name.endsWith(".hap")) {
|
|
3210
|
-
const moduleName = moduleOfHap(e.name);
|
|
3211
|
-
const bucket = result[moduleName] ??= { signed: [], unsigned: [] };
|
|
3212
|
-
if (e.name.includes("unsigned")) bucket.unsigned.push(full);
|
|
3213
|
-
else if (e.name.includes("signed")) bucket.signed.push(full);
|
|
3214
|
-
}
|
|
3215
|
-
}
|
|
3216
|
-
};
|
|
3217
|
-
await walk(projectDir, 0);
|
|
3218
|
-
return result;
|
|
3219
|
-
}
|
|
3220
|
-
|
|
3221
3991
|
// src/build/ohpm.ts
|
|
3222
|
-
var
|
|
3223
|
-
var
|
|
3992
|
+
var fs25 = __toESM(require("fs"), 1);
|
|
3993
|
+
var path25 = __toESM(require("path"), 1);
|
|
3224
3994
|
function runOhpm(opts) {
|
|
3225
3995
|
return runProcess({
|
|
3226
3996
|
command: opts.ohpmPath ?? getOhpmPath(opts.config),
|
|
@@ -3233,7 +4003,7 @@ function runOhpm(opts) {
|
|
|
3233
4003
|
});
|
|
3234
4004
|
}
|
|
3235
4005
|
async function ensureOhModules(opts) {
|
|
3236
|
-
if (
|
|
4006
|
+
if (fs25.existsSync(path25.join(opts.projectDir, "oh_modules"))) {
|
|
3237
4007
|
return { installed: false };
|
|
3238
4008
|
}
|
|
3239
4009
|
const res = await runOhpm({
|
|
@@ -3250,23 +4020,91 @@ async function ensureOhModules(opts) {
|
|
|
3250
4020
|
}
|
|
3251
4021
|
|
|
3252
4022
|
// src/build/codelinter.ts
|
|
3253
|
-
var FINDING_RE = /^(.*?):(\d+):(
|
|
4023
|
+
var FINDING_RE = /^(.*?):(\d+):(?:(\d+):)?\s*(error|warning|warn|suggestion|info)\b[:\s-]*(.*)$/i;
|
|
3254
4024
|
var TRAILING_CODE_RE = /[([]([\w@/.\-]+)[)\]]\s*$/;
|
|
3255
|
-
|
|
4025
|
+
var FILE_HEADER_RE = /^(.+\.\w+)\((\d+)\)$/;
|
|
4026
|
+
var ROW_RE = /^(\d+):(\d+)\s+(error|warning|warn|suggestion|info)\s+(.*)$/i;
|
|
4027
|
+
var RULE_RE = /^@?[\w.-]+(?:\/[\w.-]+)+$/;
|
|
4028
|
+
var SUMMARY_RE = /Defects:\s*(\d+);\s*Errors:\s*(\d+);\s*Warns:\s*(\d+);\s*Suggestions:\s*(\d+)/i;
|
|
4029
|
+
var NOTICE_RE = /does not match that of the project|error occurred during linting|incomplete report/i;
|
|
4030
|
+
var ANSI_RE = /\x1b\[[0-9;]*[A-Za-z]/g;
|
|
4031
|
+
function cleanCodelinterOutput(output) {
|
|
4032
|
+
return output.replace(ANSI_RE, "").split("\n").map((line) => (line.split("\r").filter((s) => s.trim() !== "").pop() ?? "").trimEnd()).join("\n");
|
|
4033
|
+
}
|
|
4034
|
+
function normaliseSeverity(s) {
|
|
4035
|
+
const lower = s.toLowerCase();
|
|
4036
|
+
return lower === "warn" ? "warning" : lower;
|
|
4037
|
+
}
|
|
4038
|
+
function parseCodelinterReport(output) {
|
|
3256
4039
|
const findings = [];
|
|
3257
|
-
|
|
3258
|
-
|
|
3259
|
-
|
|
3260
|
-
|
|
3261
|
-
|
|
3262
|
-
|
|
3263
|
-
|
|
3264
|
-
|
|
3265
|
-
|
|
4040
|
+
const notices = [];
|
|
4041
|
+
let summary = null;
|
|
4042
|
+
let file = "";
|
|
4043
|
+
for (const rawLine of cleanCodelinterOutput(output).split("\n")) {
|
|
4044
|
+
const line = rawLine.trim();
|
|
4045
|
+
if (!line) continue;
|
|
4046
|
+
const row = ROW_RE.exec(line);
|
|
4047
|
+
if (row) {
|
|
4048
|
+
if (!file) continue;
|
|
4049
|
+
const columns = row[4].trim().split(/\s{2,}/);
|
|
4050
|
+
const last = columns[columns.length - 1];
|
|
4051
|
+
const code = columns.length > 1 && RULE_RE.test(last) ? last : "";
|
|
4052
|
+
const message = (code ? columns.slice(0, -1) : columns).join(" ").trim();
|
|
4053
|
+
findings.push({
|
|
4054
|
+
file,
|
|
4055
|
+
line: Number(row[1]),
|
|
4056
|
+
column: Number(row[2]),
|
|
4057
|
+
severity: normaliseSeverity(row[3]),
|
|
4058
|
+
code,
|
|
4059
|
+
message
|
|
4060
|
+
});
|
|
4061
|
+
continue;
|
|
4062
|
+
}
|
|
4063
|
+
const header = FILE_HEADER_RE.exec(line);
|
|
4064
|
+
if (header) {
|
|
4065
|
+
file = header[1];
|
|
4066
|
+
continue;
|
|
3266
4067
|
}
|
|
3267
|
-
|
|
4068
|
+
const legacy = FINDING_RE.exec(line);
|
|
4069
|
+
if (legacy) {
|
|
4070
|
+
let message = legacy[5].trim();
|
|
4071
|
+
let code = "";
|
|
4072
|
+
const codeMatch = TRAILING_CODE_RE.exec(message);
|
|
4073
|
+
if (codeMatch) {
|
|
4074
|
+
code = codeMatch[1];
|
|
4075
|
+
message = message.slice(0, codeMatch.index).trim();
|
|
4076
|
+
}
|
|
4077
|
+
const finding = {
|
|
4078
|
+
file: legacy[1].trim(),
|
|
4079
|
+
line: Number(legacy[2]),
|
|
4080
|
+
severity: normaliseSeverity(legacy[4]),
|
|
4081
|
+
code,
|
|
4082
|
+
message
|
|
4083
|
+
};
|
|
4084
|
+
if (legacy[3]) finding.column = Number(legacy[3]);
|
|
4085
|
+
findings.push(finding);
|
|
4086
|
+
continue;
|
|
4087
|
+
}
|
|
4088
|
+
const totals = SUMMARY_RE.exec(line);
|
|
4089
|
+
if (totals) {
|
|
4090
|
+
summary = {
|
|
4091
|
+
defects: Number(totals[1]),
|
|
4092
|
+
errors: Number(totals[2]),
|
|
4093
|
+
warnings: Number(totals[3]),
|
|
4094
|
+
suggestions: Number(totals[4])
|
|
4095
|
+
};
|
|
4096
|
+
continue;
|
|
4097
|
+
}
|
|
4098
|
+
if (/no defects found/i.test(line)) {
|
|
4099
|
+
summary = { defects: 0, errors: 0, warnings: 0, suggestions: 0 };
|
|
4100
|
+
continue;
|
|
4101
|
+
}
|
|
4102
|
+
if (NOTICE_RE.test(line)) notices.push(line);
|
|
3268
4103
|
}
|
|
3269
|
-
return findings;
|
|
4104
|
+
return { findings, summary, notices, incomplete: notices.length > 0 };
|
|
4105
|
+
}
|
|
4106
|
+
function parseCodelinterFindings(output) {
|
|
4107
|
+
return parseCodelinterReport(output).findings;
|
|
3270
4108
|
}
|
|
3271
4109
|
async function runCodelinter(opts) {
|
|
3272
4110
|
const res = await runProcess({
|
|
@@ -3278,16 +4116,28 @@ async function runCodelinter(opts) {
|
|
|
3278
4116
|
logger: opts.logger
|
|
3279
4117
|
});
|
|
3280
4118
|
const raw = `${res.stdout}${res.stderr}`;
|
|
3281
|
-
return { code: res.code,
|
|
4119
|
+
return { code: res.code, ...parseCodelinterReport(raw), raw: cleanCodelinterOutput(raw) };
|
|
3282
4120
|
}
|
|
3283
4121
|
|
|
3284
4122
|
// src/build/buildHap.ts
|
|
4123
|
+
var DIAGNOSIS_OUTPUT_LIMIT = 2e6;
|
|
4124
|
+
function diagnoseBuildFailure(output) {
|
|
4125
|
+
if (/is not exported from Kit|has no exported member .*@systemapi/i.test(output)) {
|
|
4126
|
+
return "The project calls system APIs (`@systemapi`), which the public SDK leaves out. Build against the full OpenHarmony SDK: unpack its components into <root>/<os>/<api> of a separate SDK root and set ONIRO_SDK_ROOT_DIR to that root. Installing such an app also needs privileged signing (`oniro-app sign --apl system_core --acls \u2026 --app-privilege-capabilities \u2026`).";
|
|
4127
|
+
}
|
|
4128
|
+
if (/00308018|SDK path does not exist|SDK license agreement is not accepted/i.test(output)) {
|
|
4129
|
+
return "hvigor cannot find the SDK the project compiles against: check `oniro-app sdk list` against compileSdkVersion.";
|
|
4130
|
+
}
|
|
4131
|
+
return void 0;
|
|
4132
|
+
}
|
|
3285
4133
|
async function buildHap(opts) {
|
|
3286
4134
|
const logger = scopedLogger(opts.logger ?? noopLogger, "build");
|
|
3287
4135
|
const warnings = [];
|
|
3288
4136
|
const start = Date.now();
|
|
3289
4137
|
const projectDir = toLongPath(opts.projectDir);
|
|
3290
4138
|
const isHarmonyOs = detectRuntimeOs(projectDir, opts.product) === "HarmonyOS";
|
|
4139
|
+
const missingSdk = isHarmonyOs ? null : findMissingProjectSdk(opts.config, projectDir);
|
|
4140
|
+
if (missingSdk) throw missingSdk;
|
|
3291
4141
|
if (opts.autoInstallDeps !== false) {
|
|
3292
4142
|
const harmonySdk = isHarmonyOs ? findHarmonyOsSdk({ config: opts.config, logger: opts.logger }) : null;
|
|
3293
4143
|
const r = await ensureOhModules({
|
|
@@ -3300,23 +4150,35 @@ async function buildHap(opts) {
|
|
|
3300
4150
|
});
|
|
3301
4151
|
if (r.installed) logger.info("ran ohpm install --all (oh_modules was missing)");
|
|
3302
4152
|
}
|
|
3303
|
-
|
|
3304
|
-
|
|
3305
|
-
|
|
3306
|
-
|
|
3307
|
-
|
|
3308
|
-
|
|
3309
|
-
|
|
3310
|
-
|
|
3311
|
-
|
|
3312
|
-
|
|
3313
|
-
|
|
3314
|
-
|
|
4153
|
+
let output = "";
|
|
4154
|
+
let exitCode;
|
|
4155
|
+
try {
|
|
4156
|
+
({ exitCode } = await runHvigorw({
|
|
4157
|
+
config: opts.config,
|
|
4158
|
+
projectDir,
|
|
4159
|
+
product: opts.product,
|
|
4160
|
+
module: opts.module,
|
|
4161
|
+
buildMode: opts.mode,
|
|
4162
|
+
task: opts.task,
|
|
4163
|
+
parallel: opts.parallel,
|
|
4164
|
+
abortSignal: opts.abortSignal,
|
|
4165
|
+
onOutput: (chunk, stream) => {
|
|
4166
|
+
output = (output + chunk).slice(-DIAGNOSIS_OUTPUT_LIMIT);
|
|
4167
|
+
opts.onOutput?.(chunk, stream);
|
|
4168
|
+
},
|
|
4169
|
+
logger: opts.logger
|
|
4170
|
+
}));
|
|
4171
|
+
} catch (err) {
|
|
4172
|
+
const hint = diagnoseBuildFailure(output);
|
|
4173
|
+
if (hint && err instanceof OniroError) throw new OniroError(`${err.message}
|
|
4174
|
+
${hint}`, err);
|
|
4175
|
+
throw err;
|
|
4176
|
+
}
|
|
3315
4177
|
const discoveredHaps = await discoverHaps({ projectDir });
|
|
3316
4178
|
const anySigned = Object.values(discoveredHaps).some((m) => m.signed.length > 0);
|
|
3317
4179
|
if (!anySigned) {
|
|
3318
4180
|
warnings.push(
|
|
3319
|
-
isHarmonyOs ? "No signed HAPs found
|
|
4181
|
+
isHarmonyOs ? "No signed HAPs found. The HarmonyOS emulator installs the unsigned HAP as it is; for a device, run `oniro-app sign --harmonyos` to have AppGallery Connect issue a certificate and profile." : "No signed HAPs found: most OpenHarmony targets reject unsigned HAPs. Run `oniro-app sign` to generate local signing material, then build again."
|
|
3320
4182
|
);
|
|
3321
4183
|
}
|
|
3322
4184
|
return { exitCode, durationMs: Date.now() - start, discoveredHaps, warnings };
|
|
@@ -3333,21 +4195,21 @@ function isValidBundleName(bundleName) {
|
|
|
3333
4195
|
}
|
|
3334
4196
|
|
|
3335
4197
|
// src/project/templates.ts
|
|
3336
|
-
var
|
|
3337
|
-
var
|
|
4198
|
+
var fs26 = __toESM(require("fs"), 1);
|
|
4199
|
+
var path26 = __toESM(require("path"), 1);
|
|
3338
4200
|
function toHumanTemplateName(folderName) {
|
|
3339
4201
|
return folderName.replace(/([a-z])([A-Z])/g, "$1 $2");
|
|
3340
4202
|
}
|
|
3341
4203
|
function listTemplates(templateRoot) {
|
|
3342
|
-
if (!
|
|
3343
|
-
const entries =
|
|
4204
|
+
if (!fs26.existsSync(templateRoot)) return [];
|
|
4205
|
+
const entries = fs26.readdirSync(templateRoot, { withFileTypes: true });
|
|
3344
4206
|
return entries.filter((e) => e.isDirectory()).map((e) => {
|
|
3345
|
-
const dir =
|
|
3346
|
-
const metaPath =
|
|
4207
|
+
const dir = path26.join(templateRoot, e.name);
|
|
4208
|
+
const metaPath = path26.join(dir, "template.json");
|
|
3347
4209
|
let meta = {};
|
|
3348
|
-
if (
|
|
4210
|
+
if (fs26.existsSync(metaPath)) {
|
|
3349
4211
|
try {
|
|
3350
|
-
meta = JSON.parse(
|
|
4212
|
+
meta = JSON.parse(fs26.readFileSync(metaPath, "utf8"));
|
|
3351
4213
|
} catch {
|
|
3352
4214
|
}
|
|
3353
4215
|
}
|
|
@@ -3362,57 +4224,57 @@ function listTemplates(templateRoot) {
|
|
|
3362
4224
|
function validateTemplateLayout(templateDir, defaultModuleName) {
|
|
3363
4225
|
const required = [
|
|
3364
4226
|
"build-profile.json5",
|
|
3365
|
-
|
|
3366
|
-
|
|
3367
|
-
|
|
4227
|
+
path26.join("AppScope", "app.json5"),
|
|
4228
|
+
path26.join(defaultModuleName, "src", "main", "module.json5"),
|
|
4229
|
+
path26.join(defaultModuleName, "oh-package.json5"),
|
|
3368
4230
|
"hvigorfile.ts"
|
|
3369
4231
|
];
|
|
3370
|
-
return required.filter((rel) => !
|
|
4232
|
+
return required.filter((rel) => !fs26.existsSync(path26.join(templateDir, rel)));
|
|
3371
4233
|
}
|
|
3372
4234
|
|
|
3373
4235
|
// src/project/createScaffold.ts
|
|
3374
|
-
var
|
|
3375
|
-
var
|
|
4236
|
+
var fs28 = __toESM(require("fs"), 1);
|
|
4237
|
+
var path27 = __toESM(require("path"), 1);
|
|
3376
4238
|
|
|
3377
4239
|
// src/project/jsonHelpers.ts
|
|
3378
|
-
var
|
|
4240
|
+
var fs27 = __toESM(require("fs"), 1);
|
|
3379
4241
|
var import_json55 = __toESM(require("json5"), 1);
|
|
3380
4242
|
function readJson5File(filePath) {
|
|
3381
|
-
return import_json55.default.parse(
|
|
4243
|
+
return import_json55.default.parse(fs27.readFileSync(filePath, "utf8"));
|
|
3382
4244
|
}
|
|
3383
4245
|
function writeJson5File(filePath, value) {
|
|
3384
|
-
|
|
4246
|
+
fs27.writeFileSync(filePath, JSON.stringify(value, null, 2) + "\n", "utf8");
|
|
3385
4247
|
}
|
|
3386
4248
|
function readJsonFile(filePath) {
|
|
3387
|
-
return JSON.parse(
|
|
4249
|
+
return JSON.parse(fs27.readFileSync(filePath, "utf8"));
|
|
3388
4250
|
}
|
|
3389
4251
|
function writeJsonFile(filePath, value) {
|
|
3390
|
-
|
|
4252
|
+
fs27.writeFileSync(filePath, JSON.stringify(value, null, 2) + "\n", "utf8");
|
|
3391
4253
|
}
|
|
3392
4254
|
|
|
3393
4255
|
// src/project/createScaffold.ts
|
|
3394
4256
|
var IGNORED_DIRS = /* @__PURE__ */ new Set(["oh_modules", "node_modules", "build", ".hvigor"]);
|
|
3395
4257
|
async function pathExists(p) {
|
|
3396
4258
|
try {
|
|
3397
|
-
await
|
|
4259
|
+
await fs28.promises.access(p);
|
|
3398
4260
|
return true;
|
|
3399
4261
|
} catch {
|
|
3400
4262
|
return false;
|
|
3401
4263
|
}
|
|
3402
4264
|
}
|
|
3403
4265
|
async function copyDirRecursive(srcDir, destDir) {
|
|
3404
|
-
await
|
|
3405
|
-
const entries = await
|
|
4266
|
+
await fs28.promises.mkdir(destDir, { recursive: true });
|
|
4267
|
+
const entries = await fs28.promises.readdir(srcDir, { withFileTypes: true });
|
|
3406
4268
|
for (const entry of entries) {
|
|
3407
|
-
const src =
|
|
3408
|
-
const dest =
|
|
4269
|
+
const src = path27.join(srcDir, entry.name);
|
|
4270
|
+
const dest = path27.join(destDir, entry.name);
|
|
3409
4271
|
if (entry.isDirectory()) {
|
|
3410
4272
|
await copyDirRecursive(src, dest);
|
|
3411
4273
|
} else if (entry.isSymbolicLink()) {
|
|
3412
4274
|
continue;
|
|
3413
4275
|
} else if (entry.isFile()) {
|
|
3414
4276
|
if (entry.name === "template.json") continue;
|
|
3415
|
-
await
|
|
4277
|
+
await fs28.promises.copyFile(src, dest);
|
|
3416
4278
|
}
|
|
3417
4279
|
}
|
|
3418
4280
|
}
|
|
@@ -3420,9 +4282,9 @@ async function normalizeJson5ToJson(projectDir, logger) {
|
|
|
3420
4282
|
const stack = [projectDir];
|
|
3421
4283
|
while (stack.length > 0) {
|
|
3422
4284
|
const currentDir = stack.pop();
|
|
3423
|
-
const entries = await
|
|
4285
|
+
const entries = await fs28.promises.readdir(currentDir, { withFileTypes: true });
|
|
3424
4286
|
for (const entry of entries) {
|
|
3425
|
-
const fullPath =
|
|
4287
|
+
const fullPath = path27.join(currentDir, entry.name);
|
|
3426
4288
|
if (entry.isDirectory()) {
|
|
3427
4289
|
if (IGNORED_DIRS.has(entry.name)) continue;
|
|
3428
4290
|
stack.push(fullPath);
|
|
@@ -3444,23 +4306,23 @@ function toJavaPropertiesPath(p) {
|
|
|
3444
4306
|
function createOrUpdateLocalProperties(projectDir, sdkDir) {
|
|
3445
4307
|
const content = `sdk.dir=${toJavaPropertiesPath(sdkDir)}
|
|
3446
4308
|
`;
|
|
3447
|
-
|
|
4309
|
+
fs28.writeFileSync(path27.join(projectDir, "local.properties"), content, "utf8");
|
|
3448
4310
|
}
|
|
3449
4311
|
function renameIfExists(fromPath, toPath) {
|
|
3450
|
-
if (
|
|
3451
|
-
|
|
4312
|
+
if (fs28.existsSync(fromPath) && fromPath !== toPath) {
|
|
4313
|
+
fs28.renameSync(fromPath, toPath);
|
|
3452
4314
|
}
|
|
3453
4315
|
}
|
|
3454
4316
|
function updateTemplateConfigs(projectDir, args, sdkBaseDir) {
|
|
3455
|
-
const appJsonPath =
|
|
3456
|
-
if (
|
|
4317
|
+
const appJsonPath = path27.join(projectDir, "AppScope", "app.json5");
|
|
4318
|
+
if (fs28.existsSync(appJsonPath)) {
|
|
3457
4319
|
const appJson = readJson5File(appJsonPath);
|
|
3458
4320
|
appJson.app = appJson.app ?? {};
|
|
3459
4321
|
appJson.app.bundleName = args.bundleName;
|
|
3460
4322
|
writeJson5File(appJsonPath, appJson);
|
|
3461
4323
|
}
|
|
3462
|
-
const stringsPath =
|
|
3463
|
-
if (
|
|
4324
|
+
const stringsPath = path27.join(projectDir, "AppScope", "resources", "base", "element", "string.json");
|
|
4325
|
+
if (fs28.existsSync(stringsPath)) {
|
|
3464
4326
|
const strings = readJsonFile(stringsPath);
|
|
3465
4327
|
if (Array.isArray(strings.string)) {
|
|
3466
4328
|
const appName = strings.string.find((s) => s.name === "app_name");
|
|
@@ -3468,8 +4330,8 @@ function updateTemplateConfigs(projectDir, args, sdkBaseDir) {
|
|
|
3468
4330
|
}
|
|
3469
4331
|
writeJsonFile(stringsPath, strings);
|
|
3470
4332
|
}
|
|
3471
|
-
const buildProfilePath =
|
|
3472
|
-
if (
|
|
4333
|
+
const buildProfilePath = path27.join(projectDir, "build-profile.json5");
|
|
4334
|
+
if (fs28.existsSync(buildProfilePath)) {
|
|
3473
4335
|
const buildProfile = readJson5File(buildProfilePath);
|
|
3474
4336
|
buildProfile.app = buildProfile.app ?? {};
|
|
3475
4337
|
if (Array.isArray(buildProfile.app.products) && buildProfile.app.products.length > 0) {
|
|
@@ -3482,23 +4344,23 @@ function updateTemplateConfigs(projectDir, args, sdkBaseDir) {
|
|
|
3482
4344
|
}
|
|
3483
4345
|
writeJson5File(buildProfilePath, buildProfile);
|
|
3484
4346
|
}
|
|
3485
|
-
const moduleJsonPath =
|
|
3486
|
-
if (
|
|
4347
|
+
const moduleJsonPath = path27.join(projectDir, args.moduleName, "src", "main", "module.json5");
|
|
4348
|
+
if (fs28.existsSync(moduleJsonPath)) {
|
|
3487
4349
|
const moduleJson = readJson5File(moduleJsonPath);
|
|
3488
4350
|
moduleJson.module = moduleJson.module ?? {};
|
|
3489
4351
|
moduleJson.module.name = args.moduleName;
|
|
3490
4352
|
writeJson5File(moduleJsonPath, moduleJson);
|
|
3491
4353
|
}
|
|
3492
|
-
const moduleOhPackagePath =
|
|
3493
|
-
if (
|
|
4354
|
+
const moduleOhPackagePath = path27.join(projectDir, args.moduleName, "oh-package.json5");
|
|
4355
|
+
if (fs28.existsSync(moduleOhPackagePath)) {
|
|
3494
4356
|
const pkg = readJson5File(moduleOhPackagePath);
|
|
3495
4357
|
pkg.name = args.moduleName;
|
|
3496
4358
|
writeJson5File(moduleOhPackagePath, pkg);
|
|
3497
4359
|
}
|
|
3498
|
-
const vscodeDir =
|
|
3499
|
-
|
|
4360
|
+
const vscodeDir = path27.join(projectDir, ".vscode");
|
|
4361
|
+
fs28.mkdirSync(vscodeDir, { recursive: true });
|
|
3500
4362
|
const hapPath = `${args.moduleName}/build/default/outputs/default/${args.moduleName}-default-signed.hap`;
|
|
3501
|
-
writeJsonFile(
|
|
4363
|
+
writeJsonFile(path27.join(vscodeDir, "settings.json"), {
|
|
3502
4364
|
"oniro.hapPath": hapPath,
|
|
3503
4365
|
"files.associations": { "*.json5": "jsonc" }
|
|
3504
4366
|
});
|
|
@@ -3515,7 +4377,7 @@ async function createScaffold(opts) {
|
|
|
3515
4377
|
if (!opts.location || !await pathExists(opts.location)) {
|
|
3516
4378
|
throw new OniroError(`Location does not exist: ${opts.location}`);
|
|
3517
4379
|
}
|
|
3518
|
-
const templateDir =
|
|
4380
|
+
const templateDir = path27.join(opts.templateRoot, opts.templateId);
|
|
3519
4381
|
if (!await pathExists(templateDir)) {
|
|
3520
4382
|
throw new OniroError(`Template not found: ${templateDir}`);
|
|
3521
4383
|
}
|
|
@@ -3550,17 +4412,17 @@ async function createScaffold(opts) {
|
|
|
3550
4412
|
}
|
|
3551
4413
|
}
|
|
3552
4414
|
}
|
|
3553
|
-
const projectDir =
|
|
4415
|
+
const projectDir = path27.join(opts.location, opts.projectName);
|
|
3554
4416
|
if (await pathExists(projectDir)) {
|
|
3555
4417
|
if (!opts.overwrite) {
|
|
3556
4418
|
throw new OniroError(`Destination already exists: ${projectDir}. Pass overwrite: true to replace it.`);
|
|
3557
4419
|
}
|
|
3558
|
-
await
|
|
4420
|
+
await fs28.promises.rm(projectDir, { recursive: true, force: true });
|
|
3559
4421
|
}
|
|
3560
4422
|
logger.info(`[create] Scaffolding ${opts.templateId} at ${projectDir}`);
|
|
3561
4423
|
await copyDirRecursive(templateDir, projectDir);
|
|
3562
4424
|
if (moduleName !== defaultModuleName) {
|
|
3563
|
-
renameIfExists(
|
|
4425
|
+
renameIfExists(path27.join(projectDir, defaultModuleName), path27.join(projectDir, moduleName));
|
|
3564
4426
|
}
|
|
3565
4427
|
updateTemplateConfigs(
|
|
3566
4428
|
projectDir,
|
|
@@ -3577,11 +4439,11 @@ async function createScaffold(opts) {
|
|
|
3577
4439
|
}
|
|
3578
4440
|
|
|
3579
4441
|
// src/project/listModules.ts
|
|
3580
|
-
var
|
|
3581
|
-
var
|
|
4442
|
+
var fs29 = __toESM(require("fs"), 1);
|
|
4443
|
+
var path28 = __toESM(require("path"), 1);
|
|
3582
4444
|
function listModules(opts) {
|
|
3583
|
-
const buildProfile =
|
|
3584
|
-
if (!
|
|
4445
|
+
const buildProfile = path28.join(opts.projectDir, "build-profile.json5");
|
|
4446
|
+
if (!fs29.existsSync(buildProfile)) {
|
|
3585
4447
|
throw new OniroError(`build-profile.json5 not found at ${buildProfile}`);
|
|
3586
4448
|
}
|
|
3587
4449
|
const parsed = readJson5File(buildProfile);
|
|
@@ -3593,12 +4455,12 @@ function listModules(opts) {
|
|
|
3593
4455
|
}
|
|
3594
4456
|
|
|
3595
4457
|
// src/project/abilities.ts
|
|
3596
|
-
var
|
|
3597
|
-
var
|
|
4458
|
+
var fs30 = __toESM(require("fs"), 1);
|
|
4459
|
+
var path29 = __toESM(require("path"), 1);
|
|
3598
4460
|
function listAbilities(opts) {
|
|
3599
4461
|
const moduleName = opts.moduleName ?? "entry";
|
|
3600
|
-
const moduleJsonPath =
|
|
3601
|
-
if (!
|
|
4462
|
+
const moduleJsonPath = path29.join(opts.projectDir, moduleName, "src", "main", "module.json5");
|
|
4463
|
+
if (!fs30.existsSync(moduleJsonPath)) {
|
|
3602
4464
|
throw new OniroError(`Could not find module.json5 at ${moduleJsonPath}`);
|
|
3603
4465
|
}
|
|
3604
4466
|
const parsed = readJson5File(moduleJsonPath);
|
|
@@ -3606,12 +4468,12 @@ function listAbilities(opts) {
|
|
|
3606
4468
|
}
|
|
3607
4469
|
|
|
3608
4470
|
// src/project/prepareSigning.ts
|
|
3609
|
-
var
|
|
3610
|
-
var
|
|
4471
|
+
var fs31 = __toESM(require("fs"), 1);
|
|
4472
|
+
var path30 = __toESM(require("path"), 1);
|
|
3611
4473
|
function hasSigningArtifacts(dir) {
|
|
3612
|
-
if (!
|
|
3613
|
-
const buildProfile =
|
|
3614
|
-
if (!
|
|
4474
|
+
if (!fs31.existsSync(path30.join(dir, "signatures"))) return false;
|
|
4475
|
+
const buildProfile = path30.join(dir, "build-profile.json5");
|
|
4476
|
+
if (!fs31.existsSync(buildProfile)) return false;
|
|
3615
4477
|
try {
|
|
3616
4478
|
const parsed = readJson5File(buildProfile);
|
|
3617
4479
|
return Array.isArray(parsed.app?.signingConfigs) && parsed.app.signingConfigs.length > 0;
|
|
@@ -3629,6 +4491,7 @@ function prepareSigning(opts) {
|
|
|
3629
4491
|
apl: opts.apl,
|
|
3630
4492
|
appFeature: opts.appFeature,
|
|
3631
4493
|
acls: opts.acls,
|
|
4494
|
+
appPrivilegeCapabilities: opts.appPrivilegeCapabilities,
|
|
3632
4495
|
passwords: opts.passwords,
|
|
3633
4496
|
logger: opts.logger
|
|
3634
4497
|
});
|
|
@@ -3657,19 +4520,31 @@ var PROPS = [
|
|
|
3657
4520
|
["model", "const.product.model"],
|
|
3658
4521
|
["manufacturer", "const.product.manufacturer"],
|
|
3659
4522
|
["brand", "const.product.brand"],
|
|
4523
|
+
["osDistName", "const.product.os.dist.name"],
|
|
4524
|
+
["osDistVersion", "const.product.os.dist.version"],
|
|
3660
4525
|
["osFullName", "const.ohos.fullname"],
|
|
3661
4526
|
["osReleaseType", "const.ohos.releasetype"],
|
|
3662
4527
|
["sdkApiVersion", "const.ohos.apiversion"],
|
|
3663
|
-
["buildVersion", "const.product.software.version"]
|
|
4528
|
+
["buildVersion", "const.product.software.version"],
|
|
4529
|
+
["abi", "const.product.cpu.abilist"],
|
|
4530
|
+
["deviceType", "const.product.devicetype"]
|
|
3664
4531
|
];
|
|
3665
4532
|
async function getDeviceInfo(opts) {
|
|
3666
4533
|
const info = { serial: opts.deviceSerial ?? "(default)", display: null };
|
|
3667
|
-
|
|
3668
|
-
|
|
3669
|
-
|
|
4534
|
+
try {
|
|
4535
|
+
const values = await paramGetMany(
|
|
4536
|
+
opts.config,
|
|
4537
|
+
PROPS.map(([, prop]) => prop),
|
|
4538
|
+
{ deviceSerial: opts.deviceSerial, timeoutMs: opts.timeoutMs ?? 1e4, logger: opts.logger }
|
|
4539
|
+
);
|
|
4540
|
+
for (const [field, prop] of PROPS) {
|
|
4541
|
+
const value = values[prop];
|
|
3670
4542
|
if (value) info[field] = value;
|
|
3671
|
-
} catch {
|
|
3672
4543
|
}
|
|
4544
|
+
} catch {
|
|
4545
|
+
}
|
|
4546
|
+
if (info.osDistName || info.osFullName) {
|
|
4547
|
+
info.osFamily = /harmonyos/i.test(info.osDistName ?? "") ? "HarmonyOS" : "OpenHarmony";
|
|
3673
4548
|
}
|
|
3674
4549
|
const screen = await dumpScreen({ config: opts.config, deviceSerial: opts.deviceSerial, logger: opts.logger });
|
|
3675
4550
|
if (screen) info.display = { width: screen.width, height: screen.height };
|
|
@@ -3701,8 +4576,8 @@ async function diffHapAssets(opts) {
|
|
|
3701
4576
|
}
|
|
3702
4577
|
|
|
3703
4578
|
// src/install/apply.ts
|
|
3704
|
-
var
|
|
3705
|
-
var
|
|
4579
|
+
var fs32 = __toESM(require("fs"), 1);
|
|
4580
|
+
var path31 = __toESM(require("path"), 1);
|
|
3706
4581
|
function decideInstallMethod(s) {
|
|
3707
4582
|
if (!s.signInfoInconsistent) return "replace";
|
|
3708
4583
|
if (s.isSystemBundle && !s.allowUninstall) return "refuse";
|
|
@@ -3720,39 +4595,31 @@ async function pidOf(config, bundle, deviceSerial) {
|
|
|
3720
4595
|
const proc = await findRunningProcess({ config, bundle, deviceSerial });
|
|
3721
4596
|
return proc ? Number(proc.pid) : null;
|
|
3722
4597
|
}
|
|
3723
|
-
async function
|
|
3724
|
-
let
|
|
3725
|
-
if (hapPath) {
|
|
3726
|
-
|
|
4598
|
+
async function resolveHap(opts, logger) {
|
|
4599
|
+
let hap;
|
|
4600
|
+
if (opts.hapPath) {
|
|
4601
|
+
const hapPath = !path31.isAbsolute(opts.hapPath) && opts.projectDir ? path31.join(opts.projectDir, opts.hapPath) : opts.hapPath;
|
|
4602
|
+
hap = existingHapOrUnsigned(hapPath);
|
|
4603
|
+
if (!hap) throw new OniroError(`HAP not found at ${hapPath}.`);
|
|
3727
4604
|
} else {
|
|
3728
4605
|
if (!opts.projectDir) throw new OniroError("applyChanges requires hapPath or projectDir.");
|
|
3729
|
-
const
|
|
3730
|
-
if (opts.module
|
|
3731
|
-
|
|
3732
|
-
if (signed.length === 0) throw new OniroError(`No signed HAP for module ${opts.module}. Build first.`);
|
|
3733
|
-
hapPath = signed[0];
|
|
3734
|
-
} else {
|
|
3735
|
-
const withSigned = Object.entries(haps).filter(([, m]) => m.signed.length > 0);
|
|
3736
|
-
if (withSigned.length === 0) throw new OniroError("No signed HAP found. Build first.");
|
|
3737
|
-
if (withSigned.length > 1) {
|
|
3738
|
-
throw new OniroError(
|
|
3739
|
-
`Multiple modules have signed HAPs (${withSigned.map(([k]) => k).join(", ")}). Pass module to choose.`
|
|
3740
|
-
);
|
|
3741
|
-
}
|
|
3742
|
-
hapPath = withSigned[0][1].signed[0];
|
|
3743
|
-
}
|
|
4606
|
+
const picked = pickHap(await discoverHaps({ projectDir: opts.projectDir }), opts.module);
|
|
4607
|
+
if (!picked) throw new OniroError(`No HAP found${opts.module ? ` for module ${opts.module}` : ""}. Build first.`);
|
|
4608
|
+
hap = { path: picked.path, fallback: !picked.signed };
|
|
3744
4609
|
}
|
|
3745
|
-
if (
|
|
3746
|
-
return
|
|
4610
|
+
if (hap.fallback) logger.warn(`No signed HAP found; installing the unsigned ${path31.basename(hap.path)}.`);
|
|
4611
|
+
return hap;
|
|
3747
4612
|
}
|
|
3748
4613
|
async function applyChanges(opts) {
|
|
3749
4614
|
const logger = scopedLogger(opts.logger ?? noopLogger, "apply");
|
|
3750
4615
|
const { config, bundle, deviceSerial } = opts;
|
|
3751
4616
|
const isSystemBundle = opts.isSystemBundle ?? false;
|
|
3752
4617
|
const allowUninstall = opts.allowUninstall ?? false;
|
|
3753
|
-
const
|
|
4618
|
+
const hap = await resolveHap(opts, logger);
|
|
4619
|
+
const hapPath = hap.path;
|
|
4620
|
+
logger.info(`installing ${hapPath}`);
|
|
3754
4621
|
let assetsChanged = false;
|
|
3755
|
-
if (opts.installedHapPath &&
|
|
4622
|
+
if (opts.installedHapPath && fs32.existsSync(opts.installedHapPath)) {
|
|
3756
4623
|
try {
|
|
3757
4624
|
const diff = await diffHapAssets({ installedHap: opts.installedHapPath, newHap: hapPath });
|
|
3758
4625
|
assetsChanged = diff.addedAssetPaths.length > 0 || diff.removedAssetPaths.length > 0;
|
|
@@ -3773,12 +4640,21 @@ async function applyChanges(opts) {
|
|
|
3773
4640
|
if (decision === "uninstall-install") {
|
|
3774
4641
|
logger.warn(`sign-info inconsistent for ${bundle}; uninstalling then installing fresh.`);
|
|
3775
4642
|
await uninstallApp({ config, bundle, deviceSerial });
|
|
3776
|
-
|
|
4643
|
+
ensureInstallOk(
|
|
4644
|
+
await hdcExec({ config, args: ["install", hapPath], deviceSerial, timeoutMs: 3e5 }),
|
|
4645
|
+
`hdc install ${hapPath}`,
|
|
4646
|
+
hap
|
|
4647
|
+
);
|
|
3777
4648
|
installMethod = "uninstall-install";
|
|
3778
4649
|
} else {
|
|
3779
|
-
|
|
4650
|
+
ensureInstallOk(replaceRes, `hdc install -r ${hapPath}`, hap);
|
|
3780
4651
|
installMethod = "replace";
|
|
3781
4652
|
}
|
|
4653
|
+
if (!await isBundleInstalled({ config, bundle, deviceSerial })) {
|
|
4654
|
+
throw new OniroError(
|
|
4655
|
+
`hdc reported ${path31.basename(hapPath)} as installed, but bm dump does not list ${bundle}. Check that the HAP was built from this project (its bundleName must match).`
|
|
4656
|
+
);
|
|
4657
|
+
}
|
|
3782
4658
|
let postInstallPid = await pidOf(config, bundle, deviceSerial);
|
|
3783
4659
|
const rebootDecision = decideReboot({ assetsChanged, isSystemBundle, preInstallPid, postInstallPid });
|
|
3784
4660
|
let method = installMethod;
|
|
@@ -3853,9 +4729,9 @@ var AUTH_PATHS = {
|
|
|
3853
4729
|
};
|
|
3854
4730
|
|
|
3855
4731
|
// src/harmonyos/auth/tokenStore.ts
|
|
3856
|
-
var
|
|
4732
|
+
var fs33 = __toESM(require("fs"), 1);
|
|
3857
4733
|
var os11 = __toESM(require("os"), 1);
|
|
3858
|
-
var
|
|
4734
|
+
var path32 = __toESM(require("path"), 1);
|
|
3859
4735
|
var crypto3 = __toESM(require("crypto"), 1);
|
|
3860
4736
|
var ALGORITHM = "aes-256-gcm";
|
|
3861
4737
|
var KEY_LENGTH = 32;
|
|
@@ -3891,7 +4767,7 @@ function permissionHint(dir) {
|
|
|
3891
4767
|
}
|
|
3892
4768
|
function ensureDir(dir) {
|
|
3893
4769
|
try {
|
|
3894
|
-
|
|
4770
|
+
fs33.mkdirSync(dir, { recursive: true, mode: DIR_MODE });
|
|
3895
4771
|
} catch (err) {
|
|
3896
4772
|
if (err.code === "EACCES") {
|
|
3897
4773
|
throw new OniroError(permissionHint(dir), err);
|
|
@@ -3902,17 +4778,17 @@ function ensureDir(dir) {
|
|
|
3902
4778
|
function createTokenStore(opts) {
|
|
3903
4779
|
const logger = opts.logger ?? noopLogger;
|
|
3904
4780
|
const authDir = opts.config.get("harmonyosAuthDir", defaultPaths.harmonyosAuthDir());
|
|
3905
|
-
const tokenPath =
|
|
3906
|
-
const keyPath =
|
|
4781
|
+
const tokenPath = path32.join(authDir, TOKEN_FILE);
|
|
4782
|
+
const keyPath = path32.join(os11.homedir(), ".local", "share", "oniro-app", "keys", KEY_FILE);
|
|
3907
4783
|
function loadOrCreateKey() {
|
|
3908
|
-
if (
|
|
3909
|
-
const existing =
|
|
4784
|
+
if (fs33.existsSync(keyPath)) {
|
|
4785
|
+
const existing = fs33.readFileSync(keyPath);
|
|
3910
4786
|
if (existing.length === KEY_LENGTH) return existing;
|
|
3911
4787
|
logger.warn("[harmonyos] The token key file was malformed; regenerating (you will need to log in again).");
|
|
3912
4788
|
}
|
|
3913
|
-
ensureDir(
|
|
4789
|
+
ensureDir(path32.dirname(keyPath));
|
|
3914
4790
|
const key = crypto3.randomBytes(KEY_LENGTH);
|
|
3915
|
-
|
|
4791
|
+
fs33.writeFileSync(keyPath, key, { mode: FILE_MODE });
|
|
3916
4792
|
return key;
|
|
3917
4793
|
}
|
|
3918
4794
|
return {
|
|
@@ -3922,12 +4798,12 @@ function createTokenStore(opts) {
|
|
|
3922
4798
|
const payload = Buffer.from(JSON.stringify(session), "utf8");
|
|
3923
4799
|
const blob = encrypt2(loadOrCreateKey(), payload);
|
|
3924
4800
|
ensureDir(authDir);
|
|
3925
|
-
|
|
4801
|
+
fs33.writeFileSync(tokenPath, JSON.stringify(blob, null, 2), { mode: FILE_MODE });
|
|
3926
4802
|
},
|
|
3927
4803
|
load() {
|
|
3928
|
-
if (!
|
|
4804
|
+
if (!fs33.existsSync(tokenPath)) return null;
|
|
3929
4805
|
try {
|
|
3930
|
-
const blob = JSON.parse(
|
|
4806
|
+
const blob = JSON.parse(fs33.readFileSync(tokenPath, "utf8"));
|
|
3931
4807
|
if (!isEncryptedBlob(blob)) return null;
|
|
3932
4808
|
const { jwtToken, siteId } = JSON.parse(decrypt2(loadOrCreateKey(), blob).toString("utf8"));
|
|
3933
4809
|
return typeof jwtToken === "string" && jwtToken && typeof siteId === "string" ? { jwtToken, siteId } : null;
|
|
@@ -3939,7 +4815,7 @@ function createTokenStore(opts) {
|
|
|
3939
4815
|
}
|
|
3940
4816
|
logger.debug(`[harmonyos] Discarding unreadable token at ${tokenPath}.`);
|
|
3941
4817
|
try {
|
|
3942
|
-
|
|
4818
|
+
fs33.rmSync(tokenPath, { force: true });
|
|
3943
4819
|
} catch {
|
|
3944
4820
|
}
|
|
3945
4821
|
return null;
|
|
@@ -3947,7 +4823,7 @@ function createTokenStore(opts) {
|
|
|
3947
4823
|
},
|
|
3948
4824
|
clear() {
|
|
3949
4825
|
try {
|
|
3950
|
-
|
|
4826
|
+
fs33.rmSync(tokenPath, { force: true });
|
|
3951
4827
|
} catch (err) {
|
|
3952
4828
|
throw new OniroError(`Failed to remove ${tokenPath}.`, err);
|
|
3953
4829
|
}
|
|
@@ -4061,8 +4937,8 @@ function isQuit(quit) {
|
|
|
4061
4937
|
async function startCallbackServer(opts) {
|
|
4062
4938
|
let resolveCallback;
|
|
4063
4939
|
let rejectCallback;
|
|
4064
|
-
const callback = new Promise((
|
|
4065
|
-
resolveCallback =
|
|
4940
|
+
const callback = new Promise((resolve7, reject) => {
|
|
4941
|
+
resolveCallback = resolve7;
|
|
4066
4942
|
rejectCallback = reject;
|
|
4067
4943
|
});
|
|
4068
4944
|
callback.catch(() => {
|
|
@@ -4121,9 +4997,9 @@ async function startCallbackServer(opts) {
|
|
|
4121
4997
|
}
|
|
4122
4998
|
});
|
|
4123
4999
|
server.keepAliveTimeout = 1;
|
|
4124
|
-
await new Promise((
|
|
5000
|
+
await new Promise((resolve7, reject) => {
|
|
4125
5001
|
server.once("error", (err) => reject(new OniroError("Failed to start the local login callback server.", err)));
|
|
4126
|
-
server.listen(0, "127.0.0.1",
|
|
5002
|
+
server.listen(0, "127.0.0.1", resolve7);
|
|
4127
5003
|
});
|
|
4128
5004
|
const port = server.address().port;
|
|
4129
5005
|
return {
|
|
@@ -4139,12 +5015,12 @@ async function startCallbackServer(opts) {
|
|
|
4139
5015
|
return Promise.race([callback, timeout]).finally(() => clearTimeout(timer));
|
|
4140
5016
|
},
|
|
4141
5017
|
stop() {
|
|
4142
|
-
return new Promise((
|
|
5018
|
+
return new Promise((resolve7) => {
|
|
4143
5019
|
server.closeAllConnections?.();
|
|
4144
|
-
const timer = setTimeout(
|
|
5020
|
+
const timer = setTimeout(resolve7, 100);
|
|
4145
5021
|
server.close(() => {
|
|
4146
5022
|
clearTimeout(timer);
|
|
4147
|
-
|
|
5023
|
+
resolve7();
|
|
4148
5024
|
});
|
|
4149
5025
|
});
|
|
4150
5026
|
}
|
|
@@ -4176,11 +5052,11 @@ function openBrowser(url) {
|
|
|
4176
5052
|
windowsHide: true
|
|
4177
5053
|
});
|
|
4178
5054
|
child.unref();
|
|
4179
|
-
return new Promise((
|
|
5055
|
+
return new Promise((resolve7, reject) => {
|
|
4180
5056
|
child.on("error", (err) => reject(new OniroError(`Failed to open a browser (${command}).`, err)));
|
|
4181
5057
|
child.on(
|
|
4182
5058
|
"exit",
|
|
4183
|
-
(code) => code === 0 ?
|
|
5059
|
+
(code) => code === 0 ? resolve7() : reject(new OniroError(`Browser launcher '${command}' exited with code ${code}.`))
|
|
4184
5060
|
);
|
|
4185
5061
|
});
|
|
4186
5062
|
}
|
|
@@ -4428,7 +5304,7 @@ function mapAgcError(operation, statusCode, reasonPhrase, body) {
|
|
|
4428
5304
|
}
|
|
4429
5305
|
|
|
4430
5306
|
// src/harmonyos/signing/profile.ts
|
|
4431
|
-
var
|
|
5307
|
+
var fs34 = __toESM(require("fs"), 1);
|
|
4432
5308
|
var import_node_crypto3 = require("crypto");
|
|
4433
5309
|
var import_node_forge = __toESM(require("node-forge"), 1);
|
|
4434
5310
|
function extractProfileJson(buffer) {
|
|
@@ -4460,7 +5336,7 @@ function extractProfileJson(buffer) {
|
|
|
4460
5336
|
function readProvisionProfile(profilePath) {
|
|
4461
5337
|
let raw;
|
|
4462
5338
|
try {
|
|
4463
|
-
const json = extractProfileJson(
|
|
5339
|
+
const json = extractProfileJson(fs34.readFileSync(profilePath));
|
|
4464
5340
|
if (!json) return null;
|
|
4465
5341
|
raw = JSON.parse(json);
|
|
4466
5342
|
} catch {
|
|
@@ -4489,7 +5365,7 @@ function parseCertificate(pem) {
|
|
|
4489
5365
|
}
|
|
4490
5366
|
function keystoreHoldsKey(p12Path, keyAlias, keyPwd, certificate) {
|
|
4491
5367
|
try {
|
|
4492
|
-
const der = import_node_forge.default.util.createBuffer(
|
|
5368
|
+
const der = import_node_forge.default.util.createBuffer(fs34.readFileSync(p12Path).toString("binary"));
|
|
4493
5369
|
const p12 = import_node_forge.default.pkcs12.pkcs12FromAsn1(import_node_forge.default.asn1.fromDer(der), keyPwd);
|
|
4494
5370
|
const certBag = import_node_forge.default.pki.oids.certBag ?? "1.2.840.113549.1.12.10.1.3";
|
|
4495
5371
|
const wanted = certificate.publicKey.export({ type: "spki", format: "der" });
|
|
@@ -4505,7 +5381,7 @@ function keystoreHoldsKey(p12Path, keyAlias, keyPwd, certificate) {
|
|
|
4505
5381
|
}
|
|
4506
5382
|
}
|
|
4507
5383
|
function verifySigningMaterial(opts) {
|
|
4508
|
-
const pems =
|
|
5384
|
+
const pems = fs34.readFileSync(opts.cerPath, "utf8").match(/-----BEGIN CERTIFICATE-----[\s\S]*?-----END CERTIFICATE-----/g);
|
|
4509
5385
|
const chain = (pems ?? []).map(parseCertificate);
|
|
4510
5386
|
const leaf = chain.find((c) => !c.ca) ?? chain.at(-1);
|
|
4511
5387
|
if (!leaf) throw new AgcError(SIGNING_ERRORS.CERT_INVALID);
|
|
@@ -4524,14 +5400,14 @@ function verifySigningMaterial(opts) {
|
|
|
4524
5400
|
|
|
4525
5401
|
// src/harmonyos/signing/autoSign.ts
|
|
4526
5402
|
var crypto9 = __toESM(require("crypto"), 1);
|
|
4527
|
-
var
|
|
4528
|
-
var
|
|
5403
|
+
var fs39 = __toESM(require("fs"), 1);
|
|
5404
|
+
var path38 = __toESM(require("path"), 1);
|
|
4529
5405
|
var import_json57 = __toESM(require("json5"), 1);
|
|
4530
5406
|
|
|
4531
5407
|
// src/harmonyos/signing/agc.ts
|
|
4532
5408
|
var crypto6 = __toESM(require("crypto"), 1);
|
|
4533
|
-
var
|
|
4534
|
-
var
|
|
5409
|
+
var fs35 = __toESM(require("fs"), 1);
|
|
5410
|
+
var path33 = __toESM(require("path"), 1);
|
|
4535
5411
|
var ENDPOINTS = {
|
|
4536
5412
|
CERT_LIST: "/api/cps/harmony-cert-manage/v1/cert/list",
|
|
4537
5413
|
CERT_ADD: "/api/cps/harmony-cert-manage/v1/cert/add",
|
|
@@ -4617,8 +5493,8 @@ function createAgcClient(http3, auth) {
|
|
|
4617
5493
|
if (target.sha256 && actual !== target.sha256.toLowerCase()) {
|
|
4618
5494
|
throw new ChecksumMismatchError(target.sha256, actual);
|
|
4619
5495
|
}
|
|
4620
|
-
|
|
4621
|
-
|
|
5496
|
+
fs35.mkdirSync(path33.dirname(dest), { recursive: true, mode: 448 });
|
|
5497
|
+
fs35.writeFileSync(dest, buffer, { mode: 384 });
|
|
4622
5498
|
}
|
|
4623
5499
|
};
|
|
4624
5500
|
}
|
|
@@ -4669,22 +5545,22 @@ async function registerDevices(agc, connected, logger) {
|
|
|
4669
5545
|
|
|
4670
5546
|
// src/harmonyos/signing/keystore.ts
|
|
4671
5547
|
var crypto7 = __toESM(require("crypto"), 1);
|
|
4672
|
-
var
|
|
4673
|
-
var
|
|
5548
|
+
var fs37 = __toESM(require("fs"), 1);
|
|
5549
|
+
var path35 = __toESM(require("path"), 1);
|
|
4674
5550
|
|
|
4675
5551
|
// src/harmonyos/java.ts
|
|
4676
|
-
var
|
|
4677
|
-
var
|
|
5552
|
+
var fs36 = __toESM(require("fs"), 1);
|
|
5553
|
+
var path34 = __toESM(require("path"), 1);
|
|
4678
5554
|
var JAVA_BIN = process.platform === "win32" ? "java.exe" : "java";
|
|
4679
5555
|
function findJava(sdk, env = process.env) {
|
|
4680
5556
|
const candidates = [
|
|
4681
|
-
env.JAVA_HOME &&
|
|
4682
|
-
sdk &&
|
|
5557
|
+
env.JAVA_HOME && path34.join(env.JAVA_HOME, "bin", JAVA_BIN),
|
|
5558
|
+
sdk && path34.join(sdk.installRoot, "jbr", "bin", JAVA_BIN),
|
|
4683
5559
|
// macOS: the JBR is itself a bundle inside Studio's Contents.
|
|
4684
|
-
sdk &&
|
|
4685
|
-
...(env.PATH ?? "").split(
|
|
5560
|
+
sdk && path34.join(sdk.installRoot, "jbr", "Contents", "Home", "bin", JAVA_BIN),
|
|
5561
|
+
...(env.PATH ?? "").split(path34.delimiter).map((dir) => dir && path34.join(dir, JAVA_BIN))
|
|
4686
5562
|
];
|
|
4687
|
-
const found = candidates.find((c) => !!c &&
|
|
5563
|
+
const found = candidates.find((c) => !!c && fs36.existsSync(c));
|
|
4688
5564
|
if (found) return found;
|
|
4689
5565
|
throw new OniroError(
|
|
4690
5566
|
"No Java runtime found. HarmonyOS signing runs the SDK's hap-sign-tool.jar, which needs one: install a JDK and set JAVA_HOME, or install DevEco Studio, whose bundled runtime is used automatically."
|
|
@@ -4698,13 +5574,13 @@ function redactArgs(args) {
|
|
|
4698
5574
|
return args.map((arg, i) => args[i - 1] === "-keyPwd" || args[i - 1] === "-keystorePwd" ? "******" : arg);
|
|
4699
5575
|
}
|
|
4700
5576
|
async function generateKeystoreAndCsr(opts) {
|
|
4701
|
-
const jar =
|
|
4702
|
-
if (!
|
|
5577
|
+
const jar = path35.join(opts.sdk.sdkPath, "default", "openharmony", "toolchains", "lib", "hap-sign-tool.jar");
|
|
5578
|
+
if (!fs37.existsSync(jar)) {
|
|
4703
5579
|
throw new OniroError(`hap-sign-tool.jar not found at ${jar}; the HarmonyOS SDK looks incomplete.`);
|
|
4704
5580
|
}
|
|
4705
5581
|
const java = findJava(opts.sdk);
|
|
4706
5582
|
const keyPwd = crypto7.randomBytes(12).toString("base64url").replaceAll(/[-_]/g, "");
|
|
4707
|
-
|
|
5583
|
+
fs37.mkdirSync(path35.dirname(opts.p12Path), { recursive: true, mode: 448 });
|
|
4708
5584
|
const run = async (what, args) => {
|
|
4709
5585
|
const argv = ["-jar", jar, ...args, "-keyAlias", KEY_ALIAS, "-keystoreFile", opts.p12Path, "-keystorePwd", keyPwd, "-keyPwd", keyPwd];
|
|
4710
5586
|
opts.logger.debug(`[harmonyos] ${java} ${redactArgs(argv).join(" ")}`);
|
|
@@ -4718,28 +5594,28 @@ ${detail}` : ""}`);
|
|
|
4718
5594
|
opts.logger.info("[harmonyos] Generating the local keystore and certificate signing request...");
|
|
4719
5595
|
await run("generate the key pair", ["generate-keypair", "-keyAlg", "ECC", "-keySize", "NIST-P-256"]);
|
|
4720
5596
|
await run("generate the CSR", ["generate-csr", "-subject", "CN=DebugKey", "-signAlg", SIGN_ALG, "-outFile", opts.csrPath]);
|
|
4721
|
-
return { keyPwd, csr:
|
|
5597
|
+
return { keyPwd, csr: fs37.readFileSync(opts.csrPath, "utf8") };
|
|
4722
5598
|
}
|
|
4723
5599
|
|
|
4724
5600
|
// src/harmonyos/signing/paths.ts
|
|
4725
5601
|
var crypto8 = __toESM(require("crypto"), 1);
|
|
4726
|
-
var
|
|
5602
|
+
var path36 = __toESM(require("path"), 1);
|
|
4727
5603
|
function sanitizeNamePart(raw) {
|
|
4728
5604
|
const safe = (raw?.trim() ?? "").replace(/[\\/:*?"<>|=.-]+/g, "_");
|
|
4729
5605
|
return safe.slice(0, 64) || "default";
|
|
4730
5606
|
}
|
|
4731
5607
|
function resolveSigningMaterialPaths(config, productName, projectRoot) {
|
|
4732
5608
|
const hash = crypto8.createHash("sha256").update(projectRoot, "utf8").digest("base64url").replaceAll(/[-_]/g, "");
|
|
4733
|
-
const base =
|
|
5609
|
+
const base = path36.join(
|
|
4734
5610
|
config.get("harmonyosSigningDir", defaultPaths.harmonyosSigningDir()),
|
|
4735
|
-
`${sanitizeNamePart(productName)}_${
|
|
5611
|
+
`${sanitizeNamePart(productName)}_${path36.basename(projectRoot)}_${hash}=`
|
|
4736
5612
|
);
|
|
4737
5613
|
return { p12Path: `${base}.p12`, csrPath: `${base}.csr`, cerPath: `${base}.cer`, profilePath: `${base}.p7b` };
|
|
4738
5614
|
}
|
|
4739
5615
|
|
|
4740
5616
|
// src/harmonyos/signing/project.ts
|
|
4741
|
-
var
|
|
4742
|
-
var
|
|
5617
|
+
var fs38 = __toESM(require("fs"), 1);
|
|
5618
|
+
var path37 = __toESM(require("path"), 1);
|
|
4743
5619
|
var import_json56 = __toESM(require("json5"), 1);
|
|
4744
5620
|
|
|
4745
5621
|
// src/harmonyos/signing/aclPermissionList.ts
|
|
@@ -4807,9 +5683,9 @@ var ACL_PERMISSIONS = /* @__PURE__ */ new Set([
|
|
|
4807
5683
|
|
|
4808
5684
|
// src/harmonyos/signing/project.ts
|
|
4809
5685
|
function readRequestedPermissions(moduleConfigPath) {
|
|
4810
|
-
if (!
|
|
5686
|
+
if (!fs38.existsSync(moduleConfigPath)) return [];
|
|
4811
5687
|
try {
|
|
4812
|
-
const parsed = import_json56.default.parse(
|
|
5688
|
+
const parsed = import_json56.default.parse(fs38.readFileSync(moduleConfigPath, "utf-8"));
|
|
4813
5689
|
return (parsed.module?.requestPermissions ?? []).map((p) => p?.name).filter((n) => typeof n === "string" && n.length > 0);
|
|
4814
5690
|
} catch {
|
|
4815
5691
|
return [];
|
|
@@ -4818,10 +5694,10 @@ function readRequestedPermissions(moduleConfigPath) {
|
|
|
4818
5694
|
function collectAclPermissions(projectDir, logger = noopLogger) {
|
|
4819
5695
|
const requested = /* @__PURE__ */ new Set();
|
|
4820
5696
|
for (const module2 of listModules({ projectDir })) {
|
|
4821
|
-
const moduleRoot =
|
|
5697
|
+
const moduleRoot = path37.resolve(projectDir, module2.srcPath);
|
|
4822
5698
|
for (const sourceSet of ["main", "ohosTest"]) {
|
|
4823
5699
|
for (const name of readRequestedPermissions(
|
|
4824
|
-
|
|
5700
|
+
path37.join(moduleRoot, "src", sourceSet, "module.json5")
|
|
4825
5701
|
)) {
|
|
4826
5702
|
requested.add(name);
|
|
4827
5703
|
}
|
|
@@ -4837,7 +5713,7 @@ function collectAclPermissions(projectDir, logger = noopLogger) {
|
|
|
4837
5713
|
// src/harmonyos/signing/autoSign.ts
|
|
4838
5714
|
function shouldRegenerate(input) {
|
|
4839
5715
|
if (input.force) return "--force was passed";
|
|
4840
|
-
const missing = Object.values(input.paths).find((file) => !
|
|
5716
|
+
const missing = Object.values(input.paths).find((file) => !fs39.existsSync(file));
|
|
4841
5717
|
if (missing) return `${missing} is missing`;
|
|
4842
5718
|
const config = input.buildProfile.app?.signingConfigs?.find((c) => c.name === input.productName);
|
|
4843
5719
|
if (config?.type !== "HarmonyOS" || config.material?.profile !== input.paths.profilePath) {
|
|
@@ -4876,16 +5752,16 @@ function writeSigningConfig(opts) {
|
|
|
4876
5752
|
for (const product of app.products ?? []) {
|
|
4877
5753
|
if (product.name === opts.productName) product.signingConfig = opts.productName;
|
|
4878
5754
|
}
|
|
4879
|
-
|
|
5755
|
+
fs39.writeFileSync(opts.buildProfilePath, JSON.stringify(opts.buildProfile, null, 2));
|
|
4880
5756
|
}
|
|
4881
5757
|
async function harmonyOsAutoSign(opts) {
|
|
4882
5758
|
const logger = opts.logger ?? noopLogger;
|
|
4883
5759
|
const productName = opts.productName ?? "default";
|
|
4884
|
-
const projectDir =
|
|
4885
|
-
const buildProfilePath =
|
|
5760
|
+
const projectDir = path38.resolve(opts.projectDir);
|
|
5761
|
+
const buildProfilePath = path38.join(projectDir, "build-profile.json5");
|
|
4886
5762
|
let buildProfile;
|
|
4887
5763
|
try {
|
|
4888
|
-
buildProfile = import_json57.default.parse(
|
|
5764
|
+
buildProfile = import_json57.default.parse(fs39.readFileSync(buildProfilePath, "utf-8"));
|
|
4889
5765
|
} catch (err) {
|
|
4890
5766
|
throw new OniroError(`Could not read ${buildProfilePath}: ${err.message}`, err);
|
|
4891
5767
|
}
|
|
@@ -4925,7 +5801,7 @@ async function harmonyOsAutoSign(opts) {
|
|
|
4925
5801
|
logger.info(`[harmonyos] Regenerating signing material: ${reason}.`);
|
|
4926
5802
|
const agc = createAgcClient(opts.http ?? createHttpClient({ logger }), auth);
|
|
4927
5803
|
const deviceIds = await registerDevices(agc, connected, logger);
|
|
4928
|
-
for (const file of Object.values(paths))
|
|
5804
|
+
for (const file of Object.values(paths)) fs39.rmSync(file, { force: true });
|
|
4929
5805
|
const certName = `oniro_debug_${auth.teamId.replace(/[\\/.:]/g, "")}.cer`;
|
|
4930
5806
|
const stale = (await agc.listCertificates()).find((c) => c.certName === certName);
|
|
4931
5807
|
if (stale) {
|
|
@@ -4956,10 +5832,10 @@ async function harmonyOsAutoSign(opts) {
|
|
|
4956
5832
|
try {
|
|
4957
5833
|
verifySigningMaterial({ ...paths, keyAlias: KEY_ALIAS, keyPwd });
|
|
4958
5834
|
} catch (err) {
|
|
4959
|
-
|
|
5835
|
+
fs39.rmSync(paths.profilePath, { force: true });
|
|
4960
5836
|
throw err;
|
|
4961
5837
|
}
|
|
4962
|
-
const materialDir =
|
|
5838
|
+
const materialDir = path38.join(path38.dirname(paths.p12Path), "material");
|
|
4963
5839
|
try {
|
|
4964
5840
|
getKey(materialDir);
|
|
4965
5841
|
} catch {
|
|
@@ -4971,7 +5847,7 @@ async function harmonyOsAutoSign(opts) {
|
|
|
4971
5847
|
}
|
|
4972
5848
|
|
|
4973
5849
|
// src/index.ts
|
|
4974
|
-
var VERSION = "0.
|
|
5850
|
+
var VERSION = "0.14.0";
|
|
4975
5851
|
// Annotate the CommonJS export names for ESM import in node:
|
|
4976
5852
|
0 && (module.exports = {
|
|
4977
5853
|
ALL_SDKS,
|
|
@@ -4991,9 +5867,13 @@ var VERSION = "0.12.0";
|
|
|
4991
5867
|
SIGNING_ERRORS,
|
|
4992
5868
|
SdkNotInstalledError,
|
|
4993
5869
|
TMP_DIR_HINT,
|
|
5870
|
+
TargetNotFoundError,
|
|
4994
5871
|
UnsupportedPlatformError,
|
|
4995
5872
|
VERSION,
|
|
5873
|
+
appSandboxDir,
|
|
4996
5874
|
applyChanges,
|
|
5875
|
+
assertDevicePath,
|
|
5876
|
+
assertLayoutSelector,
|
|
4997
5877
|
attemptHdcConnection,
|
|
4998
5878
|
buildCmdWrapper,
|
|
4999
5879
|
buildGestureCommands,
|
|
@@ -5003,6 +5883,8 @@ var VERSION = "0.12.0";
|
|
|
5003
5883
|
buildInputCommand,
|
|
5004
5884
|
buildRawTouchCommand,
|
|
5005
5885
|
captureBurst,
|
|
5886
|
+
captureLog,
|
|
5887
|
+
cleanCodelinterOutput,
|
|
5006
5888
|
consoleLogger,
|
|
5007
5889
|
createHarmonyOsSession,
|
|
5008
5890
|
createInstallTempDir,
|
|
@@ -5016,10 +5898,14 @@ var VERSION = "0.12.0";
|
|
|
5016
5898
|
dedupEntries,
|
|
5017
5899
|
defaultContext,
|
|
5018
5900
|
defaultPaths,
|
|
5901
|
+
describeLayoutNode,
|
|
5019
5902
|
detectAaStartFailure,
|
|
5903
|
+
detectCmdToolsFlavour,
|
|
5904
|
+
detectInstallFailure,
|
|
5020
5905
|
detectProjectSdkVersion,
|
|
5021
5906
|
detectRuntimeOs,
|
|
5022
5907
|
detectSigningConfigNames,
|
|
5908
|
+
diagnoseBuildFailure,
|
|
5023
5909
|
diffEntryNames,
|
|
5024
5910
|
diffHapAssets,
|
|
5025
5911
|
discoverHaps,
|
|
@@ -5032,17 +5918,24 @@ var VERSION = "0.12.0";
|
|
|
5032
5918
|
dumpWindow,
|
|
5033
5919
|
encryptPwd,
|
|
5034
5920
|
ensureFreeSpace,
|
|
5921
|
+
ensureInstallOk,
|
|
5035
5922
|
ensureOhModules,
|
|
5036
5923
|
ensureOk,
|
|
5037
5924
|
ensureRemoteOk,
|
|
5038
5925
|
escapeCmdArgument,
|
|
5039
5926
|
escapeCmdCommand,
|
|
5927
|
+
existingHapOrUnsigned,
|
|
5040
5928
|
extractTarball,
|
|
5041
5929
|
extractZipWithProgress,
|
|
5042
5930
|
findAppProcessId,
|
|
5043
5931
|
findCmdToolsSourceDir,
|
|
5932
|
+
findCoveredNodes,
|
|
5044
5933
|
findHarmonyOsSdk,
|
|
5934
|
+
findKeyboardWindow,
|
|
5935
|
+
findLayoutNodes,
|
|
5936
|
+
findMissingProjectSdk,
|
|
5045
5937
|
findRunningProcess,
|
|
5938
|
+
flattenLayout,
|
|
5046
5939
|
forceStop,
|
|
5047
5940
|
formatBytes,
|
|
5048
5941
|
generateSigningConfigs,
|
|
@@ -5054,9 +5947,11 @@ var VERSION = "0.12.0";
|
|
|
5054
5947
|
getDeviceInfo,
|
|
5055
5948
|
getDisplaySize,
|
|
5056
5949
|
getEmulatorDir,
|
|
5950
|
+
getEmulatorStatus,
|
|
5057
5951
|
getFreeSpaceBytes,
|
|
5058
5952
|
getHdcPath,
|
|
5059
5953
|
getHvigorwPath,
|
|
5954
|
+
getImeBundles,
|
|
5060
5955
|
getInstalledSdks,
|
|
5061
5956
|
getKey,
|
|
5062
5957
|
getMainAbility,
|
|
@@ -5068,14 +5963,21 @@ var VERSION = "0.12.0";
|
|
|
5068
5963
|
getSupportedSdksForUi,
|
|
5069
5964
|
harmonyOsAutoSign,
|
|
5070
5965
|
harmonyOsSdkLabelForApi,
|
|
5966
|
+
hdcClientFailure,
|
|
5071
5967
|
hdcExec,
|
|
5968
|
+
hdcTargetArgs,
|
|
5969
|
+
hdcTargetArgsFor,
|
|
5970
|
+
hideKeyboard,
|
|
5072
5971
|
installApp,
|
|
5073
5972
|
installCmdTools,
|
|
5074
5973
|
installEmulator,
|
|
5974
|
+
installFailureReason,
|
|
5975
|
+
isBundleInstalled,
|
|
5075
5976
|
isCmdToolsInstalled,
|
|
5076
5977
|
isEmulatorInstalled,
|
|
5077
5978
|
isOutOfSpaceError,
|
|
5078
5979
|
isRamBackedPath,
|
|
5980
|
+
isUnsignedHap,
|
|
5079
5981
|
isValidBundleName,
|
|
5080
5982
|
isValidProjectName,
|
|
5081
5983
|
killProcessTree,
|
|
@@ -5085,19 +5987,28 @@ var VERSION = "0.12.0";
|
|
|
5085
5987
|
listModules,
|
|
5086
5988
|
listRunningProcesses,
|
|
5087
5989
|
listTemplates,
|
|
5990
|
+
locateTarget,
|
|
5088
5991
|
needsCmdWrapper,
|
|
5089
5992
|
nonInteractivePrompter,
|
|
5090
5993
|
noopLogger,
|
|
5091
5994
|
noopProgress,
|
|
5995
|
+
normaliseLocalPath,
|
|
5092
5996
|
paramGet,
|
|
5997
|
+
paramGetMany,
|
|
5093
5998
|
paramSet,
|
|
5094
5999
|
parseBounds,
|
|
5095
6000
|
parseCodelinterFindings,
|
|
6001
|
+
parseCodelinterReport,
|
|
5096
6002
|
parseDeviceList,
|
|
5097
6003
|
parseHilogLine,
|
|
6004
|
+
parseImeBundles,
|
|
5098
6005
|
parseMainAbilityFromBmDump,
|
|
6006
|
+
parseParamValues,
|
|
5099
6007
|
parseRemoteExit,
|
|
6008
|
+
performInput,
|
|
6009
|
+
pickHap,
|
|
5100
6010
|
pickSigningKind,
|
|
6011
|
+
pickTarget,
|
|
5101
6012
|
prepareSigning,
|
|
5102
6013
|
pruneLayout,
|
|
5103
6014
|
readJson5File,
|
|
@@ -5110,6 +6021,8 @@ var VERSION = "0.12.0";
|
|
|
5110
6021
|
removeSdk,
|
|
5111
6022
|
removeTempWorkDir,
|
|
5112
6023
|
requireHarmonyOsSdk,
|
|
6024
|
+
resolveDeviceSerial,
|
|
6025
|
+
resolveKeyEvent,
|
|
5113
6026
|
resolveTmpRoot,
|
|
5114
6027
|
runCodelinter,
|
|
5115
6028
|
runHvigorw,
|
|
@@ -5133,14 +6046,17 @@ var VERSION = "0.12.0";
|
|
|
5133
6046
|
toLongPath,
|
|
5134
6047
|
toSpaceError,
|
|
5135
6048
|
uninstallApp,
|
|
6049
|
+
unsignedHapRejection,
|
|
5136
6050
|
validateTemplateLayout,
|
|
5137
6051
|
verifySha256,
|
|
5138
6052
|
waitForBoot,
|
|
5139
6053
|
waitForBundle,
|
|
5140
6054
|
waitForCondition,
|
|
5141
6055
|
waitForLog,
|
|
6056
|
+
waitForTarget,
|
|
5142
6057
|
watchLog,
|
|
5143
6058
|
waypointsToRawEvents,
|
|
6059
|
+
withoutWindow,
|
|
5144
6060
|
writeJson5File,
|
|
5145
6061
|
writeJsonFile
|
|
5146
6062
|
});
|