droidline 0.0.0-stage → 0.1.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.
@@ -0,0 +1,397 @@
1
+ // generated by scripts/gen.mjs from spec/commands.json, do not edit
2
+ import { EventEmitter } from "node:events";
3
+ import { collect } from "./args.js";
4
+ export const PROTO = 1;
5
+ /** HTTP status and default retryable flag of each error code. */
6
+ export const ERRORS = {
7
+ /** The target did not appear in time. The current package/activity is returned in screen. */
8
+ NOT_FOUND: { http: 422, retryable: true },
9
+ /** Several elements match. Only raised in strict mode; candidates are returned. */
10
+ AMBIGUOUS: { http: 422, retryable: false },
11
+ /** Both the node action and the coordinate tap failed. */
12
+ NOT_CLICKABLE: { http: 422, retryable: true },
13
+ /** The action itself timed out. Safe to retry. */
14
+ TIMEOUT: { http: 504, retryable: true },
15
+ /** Accessibility service is off. */
16
+ NO_ACCESSIBILITY: { http: 409, retryable: false },
17
+ /** The Droidline keyboard is not selected, so key codes, text and clipboard reads are unavailable. */
18
+ NO_IME: { http: 409, retryable: false },
19
+ /** A permission such as VPN or notification access is missing. The permission name is returned. */
20
+ NO_PERMISSION: { http: 409, retryable: false },
21
+ /** Package is not installed. Similar package names are suggested. */
22
+ APP_NOT_FOUND: { http: 422, retryable: false },
23
+ /** Tried to start a non-exported activity. Suggests launching the package default. */
24
+ ACTIVITY_BLOCKED: { http: 422, retryable: false },
25
+ /** Upstream connection or authentication failed. reason is auth, timeout, refused or protocol. */
26
+ PROXY_FAILED: { http: 502, retryable: true },
27
+ /** Command not supported. The agent version is returned. */
28
+ UNKNOWN_CMD: { http: 404, retryable: false },
29
+ /** The phone's Android version cannot do this without ADB or root. */
30
+ UNSUPPORTED: { http: 422, retryable: false },
31
+ /** Missing or invalid parameters. */
32
+ BAD_ARGS: { http: 400, retryable: false },
33
+ /** No paired device has that ID or name. */
34
+ DEVICE_NOT_FOUND: { http: 404, retryable: false },
35
+ /** The device is paired but offline. Commands wait for a reconnect before failing. */
36
+ DEVICE_OFFLINE: { http: 503, retryable: true },
37
+ /** No device was given and more than one is online. */
38
+ DEVICE_AMBIGUOUS: { http: 400, retryable: false },
39
+ /** The phone is leased and the request did not carry that lease. */
40
+ DEVICE_LEASED: { http: 409, retryable: true },
41
+ /** The lease in the request was released or expired. */
42
+ LEASE_NOT_FOUND: { http: 404, retryable: false },
43
+ /** lease waited and every matching phone stayed leased or offline. */
44
+ NO_FREE_DEVICE: { http: 409, retryable: true },
45
+ /** Tesseract or the requested language data is missing on the PC. */
46
+ OCR_UNAVAILABLE: { http: 501, retryable: false },
47
+ /** The phone app restarted with this command in flight. Not retried automatically to avoid running it twice. */
48
+ AGENT_RESTARTED: { http: 503, retryable: true },
49
+ /** Client API token missing or wrong. */
50
+ UNAUTHORIZED: { http: 401, retryable: false },
51
+ /** The pairing code did not match a waiting phone. */
52
+ PAIRING_FAILED: { http: 422, retryable: false },
53
+ /** Bug. Please report it with the server log. */
54
+ INTERNAL: { http: 500, retryable: false },
55
+ };
56
+ /** The chrome.* commands, reached as d.chrome. */
57
+ export class ChromeCommands {
58
+ _run;
59
+ constructor(_run) {
60
+ this._run = _run;
61
+ }
62
+ async go(...args) {
63
+ return this._run("chrome.go", "fields", collect("chrome.go", args, ["url"], ["new_tab"]));
64
+ }
65
+ }
66
+ /** Commands that run on one phone. Device extends this and implements the hooks. */
67
+ export class DeviceCommands {
68
+ get chrome() {
69
+ return new ChromeCommands((cmd, kind, params) => this._run(cmd, kind, params));
70
+ }
71
+ async dump(...args) {
72
+ return this._saveJson("dump", collect("dump", args, [], ["path"]));
73
+ }
74
+ async screenshot(...args) {
75
+ return this._saveImage("screenshot", collect("screenshot", args, [], ["path"]));
76
+ }
77
+ async record(...args) {
78
+ return this._run("record", "none", collect("record", args, ["on"], ["minutes"]));
79
+ }
80
+ async find_image(...args) {
81
+ const params = collect("find_image", args, ["image"], ["threshold", "timeout"]);
82
+ params["image"] = await this._loadImage(params["image"]);
83
+ return this._run("find_image", "fields", params);
84
+ }
85
+ async findImage(...args) {
86
+ const params = collect("find_image", args, ["image"], ["threshold", "timeout"]);
87
+ params["image"] = await this._loadImage(params["image"]);
88
+ return this._run("find_image", "fields", params);
89
+ }
90
+ async tap_image(...args) {
91
+ const params = collect("tap_image", args, ["image"], ["threshold", "timeout"]);
92
+ params["image"] = await this._loadImage(params["image"]);
93
+ return this._run("tap_image", "fields", params);
94
+ }
95
+ async tapImage(...args) {
96
+ const params = collect("tap_image", args, ["image"], ["threshold", "timeout"]);
97
+ params["image"] = await this._loadImage(params["image"]);
98
+ return this._run("tap_image", "fields", params);
99
+ }
100
+ async ocr(...args) {
101
+ return this._run("ocr", "fields", collect("ocr", args, [], ["lang"]));
102
+ }
103
+ async ocr_find(...args) {
104
+ return this._run("ocr_find", "fields", collect("ocr_find", args, ["text"], ["lang", "timeout"]));
105
+ }
106
+ async ocrFind(...args) {
107
+ return this._run("ocr_find", "fields", collect("ocr_find", args, ["text"], ["lang", "timeout"]));
108
+ }
109
+ async ocr_tap(...args) {
110
+ return this._run("ocr_tap", "fields", collect("ocr_tap", args, ["text"], ["lang", "timeout"]));
111
+ }
112
+ async ocrTap(...args) {
113
+ return this._run("ocr_tap", "fields", collect("ocr_tap", args, ["text"], ["lang", "timeout"]));
114
+ }
115
+ async current(...args) {
116
+ return this._run("current", "fields", collect("current", args, [], []));
117
+ }
118
+ async tap(...args) {
119
+ return this._run("tap", "fields", collect("tap", args, ["x", "y"], []));
120
+ }
121
+ async long_tap(...args) {
122
+ return this._run("long_tap", "fields", collect("long_tap", args, ["x", "y"], ["ms"]));
123
+ }
124
+ async longTap(...args) {
125
+ return this._run("long_tap", "fields", collect("long_tap", args, ["x", "y"], ["ms"]));
126
+ }
127
+ async swipe(...args) {
128
+ return this._run("swipe", "fields", collect("swipe", args, ["x1"], ["y1", "x2", "y2", "ms"]));
129
+ }
130
+ async touch(...args) {
131
+ return this._run("touch", "fields", collect("touch", args, typeof args[0] === "string" ? ["by", "value"] : ["by"], ["nth", "timeout"]));
132
+ }
133
+ async touchById(...args) {
134
+ return this._run("touchById", "fields", collect("touchById", args, ["value"], ["nth", "timeout"]));
135
+ }
136
+ async touchByText(...args) {
137
+ return this._run("touchByText", "fields", collect("touchByText", args, ["value"], ["nth", "timeout"]));
138
+ }
139
+ async touchByDesc(...args) {
140
+ return this._run("touchByDesc", "fields", collect("touchByDesc", args, ["value"], ["nth", "timeout"]));
141
+ }
142
+ async long_touch(...args) {
143
+ return this._run("long_touch", "fields", collect("long_touch", args, typeof args[0] === "string" ? ["by", "value"] : ["by"], ["ms", "nth", "timeout"]));
144
+ }
145
+ async longTouch(...args) {
146
+ return this._run("long_touch", "fields", collect("long_touch", args, typeof args[0] === "string" ? ["by", "value"] : ["by"], ["ms", "nth", "timeout"]));
147
+ }
148
+ async scroll_to(...args) {
149
+ return this._run("scroll_to", "fields", collect("scroll_to", args, typeof args[0] === "string" ? ["by", "value"] : ["by"], ["direction", "max_swipes", "nth"]));
150
+ }
151
+ async scrollTo(...args) {
152
+ return this._run("scroll_to", "fields", collect("scroll_to", args, typeof args[0] === "string" ? ["by", "value"] : ["by"], ["direction", "max_swipes", "nth"]));
153
+ }
154
+ async input(...args) {
155
+ return this._run("input", "fields", collect("input", args, typeof args[0] === "string" ? ["by", "value", "text"] : ["by", "text"], ["append", "nth", "timeout"]));
156
+ }
157
+ async clear(...args) {
158
+ return this._run("clear", "fields", collect("clear", args, typeof args[0] === "string" ? ["by", "value"] : ["by"], ["nth", "timeout"]));
159
+ }
160
+ async sendkey(...args) {
161
+ return this._run("sendkey", "fields", collect("sendkey", args, [], ["key", "text"]));
162
+ }
163
+ async exists(...args) {
164
+ return this._run("exists", "value", collect("exists", args, typeof args[0] === "string" ? ["by", "value"] : ["by"], ["nth"]));
165
+ }
166
+ async wait(...args) {
167
+ return this._run("wait", "fields", collect("wait", args, typeof args[0] === "string" ? ["by", "value"] : ["by"], ["timeout", "nth"]));
168
+ }
169
+ async wait_gone(...args) {
170
+ return this._run("wait_gone", "fields", collect("wait_gone", args, typeof args[0] === "string" ? ["by", "value"] : ["by"], ["timeout"]));
171
+ }
172
+ async waitGone(...args) {
173
+ return this._run("wait_gone", "fields", collect("wait_gone", args, typeof args[0] === "string" ? ["by", "value"] : ["by"], ["timeout"]));
174
+ }
175
+ async wait_idle(...args) {
176
+ return this._run("wait_idle", "fields", collect("wait_idle", args, [], ["ms", "timeout"]));
177
+ }
178
+ async waitIdle(...args) {
179
+ return this._run("wait_idle", "fields", collect("wait_idle", args, [], ["ms", "timeout"]));
180
+ }
181
+ async find(...args) {
182
+ return this._runElement("find", "value", collect("find", args, typeof args[0] === "string" ? ["by", "value"] : ["by"], ["nth", "timeout"]));
183
+ }
184
+ async find_all(...args) {
185
+ return this._runElement("find_all", "value", collect("find_all", args, typeof args[0] === "string" ? ["by", "value"] : ["by"], ["timeout"]));
186
+ }
187
+ async findAll(...args) {
188
+ return this._runElement("find_all", "value", collect("find_all", args, typeof args[0] === "string" ? ["by", "value"] : ["by"], ["timeout"]));
189
+ }
190
+ async get_text(...args) {
191
+ return this._run("get_text", "value", collect("get_text", args, typeof args[0] === "string" ? ["by", "value"] : ["by"], ["nth"]));
192
+ }
193
+ async getText(...args) {
194
+ return this._run("get_text", "value", collect("get_text", args, typeof args[0] === "string" ? ["by", "value"] : ["by"], ["nth"]));
195
+ }
196
+ async checked(...args) {
197
+ return this._run("checked", "value", collect("checked", args, typeof args[0] === "string" ? ["by", "value"] : ["by"], ["nth"]));
198
+ }
199
+ async enabled(...args) {
200
+ return this._run("enabled", "value", collect("enabled", args, typeof args[0] === "string" ? ["by", "value"] : ["by"], ["nth"]));
201
+ }
202
+ async selected(...args) {
203
+ return this._run("selected", "value", collect("selected", args, typeof args[0] === "string" ? ["by", "value"] : ["by"], ["nth"]));
204
+ }
205
+ async count(...args) {
206
+ return this._run("count", "value", collect("count", args, typeof args[0] === "string" ? ["by", "value"] : ["by"], []));
207
+ }
208
+ async which(...args) {
209
+ return this._run("which", "value", collect("which", args, ["candidates"], ["timeout"]));
210
+ }
211
+ async in_app(...args) {
212
+ return this._run("in_app", "value", collect("in_app", args, ["package"], []));
213
+ }
214
+ async inApp(...args) {
215
+ return this._run("in_app", "value", collect("in_app", args, ["package"], []));
216
+ }
217
+ async keyboard_shown(...args) {
218
+ return this._run("keyboard_shown", "value", collect("keyboard_shown", args, [], []));
219
+ }
220
+ async keyboardShown(...args) {
221
+ return this._run("keyboard_shown", "value", collect("keyboard_shown", args, [], []));
222
+ }
223
+ async last_toast(...args) {
224
+ return this._run("last_toast", "value", collect("last_toast", args, [], ["max_age"]));
225
+ }
226
+ async lastToast(...args) {
227
+ return this._run("last_toast", "value", collect("last_toast", args, [], ["max_age"]));
228
+ }
229
+ async color(...args) {
230
+ return this._run("color", "value", collect("color", args, ["x", "y"], []));
231
+ }
232
+ async screen_on(...args) {
233
+ return this._run("screen_on", "value", collect("screen_on", args, [], []));
234
+ }
235
+ async screenOn(...args) {
236
+ return this._run("screen_on", "value", collect("screen_on", args, [], []));
237
+ }
238
+ async locked(...args) {
239
+ return this._run("locked", "value", collect("locked", args, [], []));
240
+ }
241
+ async wake(...args) {
242
+ return this._run("wake", "fields", collect("wake", args, [], []));
243
+ }
244
+ async lock(...args) {
245
+ return this._run("lock", "none", collect("lock", args, [], []));
246
+ }
247
+ async battery(...args) {
248
+ return this._run("battery", "fields", collect("battery", args, [], []));
249
+ }
250
+ async orientation(...args) {
251
+ return this._run("orientation", "value", collect("orientation", args, [], []));
252
+ }
253
+ async info(...args) {
254
+ return this._run("info", "fields", collect("info", args, [], []));
255
+ }
256
+ async network(...args) {
257
+ return this._run("network", "fields", collect("network", args, [], []));
258
+ }
259
+ async launch(...args) {
260
+ return this._run("launch", "fields", collect("launch", args, ["package"], ["activity"]));
261
+ }
262
+ async open_url(...args) {
263
+ return this._run("open_url", "none", collect("open_url", args, ["url"], ["package"]));
264
+ }
265
+ async openUrl(...args) {
266
+ return this._run("open_url", "none", collect("open_url", args, ["url"], ["package"]));
267
+ }
268
+ async intent(...args) {
269
+ return this._run("intent", "none", collect("intent", args, ["action"], ["data", "package"]));
270
+ }
271
+ async clear_data(...args) {
272
+ return this._run("clear_data", "fields", collect("clear_data", args, ["package"], []));
273
+ }
274
+ async clearData(...args) {
275
+ return this._run("clear_data", "fields", collect("clear_data", args, ["package"], []));
276
+ }
277
+ async apps(...args) {
278
+ return this._run("apps", "value", collect("apps", args, [], ["system"]));
279
+ }
280
+ async installed(...args) {
281
+ return this._run("installed", "value", collect("installed", args, ["package"], []));
282
+ }
283
+ async back(...args) {
284
+ return this._run("back", "none", collect("back", args, [], []));
285
+ }
286
+ async home(...args) {
287
+ return this._run("home", "none", collect("home", args, [], []));
288
+ }
289
+ async recents(...args) {
290
+ return this._run("recents", "none", collect("recents", args, [], []));
291
+ }
292
+ async open_notifications(...args) {
293
+ return this._run("open_notifications", "none", collect("open_notifications", args, [], []));
294
+ }
295
+ async openNotifications(...args) {
296
+ return this._run("open_notifications", "none", collect("open_notifications", args, [], []));
297
+ }
298
+ async quick_settings(...args) {
299
+ return this._run("quick_settings", "none", collect("quick_settings", args, [], []));
300
+ }
301
+ async quickSettings(...args) {
302
+ return this._run("quick_settings", "none", collect("quick_settings", args, [], []));
303
+ }
304
+ async clipboard(...args) {
305
+ return this._run("clipboard", "value", collect("clipboard", args, [], ["text"]));
306
+ }
307
+ async batch(...args) {
308
+ return this._run("batch", "fields", collect("batch", args, ["steps"], ["stop_on_error", "cuts_network"]));
309
+ }
310
+ async proxy(...args) {
311
+ return this._run("proxy", "fields", collect("proxy", args, ["url"], ["app"]));
312
+ }
313
+ async proxy_check(...args) {
314
+ return this._run("proxy_check", "fields", collect("proxy_check", args, [], ["app"]));
315
+ }
316
+ async proxyCheck(...args) {
317
+ return this._run("proxy_check", "fields", collect("proxy_check", args, [], ["app"]));
318
+ }
319
+ async notifications(...args) {
320
+ return this._run("notifications", "value", collect("notifications", args, [], ["package"]));
321
+ }
322
+ async has_notification(...args) {
323
+ return this._run("has_notification", "value", collect("has_notification", args, typeof args[0] === "string" ? ["by", "value"] : ["by"], []));
324
+ }
325
+ async hasNotification(...args) {
326
+ return this._run("has_notification", "value", collect("has_notification", args, typeof args[0] === "string" ? ["by", "value"] : ["by"], []));
327
+ }
328
+ async wait_notification(...args) {
329
+ return this._run("wait_notification", "value", collect("wait_notification", args, typeof args[0] === "string" ? ["by", "value"] : ["by"], ["timeout", "package"]));
330
+ }
331
+ async waitNotification(...args) {
332
+ return this._run("wait_notification", "value", collect("wait_notification", args, typeof args[0] === "string" ? ["by", "value"] : ["by"], ["timeout", "package"]));
333
+ }
334
+ async notification_reply(...args) {
335
+ return this._run("notification_reply", "none", collect("notification_reply", args, ["key", "text"], []));
336
+ }
337
+ async notificationReply(...args) {
338
+ return this._run("notification_reply", "none", collect("notification_reply", args, ["key", "text"], []));
339
+ }
340
+ async notification_click(...args) {
341
+ return this._run("notification_click", "none", collect("notification_click", args, ["key"], []));
342
+ }
343
+ async notificationClick(...args) {
344
+ return this._run("notification_click", "none", collect("notification_click", args, ["key"], []));
345
+ }
346
+ async notification_dismiss(...args) {
347
+ return this._run("notification_dismiss", "none", collect("notification_dismiss", args, ["key"], []));
348
+ }
349
+ async notificationDismiss(...args) {
350
+ return this._run("notification_dismiss", "none", collect("notification_dismiss", args, ["key"], []));
351
+ }
352
+ async notify_filter(...args) {
353
+ return this._run("notify_filter", "value", collect("notify_filter", args, [], ["packages"]));
354
+ }
355
+ async notifyFilter(...args) {
356
+ return this._run("notify_filter", "value", collect("notify_filter", args, [], ["packages"]));
357
+ }
358
+ }
359
+ /** Commands the PC server answers itself. Droidline extends this and implements _run. */
360
+ export class ClientCommands extends EventEmitter {
361
+ async wait_notification(...args) {
362
+ return this._run("wait_notification", "value", collect("wait_notification", args, typeof args[0] === "string" ? ["by", "value"] : ["by"], ["timeout", "package"]));
363
+ }
364
+ async waitNotification(...args) {
365
+ return this._run("wait_notification", "value", collect("wait_notification", args, typeof args[0] === "string" ? ["by", "value"] : ["by"], ["timeout", "package"]));
366
+ }
367
+ async devices(...args) {
368
+ return this._run("devices", "value", collect("devices", args, [], []));
369
+ }
370
+ async pair(...args) {
371
+ return this._run("pair", "value", collect("pair", args, ["code"], ["name"]));
372
+ }
373
+ async pair_qr(...args) {
374
+ return this._run("pair_qr", "fields", collect("pair_qr", args, [], ["name"]));
375
+ }
376
+ async pairQr(...args) {
377
+ return this._run("pair_qr", "fields", collect("pair_qr", args, [], ["name"]));
378
+ }
379
+ async rename(...args) {
380
+ return this._run("rename", "value", collect("rename", args, ["device", "name"], []));
381
+ }
382
+ async revoke(...args) {
383
+ return this._run("revoke", "none", collect("revoke", args, ["device"], []));
384
+ }
385
+ async subscribe(...args) {
386
+ return this._run("subscribe", "none", collect("subscribe", args, ["events"], ["device"]));
387
+ }
388
+ async server_info(...args) {
389
+ return this._run("server_info", "fields", collect("server_info", args, [], []));
390
+ }
391
+ async serverInfo(...args) {
392
+ return this._run("server_info", "fields", collect("server_info", args, [], []));
393
+ }
394
+ async auth(...args) {
395
+ return this._run("auth", "none", collect("auth", args, ["token"], []));
396
+ }
397
+ }
@@ -0,0 +1,5 @@
1
+ export { connect, lease, Droidline, Device, LeasedDevice, DEFAULT_HOST, DEFAULT_PORT, type ClientOptions, type ConnectOptions, type HistoryEntry, type LeaseOptions, type NotificationCallback, type NotificationFilter, } from "./client.js";
2
+ export { DroidlineError, type ClientErrorCode } from "./errors.js";
3
+ export { Element } from "./element.js";
4
+ export type { Params } from "./args.js";
5
+ export * from "./generated.js";
package/dist/index.js ADDED
@@ -0,0 +1,4 @@
1
+ export { connect, lease, Droidline, Device, LeasedDevice, DEFAULT_HOST, DEFAULT_PORT, } from "./client.js";
2
+ export { DroidlineError } from "./errors.js";
3
+ export { Element } from "./element.js";
4
+ export * from "./generated.js";
@@ -0,0 +1,14 @@
1
+ import type { Device, HistoryEntry } from "./client.js";
2
+ /** The command log as text, one line per request. */
3
+ export declare function formatHistory(entries: readonly HistoryEntry[]): string;
4
+ /**
5
+ * Save a screenshot, the screen tree and the command log of a phone into folder, for a failed
6
+ * test. Each part is best effort: a phone that is offline still gets its command log.
7
+ * Resolves with the files written.
8
+ *
9
+ * @example
10
+ * afterEach(async (ctx) => {
11
+ * if (ctx.task.result?.state === "fail") await saveFailure(d, `artifacts/${ctx.task.name}`);
12
+ * });
13
+ */
14
+ export declare function saveFailure(device: Device, folder: string): Promise<string[]>;
@@ -0,0 +1,43 @@
1
+ import { mkdir, writeFile } from "node:fs/promises";
2
+ import { join } from "node:path";
3
+ /** The command log as text, one line per request. */
4
+ export function formatHistory(entries) {
5
+ return entries
6
+ .map((e) => {
7
+ const stamp = new Date(e.time).toTimeString().slice(0, 8);
8
+ return `${stamp} ${e.cmd} ${JSON.stringify(e.params)} -> ${e.ok ? "ok" : e.error} (${e.ms} ms)`;
9
+ })
10
+ .join("\n");
11
+ }
12
+ /**
13
+ * Save a screenshot, the screen tree and the command log of a phone into folder, for a failed
14
+ * test. Each part is best effort: a phone that is offline still gets its command log.
15
+ * Resolves with the files written.
16
+ *
17
+ * @example
18
+ * afterEach(async (ctx) => {
19
+ * if (ctx.task.result?.state === "fail") await saveFailure(d, `artifacts/${ctx.task.name}`);
20
+ * });
21
+ */
22
+ export async function saveFailure(device, folder) {
23
+ await mkdir(folder, { recursive: true });
24
+ const saved = [];
25
+ const log = join(folder, "commands.txt");
26
+ await writeFile(log, formatHistory(device.history) + "\n", "utf8");
27
+ saved.push(log);
28
+ const parts = [
29
+ ["screen.png", (p) => device.screenshot(p, { format: "png" })],
30
+ ["screen.json", (p) => device.dump(p)],
31
+ ];
32
+ for (const [name, take] of parts) {
33
+ const path = join(folder, name);
34
+ try {
35
+ await take(path);
36
+ saved.push(path);
37
+ }
38
+ catch {
39
+ // The phone may be offline or the screen protected; the log above is still useful.
40
+ }
41
+ }
42
+ return saved;
43
+ }
package/package.json CHANGED
@@ -1,6 +1,62 @@
1
1
  {
2
2
  "name": "droidline",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.1.2",
4
+ "description": "Control Android phones from Node.js without ADB or root: find elements, tap, type, take screenshots and read notifications.",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "main": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "import": "./dist/index.js"
13
+ },
14
+ "./testing": {
15
+ "types": "./dist/testing.d.ts",
16
+ "import": "./dist/testing.js"
17
+ },
18
+ "./package.json": "./package.json"
19
+ },
20
+ "files": [
21
+ "dist",
22
+ "README.md"
23
+ ],
24
+ "engines": {
25
+ "node": ">=18"
26
+ },
27
+ "scripts": {
28
+ "build": "tsc -p tsconfig.json",
29
+ "test": "tsc -p tsconfig.test.json && node --test build-test/test/client.test.js",
30
+ "prepublishOnly": "npm run build"
31
+ },
32
+ "keywords": [
33
+ "android",
34
+ "android-automation",
35
+ "mobile-automation",
36
+ "ui-automation",
37
+ "test-automation",
38
+ "testing",
39
+ "appium",
40
+ "uiautomator",
41
+ "adb",
42
+ "accessibility",
43
+ "rpa",
44
+ "device-farm",
45
+ "mcp",
46
+ "typescript",
47
+ "droidline"
48
+ ],
49
+ "homepage": "https://droidline.dev",
50
+ "repository": {
51
+ "type": "git",
52
+ "url": "git+https://github.com/KnifeLemon/Droidline.git",
53
+ "directory": "sdk/node"
54
+ },
55
+ "bugs": {
56
+ "url": "https://github.com/KnifeLemon/Droidline/issues"
57
+ },
58
+ "devDependencies": {
59
+ "@types/node": "^18.19.0",
60
+ "typescript": "^5.9.3"
61
+ }
62
+ }