@androperator/emulator 0.1.1 → 0.2.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/.agents/skills/create-android-emulator/SKILL.md +137 -0
- package/.agents/skills/create-android-emulator/agents/openai.yaml +4 -0
- package/README.md +75 -1
- package/dist/catalog.d.ts +27 -0
- package/dist/catalog.js +98 -0
- package/dist/cli.js +8 -4
- package/dist/errors.d.ts +1 -0
- package/dist/errors.js +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/package.json +3 -2
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: create-android-emulator
|
|
3
|
+
description: Create or recreate an Android development emulator with the @androperator/emulator library or CLI, choose an installed phone image and profile, configure practical storage, and verify boot and guest capacity. Use for local AVD provisioning, including insufficient storage during repeated APK installs.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Create Android Emulator
|
|
7
|
+
|
|
8
|
+
Use `@androperator/emulator` for AVD lifecycle operations. Prefer its JSON CLI for
|
|
9
|
+
interactive provisioning; use the library exports when integrating into code.
|
|
10
|
+
This skill creates a device, not an Android UI automation workflow.
|
|
11
|
+
|
|
12
|
+
## Discover and choose
|
|
13
|
+
|
|
14
|
+
Find an installed `androperator-emulator` and check `--version` and `--help`.
|
|
15
|
+
In this library's source checkout, use `npm ci` if dependencies are missing,
|
|
16
|
+
`npm run build`, then `node dist/cli.js` as the CLI. Outside the checkout, use
|
|
17
|
+
`npm install -g @androperator/emulator` when installation is within the request.
|
|
18
|
+
Requires Node.js 24+, Android SDK tools and their Java runtime. Missing SDK or
|
|
19
|
+
Java prerequisites should be reported before provisioning; the package does not
|
|
20
|
+
install the SDK itself.
|
|
21
|
+
|
|
22
|
+
CLI tool overrides are `ADB_PATH`, `EMULATOR_PATH`, `SDKMANAGER_PATH` and
|
|
23
|
+
`AVDMANAGER_PATH`. Otherwise discovery checks `ANDROID_HOME`, `ANDROID_SDK_ROOT`,
|
|
24
|
+
standard macOS/Linux SDK roots, then PATH. Use the same resolved tools for the SDK
|
|
25
|
+
and adb commands below. Keep `ANDROID_AVD_HOME` consistent across all commands
|
|
26
|
+
if set; other custom Android home conventions are not supported by the library.
|
|
27
|
+
|
|
28
|
+
Inspect before mutating:
|
|
29
|
+
|
|
30
|
+
```sh
|
|
31
|
+
androperator-emulator list
|
|
32
|
+
androperator-emulator status
|
|
33
|
+
"$sdkmanager" --list_installed
|
|
34
|
+
"$avdmanager" list device -c
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Here `$sdkmanager`, `$avdmanager` and later `$adb` stand for the resolved executable
|
|
38
|
+
paths. The CLI has no image/profile enumeration command, so use SDK tools for
|
|
39
|
+
those read-only queries.
|
|
40
|
+
|
|
41
|
+
Respect requested API, image, profile, storage and name. Otherwise prefer:
|
|
42
|
+
|
|
43
|
+
- Name: `Android_Dev_API_<api>`.
|
|
44
|
+
- Data capacity: `24G`, for repeated development APK installs.
|
|
45
|
+
- Image: highest installed stable API with a Google Play phone image matching
|
|
46
|
+
the host architecture (`arm64-v8a` on ARM, `x86_64` on x86).
|
|
47
|
+
- Profile: newest installed Pixel phone profile compatible with that image.
|
|
48
|
+
|
|
49
|
+
Use actual package and profile IDs from discovery, not guessed IDs. If no suitable
|
|
50
|
+
Google Play image is installed, choose an installed compatible Google APIs phone
|
|
51
|
+
image when Play Store access is unnecessary. If an image must be downloaded,
|
|
52
|
+
query `"$sdkmanager" --list` and choose an available compatible package. Creation
|
|
53
|
+
installs a missing selected image. `--accept-licenses` accepts **all outstanding
|
|
54
|
+
SDK licenses**, so use it only with explicit authorization for that acceptance.
|
|
55
|
+
|
|
56
|
+
## Create and boot
|
|
57
|
+
|
|
58
|
+
Set `$name`, `$image` and `$profile` to the selected values. For a fresh device:
|
|
59
|
+
|
|
60
|
+
```sh
|
|
61
|
+
androperator-emulator create "$name" \
|
|
62
|
+
--image "$image" --profile "$profile" --storage-size 24G
|
|
63
|
+
androperator-emulator inspect "$name"
|
|
64
|
+
androperator-emulator start "$name" --headless
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Substitute the requested capacity for `24G`. Omit `--headless` for a visible
|
|
68
|
+
emulator. Every CLI invocation returns one JSON envelope; check exit status and
|
|
69
|
+
`ok` before using `data`. Stop on failure, inspect the error and relevant state,
|
|
70
|
+
and retry only after addressing its cause. A failed start can leave an emulator
|
|
71
|
+
running; check `status` before launching again.
|
|
72
|
+
|
|
73
|
+
Existing names are refused. Reuse a suitable existing device or choose a fresh
|
|
74
|
+
name. Use `create --replace` only when the user has authorized discarding that
|
|
75
|
+
AVD's data. Before replacement, stop the target if running, then poll `status`
|
|
76
|
+
with a bounded timeout to confirm its serial disappears. `stop` only requests
|
|
77
|
+
shutdown. Replacement/deletion refuse running AVDs and fail closed on uncertain
|
|
78
|
+
ADB state; resolve that state instead of bypassing the checks. Serialize lifecycle
|
|
79
|
+
operations for the same AVD.
|
|
80
|
+
|
|
81
|
+
`storage` changes configured capacity only; it does not resize existing userdata.
|
|
82
|
+
Prefer a fresh AVD for a capacity change. Creation does not configure an SD card,
|
|
83
|
+
force qcow2 allocation, or disable snapshot saving. Do not promise the reference
|
|
84
|
+
AVD workflow's sparse disk or snapshot-free behavior. CLI launch disables snapshot
|
|
85
|
+
loading. If snapshot saving must be disabled, use library `startAvd` with
|
|
86
|
+
`["-no-snapshot-save"]`, then both readiness waits described below.
|
|
87
|
+
|
|
88
|
+
## Verify and report
|
|
89
|
+
|
|
90
|
+
Successful CLI `start` returns `data.serial` and `booted: true` after registration
|
|
91
|
+
and both boot properties pass. Use that returned serial, never assume port 5554.
|
|
92
|
+
If reusing an already-running target, resolve its serial by AVD name in `status`
|
|
93
|
+
and use `wait <serial>` to verify readiness.
|
|
94
|
+
|
|
95
|
+
```sh
|
|
96
|
+
"$adb" -s "$serial" shell df -h /data
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
Check actual total and free guest capacity against the requested size; `inspect`
|
|
100
|
+
reports device metadata, not storage capacity. If an APK is supplied for a smoke
|
|
101
|
+
test, install it on this serial with `"$adb" -s "$serial" install -r "$apk"`.
|
|
102
|
+
If host disk usage matters, obtain the actual config path with library
|
|
103
|
+
`getAvdConfigPath(name)` (AVD locator files may redirect it), and measure its
|
|
104
|
+
parent directory with `du -sh`. Inspect existing userdata with SDK `qemu-img info`
|
|
105
|
+
only when that tool and file exist; report measured allocation rather than
|
|
106
|
+
assuming qcow2 or a small physical size.
|
|
107
|
+
|
|
108
|
+
Leave a requested development device running. Stop a device launched only for
|
|
109
|
+
verification when the requested outcome does not require it running; confirm
|
|
110
|
+
shutdown before reporting it stopped. Do not delete the created AVD as cleanup.
|
|
111
|
+
Report name, API/image/profile, configured capacity, measured `/data` total/free,
|
|
112
|
+
serial and boot state, APK result if tested, and the normal launch command.
|
|
113
|
+
Distinguish unavailable measurements from verified results.
|
|
114
|
+
|
|
115
|
+
## Library integration
|
|
116
|
+
|
|
117
|
+
The root export supplies `getDefaultRuntimeConfig`, `createAvd`,
|
|
118
|
+
`inspectConfiguredAvd`, `listRunningEmulators`, `startAvd`,
|
|
119
|
+
`waitForEmulatorRegistration`, `waitForBootCompletion`, `stopAvd`, and
|
|
120
|
+
`getAvdConfigPath`. Use the same selections and replacement/license boundaries.
|
|
121
|
+
|
|
122
|
+
```js
|
|
123
|
+
import {
|
|
124
|
+
getDefaultRuntimeConfig, createAvd, startAvd,
|
|
125
|
+
waitForEmulatorRegistration, waitForBootCompletion,
|
|
126
|
+
} from '@androperator/emulator';
|
|
127
|
+
|
|
128
|
+
const config = getDefaultRuntimeConfig();
|
|
129
|
+
await createAvd(config, {
|
|
130
|
+
name, systemImage: image, deviceProfile: profile, dataPartitionSize: '24G',
|
|
131
|
+
});
|
|
132
|
+
await startAvd(config, name, ['-no-window', '-no-audio']);
|
|
133
|
+
const serial = await waitForEmulatorRegistration(config, name);
|
|
134
|
+
await waitForBootCompletion(config, serial);
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
`startAvd` confirms spawn only; both waits are required to confirm readiness.
|
package/README.md
CHANGED
|
@@ -12,7 +12,7 @@ npm ci
|
|
|
12
12
|
npm run build
|
|
13
13
|
npm test
|
|
14
14
|
npm pack
|
|
15
|
-
npm install -g ./androperator-emulator-0.
|
|
15
|
+
npm install -g ./androperator-emulator-0.2.0.tgz
|
|
16
16
|
androperator-emulator --version
|
|
17
17
|
```
|
|
18
18
|
|
|
@@ -22,9 +22,27 @@ dependencies.
|
|
|
22
22
|
The executable remains `androperator-emulator`. See [provenance](docs/provenance.md)
|
|
23
23
|
for source history and [plan](docs/plan.md) for follow-on work.
|
|
24
24
|
|
|
25
|
+
## Homebrew installation
|
|
26
|
+
|
|
27
|
+
```sh
|
|
28
|
+
brew install androperator/tap/emulator
|
|
29
|
+
brew upgrade androperator/tap/emulator
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
The executable remains `androperator-emulator`. Homebrew manages Node and uses
|
|
33
|
+
this package's npm release archive. Android SDK tools and Java are configured
|
|
34
|
+
separately. When moving from a global npm installation, first remove it with
|
|
35
|
+
`npm uninstall -g @androperator/emulator` to avoid executable conflicts.
|
|
36
|
+
New versions become available after automatic tap validation; Homebrew can
|
|
37
|
+
hold newly published dependencies for 24 hours. Do not upgrade a Homebrew
|
|
38
|
+
installation using npm.
|
|
39
|
+
|
|
25
40
|
## CLI
|
|
26
41
|
|
|
27
42
|
```sh
|
|
43
|
+
androperator-emulator profiles
|
|
44
|
+
androperator-emulator images
|
|
45
|
+
androperator-emulator images --installed
|
|
28
46
|
androperator-emulator list
|
|
29
47
|
androperator-emulator inspect My_AVD
|
|
30
48
|
androperator-emulator status
|
|
@@ -52,6 +70,37 @@ The `--version` response identifies the package and machine protocol without SDK
|
|
|
52
70
|
access. ADBuddy can probe this command after finding the separately installed
|
|
53
71
|
executable; native discovery and installation UI are separate work.
|
|
54
72
|
|
|
73
|
+
### Creation catalogs
|
|
74
|
+
|
|
75
|
+
`profiles` returns `{profiles: [{id, name, manufacturer, tag}]}` from the SDK's
|
|
76
|
+
hardware definitions. Pass the stable `id` to `create --profile`; the SDK's
|
|
77
|
+
numeric row index is not returned. Missing manufacturer or tag values are null.
|
|
78
|
+
|
|
79
|
+
`images` queries installed and available SDK packages and returns
|
|
80
|
+
`{images: [{id, platform, apiLevel, tag, abi, description, installed,
|
|
81
|
+
installedVersion, availableVersion}]}`. `images --installed` queries only local
|
|
82
|
+
packages, so it does not require fetching the remote catalog. Pass `id` directly
|
|
83
|
+
to `create --image`. IDs use semicolons regardless of the SDK output format.
|
|
84
|
+
Versions remain strings; an absent installed or available version is null.
|
|
85
|
+
`platform` preserves the complete platform identifier, including minor versions
|
|
86
|
+
or preview names. `apiLevel` is the numeric major API for numeric platforms and
|
|
87
|
+
null for codenames. Results are sorted by ID, with each image appearing once.
|
|
88
|
+
|
|
89
|
+
These are catalogs, not compatibility recommendations. Callers must select a
|
|
90
|
+
suitable image/profile/host combination; do not assume every listed image runs
|
|
91
|
+
on this Mac. Remote queries can require network access and may take longer than
|
|
92
|
+
local queries. Tool or unrecognized-format failures return
|
|
93
|
+
`ANDROID_CATALOG_QUERY_FAILED`; empty recognized catalogs return empty arrays.
|
|
94
|
+
The legacy SDK table and the Android CLI shim's slash-separated table are supported.
|
|
95
|
+
|
|
96
|
+
Library consumers can call `listHardwareProfiles(config)` and
|
|
97
|
+
`listSystemImages(config, {installedOnly: true})`; omitting the latter option
|
|
98
|
+
includes available images. Neither command starts ADB or accepts licenses.
|
|
99
|
+
`--version` now includes `capabilities: ["catalog.profiles", "catalog.images"]`
|
|
100
|
+
in its data object. Consumers should treat missing capabilities as unsupported;
|
|
101
|
+
the JSON envelope remains protocol version 1. Earlier published versions lack
|
|
102
|
+
these catalog commands.
|
|
103
|
+
|
|
55
104
|
Create requires explicit image, hardware profile and storage capacity. Existing
|
|
56
105
|
AVDs are refused unless `--replace` is supplied, which discards existing data.
|
|
57
106
|
Replacement and deletion refuse running AVDs and fail closed if ADB discovery
|
|
@@ -75,6 +124,31 @@ It does not resize existing userdata, guarantee free space, or impose a host dis
|
|
|
75
124
|
quota. Use a fresh AVD when changing capacity. Dynamic qcow2 allocation and
|
|
76
125
|
snapshot-free storage are follow-on capabilities.
|
|
77
126
|
|
|
127
|
+
## Agent skill
|
|
128
|
+
|
|
129
|
+
[Create Android Emulator](.agents/skills/create-android-emulator/SKILL.md) guides an
|
|
130
|
+
agent through image/profile discovery, creation with practical development
|
|
131
|
+
storage (24G by default), boot verification and actual guest storage checks using
|
|
132
|
+
this package. Invoke it as `$create-android-emulator`.
|
|
133
|
+
|
|
134
|
+
The source lives in `.agents/skills/create-android-emulator/`, the repository
|
|
135
|
+
skill location supported by Codex. The same folder is included in the npm
|
|
136
|
+
archive; the release-only skill is excluded. Installing the npm package does
|
|
137
|
+
not automatically register its skill with an agent.
|
|
138
|
+
|
|
139
|
+
To use it in another project, copy the entire skill folder from
|
|
140
|
+
`node_modules/@androperator/emulator/.agents/skills/create-android-emulator/`
|
|
141
|
+
into that project's `.agents/skills/`. For a global npm installation, the source
|
|
142
|
+
is under `$(npm root -g)/@androperator/emulator/.agents/skills/`. For personal
|
|
143
|
+
Codex use across projects, copy it into `~/.agents/skills/` instead. Copies should
|
|
144
|
+
be refreshed when upgrading the library.
|
|
145
|
+
|
|
146
|
+
Each skill needs a `SKILL.md` with name/description frontmatter and instructions;
|
|
147
|
+
`agents/openai.yaml` supplies optional UI metadata. Repository skills work well
|
|
148
|
+
for checked-in workflows. Plugins provide an installation unit for broader skill
|
|
149
|
+
distribution and optional connectors. See the official
|
|
150
|
+
[skill discovery and distribution guidance](https://learn.chatgpt.com/docs/build-skills).
|
|
151
|
+
|
|
78
152
|
## Library
|
|
79
153
|
|
|
80
154
|
```js
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { RuntimeConfig } from "./runtimeConfig.js";
|
|
2
|
+
export interface HardwareProfile {
|
|
3
|
+
/** Stable identifier accepted by create --profile; never the catalog row number. */
|
|
4
|
+
id: string;
|
|
5
|
+
name: string;
|
|
6
|
+
manufacturer: string | null;
|
|
7
|
+
tag: string | null;
|
|
8
|
+
}
|
|
9
|
+
export interface SystemImage {
|
|
10
|
+
/** Canonical semicolon-delimited identifier accepted by create --image. */
|
|
11
|
+
id: string;
|
|
12
|
+
platform: string;
|
|
13
|
+
apiLevel: number | null;
|
|
14
|
+
tag: string;
|
|
15
|
+
abi: string;
|
|
16
|
+
description: string;
|
|
17
|
+
installed: boolean;
|
|
18
|
+
installedVersion: string | null;
|
|
19
|
+
availableVersion: string | null;
|
|
20
|
+
}
|
|
21
|
+
export declare function parseHardwareProfiles(output: string): HardwareProfile[];
|
|
22
|
+
export declare function parseSystemImages(output: string): SystemImage[];
|
|
23
|
+
export declare function listHardwareProfiles(config: RuntimeConfig): Promise<HardwareProfile[]>;
|
|
24
|
+
export declare function listSystemImages(config: RuntimeConfig, options?: {
|
|
25
|
+
installedOnly?: boolean;
|
|
26
|
+
}): Promise<SystemImage[]>;
|
|
27
|
+
//# sourceMappingURL=catalog.d.ts.map
|
package/dist/catalog.js
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { runAndroidSdkTool } from "./hostToolClient.js";
|
|
2
|
+
function catalogError(message) {
|
|
3
|
+
throw { code: "ANDROID_CATALOG_QUERY_FAILED", message };
|
|
4
|
+
}
|
|
5
|
+
export function parseHardwareProfiles(output) {
|
|
6
|
+
const profiles = [];
|
|
7
|
+
let profile;
|
|
8
|
+
function finish() {
|
|
9
|
+
if (!profile)
|
|
10
|
+
return;
|
|
11
|
+
if (!profile.name)
|
|
12
|
+
catalogError(`Missing name for hardware profile ${profile.id}`);
|
|
13
|
+
profiles.push(profile);
|
|
14
|
+
}
|
|
15
|
+
for (const line of output.split(/\r?\n/)) {
|
|
16
|
+
const id = /^\s*id:\s*\d+\s+or\s+"([^"]+)"\s*$/.exec(line);
|
|
17
|
+
if (id) {
|
|
18
|
+
finish();
|
|
19
|
+
profile = { id: id[1], name: "", manufacturer: null, tag: null };
|
|
20
|
+
}
|
|
21
|
+
else if (/^\s*id:/.test(line)) {
|
|
22
|
+
catalogError(`Unrecognized hardware profile identifier: ${line.trim()}`);
|
|
23
|
+
}
|
|
24
|
+
else if (profile) {
|
|
25
|
+
const field = /^\s*(Name|OEM|Tag)\s*:\s*(.*?)\s*$/.exec(line);
|
|
26
|
+
if (field?.[1] === "Name")
|
|
27
|
+
profile.name = field[2];
|
|
28
|
+
if (field?.[1] === "OEM")
|
|
29
|
+
profile.manufacturer = field[2] || null;
|
|
30
|
+
if (field?.[1] === "Tag")
|
|
31
|
+
profile.tag = field[2] || null;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
finish();
|
|
35
|
+
if (!profiles.length && !output.includes("Available devices definitions:")) {
|
|
36
|
+
catalogError("Unrecognized hardware profile catalog; expected avdmanager list device output");
|
|
37
|
+
}
|
|
38
|
+
return profiles.sort((a, b) => a.id.localeCompare(b.id));
|
|
39
|
+
}
|
|
40
|
+
export function parseSystemImages(output) {
|
|
41
|
+
const images = new Map();
|
|
42
|
+
let section;
|
|
43
|
+
let recognized = false;
|
|
44
|
+
for (const line of output.split(/\r?\n/)) {
|
|
45
|
+
const heading = /^\s*(Installed packages|Available Packages|Available packages|Available Updates|Available updates):\s*$/.exec(line);
|
|
46
|
+
if (heading) {
|
|
47
|
+
recognized = true;
|
|
48
|
+
section = heading[1] === "Installed packages" ? "installed"
|
|
49
|
+
: heading[1].toLowerCase() === "available packages" ? "available" : undefined;
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
if (!/^\s*system-images[;/]/.test(line))
|
|
53
|
+
continue;
|
|
54
|
+
// Update tables have a different schema; their rows are already represented
|
|
55
|
+
// by the installed and available package sections.
|
|
56
|
+
if (!section)
|
|
57
|
+
continue;
|
|
58
|
+
const columns = line.includes("|") ? line.split("|").map((column) => column.trim())
|
|
59
|
+
: line.trim().split(/\s{2,}/);
|
|
60
|
+
const [rawId, version, description] = columns;
|
|
61
|
+
const id = rawId.replaceAll("/", ";");
|
|
62
|
+
const parts = /^system-images;(android-[^;\s]+);([^;\s]+);([^;\s]+)$/.exec(id);
|
|
63
|
+
if (!parts || !version || !description)
|
|
64
|
+
catalogError(`Unrecognized system image row: ${line.trim()}`);
|
|
65
|
+
const [, platform, tag, abi] = parts;
|
|
66
|
+
const api = /^android-(\d+)(?:\.\d+)*(?:-ext\d+)?$/.exec(platform);
|
|
67
|
+
const image = images.get(id) ?? {
|
|
68
|
+
id, platform, apiLevel: api ? Number(api[1]) : null, tag, abi, description,
|
|
69
|
+
installed: false, installedVersion: null, availableVersion: null,
|
|
70
|
+
};
|
|
71
|
+
if (section === "installed") {
|
|
72
|
+
image.installed = true;
|
|
73
|
+
image.installedVersion = version;
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
image.availableVersion = version;
|
|
77
|
+
image.description = description;
|
|
78
|
+
}
|
|
79
|
+
images.set(id, image);
|
|
80
|
+
}
|
|
81
|
+
if (!recognized)
|
|
82
|
+
catalogError("Unrecognized system image catalog; expected sdkmanager package sections");
|
|
83
|
+
return [...images.values()].sort((a, b) => a.id.localeCompare(b.id));
|
|
84
|
+
}
|
|
85
|
+
export async function listHardwareProfiles(config) {
|
|
86
|
+
const result = await runAndroidSdkTool(config, "avdmanager", ["list", "device"], { timeoutMs: 30_000 });
|
|
87
|
+
if (result.code !== 0 || result.error)
|
|
88
|
+
catalogError(result.stderr || result.error?.message || "Could not list hardware profiles");
|
|
89
|
+
return parseHardwareProfiles(result.stdout);
|
|
90
|
+
}
|
|
91
|
+
export async function listSystemImages(config, options = {}) {
|
|
92
|
+
const result = await runAndroidSdkTool(config, "sdkmanager", [options.installedOnly ? "--list_installed" : "--list"], { timeoutMs: 120_000 });
|
|
93
|
+
if (result.code !== 0 || result.error)
|
|
94
|
+
catalogError(result.stderr || result.error?.message || "Could not list system images");
|
|
95
|
+
const images = parseSystemImages(result.stdout);
|
|
96
|
+
return options.installedOnly ? images.filter((image) => image.installed) : images;
|
|
97
|
+
}
|
|
98
|
+
//# sourceMappingURL=catalog.js.map
|
package/dist/cli.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { readFileSync } from "node:fs";
|
|
3
3
|
import { parseArgs } from "node:util";
|
|
4
|
-
import { getDefaultRuntimeConfig, listRunningEmulators, listConfiguredAvds, inspectConfiguredAvd, createAvd, startAvd, stopAvd, deleteAvd, waitForBootCompletion, waitForEmulatorRegistration, ensureSystemImageInstalled, setAvdDataPartitionSize } from "./index.js";
|
|
4
|
+
import { getDefaultRuntimeConfig, listRunningEmulators, listConfiguredAvds, inspectConfiguredAvd, createAvd, startAvd, stopAvd, deleteAvd, waitForBootCompletion, waitForEmulatorRegistration, ensureSystemImageInstalled, setAvdDataPartitionSize, listHardwareProfiles, listSystemImages } from "./index.js";
|
|
5
5
|
const commands = {
|
|
6
|
+
profiles: "profiles", images: "images [--installed]",
|
|
6
7
|
list: "list", inspect: "inspect <name>", status: "status",
|
|
7
8
|
download: "download <system-image> [--accept-licenses]",
|
|
8
9
|
create: "create <name> --image <system-image> --profile <hardware-profile> --storage-size <12G> [--replace] [--accept-licenses]",
|
|
@@ -14,7 +15,7 @@ function usage(message) {
|
|
|
14
15
|
}
|
|
15
16
|
async function main() {
|
|
16
17
|
const { values, positionals } = parseArgs({ allowPositionals: true, strict: true, options: {
|
|
17
|
-
help: { type: "boolean", short: "h" }, version: { type: "boolean" }, json: { type: "boolean" },
|
|
18
|
+
installed: { type: "boolean" }, help: { type: "boolean", short: "h" }, version: { type: "boolean" }, json: { type: "boolean" },
|
|
18
19
|
output: { type: "string" }, image: { type: "string" }, profile: { type: "string" },
|
|
19
20
|
"storage-size": { type: "string" }, replace: { type: "boolean" },
|
|
20
21
|
"accept-licenses": { type: "boolean" }, headless: { type: "boolean" }, "timeout-ms": { type: "string" },
|
|
@@ -23,17 +24,18 @@ async function main() {
|
|
|
23
24
|
usage("--output supports json; for example --output json list");
|
|
24
25
|
if (values.version) {
|
|
25
26
|
const { name, version } = JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf8"));
|
|
26
|
-
return { name, version, protocolVersion: 1 };
|
|
27
|
+
return { name, version, protocolVersion: 1, capabilities: ["catalog.profiles", "catalog.images"] };
|
|
27
28
|
}
|
|
28
29
|
if (values.help)
|
|
29
30
|
return { commands, output: "One JSON envelope on stdout; exit 0 success, 1 operation failure, 2 usage error" };
|
|
30
31
|
const [command, target, ...extra] = positionals;
|
|
31
32
|
if (!Object.hasOwn(commands, command ?? ""))
|
|
32
33
|
usage(`Unknown or missing command: ${command ?? ""}; use --help`);
|
|
33
|
-
const needsTarget = !["list", "status"].includes(command);
|
|
34
|
+
const needsTarget = !["list", "status", "profiles", "images"].includes(command);
|
|
34
35
|
if (extra.length || (needsTarget ? target === undefined || target.trim() === "" : target !== undefined))
|
|
35
36
|
usage(`Use ${commands[command]}`);
|
|
36
37
|
const allowed = {
|
|
38
|
+
images: ["installed"],
|
|
37
39
|
create: ["image", "profile", "storage-size", "replace", "accept-licenses"],
|
|
38
40
|
download: ["accept-licenses"], start: ["headless"], wait: ["timeout-ms"], storage: ["storage-size"],
|
|
39
41
|
};
|
|
@@ -49,6 +51,8 @@ async function main() {
|
|
|
49
51
|
}
|
|
50
52
|
const config = getDefaultRuntimeConfig();
|
|
51
53
|
switch (command) {
|
|
54
|
+
case "profiles": return { profiles: await listHardwareProfiles(config) };
|
|
55
|
+
case "images": return { images: await listSystemImages(config, { installedOnly: values.installed }) };
|
|
52
56
|
case "status": return { devices: await listRunningEmulators(config) };
|
|
53
57
|
case "list": {
|
|
54
58
|
const running = await listRunningEmulators(config);
|
package/dist/errors.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export declare const ERROR_CODES: {
|
|
2
|
+
readonly ANDROID_CATALOG_QUERY_FAILED: "ANDROID_CATALOG_QUERY_FAILED";
|
|
2
3
|
readonly ADB_QUERY_FAILED: "ADB_QUERY_FAILED";
|
|
3
4
|
readonly ANDROID_AVD_CREATE_FAILED: "ANDROID_AVD_CREATE_FAILED";
|
|
4
5
|
readonly ANDROID_SDK_TOOL_MISSING: "ANDROID_SDK_TOOL_MISSING";
|
package/dist/errors.js
CHANGED
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@androperator/emulator",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Android emulator lifecycle library and structured CLI extracted from Androperator",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -21,7 +21,8 @@
|
|
|
21
21
|
"README.md",
|
|
22
22
|
"LICENSE",
|
|
23
23
|
"NOTICE",
|
|
24
|
-
"docs/provenance.md"
|
|
24
|
+
"docs/provenance.md",
|
|
25
|
+
".agents/skills/create-android-emulator/"
|
|
25
26
|
],
|
|
26
27
|
"engines": {
|
|
27
28
|
"node": ">=24"
|