@alumnium/maestro 0.0.0-stage → 0.24.0-alpha.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.md ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2024 Alexey Rodionov, Tatiana Shepeleva
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,3 @@
1
- # Temporary Holding Version
1
+ # @alumnium/maestro
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Alumnium Maestro driver. See [the main `alumnium` package page](https://www.npmjs.com/package/alumnium) for more details.
package/package.json CHANGED
@@ -1,6 +1,42 @@
1
1
  {
2
2
  "name": "@alumnium/maestro",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.24.0-alpha.2",
4
+ "description": "Alumnium Maestro driver",
5
+ "license": "MIT",
6
+ "author": "Alex Rodionov <p0deje@gmail.com>",
7
+ "contributors": [
8
+ "Alex Rodionov <p0deje@gmail.com>",
9
+ "Sasha Koss <koss@nocorp.me>",
10
+ "Tatiana Shepeleva <tati.shep@gmail.com>"
11
+ ],
12
+ "repository": {
13
+ "type": "git",
14
+ "url": "https://github.com/alumnium-hq/alumnium.git"
15
+ },
16
+ "files": [
17
+ "src",
18
+ "README.md",
19
+ "LICENSE.md"
20
+ ],
21
+ "type": "module",
22
+ "exports": {
23
+ ".": {
24
+ "import": {
25
+ "types": "./src/index.d.ts",
26
+ "default": "./src/index.js"
27
+ },
28
+ "require": {
29
+ "types": "./src/index.d.ts",
30
+ "default": "./src/index.cjs"
31
+ }
32
+ }
33
+ },
34
+ "dependencies": {
35
+ "@alumnium/core": "0.24.0-alpha.2",
36
+ "@modelcontextprotocol/sdk": "^1.29.0",
37
+ "alwaysly": "^0.4.0",
38
+ "domhandler": "^6.0.0",
39
+ "smollit": "^0.2.0",
40
+ "zod": "^4.4.1"
41
+ }
42
+ }
@@ -0,0 +1,16 @@
1
+ import { Alumni as BaseAlumni } from "@alumnium/core";
2
+ import { MaestroDriver } from "./drivers/MaestroDriver.ts";
3
+ import { MaestroSession } from "./drivers/MaestroSession.ts";
4
+ /** Alumni driving a `MaestroSession`. */
5
+ export declare class Alumni extends BaseAlumni<MaestroDriver> {
6
+ constructor(driver: MaestroSession, options?: Alumni.Options);
7
+ /** Whether the given object is a native driver this Alumni accepts. */
8
+ static accepts(driver: unknown): driver is Alumni.Driver;
9
+ }
10
+ export declare namespace Alumni {
11
+ type Driver = MaestroSession;
12
+ type Options = BaseAlumni.Options;
13
+ type VisionOptions = BaseAlumni.VisionOptions;
14
+ type CheckOptions = BaseAlumni.CheckOptions;
15
+ type CheckAssert = BaseAlumni.CheckAssert;
16
+ }
@@ -0,0 +1,34 @@
1
+ import {
2
+ type AccessibilityElement,
3
+ BaseAccessibilityTree,
4
+ } from "@alumnium/core/internal";
5
+ import type { MaestroSession } from "../drivers/MaestroSession.ts";
6
+ export declare namespace MaestroAccessibilityTree {
7
+ /** Role names inferred from the attributes Maestro provides. */
8
+ type Role = "TextField" | "CheckBox" | "Button" | "Text" | "View";
9
+ }
10
+ /**
11
+ * Maestro's view hierarchy, rendered as XML with `raw_id` markers.
12
+ *
13
+ * Maestro normalizes iOS and Android into one flat shape of `{attributes, children}`, which means
14
+ * it drops the platform element type — there is no `XCUIElementTypeButton` or
15
+ * `android.widget.TextView` to key off. Roles are therefore inferred from the attributes that do
16
+ * survive, and most nodes end up as the generic `View` that the server tree collapses away.
17
+ */
18
+ export declare class MaestroAccessibilityTree extends BaseAccessibilityTree<MaestroSession.Hierarchy> {
19
+ #private;
20
+ protected get kind(): string;
21
+ constructor(hierarchy: MaestroSession.Hierarchy);
22
+ /** Renders the hierarchy as XML, stamping `raw_id` on every node. */
23
+ toStr(): string;
24
+ /** True when any node in the hierarchy carries the given `resource-id`. */
25
+ hasResourceId(resourceId: string): boolean;
26
+ elementById(rawId: number): AccessibilityElement;
27
+ /** Scopes the tree to the subtree rooted at `rawId`. */
28
+ scopeToArea(rawId: number): MaestroAccessibilityTree;
29
+ /** Parses the centre point of a Maestro `[x1,y1][x2,y2]` bounds string. */
30
+ static centreOf(bounds: string): {
31
+ x: number;
32
+ y: number;
33
+ };
34
+ }
@@ -0,0 +1,50 @@
1
+ import {
2
+ AppId,
3
+ type BaseAccessibilityTree,
4
+ BaseDriver,
5
+ type Driver,
6
+ type Keys,
7
+ type ToolClass,
8
+ } from "@alumnium/core/internal";
9
+ import { MaestroSession } from "./MaestroSession.ts";
10
+ export declare class MaestroDriver extends BaseDriver<never> {
11
+ #private;
12
+ kind: "maestro";
13
+ get platform(): Driver.MobileOs;
14
+ supportedTools: Set<ToolClass>;
15
+ delay: number;
16
+ hideKeyboardAfterTyping: boolean;
17
+ readonly session: MaestroSession;
18
+ constructor(session: MaestroSession);
19
+ protected fetchAccessibilityTree(): Promise<BaseAccessibilityTree>;
20
+ click(id: number): Promise<void>;
21
+ type(id: number, text: string): Promise<void>;
22
+ pressKey(key: Keys.Key): Promise<void>;
23
+ back(): Promise<void>;
24
+ visit(url: string): Promise<void>;
25
+ screenshot(): Promise<string>;
26
+ app(): AppId;
27
+ title(): string;
28
+ url(): string;
29
+ wait(seconds: number): Promise<void>;
30
+ quit(): Promise<void>;
31
+ /** Maestro has no programmatic access to elements. */
32
+ findElement(): Promise<never>;
33
+ dragSlider(): void;
34
+ dragAndDrop(): Promise<void>;
35
+ scrollTo(): Promise<void>;
36
+ executeScript(): Promise<void>;
37
+ switchToNextTab(): Promise<void>;
38
+ switchToPreviousTab(): Promise<void>;
39
+ waitForSelector(): Promise<void>;
40
+ printToPdf(): Promise<void>;
41
+ checkNavigationPolicy(_url?: string): void;
42
+ /**
43
+ * Resolves a raw id to the tap target it would produce, so the tree drill can report which ids
44
+ * are actionable. Maestro has no locators, so the bounds string stands in for one.
45
+ */
46
+ protected devDrillProbeTree(
47
+ tree: BaseAccessibilityTree,
48
+ rawId: number,
49
+ ): Promise<string>;
50
+ }
@@ -0,0 +1,91 @@
1
+ import z from "zod";
2
+ export declare namespace MaestroSession {
3
+ type Os = z.infer<typeof MaestroSession.Os>;
4
+ interface Node {
5
+ [key: string]: unknown;
6
+ }
7
+ interface Hierarchy {
8
+ ui_schema: {
9
+ platform?: string;
10
+ abbreviations: Record<string, string>;
11
+ defaults: Record<string, unknown>;
12
+ };
13
+ elements: Node[];
14
+ }
15
+ interface Device {
16
+ device_id: string;
17
+ name: string;
18
+ platform: string;
19
+ type: string;
20
+ connected: boolean;
21
+ }
22
+ interface Props {
23
+ /** Application identifier, e.g. an iOS bundle id: `com.ayodeji.TodoList`. */
24
+ appId: string;
25
+ /** Maestro device id, i.e. a simulator UDID. Defaults to the first connected device. */
26
+ deviceId?: string | undefined;
27
+ /** Hide the macOS Simulator window. Defaults to false. iOS simulators only. */
28
+ headless?: boolean | undefined;
29
+ /** Path to the `maestro` executable. Defaults to `~/.maestro/bin/maestro`. */
30
+ executablePath?: string | undefined;
31
+ /** Arguments the app is launched with. */
32
+ launchArgs?: string[] | undefined;
33
+ /**
34
+ * Environment the app is launched with. Maestro's own `launchApp` cannot set one, so a session
35
+ * that needs it is launched through `simctl` instead — see `launchApp`. iOS simulators only.
36
+ */
37
+ launchEnv?: Record<string, string> | undefined;
38
+ }
39
+ interface LaunchAppOptions {
40
+ clearState?: boolean;
41
+ }
42
+ /** A single content item of an MCP tool result. */
43
+ interface Content {
44
+ type: string;
45
+ text?: string;
46
+ data?: string;
47
+ mimeType?: string;
48
+ }
49
+ }
50
+ /**
51
+ * A live Maestro session, driven through Maestro's own MCP server (`maestro mcp`) over stdio.
52
+ */
53
+ export declare class MaestroSession {
54
+ #private;
55
+ static Os: z.ZodEnum<{
56
+ android: "android";
57
+ ios: "ios";
58
+ }>;
59
+ /** Marks the object for `Alumni`'s duck-typed driver detection. */
60
+ readonly isMaestroSession: true;
61
+ readonly appId: string;
62
+ /** Upper bound for one Maestro MCP tool call. */
63
+ static readonly TOOL_TIMEOUT_MS: number;
64
+ constructor(props: MaestroSession.Props);
65
+ get deviceId(): string;
66
+ get os(): MaestroSession.Os;
67
+ static start(props: MaestroSession.Props): Promise<MaestroSession>;
68
+ inspectScreen(): Promise<MaestroSession.Hierarchy>;
69
+ /**
70
+ * Runs Maestro flow commands against the running app.
71
+ *
72
+ * The commands are wrapped in a flow with an `appId` header. Maestro does not relaunch or reset
73
+ * the app for a flow that has no explicit `launchApp`, so consecutive calls act on the state the
74
+ * previous one left behind.
75
+ *
76
+ * @param commands Flow body in Maestro YAML, e.g. `- tapOn:\n point: "10,20"`.
77
+ */
78
+ run(commands: string): Promise<void>;
79
+ /**
80
+ * Launches the app, optionally wiping its state first.
81
+ *
82
+ * With a launch environment or arguments, the launch goes through `simctl` rather than Maestro:
83
+ * Maestro's `launchApp` can pass neither, and an app that reads its configuration from the
84
+ * environment only sees it on a launch that actually sets one. Maestro then drives whatever is on
85
+ * screen, which is the app this just started — its flows never relaunch on their own.
86
+ */
87
+ launchApp(options?: MaestroSession.LaunchAppOptions): Promise<void>;
88
+ /** Takes a screenshot and returns it base64-encoded. */
89
+ screenshot(): Promise<string>;
90
+ close(): Promise<void>;
91
+ }