@hmharness/domain-harmony 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/apikg.d.ts +34 -0
- package/dist/apikg.js +185 -0
- package/dist/apimatrix.d.ts +43 -0
- package/dist/apimatrix.js +63 -0
- package/dist/builddoctor.d.ts +24 -0
- package/dist/builddoctor.js +99 -0
- package/dist/cangjie.d.ts +4 -0
- package/dist/cangjie.js +129 -0
- package/dist/emulator.d.ts +11 -0
- package/dist/emulator.js +368 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.js +439 -0
- package/dist/lint.d.ts +2 -0
- package/dist/lint.js +65 -0
- package/dist/ondevice.d.ts +23 -0
- package/dist/ondevice.js +174 -0
- package/dist/profile.d.ts +22 -0
- package/dist/profile.js +159 -0
- package/dist/project.d.ts +22 -0
- package/dist/project.js +414 -0
- package/dist/schema.d.ts +22 -0
- package/dist/schema.js +215 -0
- package/dist/signing.d.ts +26 -0
- package/dist/signing.js +209 -0
- package/dist/uiregress.d.ts +27 -0
- package/dist/uiregress.js +147 -0
- package/package.json +31 -0
package/dist/emulator.js
ADDED
|
@@ -0,0 +1,368 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @hmharness/domain-harmony - emulator management (no DevEco GUI)
|
|
3
|
+
* DevEco's Emulator.exe is itself a headless CLI:
|
|
4
|
+
* Emulator.exe -hvd <name> -path <deployedDir> -imageRoot <imageRoot>
|
|
5
|
+
* A deployed device is just: lists.json entry + instance dir with two
|
|
6
|
+
* key=value INIs (qemu overlays are created on first boot). So hmharness
|
|
7
|
+
* can list/start/stop/create/delete emulators entirely from the CLI - the
|
|
8
|
+
* IDE never needs to open. Creating NEW device TYPES still requires an
|
|
9
|
+
* image downloaded via DevEco's component manager (account-bound); new
|
|
10
|
+
* INSTANCES of installed images are fully self-serve here.
|
|
11
|
+
*/
|
|
12
|
+
import { spawn, execFile } from 'node:child_process';
|
|
13
|
+
import { randomUUID } from 'node:crypto';
|
|
14
|
+
import { mkdir, readdir, readFile, rm, writeFile } from 'node:fs/promises';
|
|
15
|
+
import { accessSync } from 'node:fs';
|
|
16
|
+
import { join } from 'node:path';
|
|
17
|
+
import { promisify } from 'node:util';
|
|
18
|
+
const exec = promisify(execFile);
|
|
19
|
+
function localAppData() {
|
|
20
|
+
return process.env.LOCALAPPDATA ?? join(process.env.USERPROFILE ?? '.', 'AppData', 'Local');
|
|
21
|
+
}
|
|
22
|
+
export function deployedDir() {
|
|
23
|
+
return join(localAppData(), 'Huawei', 'Emulator', 'deployed');
|
|
24
|
+
}
|
|
25
|
+
export function imageRoot() {
|
|
26
|
+
return join(localAppData(), 'Huawei', 'Sdk');
|
|
27
|
+
}
|
|
28
|
+
function emulatorExe() {
|
|
29
|
+
return join(process.env.HM_DEVECO_HOME ?? 'C:\\DevEco-Studio', 'tools', 'emulator', 'Emulator.exe');
|
|
30
|
+
}
|
|
31
|
+
async function readLists() {
|
|
32
|
+
try {
|
|
33
|
+
return JSON.parse(await readFile(join(deployedDir(), 'lists.json'), 'utf8'));
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
return [];
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
async function writeLists(devices) {
|
|
40
|
+
await writeFile(join(deployedDir(), 'lists.json'), JSON.stringify(devices, null, '\t'), 'utf8');
|
|
41
|
+
}
|
|
42
|
+
async function hdcTargets() {
|
|
43
|
+
try {
|
|
44
|
+
const { stdout } = await exec('hdc', ['list', 'targets'], { timeout: 8000, windowsHide: true });
|
|
45
|
+
return stdout.split('\n').map((l) => l.trim()).filter((l) => l && l !== '[Empty]');
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
return [];
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
/** Emulator processes with their -hvd device name (Windows CIM query). */
|
|
52
|
+
async function runningEmulators() {
|
|
53
|
+
try {
|
|
54
|
+
const { stdout } = await exec('powershell', [
|
|
55
|
+
'-NoProfile', '-Command',
|
|
56
|
+
`Get-CimInstance Win32_Process | Where-Object { $_.Name -eq 'Emulator.exe' } | ForEach-Object { "$($_.ProcessId)|$($_.CommandLine)" }`,
|
|
57
|
+
], { timeout: 15000, windowsHide: true });
|
|
58
|
+
return stdout.split('\n').map((l) => l.trim()).filter(Boolean).map((l) => {
|
|
59
|
+
const [pidStr, cmd] = l.split('|');
|
|
60
|
+
// -hvd "Name With Spaces" or -hvd Simple
|
|
61
|
+
const m = (cmd ?? '').match(/-hvd\s+"([^"]+)"|-hvd\s+([^\s]+)/);
|
|
62
|
+
return { pid: Number(pidStr), hvd: m?.[1] ?? m?.[2] ?? '?' };
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
return [];
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
async function waitTargets(before, ms) {
|
|
70
|
+
const deadline = Date.now() + ms;
|
|
71
|
+
while (Date.now() < deadline) {
|
|
72
|
+
await new Promise((r) => setTimeout(r, 5000));
|
|
73
|
+
const now = await hdcTargets();
|
|
74
|
+
const fresh = now.filter((t) => !before.has(t));
|
|
75
|
+
if (fresh.length > 0)
|
|
76
|
+
return now;
|
|
77
|
+
}
|
|
78
|
+
return await hdcTargets();
|
|
79
|
+
}
|
|
80
|
+
/* ------------------------------------------------------------------ */
|
|
81
|
+
/* Tools */
|
|
82
|
+
/* ------------------------------------------------------------------ */
|
|
83
|
+
export const harmonyEmulatorList = {
|
|
84
|
+
name: 'harmony_emulator_list',
|
|
85
|
+
description: 'List HarmonyOS emulators: deployed devices (name/type/version/resolution + running state) and installed system images. Read-only. Pair with harmony_emulator_start / harmony_devices.',
|
|
86
|
+
parameters: { type: 'object', properties: {}, required: [] },
|
|
87
|
+
async execute() {
|
|
88
|
+
const [devices, running, targets] = await Promise.all([readLists(), runningEmulators(), hdcTargets()]);
|
|
89
|
+
let images = [];
|
|
90
|
+
try {
|
|
91
|
+
const verDirs = await readdir(join(imageRoot(), 'system-image'));
|
|
92
|
+
for (const v of verDirs) {
|
|
93
|
+
for (const t of await readdir(join(imageRoot(), 'system-image', v)))
|
|
94
|
+
images.push(`${v}/${t}`);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
catch {
|
|
98
|
+
/* no images */
|
|
99
|
+
}
|
|
100
|
+
const lines = ['deployed devices:'];
|
|
101
|
+
if (devices.length === 0)
|
|
102
|
+
lines.push(' (none - create one with harmony_emulator_create)');
|
|
103
|
+
for (const d of devices) {
|
|
104
|
+
const run = running.some((r) => r.hvd === d.name);
|
|
105
|
+
lines.push(` ${d.name} [${d.type ?? '?'} · ${d.showVersion ?? d.version ?? '?'} · ${d.resolutionWidth ?? '?'}x${d.resolutionHeight ?? '?'}] ${run ? 'RUNNING' : 'stopped'}`);
|
|
106
|
+
}
|
|
107
|
+
lines.push(`hdc targets: ${targets.length ? targets.join(', ') : '(none)'}`, 'installed images:');
|
|
108
|
+
for (const i of images)
|
|
109
|
+
lines.push(` ${i}`);
|
|
110
|
+
return { output: lines.join('\n') };
|
|
111
|
+
},
|
|
112
|
+
};
|
|
113
|
+
export const harmonyEmulatorStart = {
|
|
114
|
+
name: 'harmony_emulator_start',
|
|
115
|
+
description: 'Start a deployed emulator headlessly (spawns Emulator.exe directly - no DevEco GUI). Boots take 30-90s; the tool polls until the device appears in hdc (max 3 min). Heavy: each instance uses ~4 GB RAM.',
|
|
116
|
+
parameters: {
|
|
117
|
+
type: 'object',
|
|
118
|
+
properties: { name: { type: 'string', description: 'deployed device name (from harmony_emulator_list; default: the only/first one)' } },
|
|
119
|
+
required: [],
|
|
120
|
+
},
|
|
121
|
+
needsApproval: () => true,
|
|
122
|
+
async execute(args) {
|
|
123
|
+
const exe = emulatorExe();
|
|
124
|
+
try {
|
|
125
|
+
accessSync(exe);
|
|
126
|
+
}
|
|
127
|
+
catch {
|
|
128
|
+
return { output: `Emulator.exe not found at ${exe}. Install DevEco Studio (the emulator component).`, isError: true };
|
|
129
|
+
}
|
|
130
|
+
const devices = await readLists();
|
|
131
|
+
const dev = typeof args.name === 'string' && args.name ? devices.find((d) => d.name === args.name) : devices[0];
|
|
132
|
+
if (!dev)
|
|
133
|
+
return { output: `Deployed device "${args.name ?? '(any)'}" not found. Use harmony_emulator_list / harmony_emulator_create.`, isError: true };
|
|
134
|
+
const running = await runningEmulators();
|
|
135
|
+
if (running.some((r) => r.hvd === dev.name)) {
|
|
136
|
+
return { output: `${dev.name} is already running.` };
|
|
137
|
+
}
|
|
138
|
+
const before = new Set(await hdcTargets());
|
|
139
|
+
// headless launch: the exact invocation DevEco itself uses
|
|
140
|
+
const child = spawn(exe, ['-hvd', dev.name, '-path', deployedDir(), '-imageRoot', imageRoot()], {
|
|
141
|
+
detached: true,
|
|
142
|
+
stdio: 'ignore',
|
|
143
|
+
windowsHide: true,
|
|
144
|
+
});
|
|
145
|
+
child.unref();
|
|
146
|
+
const after = await waitTargets(before, 180_000);
|
|
147
|
+
const fresh = after.filter((t) => !before.has(t));
|
|
148
|
+
return {
|
|
149
|
+
output: [
|
|
150
|
+
`started ${dev.name} (pid ${child.pid}).`,
|
|
151
|
+
after.length > 0 ? `hdc targets now: ${after.join(', ')}` : 'no hdc target appeared yet - first boot may take longer; check harmony_devices in a minute.',
|
|
152
|
+
...(fresh.length ? [`new target: ${fresh.join(', ')}`] : []),
|
|
153
|
+
].join('\n'),
|
|
154
|
+
};
|
|
155
|
+
},
|
|
156
|
+
};
|
|
157
|
+
export const harmonyEmulatorStop = {
|
|
158
|
+
name: 'harmony_emulator_stop',
|
|
159
|
+
description: 'Stop running emulator(s) by terminating their exact Emulator.exe process (matched via -hvd name; state on disk is preserved like a power loss - Android-style cold stop). With a name, stops only that instance; with no name, stops ALL running emulators. Requires approval.',
|
|
160
|
+
parameters: {
|
|
161
|
+
type: 'object',
|
|
162
|
+
properties: { name: { type: 'string', description: 'device name (omit to stop all running emulators)' } },
|
|
163
|
+
required: [],
|
|
164
|
+
},
|
|
165
|
+
needsApproval: () => true,
|
|
166
|
+
async execute(args) {
|
|
167
|
+
const running = await runningEmulators();
|
|
168
|
+
if (running.length === 0)
|
|
169
|
+
return { output: 'No emulators running.' };
|
|
170
|
+
const targets = typeof args.name === 'string' && args.name ? running.filter((r) => r.hvd === args.name) : running;
|
|
171
|
+
if (targets.length === 0)
|
|
172
|
+
return { output: `No running emulator named "${args.name}". Running: ${running.map((r) => r.hvd).join(', ')}`, isError: true };
|
|
173
|
+
for (const t of targets) {
|
|
174
|
+
try {
|
|
175
|
+
await exec('taskkill', ['/F', '/PID', String(t.pid)], { timeout: 15000, windowsHide: true });
|
|
176
|
+
}
|
|
177
|
+
catch {
|
|
178
|
+
/* already gone */
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
return { output: `stopped: ${targets.map((t) => `${t.hvd} (pid ${t.pid})`).join(', ')}` };
|
|
182
|
+
},
|
|
183
|
+
};
|
|
184
|
+
export const harmonyEmulatorCatalog = {
|
|
185
|
+
name: 'harmony_emulator_catalog',
|
|
186
|
+
description: 'List the full Huawei device catalog (productConfig.json: phones/tablets/2in1/wearables with resolution/density/diagonal) cross-referenced with locally installed images - shows which device variants can be deployed RIGHT NOW vs which need a one-time image download via DevEco component manager (account-bound; no public anonymous channel exists).',
|
|
187
|
+
parameters: { type: 'object', properties: {}, required: [] },
|
|
188
|
+
async execute() {
|
|
189
|
+
const lines = ['device catalog (from DevEco productConfig.json):'];
|
|
190
|
+
try {
|
|
191
|
+
const cat = JSON.parse(await readFile(join(imageRoot(), 'productConfig.json'), 'utf8'));
|
|
192
|
+
let images = '';
|
|
193
|
+
try {
|
|
194
|
+
const v = (await readdir(join(imageRoot(), 'system-image')))[0] ?? '';
|
|
195
|
+
images = (await readdir(join(imageRoot(), 'system-image', v))).join(',');
|
|
196
|
+
}
|
|
197
|
+
catch {
|
|
198
|
+
/* none */
|
|
199
|
+
}
|
|
200
|
+
for (const [type, devices] of Object.entries(cat)) {
|
|
201
|
+
lines.push(`${type}: ${devices.map((d) => `${d.name}(${d.screenWidth}x${d.screenHeight}@${d.screenDensity})`).join(' · ')}`);
|
|
202
|
+
}
|
|
203
|
+
lines.push(`local images: ${images || '(none)'}`);
|
|
204
|
+
lines.push('variants of installed types are deployable headlessly (harmony_emulator_create model=...); other types need one GUI-side image download.');
|
|
205
|
+
}
|
|
206
|
+
catch {
|
|
207
|
+
lines.push('productConfig.json not found - install DevEco Studio first.');
|
|
208
|
+
}
|
|
209
|
+
return { output: lines.join('\n') };
|
|
210
|
+
},
|
|
211
|
+
};
|
|
212
|
+
export const harmonyEmulatorCreate = {
|
|
213
|
+
name: 'harmony_emulator_create',
|
|
214
|
+
description: 'Create (deploy) a NEW emulator instance - no DevEco GUI. Two sources: clone an existing deployed device (from=...), or materialize a CATALOG VARIANT (model="Pura 90 Pro" etc. from harmony_emulator_catalog) on the installed image with that model\'s screen specs - "various devices" without downloading anything. Fresh identity each time (new uuid, empty data). New device TYPES (tablet/wearable) need their image installed once via DevEco component manager. Requires approval.',
|
|
215
|
+
parameters: {
|
|
216
|
+
type: 'object',
|
|
217
|
+
properties: {
|
|
218
|
+
name: { type: 'string', description: 'new instance name (letters/digits/spaces)' },
|
|
219
|
+
from: { type: 'string', description: 'existing deployed device to clone (default: first)' },
|
|
220
|
+
model: { type: 'string', description: 'catalog model name (e.g. "Pura 90 Pro") to take screen specs from, overriding the clone source' },
|
|
221
|
+
},
|
|
222
|
+
required: ['name'],
|
|
223
|
+
},
|
|
224
|
+
needsApproval: () => true,
|
|
225
|
+
async execute(args) {
|
|
226
|
+
const name = String(args.name ?? '').trim();
|
|
227
|
+
if (!/^[\w][\w \-]{0,30}$/.test(name))
|
|
228
|
+
return { output: 'Invalid name (letters/digits/spaces, max 31 chars, not starting with a space).', isError: true };
|
|
229
|
+
const devices = await readLists();
|
|
230
|
+
if (devices.some((d) => d.name === name))
|
|
231
|
+
return { output: `Device "${name}" already exists.`, isError: true };
|
|
232
|
+
const src = typeof args.from === 'string' && args.from ? devices.find((d) => d.name === args.from) : devices[0];
|
|
233
|
+
if (!src) {
|
|
234
|
+
return { output: 'No deployed device to clone a hardware profile from, and no image default available headlessly. Create the first device once in DevEco, then hmharness can clone infinitely.', isError: true };
|
|
235
|
+
}
|
|
236
|
+
const instPath = join(deployedDir(), name);
|
|
237
|
+
await mkdir(instPath, { recursive: true });
|
|
238
|
+
// lists.json entry: cloned profile (+ optional catalog-variant screen specs), fresh identity
|
|
239
|
+
const entry = {
|
|
240
|
+
...src,
|
|
241
|
+
name,
|
|
242
|
+
uuid: randomUUID(),
|
|
243
|
+
path: instPath.replace(/\\/g, '/'),
|
|
244
|
+
};
|
|
245
|
+
if (typeof args.model === 'string' && args.model) {
|
|
246
|
+
try {
|
|
247
|
+
const cat = JSON.parse(await readFile(join(imageRoot(), 'productConfig.json'), 'utf8'));
|
|
248
|
+
const spec = Object.values(cat).flat().find((d) => d.name === args.model);
|
|
249
|
+
if (!spec) {
|
|
250
|
+
await rm(instPath, { recursive: true, force: true });
|
|
251
|
+
return { output: `Model "${args.model}" not in catalog. See harmony_emulator_catalog.`, isError: true };
|
|
252
|
+
}
|
|
253
|
+
Object.assign(entry, {
|
|
254
|
+
resolutionWidth: spec.screenWidth,
|
|
255
|
+
resolutionHeight: spec.screenHeight,
|
|
256
|
+
density: spec.screenDensity,
|
|
257
|
+
diagonalSize: spec.screenDiagonal,
|
|
258
|
+
productModel: spec.name,
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
catch {
|
|
262
|
+
/* productConfig unavailable - keep clone profile */
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
delete entry.extra;
|
|
266
|
+
await writeLists([...devices, entry]);
|
|
267
|
+
// <name>..ini pointer
|
|
268
|
+
await writeFile(join(deployedDir(), `${name}.ini`), `hvd.ini.encoding=UTF-8\npath=${instPath.replace(/\\/g, '/')}\n`, 'utf8');
|
|
269
|
+
// config.ini / hardware-qemu.ini: clone from source instance, rewrite identity paths
|
|
270
|
+
for (const f of ['config.ini', 'hardware-qemu.ini']) {
|
|
271
|
+
try {
|
|
272
|
+
let text = await readFile(join(String(src.path), f), 'utf8');
|
|
273
|
+
text = text
|
|
274
|
+
.split('\n')
|
|
275
|
+
.map((line) => {
|
|
276
|
+
if (/^name=/.test(line))
|
|
277
|
+
return `name=${name}`;
|
|
278
|
+
if (/^hvd\.(name|id)=/.test(line))
|
|
279
|
+
return `hvd.${line.split('=')[0]}=${name}`;
|
|
280
|
+
if (/^uuid=/.test(line))
|
|
281
|
+
return `uuid=${entry.uuid}`;
|
|
282
|
+
if (/^productModel=/.test(line))
|
|
283
|
+
return `productModel=${name}`;
|
|
284
|
+
if (/^instancePath=/.test(line))
|
|
285
|
+
return `instancePath=${instPath.replace(/\\/g, '/')}`;
|
|
286
|
+
return line;
|
|
287
|
+
})
|
|
288
|
+
.join('\n');
|
|
289
|
+
await writeFile(join(instPath, f), text, 'utf8');
|
|
290
|
+
}
|
|
291
|
+
catch {
|
|
292
|
+
/* source config missing - first boot may still self-initialize */
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
return { output: `created "${name}" (${entry.type ?? 'phone'} · ${entry.showVersion ?? ''}). Start it with harmony_emulator_start.` };
|
|
296
|
+
},
|
|
297
|
+
};
|
|
298
|
+
export const harmonyEmulatorDelete = {
|
|
299
|
+
name: 'harmony_emulator_delete',
|
|
300
|
+
description: 'Delete a deployed emulator instance (data dir, lists entry, ini). Refuses while it is running. Destructive - requires approval.',
|
|
301
|
+
parameters: {
|
|
302
|
+
type: 'object',
|
|
303
|
+
properties: { name: { type: 'string', description: 'device name' } },
|
|
304
|
+
required: ['name'],
|
|
305
|
+
},
|
|
306
|
+
needsApproval: () => true,
|
|
307
|
+
async execute(args) {
|
|
308
|
+
const name = String(args.name ?? '').trim();
|
|
309
|
+
const running = await runningEmulators();
|
|
310
|
+
if (running.some((r) => r.hvd === name))
|
|
311
|
+
return { output: `"${name}" is running - stop it first (harmony_emulator_stop).`, isError: true };
|
|
312
|
+
const devices = await readLists();
|
|
313
|
+
const dev = devices.find((d) => d.name === name);
|
|
314
|
+
if (!dev)
|
|
315
|
+
return { output: `No deployed device named "${name}".`, isError: true };
|
|
316
|
+
await rm(join(deployedDir(), name), { recursive: true, force: true });
|
|
317
|
+
await rm(join(deployedDir(), `${name}.ini`), { force: true });
|
|
318
|
+
await writeLists(devices.filter((d) => d.name !== name));
|
|
319
|
+
return { output: `deleted "${name}".` };
|
|
320
|
+
},
|
|
321
|
+
};
|
|
322
|
+
export const harmonyImageDownloadCheck = {
|
|
323
|
+
name: 'harmony_image_check',
|
|
324
|
+
description: 'Check emulator system-image availability: which API version and device types are installed locally (deployable headlessly right now), and for anything else the exact DevEco manual step (Huawei provides no public image-download channel - account-bound component manager only). Honest probe, never a fake download.',
|
|
325
|
+
parameters: {
|
|
326
|
+
type: 'object',
|
|
327
|
+
properties: {
|
|
328
|
+
type: { type: 'string', description: 'optional device type to check, e.g. "phone"' },
|
|
329
|
+
},
|
|
330
|
+
required: [],
|
|
331
|
+
},
|
|
332
|
+
needsApproval: () => false,
|
|
333
|
+
async execute(args) {
|
|
334
|
+
const want = typeof args.type === 'string' ? args.type.toLowerCase() : '';
|
|
335
|
+
const lines = [];
|
|
336
|
+
let ver = '';
|
|
337
|
+
let types = [];
|
|
338
|
+
try {
|
|
339
|
+
const vers = await readdir(join(imageRoot(), 'system-image'));
|
|
340
|
+
ver = vers[0] ?? '';
|
|
341
|
+
types = ver ? await readdir(join(imageRoot(), 'system-image', ver)) : [];
|
|
342
|
+
}
|
|
343
|
+
catch {
|
|
344
|
+
return {
|
|
345
|
+
output: `No emulator images installed at ${imageRoot()}.\nImages ship only through DevEco Studio > Settings > SDK component manager (account-bound; no public download channel exists).\nAfter installing one image there, instances of that type are fully self-serve here (harmony_emulator_create).`,
|
|
346
|
+
isError: true,
|
|
347
|
+
};
|
|
348
|
+
}
|
|
349
|
+
lines.push(`installed image: API ${ver}`);
|
|
350
|
+
lines.push(`device types deployable now: ${types.join(', ') || '(none)'}`);
|
|
351
|
+
if (want) {
|
|
352
|
+
lines.push(types.includes(want)
|
|
353
|
+
? `"${want}" IS installed - create instances with harmony_emulator_create.`
|
|
354
|
+
: `"${want}" NOT installed - DevEco Studio > Settings > SDK component manager is the only channel (no public URL exists); this is a manual, account-bound step by vendor design.`);
|
|
355
|
+
}
|
|
356
|
+
lines.push('note: new INSTANCES of installed types are fully automatable; new IMAGE acquisition is manual by vendor design.');
|
|
357
|
+
return { output: lines.join('\n'), isError: want ? !types.includes(want) : false };
|
|
358
|
+
},
|
|
359
|
+
};
|
|
360
|
+
export const emulatorTools = [
|
|
361
|
+
harmonyEmulatorList,
|
|
362
|
+
harmonyEmulatorCatalog,
|
|
363
|
+
harmonyEmulatorStart,
|
|
364
|
+
harmonyEmulatorStop,
|
|
365
|
+
harmonyEmulatorCreate,
|
|
366
|
+
harmonyEmulatorDelete,
|
|
367
|
+
harmonyImageDownloadCheck,
|
|
368
|
+
];
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { Tool } from '@hmharness/kernel';
|
|
2
|
+
export declare const harmonyDevices: Tool;
|
|
3
|
+
export declare const harmonyToolchainCheck: Tool;
|
|
4
|
+
export declare const harmonyBuild: Tool;
|
|
5
|
+
export declare const harmonyInstall: Tool;
|
|
6
|
+
export declare const harmonyLaunch: Tool;
|
|
7
|
+
export declare const harmonyLogs: Tool;
|
|
8
|
+
export declare const harmonyUninstall: Tool;
|
|
9
|
+
export declare const harmonyTools: Tool[];
|
|
10
|
+
export { harmonyProjectCreate, scaffoldProject, solidPng, sdkVersion } from './project.ts';
|
|
11
|
+
export { harmonyCjpmBuild, harmonyCjpmTest, findCjpm } from './cangjie.ts';
|
|
12
|
+
export { harmonySchemaCheck, checkProjectSchemas, parseJson5 as parseJson5Strict, validateModuleJson5, validateBuildProfile } from './schema.ts';
|
|
13
|
+
export { parseSdkVersion, compareSdk, capabilitiesFor, CAPABILITY_MATRIX, type SdkVersion, type CapabilityRule } from './apimatrix.ts';
|
|
14
|
+
export { harmonyBuildDoctor, diagnoseBuildFailure, firstErrorBlock } from './builddoctor.ts';
|
|
15
|
+
export { harmonyProjectProfile, profileProject } from './profile.ts';
|
|
16
|
+
export { harmonySign, resolveSigningIdentity, ensureDebugProfile, signHap, hapsignToolPaths, type SigningIdentity } from './signing.ts';
|
|
17
|
+
export { harmonyDeviceTest, runDeviceTest, type DeviceTestStep } from './ondevice.ts';
|
|
18
|
+
export { harmonyApiLookup, buildApiIndex, loadApiIndex, lookupSymbol, parseDeclaration, sdkApiDir, type ApiIndex, type ApiSymbolEntry } from './apikg.ts';
|
|
19
|
+
export { harmonyUiRegression, runUiRegression, captureDeviceScreen, type UiRegressionCase, type UiRegressionResult } from './uiregress.ts';
|
|
20
|
+
export { harmonyImageDownloadCheck } from './emulator.ts';
|