@arkts/image-manager 0.0.2
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/LICENSE +21 -0
- package/README.md +81 -0
- package/dist/default-product-config.cjs +370 -0
- package/dist/default-product-config.d.cts +5 -0
- package/dist/default-product-config.d.mts +6 -0
- package/dist/default-product-config.mjs +369 -0
- package/dist/index.cjs +663 -0
- package/dist/index.d.cts +264 -0
- package/dist/index.d.mts +264 -0
- package/dist/index.mjs +629 -0
- package/dist/product-config-BbaYsaoV.d.mts +250 -0
- package/dist/product-config-Dk21FD9U.d.cts +250 -0
- package/package.json +67 -0
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,629 @@
|
|
|
1
|
+
import axios, { AxiosError } from "axios";
|
|
2
|
+
import mitt from "mitt";
|
|
3
|
+
import progress from "progress-stream";
|
|
4
|
+
import unzipper from "unzipper";
|
|
5
|
+
|
|
6
|
+
//#region src/deployer/list.ts
|
|
7
|
+
let DevModel = /* @__PURE__ */ function(DevModel) {
|
|
8
|
+
DevModel["MCHEMU_AL00CN"] = "MCHEMU-AL00CN";
|
|
9
|
+
DevModel["PHEMU_FD00"] = "PHEMU-FD00";
|
|
10
|
+
DevModel["PHEMU_FD01"] = "PHEMU-FD01";
|
|
11
|
+
DevModel["PHEMU_FD02"] = "PHEMU-FD02";
|
|
12
|
+
DevModel["PHEMU_FD06"] = "PHEMU-FD06";
|
|
13
|
+
DevModel["PCEMU_FD00"] = "PCEMU-FD00";
|
|
14
|
+
DevModel["PCEMU_FD05"] = "PCEMU-FD05";
|
|
15
|
+
return DevModel;
|
|
16
|
+
}({});
|
|
17
|
+
|
|
18
|
+
//#endregion
|
|
19
|
+
//#region src/errors/request-url-error.ts
|
|
20
|
+
var RequestUrlError = class extends Error {
|
|
21
|
+
constructor(message, code, cause) {
|
|
22
|
+
super(message, { cause });
|
|
23
|
+
this.message = message;
|
|
24
|
+
this.code = code;
|
|
25
|
+
this.cause = cause;
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
//#endregion
|
|
30
|
+
//#region src/deployer/image-deployer.ts
|
|
31
|
+
var ImageDeployerImpl = class {
|
|
32
|
+
options = {};
|
|
33
|
+
isDefault = true;
|
|
34
|
+
isCustomize = false;
|
|
35
|
+
isPublic = true;
|
|
36
|
+
vendorCountry = "CN";
|
|
37
|
+
hwHdcPort = "notset";
|
|
38
|
+
constructor(image, uuid, name, config) {
|
|
39
|
+
this.image = image;
|
|
40
|
+
this.config = config;
|
|
41
|
+
this.options.uuid = uuid;
|
|
42
|
+
this.options.name = name;
|
|
43
|
+
Object.assign(this.options, config);
|
|
44
|
+
}
|
|
45
|
+
setUuid(uuid) {
|
|
46
|
+
this.options.uuid = uuid;
|
|
47
|
+
return this;
|
|
48
|
+
}
|
|
49
|
+
setModel(model) {
|
|
50
|
+
this.options.model = model;
|
|
51
|
+
return this;
|
|
52
|
+
}
|
|
53
|
+
setDevModel(devModel) {
|
|
54
|
+
this.options.devModel = devModel;
|
|
55
|
+
return this;
|
|
56
|
+
}
|
|
57
|
+
setCpuNumber(cpuNumber) {
|
|
58
|
+
this.options.cpuNumber = cpuNumber.toString();
|
|
59
|
+
return this;
|
|
60
|
+
}
|
|
61
|
+
setMemoryRamSize(memoryRamSize) {
|
|
62
|
+
this.options.memoryRamSize = memoryRamSize.toString();
|
|
63
|
+
return this;
|
|
64
|
+
}
|
|
65
|
+
setDataDiskSize(dataDiskSize) {
|
|
66
|
+
this.options.dataDiskSize = dataDiskSize.toString();
|
|
67
|
+
return this;
|
|
68
|
+
}
|
|
69
|
+
setCoverResolutionWidth(coverResolutionWidth) {
|
|
70
|
+
this.options.coverResolutionWidth = coverResolutionWidth.toString();
|
|
71
|
+
return this;
|
|
72
|
+
}
|
|
73
|
+
setCoverResolutionHeight(coverResolutionHeight) {
|
|
74
|
+
this.options.coverResolutionHeight = coverResolutionHeight.toString();
|
|
75
|
+
return this;
|
|
76
|
+
}
|
|
77
|
+
setCoverDiagonalSize(coverDiagonalSize) {
|
|
78
|
+
this.options.coverDiagonalSize = coverDiagonalSize.toString();
|
|
79
|
+
return this;
|
|
80
|
+
}
|
|
81
|
+
setIsDefault(isDefault) {
|
|
82
|
+
this.isDefault = isDefault;
|
|
83
|
+
return this;
|
|
84
|
+
}
|
|
85
|
+
setIsCustomize(isCustomize) {
|
|
86
|
+
this.isCustomize = isCustomize;
|
|
87
|
+
return this;
|
|
88
|
+
}
|
|
89
|
+
setIsPublic(isPublic) {
|
|
90
|
+
this.isPublic = isPublic;
|
|
91
|
+
return this;
|
|
92
|
+
}
|
|
93
|
+
setHwHdcPort(hwHdcPort) {
|
|
94
|
+
this.hwHdcPort = hwHdcPort.toString();
|
|
95
|
+
return this;
|
|
96
|
+
}
|
|
97
|
+
setVendorCountry(vendorCountry) {
|
|
98
|
+
this.vendorCountry = vendorCountry;
|
|
99
|
+
return this;
|
|
100
|
+
}
|
|
101
|
+
async getDevModel() {
|
|
102
|
+
if (this.options.devModel) return this.options.devModel;
|
|
103
|
+
const productConfigItem = (await this.image.getProductConfig()).find((item) => item.name === this.config.productName);
|
|
104
|
+
if (!productConfigItem) throw new Error(`Product config item ${this.config.productName} not found`);
|
|
105
|
+
if (productConfigItem.devModel) return productConfigItem.devModel;
|
|
106
|
+
const defaultProductConfigItem = (await this.image.getProductConfig(true)).find((item) => item.name === this.config.productName);
|
|
107
|
+
if (!defaultProductConfigItem) throw new Error(`Default product config item ${this.config.productName} not found`);
|
|
108
|
+
if (defaultProductConfigItem.devModel) return defaultProductConfigItem.devModel;
|
|
109
|
+
}
|
|
110
|
+
async buildList() {
|
|
111
|
+
if (!(await this.image.getProductConfig()).find((item) => item.name === this.config.productName)) throw new Error(`Product config item ${this.config.productName} not found`);
|
|
112
|
+
return {
|
|
113
|
+
...this.options,
|
|
114
|
+
"imageDir": this.image.getPath().split(",").join(this.image.getImageManager().getOptions().path.sep) + this.image.getImageManager().getOptions().path.sep,
|
|
115
|
+
"version": this.image.getVersion(),
|
|
116
|
+
"abi": this.image.getArch(),
|
|
117
|
+
"apiVersion": this.image.getApiVersion(),
|
|
118
|
+
"path": this.image.getImageManager().getOptions().path.resolve(this.image.getImageManager().getOptions().deployedPath, this.options.name ?? ""),
|
|
119
|
+
"showVersion": `${this.image.getTargetOS()} ${this.image.getTargetVersion()}(${this.image.getApiVersion()})`,
|
|
120
|
+
"harmonyOSVersion": `${this.image.getTargetOS()}-${this.image.getTargetVersion()}`,
|
|
121
|
+
"hw.apiName": this.image.getTargetVersion(),
|
|
122
|
+
"harmonyos.sdk.path": this.image.getImageManager().getOptions().imageBasePath,
|
|
123
|
+
"harmonyos.config.path": this.image.getImageManager().getOptions().configPath,
|
|
124
|
+
"harmonyos.log.path": this.image.getImageManager().getOptions().logPath,
|
|
125
|
+
"type": this.image.getSnakecaseDeviceType(),
|
|
126
|
+
"devModel": await this.getDevModel()
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
async buildIni() {
|
|
130
|
+
const config = await this.buildList();
|
|
131
|
+
const productConfigItem = (await this.image.getProductConfig()).find((item) => item.name === this.config.productName);
|
|
132
|
+
if (!productConfigItem) throw new Error(`Product config item ${this.config.productName} not found`);
|
|
133
|
+
return {
|
|
134
|
+
"name": config.name,
|
|
135
|
+
"hw.lcd.density": config.density,
|
|
136
|
+
"hw.lcd.height": productConfigItem.screenHeight,
|
|
137
|
+
"hw.lcd.width": productConfigItem.screenWidth,
|
|
138
|
+
"hw.cpu.ncore": config.cpuNumber,
|
|
139
|
+
"hw.phy.height": productConfigItem.outerScreenHeight,
|
|
140
|
+
"hw.phy.width": productConfigItem.outerScreenWidth,
|
|
141
|
+
"hw.cover.height": productConfigItem.outerScreenHeight,
|
|
142
|
+
"hw.cover.width": productConfigItem.outerScreenWidth,
|
|
143
|
+
"coverDiagonalSize": config.coverDiagonalSize ?? productConfigItem.outerScreenDiagonal,
|
|
144
|
+
"diagonalSize": config.diagonalSize,
|
|
145
|
+
"hw.ramSize": config.memoryRamSize,
|
|
146
|
+
"deviceType": config.type,
|
|
147
|
+
"uuid": config.uuid,
|
|
148
|
+
"hmApiVersion": config.apiVersion,
|
|
149
|
+
"hmAbi": config.abi,
|
|
150
|
+
"hmVersion": config.version,
|
|
151
|
+
"hw.cpu.arch": config.abi,
|
|
152
|
+
"hw.apiName": config["hw.apiName"],
|
|
153
|
+
"image.sysdir.1": config.imageDir,
|
|
154
|
+
"hvd.path": config.path,
|
|
155
|
+
"disk.dataPartition.size": `${config.dataDiskSize}M`,
|
|
156
|
+
"hmShowVersion": config.showVersion,
|
|
157
|
+
"harmonyOSVersion": config.harmonyOSVersion,
|
|
158
|
+
"harmonyos.sdk.path": config["harmonyos.sdk.path"],
|
|
159
|
+
"harmonyos.config.path": config["harmonyos.config.path"],
|
|
160
|
+
"harmonyos.log.path": config["harmonyos.log.path"],
|
|
161
|
+
"guest.version": config.version,
|
|
162
|
+
"devModel": config.devModel,
|
|
163
|
+
"model": config.model,
|
|
164
|
+
"isDefault": this.isDefault ? "true" : "false",
|
|
165
|
+
"isCustomize": this.isCustomize ? "true" : "false",
|
|
166
|
+
"isPublic": this.isPublic ? "true" : "false",
|
|
167
|
+
"vendorCountry": this.vendorCountry,
|
|
168
|
+
"hw.hdc.port": this.hwHdcPort.toString()
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
async toIniString() {
|
|
172
|
+
return `${Object.entries(await this.buildIni()).filter(([, value]) => value !== void 0 && value !== null).map(([key, value]) => `${key}=${value}`).join("\n")}\n`;
|
|
173
|
+
}
|
|
174
|
+
writeLists(config) {
|
|
175
|
+
const fs = this.image.getImageManager().getOptions().fs;
|
|
176
|
+
const listsPath = this.image.getImageManager().getOptions().path.resolve(this.image.getImageManager().getOptions().deployedPath, "lists.json");
|
|
177
|
+
if (!fs.existsSync(listsPath)) fs.writeFileSync(listsPath, JSON.stringify([config], null, 2));
|
|
178
|
+
else {
|
|
179
|
+
const lists = JSON.parse(fs.readFileSync(listsPath, "utf-8")) ?? [];
|
|
180
|
+
if (!Array.isArray(lists)) return /* @__PURE__ */ new Error("Lists is not an array");
|
|
181
|
+
if (lists.find((item) => item.name === config.name)) return;
|
|
182
|
+
lists.push(config);
|
|
183
|
+
fs.writeFileSync(listsPath, JSON.stringify(lists, null, 2));
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
async deploy(symlinkImage = true) {
|
|
187
|
+
const { fs, path, imageBasePath, sdkPath } = this.image.getImageManager().getOptions();
|
|
188
|
+
const config = await this.buildList();
|
|
189
|
+
if (fs.existsSync(config.path)) return /* @__PURE__ */ new Error(`Image ${config.name} already deployed`);
|
|
190
|
+
const error = this.writeLists(config);
|
|
191
|
+
if (error) return error;
|
|
192
|
+
fs.mkdirSync(config.path, { recursive: true });
|
|
193
|
+
fs.writeFileSync(path.join(config.path, "config.ini"), await this.toIniString());
|
|
194
|
+
const symlinkSdkPath = path.resolve(imageBasePath, "default", "openharmony");
|
|
195
|
+
if (!fs.existsSync(path.dirname(symlinkSdkPath))) fs.mkdirSync(path.dirname(symlinkSdkPath), { recursive: true });
|
|
196
|
+
try {
|
|
197
|
+
if (fs.lstatSync(symlinkSdkPath).isSymbolicLink()) fs.unlinkSync(symlinkSdkPath);
|
|
198
|
+
else return void 0;
|
|
199
|
+
} catch {}
|
|
200
|
+
fs.symlinkSync(sdkPath, symlinkSdkPath, "dir");
|
|
201
|
+
const systemImageDir = path.join(imageBasePath, "system-image");
|
|
202
|
+
if (symlinkImage && fs.existsSync(systemImageDir)) {
|
|
203
|
+
const linkPath = path.join(config.path, "system-image");
|
|
204
|
+
try {
|
|
205
|
+
const target = path.relative(config.path, systemImageDir);
|
|
206
|
+
fs.symlinkSync(target, linkPath);
|
|
207
|
+
} catch (err) {
|
|
208
|
+
return err instanceof Error ? err : new Error(String(err));
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
};
|
|
213
|
+
function createImageDeployer(image, uuid, name, config) {
|
|
214
|
+
return new ImageDeployerImpl(image, uuid, name, config);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
//#endregion
|
|
218
|
+
//#region src/image-downloader.ts
|
|
219
|
+
var ImageDownloaderImpl = class {
|
|
220
|
+
emitter = mitt();
|
|
221
|
+
all = this.emitter.all;
|
|
222
|
+
on(type, handler) {
|
|
223
|
+
this.emitter.on(type, handler);
|
|
224
|
+
}
|
|
225
|
+
off(type, handler) {
|
|
226
|
+
this.emitter.off(type, handler);
|
|
227
|
+
}
|
|
228
|
+
emit(type, event) {
|
|
229
|
+
this.emitter.emit(type, event);
|
|
230
|
+
}
|
|
231
|
+
constructor(image, url) {
|
|
232
|
+
this.image = image;
|
|
233
|
+
this.url = url;
|
|
234
|
+
}
|
|
235
|
+
getImage() {
|
|
236
|
+
return this.image;
|
|
237
|
+
}
|
|
238
|
+
getUrl() {
|
|
239
|
+
return this.url;
|
|
240
|
+
}
|
|
241
|
+
getCacheFsPath() {
|
|
242
|
+
const { path, cachePath } = this.image.getImageManager().getOptions();
|
|
243
|
+
return path.resolve(cachePath, path.basename(this.url));
|
|
244
|
+
}
|
|
245
|
+
async makeRequest(signal, startByte = 0, retried416 = false) {
|
|
246
|
+
const { fs } = this.image.getImageManager().getOptions();
|
|
247
|
+
const cacheFsPath = this.getCacheFsPath();
|
|
248
|
+
const transformProgress = this.createProgressTransformer(startByte);
|
|
249
|
+
try {
|
|
250
|
+
return await axios.get(this.url, {
|
|
251
|
+
headers: startByte > 0 ? { Range: `bytes=${startByte}-` } : {},
|
|
252
|
+
responseType: "stream",
|
|
253
|
+
validateStatus: (status) => status === 200 || status === 206,
|
|
254
|
+
onDownloadProgress: (progress) => this.emit("download-progress", transformProgress(progress)),
|
|
255
|
+
signal
|
|
256
|
+
});
|
|
257
|
+
} catch (err) {
|
|
258
|
+
if (err instanceof AxiosError && err.response?.status === 416 && !retried416) {
|
|
259
|
+
if (fs.existsSync(cacheFsPath)) fs.rmSync(cacheFsPath, { force: true });
|
|
260
|
+
return this.makeRequest(signal, startByte, true);
|
|
261
|
+
}
|
|
262
|
+
throw err;
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
async startDownload(signal, retried416 = false) {
|
|
266
|
+
const { fs, cachePath } = this.image.getImageManager().getOptions();
|
|
267
|
+
const cacheFsPath = this.getCacheFsPath();
|
|
268
|
+
if (!fs.existsSync(cachePath)) fs.mkdirSync(cachePath, { recursive: true });
|
|
269
|
+
const startByte = fs.existsSync(cacheFsPath) ? fs.statSync(cacheFsPath).size : 0;
|
|
270
|
+
const response = await this.makeRequest(signal, startByte, retried416);
|
|
271
|
+
const isPartialContent = response.status === 206;
|
|
272
|
+
const start = startByte > 0 && isPartialContent ? startByte : 0;
|
|
273
|
+
const flags = start > 0 ? "a" : "w";
|
|
274
|
+
if (startByte > 0 && !isPartialContent) fs.rmSync(cacheFsPath, { force: true });
|
|
275
|
+
const writeStream = fs.createWriteStream(cacheFsPath, {
|
|
276
|
+
flags,
|
|
277
|
+
start
|
|
278
|
+
});
|
|
279
|
+
response.data.pipe(writeStream);
|
|
280
|
+
await new Promise((resolve, reject) => {
|
|
281
|
+
let settled = false;
|
|
282
|
+
const onError = (err) => {
|
|
283
|
+
if (settled) return;
|
|
284
|
+
settled = true;
|
|
285
|
+
response.data.destroy();
|
|
286
|
+
writeStream.destroy();
|
|
287
|
+
reject(err);
|
|
288
|
+
};
|
|
289
|
+
const onFinish = () => {
|
|
290
|
+
if (settled) return;
|
|
291
|
+
settled = true;
|
|
292
|
+
resolve();
|
|
293
|
+
};
|
|
294
|
+
response.data.on("error", onError);
|
|
295
|
+
writeStream.on("error", onError);
|
|
296
|
+
response.data.on("end", () => writeStream.end());
|
|
297
|
+
writeStream.on("finish", onFinish);
|
|
298
|
+
});
|
|
299
|
+
}
|
|
300
|
+
async checkChecksum(signal) {
|
|
301
|
+
const { crypto, fs } = this.image.getImageManager().getOptions();
|
|
302
|
+
const checksum = this.image.getChecksum();
|
|
303
|
+
const hash = crypto.createHash("sha256", { signal });
|
|
304
|
+
const stream = fs.createReadStream(this.getCacheFsPath(), { signal });
|
|
305
|
+
stream.on("data", (chunk) => hash.update(chunk));
|
|
306
|
+
await new Promise((resolve, reject) => stream.on("end", resolve).on("error", reject));
|
|
307
|
+
return hash.digest("hex") === checksum;
|
|
308
|
+
}
|
|
309
|
+
async extract(signal, symlinkOpenHarmonySdk = true) {
|
|
310
|
+
const { fs, path, imageBasePath, sdkPath } = this.image.getImageManager().getOptions();
|
|
311
|
+
const cacheFsPath = this.getCacheFsPath();
|
|
312
|
+
const stream = fs.createReadStream(cacheFsPath, { signal });
|
|
313
|
+
const progressStream = progress({ length: fs.statSync(cacheFsPath).size });
|
|
314
|
+
const extractStream = unzipper.Extract({ path: this.image.getFsPath() });
|
|
315
|
+
progressStream.on("progress", (progress) => this.emitter.emit("extract-progress", progress));
|
|
316
|
+
stream.pipe(progressStream).pipe(extractStream);
|
|
317
|
+
await extractStream.promise();
|
|
318
|
+
if (symlinkOpenHarmonySdk) {
|
|
319
|
+
const symlinkSdkPath = path.resolve(imageBasePath, "default", "openharmony");
|
|
320
|
+
if (!fs.existsSync(path.dirname(symlinkSdkPath))) fs.mkdirSync(path.dirname(symlinkSdkPath), { recursive: true });
|
|
321
|
+
if (!fs.existsSync(symlinkSdkPath)) fs.symlinkSync(sdkPath, symlinkSdkPath, "dir");
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
async clean() {
|
|
325
|
+
const { fs } = this.image.getImageManager().getOptions();
|
|
326
|
+
const cacheFsPath = this.getCacheFsPath();
|
|
327
|
+
if (fs.existsSync(cacheFsPath)) fs.rmSync(cacheFsPath, { recursive: true });
|
|
328
|
+
}
|
|
329
|
+
createProgressTransformer(startByte) {
|
|
330
|
+
let previousPercentage = 0;
|
|
331
|
+
const bytesPerKB = 1024;
|
|
332
|
+
const bytesPerMB = bytesPerKB * 1024;
|
|
333
|
+
return (progress) => {
|
|
334
|
+
const rangeTotal = progress.total ?? 0;
|
|
335
|
+
const rangeLoaded = progress.loaded ?? 0;
|
|
336
|
+
const total = startByte + rangeTotal;
|
|
337
|
+
const loaded = startByte + rangeLoaded;
|
|
338
|
+
const percentage = total > 0 ? loaded / total * 100 : 0;
|
|
339
|
+
const increment = Math.max(0, percentage - previousPercentage);
|
|
340
|
+
previousPercentage = percentage;
|
|
341
|
+
const rate = progress.rate ?? 0;
|
|
342
|
+
const unit = rate >= bytesPerMB ? "MB" : "KB";
|
|
343
|
+
const network = unit === "MB" ? rate / bytesPerMB : rate / bytesPerKB;
|
|
344
|
+
return {
|
|
345
|
+
...progress,
|
|
346
|
+
total,
|
|
347
|
+
loaded,
|
|
348
|
+
network,
|
|
349
|
+
unit,
|
|
350
|
+
increment
|
|
351
|
+
};
|
|
352
|
+
};
|
|
353
|
+
}
|
|
354
|
+
};
|
|
355
|
+
function createImageDownloader(image, url) {
|
|
356
|
+
return new ImageDownloaderImpl(image, url);
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
//#endregion
|
|
360
|
+
//#region src/images/image.ts
|
|
361
|
+
var ImageBase = class {
|
|
362
|
+
constructor(response, imageManager, resolvedFsPath) {
|
|
363
|
+
this.response = response;
|
|
364
|
+
this.imageManager = imageManager;
|
|
365
|
+
this.resolvedFsPath = resolvedFsPath;
|
|
366
|
+
}
|
|
367
|
+
getImageManager() {
|
|
368
|
+
return this.imageManager;
|
|
369
|
+
}
|
|
370
|
+
getArch() {
|
|
371
|
+
const [, , deviceTypeWithArch] = this.getPath()?.split(",") ?? [];
|
|
372
|
+
const split = deviceTypeWithArch?.split("_") ?? [];
|
|
373
|
+
return split[split.length - 1];
|
|
374
|
+
}
|
|
375
|
+
getPath() {
|
|
376
|
+
return this.response?.path;
|
|
377
|
+
}
|
|
378
|
+
getChecksum() {
|
|
379
|
+
return this.response?.archive?.complete?.checksum;
|
|
380
|
+
}
|
|
381
|
+
getFsPath() {
|
|
382
|
+
return this.resolvedFsPath;
|
|
383
|
+
}
|
|
384
|
+
async isDownloaded() {
|
|
385
|
+
return this.imageManager.getOptions().fs.existsSync(this.getFsPath()) && this.imageManager.getOptions().fs.statSync(this.getFsPath()).isDirectory();
|
|
386
|
+
}
|
|
387
|
+
getVersion() {
|
|
388
|
+
return this.response?.version;
|
|
389
|
+
}
|
|
390
|
+
getApiVersion() {
|
|
391
|
+
return this.response?.apiVersion;
|
|
392
|
+
}
|
|
393
|
+
getTargetOS() {
|
|
394
|
+
const [, systemNameWithVersion] = this.getPath()?.split(",") ?? [];
|
|
395
|
+
const [systemName] = systemNameWithVersion?.split("-") ?? [];
|
|
396
|
+
return systemName ?? "";
|
|
397
|
+
}
|
|
398
|
+
getTargetVersion() {
|
|
399
|
+
const [, systemNameWithVersion] = this.getPath()?.split(",") ?? [];
|
|
400
|
+
const [, version] = systemNameWithVersion?.split("-") ?? [];
|
|
401
|
+
return version ?? "";
|
|
402
|
+
}
|
|
403
|
+
getDeviceType() {
|
|
404
|
+
const [, , deviceTypeWithArch] = this.getPath()?.split(",") ?? [];
|
|
405
|
+
const [deviceType] = deviceTypeWithArch?.split("_") ?? [];
|
|
406
|
+
return deviceType ?? "";
|
|
407
|
+
}
|
|
408
|
+
getSnakecaseDeviceType() {
|
|
409
|
+
const deviceType = this.getDeviceType();
|
|
410
|
+
if (deviceType === "pc") return "2in1_foldable";
|
|
411
|
+
return deviceType.toLowerCase();
|
|
412
|
+
}
|
|
413
|
+
async createDownloader() {
|
|
414
|
+
const url = await this.getUrl();
|
|
415
|
+
if (url instanceof RequestUrlError) return url;
|
|
416
|
+
return createImageDownloader(this, url);
|
|
417
|
+
}
|
|
418
|
+
async getUrl() {
|
|
419
|
+
try {
|
|
420
|
+
const response = await axios.post("https://devecostudio-drcn.deveco.dbankcloud.com/sdkmanager/v7/hos/download", {
|
|
421
|
+
osArch: this.imageManager.getArch(),
|
|
422
|
+
osType: this.imageManager.getOS(),
|
|
423
|
+
path: {
|
|
424
|
+
path: this.getPath(),
|
|
425
|
+
version: this.getVersion()
|
|
426
|
+
},
|
|
427
|
+
imei: "d490a470-8719-4baf-9cc4-9c78d40d"
|
|
428
|
+
});
|
|
429
|
+
if (typeof response.data?.url !== "string") return new RequestUrlError(response.data?.body, response.data?.code);
|
|
430
|
+
return response.data.url;
|
|
431
|
+
} catch (error) {
|
|
432
|
+
return new RequestUrlError(error.message, error.code, error);
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
};
|
|
436
|
+
|
|
437
|
+
//#endregion
|
|
438
|
+
//#region src/images/local-image.ts
|
|
439
|
+
var LocalImageImpl = class extends ImageBase {
|
|
440
|
+
imageType = "local";
|
|
441
|
+
createDeployer(name, config) {
|
|
442
|
+
return createImageDeployer(this, this.getImageManager().getOptions().crypto.randomUUID(), name, config);
|
|
443
|
+
}
|
|
444
|
+
async getProductConfig(usingDefaultProductConfig = false) {
|
|
445
|
+
const productConfig = usingDefaultProductConfig ? (await import("./default-product-config.mjs")).default : await this.getImageManager().getProductConfig();
|
|
446
|
+
const deviceType = this.getDeviceType().toLowerCase();
|
|
447
|
+
if (!deviceType) return [];
|
|
448
|
+
if (deviceType === "pc") return productConfig["2in1 Foldable"] ?? [];
|
|
449
|
+
const key = Object.keys(productConfig).find((key) => key.toLowerCase() === deviceType);
|
|
450
|
+
if (key) return productConfig[key] ?? [];
|
|
451
|
+
return [];
|
|
452
|
+
}
|
|
453
|
+
async delete() {
|
|
454
|
+
const fs = this.getImageManager().getOptions().fs;
|
|
455
|
+
const path = this.getFsPath();
|
|
456
|
+
if (!fs.existsSync(path) || !fs.statSync(path).isDirectory()) return /* @__PURE__ */ new Error("Image path does not exist");
|
|
457
|
+
fs.rmSync(path, { recursive: true });
|
|
458
|
+
}
|
|
459
|
+
getExecutablePath() {
|
|
460
|
+
const { emulatorPath, process, path } = this.getImageManager().getOptions();
|
|
461
|
+
return process.platform === "win32" ? path.join(emulatorPath, "Emulator.exe") : path.join(emulatorPath, "Emulator");
|
|
462
|
+
}
|
|
463
|
+
async buildStartCommand(deployer) {
|
|
464
|
+
const config = await deployer.buildList();
|
|
465
|
+
return `${this.getExecutablePath()} ${[
|
|
466
|
+
"-hvd",
|
|
467
|
+
`"${config.name.replace(/"/g, "\\\"")}"`,
|
|
468
|
+
"-path",
|
|
469
|
+
`"${this.getImageManager().getOptions().deployedPath.replace(/"/g, "\\\"")}"`,
|
|
470
|
+
"-imageRoot",
|
|
471
|
+
`"${config.path.replace(/"/g, "\\\"")}"`
|
|
472
|
+
].join(" ")}`;
|
|
473
|
+
}
|
|
474
|
+
async start(deployer) {
|
|
475
|
+
const { child_process, emulatorPath } = this.getImageManager().getOptions();
|
|
476
|
+
return child_process.exec(await this.buildStartCommand(deployer), { cwd: emulatorPath });
|
|
477
|
+
}
|
|
478
|
+
async buildStopCommand(deployer) {
|
|
479
|
+
const config = await deployer.buildList();
|
|
480
|
+
return `${this.getExecutablePath()} ${["-stop", `"${config.name.replace(/"/g, "\\\"")}"`].join(" ")}`;
|
|
481
|
+
}
|
|
482
|
+
async stop(deployer) {
|
|
483
|
+
const { child_process, emulatorPath } = this.getImageManager().getOptions();
|
|
484
|
+
return child_process.exec(await this.buildStopCommand(deployer), { cwd: emulatorPath });
|
|
485
|
+
}
|
|
486
|
+
toJSON() {
|
|
487
|
+
return {
|
|
488
|
+
imageType: this.imageType,
|
|
489
|
+
path: this.getPath(),
|
|
490
|
+
version: this.getVersion(),
|
|
491
|
+
apiVersion: this.getApiVersion(),
|
|
492
|
+
targetVersion: this.getTargetVersion(),
|
|
493
|
+
checksum: this.getChecksum()
|
|
494
|
+
};
|
|
495
|
+
}
|
|
496
|
+
};
|
|
497
|
+
|
|
498
|
+
//#endregion
|
|
499
|
+
//#region src/images/remote-image.ts
|
|
500
|
+
var RemoteImageImpl = class extends ImageBase {
|
|
501
|
+
imageType = "remote";
|
|
502
|
+
toJSON() {
|
|
503
|
+
return {
|
|
504
|
+
imageType: this.imageType,
|
|
505
|
+
path: this.getPath(),
|
|
506
|
+
apiVersion: this.getApiVersion(),
|
|
507
|
+
checksum: this.getChecksum()
|
|
508
|
+
};
|
|
509
|
+
}
|
|
510
|
+
};
|
|
511
|
+
|
|
512
|
+
//#endregion
|
|
513
|
+
//#region src/options.ts
|
|
514
|
+
async function resolveImageManagerOptions(options) {
|
|
515
|
+
const os = options.os ?? await import("node:os");
|
|
516
|
+
const path = options.path ?? await import("node:path");
|
|
517
|
+
const process = options.process ?? await import("node:process");
|
|
518
|
+
function resolveDefaultImageBasePath() {
|
|
519
|
+
switch (process.platform) {
|
|
520
|
+
case "win32": return typeof process.env.APPDATA === "string" && process.env.APPDATA.length > 0 ? path.resolve(process.env.APPDATA, "Local", "Huawei", "Sdk") : path.resolve(os.homedir(), "AppData", "Local", "Huawei", "Sdk");
|
|
521
|
+
case "darwin": return path.resolve(os.homedir(), "Library", "Huawei", "Sdk");
|
|
522
|
+
default: return path.resolve(os.homedir(), ".huawei", "Sdk");
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
function resolveDefaultDeployedPath() {
|
|
526
|
+
switch (process.platform) {
|
|
527
|
+
case "win32": return typeof process.env.APPDATA === "string" && process.env.APPDATA.length > 0 ? path.resolve(process.env.APPDATA, "Local", "Huawei", "Emulator", "deployed") : path.resolve(os.homedir(), "AppData", "Local", "Huawei", "Emulator", "deployed");
|
|
528
|
+
default: return path.resolve(os.homedir(), ".huawei", "Emulator", "deployed");
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
function resolveDefaultSdkPath() {
|
|
532
|
+
switch (process.platform) {
|
|
533
|
+
case "darwin": return "/Applications/DevEco-Studio.app/Contents/sdk/default/openharmony";
|
|
534
|
+
case "win32": return "C:\\Program Files\\Huawei\\DevEco Studio\\sdk\\default\\openharmony";
|
|
535
|
+
default: return path.resolve(os.homedir(), ".huawei", "Sdk", "default", "openharmony");
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
function resolveDefaultConfigPath() {
|
|
539
|
+
switch (process.platform) {
|
|
540
|
+
case "darwin": return path.resolve(os.homedir(), "Library", "Application Support", "Huawei", "DevEcoStudio6.0");
|
|
541
|
+
case "win32": return path.resolve(process.env.APPDATA ?? os.homedir(), "Roaming", "Huawei", "DevEcoStudio6.0");
|
|
542
|
+
default: return path.resolve(os.homedir(), ".huawei", "DevEcoStudio6.0");
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
function resolveDefaultLogPath() {
|
|
546
|
+
switch (process.platform) {
|
|
547
|
+
case "darwin": return path.resolve(os.homedir(), "Library", "Logs", "Huawei", "DevEcoStudio6.0");
|
|
548
|
+
case "win32": return path.resolve(process.env.APPDATA ?? os.homedir(), "Local", "Huawei", "DevEcoStudio6.0", "log");
|
|
549
|
+
default: return path.resolve(os.homedir(), ".huawei", "DevEcoStudio6.0", "log");
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
function resolveDefaultEmulatorPath() {
|
|
553
|
+
switch (process.platform) {
|
|
554
|
+
case "darwin": return "/Applications/DevEco-Studio.app/Contents/tools/emulator/Emulator";
|
|
555
|
+
case "win32": return path.resolve("C:", "Program Files", "Huawei", "DevEco Studio", "tools", "emulator", "Emulator.exe");
|
|
556
|
+
default: return path.resolve(os.homedir(), ".huawei", "Emulator");
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
const imageBasePath = options.imageBasePath ?? resolveDefaultImageBasePath();
|
|
560
|
+
const cachePath = options.cachePath ?? path.resolve(imageBasePath, "cache");
|
|
561
|
+
return {
|
|
562
|
+
imageBasePath,
|
|
563
|
+
deployedPath: options.deployedPath ?? resolveDefaultDeployedPath(),
|
|
564
|
+
cachePath,
|
|
565
|
+
sdkPath: options.sdkPath ?? resolveDefaultSdkPath(),
|
|
566
|
+
configPath: options.configPath ?? resolveDefaultConfigPath(),
|
|
567
|
+
logPath: options.logPath ?? resolveDefaultLogPath(),
|
|
568
|
+
emulatorPath: options.emulatorPath ?? resolveDefaultEmulatorPath(),
|
|
569
|
+
path,
|
|
570
|
+
os,
|
|
571
|
+
fs: options.fs ?? await import("node:fs"),
|
|
572
|
+
process: options.process ?? await import("node:process"),
|
|
573
|
+
crypto: options.crypto ?? await import("node:crypto"),
|
|
574
|
+
child_process: options.child_process ?? await import("node:child_process")
|
|
575
|
+
};
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
//#endregion
|
|
579
|
+
//#region src/image-manager.ts
|
|
580
|
+
var ImageManagerImpl = class {
|
|
581
|
+
constructor(resolvedOptions) {
|
|
582
|
+
this.resolvedOptions = resolvedOptions;
|
|
583
|
+
}
|
|
584
|
+
getOptions() {
|
|
585
|
+
return this.resolvedOptions;
|
|
586
|
+
}
|
|
587
|
+
getOS() {
|
|
588
|
+
switch (this.resolvedOptions.process.platform) {
|
|
589
|
+
case "win32": return "windows";
|
|
590
|
+
case "darwin": return "mac";
|
|
591
|
+
default: return "linux";
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
getArch() {
|
|
595
|
+
if (this.resolvedOptions.process.arch.toLowerCase().includes("arm")) return "arm64";
|
|
596
|
+
return "x86";
|
|
597
|
+
}
|
|
598
|
+
async getImages() {
|
|
599
|
+
const response = await axios.post("https://devecostudio-drcn.deveco.dbankcloud.com/sdkmanager/v8/hos/getSdkList", {
|
|
600
|
+
osArch: this.getArch(),
|
|
601
|
+
osType: this.getOS(),
|
|
602
|
+
supportVersion: "6.0-hos-single-8"
|
|
603
|
+
});
|
|
604
|
+
if (!Array.isArray(response.data)) return [];
|
|
605
|
+
const images = [];
|
|
606
|
+
for (const responseItem of response.data) {
|
|
607
|
+
const resolvedFsPath = this.resolvedOptions.path.resolve(this.resolvedOptions.imageBasePath, ...responseItem.path.split(","));
|
|
608
|
+
if (this.resolvedOptions.fs.existsSync(resolvedFsPath) && this.resolvedOptions.fs.statSync(resolvedFsPath).isDirectory()) images.push(new LocalImageImpl(responseItem, this, resolvedFsPath));
|
|
609
|
+
else images.push(new RemoteImageImpl(responseItem, this, resolvedFsPath));
|
|
610
|
+
}
|
|
611
|
+
return images;
|
|
612
|
+
}
|
|
613
|
+
async getProductConfig() {
|
|
614
|
+
const productConfigPath = this.resolvedOptions.path.resolve(this.resolvedOptions.imageBasePath, "productConfig.json");
|
|
615
|
+
if (!this.resolvedOptions.fs.existsSync(productConfigPath) || !this.resolvedOptions.fs.statSync(productConfigPath).isFile()) return (await import("./default-product-config.mjs")).default;
|
|
616
|
+
return JSON.parse(this.resolvedOptions.fs.readFileSync(productConfigPath, "utf-8"));
|
|
617
|
+
}
|
|
618
|
+
async writeDefaultProductConfig(existSkip = false) {
|
|
619
|
+
const productConfigPath = this.resolvedOptions.path.resolve(this.resolvedOptions.imageBasePath, "productConfig.json");
|
|
620
|
+
if (existSkip && this.resolvedOptions.fs.existsSync(productConfigPath)) return;
|
|
621
|
+
this.resolvedOptions.fs.writeFileSync(productConfigPath, JSON.stringify((await import("./default-product-config.mjs")).default, null, 2));
|
|
622
|
+
}
|
|
623
|
+
};
|
|
624
|
+
async function createImageManager(options = {}) {
|
|
625
|
+
return new ImageManagerImpl(await resolveImageManagerOptions(options));
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
//#endregion
|
|
629
|
+
export { DevModel, RequestUrlError, createImageManager };
|