gpt-driver-node 1.0.0-alpha.3 → 1.0.0-alpha.4

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/index.cjs ADDED
@@ -0,0 +1,202 @@
1
+ 'use strict';
2
+
3
+ var axios = require('axios');
4
+ var webdriverio = require('webdriverio');
5
+ var sharp = require('sharp');
6
+
7
+ const delay = async (milliseconds) => {
8
+ await new Promise((resolve) => setTimeout(resolve, milliseconds));
9
+ };
10
+ const getScreenshot = async (driver, deviceSize) => {
11
+ let screenshot = await driver.takeScreenshot();
12
+ if (driver.capabilities.platformName === "iOS") {
13
+ const imageBuffer = Buffer.from(screenshot, "base64");
14
+ const transformedImage = await sharp(imageBuffer).resize(deviceSize.width, deviceSize.height).toBuffer();
15
+ screenshot = transformedImage.toString("base64");
16
+ }
17
+ return screenshot;
18
+ };
19
+ const delayIfNeeded = async (lastResponseTime) => {
20
+ if (lastResponseTime == null) {
21
+ return;
22
+ }
23
+ const currentTime = Date.now();
24
+ const difference = currentTime - lastResponseTime;
25
+ if (difference < 1500) {
26
+ await delay(difference);
27
+ }
28
+ };
29
+
30
+ class GptDriver {
31
+ apiKey;
32
+ baseUrl;
33
+ driver;
34
+ deviceConfig;
35
+ firestoreSessionId;
36
+ deviceSize;
37
+ lastCommandExecutionTime;
38
+ constructor(config) {
39
+ this.apiKey = config.apiKey;
40
+ this.baseUrl = "https://api.mobileboost.io";
41
+ if (config.driver !== void 0) {
42
+ this.driver = config.driver;
43
+ } else if (config.deviceConfig !== void 0) {
44
+ this.deviceConfig = config.deviceConfig;
45
+ } else {
46
+ throw new Error("Either provide an appium driver or a deviceConfig object");
47
+ }
48
+ }
49
+ async startSession() {
50
+ console.log(">> Starting session...");
51
+ if (this.driver == void 0 && this.deviceConfig != void 0) {
52
+ const capabilities = {
53
+ platformName: this.deviceConfig.platform,
54
+ "appium:automationName": this.deviceConfig.platform === "Android" ? "UiAutomator2" : "XCUITest",
55
+ "appium:deviceName": this.deviceConfig.deviceName
56
+ };
57
+ const wdOpts = {
58
+ hostname: process.env.APPIUM_HOST || "localhost",
59
+ port: process.env.APPIUM_PORT != null ? parseInt(process.env.APPIUM_PORT, 10) : 4723,
60
+ logLevel: "error",
61
+ capabilities
62
+ };
63
+ this.driver = await webdriverio.remote(wdOpts);
64
+ }
65
+ const driver = this.driver;
66
+ const response = await axios.post(
67
+ `${this.baseUrl}/sessions/create`,
68
+ {
69
+ api_key: this.apiKey,
70
+ appium_session_id: driver.sessionId,
71
+ device_config: {
72
+ // @ts-ignore
73
+ platform: driver.capabilities.platformName,
74
+ // @ts-ignore
75
+ device: driver.capabilities.deviceName,
76
+ // @ts-ignore
77
+ os: driver.capabilities.platformVersion
78
+ }
79
+ }
80
+ );
81
+ const rect = await driver.getWindowRect();
82
+ this.deviceSize = { width: rect.width, height: rect.height };
83
+ if (response.data.sessionId) {
84
+ const sessionLink = `https://app.mobileboost.io/gpt-driver/sessions/${response.data.sessionId}`;
85
+ console.log(`>> Session created. Monitor execution at: ${sessionLink}`);
86
+ this.firestoreSessionId = response.data.sessionId;
87
+ }
88
+ }
89
+ async stopSession({ status }) {
90
+ console.log(">> Stopping session...");
91
+ await axios.post(
92
+ `${this.baseUrl}/sessions/${this.firestoreSessionId}/stop`,
93
+ {
94
+ api_key: this.apiKey,
95
+ status
96
+ }
97
+ );
98
+ console.log(">> Session stopped.");
99
+ this.firestoreSessionId = void 0;
100
+ }
101
+ async execute(command, appiumHandler) {
102
+ console.log(">> Executing command:", command);
103
+ const driver = this.driver;
104
+ if (appiumHandler != null) {
105
+ try {
106
+ await appiumHandler(driver);
107
+ } catch (e) {
108
+ await this.gptHandler(command);
109
+ }
110
+ } else {
111
+ await this.gptHandler(command);
112
+ }
113
+ }
114
+ async assert(assertions) {
115
+ console.log(">> Asserting:", assertions);
116
+ const driver = this.driver;
117
+ const screenshot = await getScreenshot(driver, this.deviceSize);
118
+ const response = await axios.post(
119
+ `${this.baseUrl}/sessions/${this.firestoreSessionId}/assert`,
120
+ {
121
+ api_key: this.apiKey,
122
+ base64_screenshot: screenshot,
123
+ assertions,
124
+ command: `Assert: ${JSON.stringify(assertions)}`
125
+ }
126
+ );
127
+ const results = response.data.results;
128
+ const failedAssertions = results.reduce((prev, current, currentIndex) => {
129
+ if (!current) {
130
+ return [...prev, assertions.at(currentIndex)];
131
+ }
132
+ return prev;
133
+ }, []);
134
+ if (failedAssertions.length > 0) {
135
+ throw new Error(`Failed assertions: ${failedAssertions.join(", ")}`);
136
+ }
137
+ }
138
+ async extract(extractions) {
139
+ console.log(">> Extracting:", extractions);
140
+ const driver = this.driver;
141
+ const screenshot = await getScreenshot(driver, this.deviceSize);
142
+ const response = await axios.post(
143
+ `${this.baseUrl}/sessions/${this.firestoreSessionId}/extract`,
144
+ {
145
+ api_key: this.apiKey,
146
+ base64_screenshot: screenshot,
147
+ extractions,
148
+ command: `Extract: ${JSON.stringify(extractions)}`
149
+ }
150
+ );
151
+ return response.data.results;
152
+ }
153
+ async gptHandler(command) {
154
+ const driver = this.driver;
155
+ try {
156
+ let conditionSucceeded = false;
157
+ while (!conditionSucceeded) {
158
+ await delayIfNeeded(this.lastCommandExecutionTime);
159
+ const screenshot = await getScreenshot(driver, this.deviceSize);
160
+ console.log(">> Waiting for GTPDriver response...");
161
+ const response = await axios.request(
162
+ {
163
+ url: `${this.baseUrl}/sessions/${this.firestoreSessionId}/execute`,
164
+ method: "POST",
165
+ data: {
166
+ api_key: this.apiKey,
167
+ command,
168
+ base64_screenshot: screenshot
169
+ }
170
+ }
171
+ );
172
+ const executeStatus = response.data.status;
173
+ if (executeStatus === "failed") {
174
+ throw new Error("Execution failed");
175
+ }
176
+ conditionSucceeded = executeStatus !== "inProgress";
177
+ const executeResponse = response.data;
178
+ for (const command2 of executeResponse.commands) {
179
+ await this.executeCommand(command2);
180
+ }
181
+ this.lastCommandExecutionTime = Date.now();
182
+ }
183
+ } catch (e) {
184
+ await this.stopSession({ status: "failed" });
185
+ throw e;
186
+ }
187
+ }
188
+ async executeCommand(command) {
189
+ const firstAction = command.data.actions?.at(0);
190
+ if (firstAction?.type === "pause" && firstAction.duration != null) {
191
+ await delay(firstAction * 1e3);
192
+ } else {
193
+ await axios.request({
194
+ url: command.url,
195
+ method: command.method,
196
+ data: command.data
197
+ });
198
+ }
199
+ }
200
+ }
201
+
202
+ module.exports = GptDriver;
@@ -0,0 +1,39 @@
1
+ interface AppiumHandler {
2
+ (driver: WebdriverIO.Browser): Promise<any>;
3
+ }
4
+ interface DeviceConfig {
5
+ platform: "iOS" | "Android";
6
+ deviceName?: string;
7
+ platformVersion?: string;
8
+ }
9
+ interface GptDriverConfig {
10
+ apiKey: string;
11
+ driver?: WebdriverIO.Browser;
12
+ deviceConfig?: DeviceConfig;
13
+ }
14
+ interface DeviceSize {
15
+ width: number;
16
+ height: number;
17
+ }
18
+
19
+ declare class GptDriver {
20
+ apiKey: string;
21
+ baseUrl: string;
22
+ driver?: WebdriverIO.Browser;
23
+ deviceConfig?: DeviceConfig;
24
+ firestoreSessionId?: string;
25
+ deviceSize?: DeviceSize;
26
+ lastCommandExecutionTime?: number;
27
+ constructor(config: GptDriverConfig);
28
+ startSession(): Promise<void>;
29
+ stopSession({ status }: {
30
+ status: "failed" | "success";
31
+ }): Promise<void>;
32
+ execute(command: string, appiumHandler?: AppiumHandler): Promise<void>;
33
+ assert(assertions: string[]): Promise<void>;
34
+ extract(extractions: string[]): Promise<string[]>;
35
+ private gptHandler;
36
+ private executeCommand;
37
+ }
38
+
39
+ export { GptDriver as default };
package/dist/index.mjs ADDED
@@ -0,0 +1,200 @@
1
+ import axios from 'axios';
2
+ import { remote } from 'webdriverio';
3
+ import sharp from 'sharp';
4
+
5
+ const delay = async (milliseconds) => {
6
+ await new Promise((resolve) => setTimeout(resolve, milliseconds));
7
+ };
8
+ const getScreenshot = async (driver, deviceSize) => {
9
+ let screenshot = await driver.takeScreenshot();
10
+ if (driver.capabilities.platformName === "iOS") {
11
+ const imageBuffer = Buffer.from(screenshot, "base64");
12
+ const transformedImage = await sharp(imageBuffer).resize(deviceSize.width, deviceSize.height).toBuffer();
13
+ screenshot = transformedImage.toString("base64");
14
+ }
15
+ return screenshot;
16
+ };
17
+ const delayIfNeeded = async (lastResponseTime) => {
18
+ if (lastResponseTime == null) {
19
+ return;
20
+ }
21
+ const currentTime = Date.now();
22
+ const difference = currentTime - lastResponseTime;
23
+ if (difference < 1500) {
24
+ await delay(difference);
25
+ }
26
+ };
27
+
28
+ class GptDriver {
29
+ apiKey;
30
+ baseUrl;
31
+ driver;
32
+ deviceConfig;
33
+ firestoreSessionId;
34
+ deviceSize;
35
+ lastCommandExecutionTime;
36
+ constructor(config) {
37
+ this.apiKey = config.apiKey;
38
+ this.baseUrl = "https://api.mobileboost.io";
39
+ if (config.driver !== void 0) {
40
+ this.driver = config.driver;
41
+ } else if (config.deviceConfig !== void 0) {
42
+ this.deviceConfig = config.deviceConfig;
43
+ } else {
44
+ throw new Error("Either provide an appium driver or a deviceConfig object");
45
+ }
46
+ }
47
+ async startSession() {
48
+ console.log(">> Starting session...");
49
+ if (this.driver == void 0 && this.deviceConfig != void 0) {
50
+ const capabilities = {
51
+ platformName: this.deviceConfig.platform,
52
+ "appium:automationName": this.deviceConfig.platform === "Android" ? "UiAutomator2" : "XCUITest",
53
+ "appium:deviceName": this.deviceConfig.deviceName
54
+ };
55
+ const wdOpts = {
56
+ hostname: process.env.APPIUM_HOST || "localhost",
57
+ port: process.env.APPIUM_PORT != null ? parseInt(process.env.APPIUM_PORT, 10) : 4723,
58
+ logLevel: "error",
59
+ capabilities
60
+ };
61
+ this.driver = await remote(wdOpts);
62
+ }
63
+ const driver = this.driver;
64
+ const response = await axios.post(
65
+ `${this.baseUrl}/sessions/create`,
66
+ {
67
+ api_key: this.apiKey,
68
+ appium_session_id: driver.sessionId,
69
+ device_config: {
70
+ // @ts-ignore
71
+ platform: driver.capabilities.platformName,
72
+ // @ts-ignore
73
+ device: driver.capabilities.deviceName,
74
+ // @ts-ignore
75
+ os: driver.capabilities.platformVersion
76
+ }
77
+ }
78
+ );
79
+ const rect = await driver.getWindowRect();
80
+ this.deviceSize = { width: rect.width, height: rect.height };
81
+ if (response.data.sessionId) {
82
+ const sessionLink = `https://app.mobileboost.io/gpt-driver/sessions/${response.data.sessionId}`;
83
+ console.log(`>> Session created. Monitor execution at: ${sessionLink}`);
84
+ this.firestoreSessionId = response.data.sessionId;
85
+ }
86
+ }
87
+ async stopSession({ status }) {
88
+ console.log(">> Stopping session...");
89
+ await axios.post(
90
+ `${this.baseUrl}/sessions/${this.firestoreSessionId}/stop`,
91
+ {
92
+ api_key: this.apiKey,
93
+ status
94
+ }
95
+ );
96
+ console.log(">> Session stopped.");
97
+ this.firestoreSessionId = void 0;
98
+ }
99
+ async execute(command, appiumHandler) {
100
+ console.log(">> Executing command:", command);
101
+ const driver = this.driver;
102
+ if (appiumHandler != null) {
103
+ try {
104
+ await appiumHandler(driver);
105
+ } catch (e) {
106
+ await this.gptHandler(command);
107
+ }
108
+ } else {
109
+ await this.gptHandler(command);
110
+ }
111
+ }
112
+ async assert(assertions) {
113
+ console.log(">> Asserting:", assertions);
114
+ const driver = this.driver;
115
+ const screenshot = await getScreenshot(driver, this.deviceSize);
116
+ const response = await axios.post(
117
+ `${this.baseUrl}/sessions/${this.firestoreSessionId}/assert`,
118
+ {
119
+ api_key: this.apiKey,
120
+ base64_screenshot: screenshot,
121
+ assertions,
122
+ command: `Assert: ${JSON.stringify(assertions)}`
123
+ }
124
+ );
125
+ const results = response.data.results;
126
+ const failedAssertions = results.reduce((prev, current, currentIndex) => {
127
+ if (!current) {
128
+ return [...prev, assertions.at(currentIndex)];
129
+ }
130
+ return prev;
131
+ }, []);
132
+ if (failedAssertions.length > 0) {
133
+ throw new Error(`Failed assertions: ${failedAssertions.join(", ")}`);
134
+ }
135
+ }
136
+ async extract(extractions) {
137
+ console.log(">> Extracting:", extractions);
138
+ const driver = this.driver;
139
+ const screenshot = await getScreenshot(driver, this.deviceSize);
140
+ const response = await axios.post(
141
+ `${this.baseUrl}/sessions/${this.firestoreSessionId}/extract`,
142
+ {
143
+ api_key: this.apiKey,
144
+ base64_screenshot: screenshot,
145
+ extractions,
146
+ command: `Extract: ${JSON.stringify(extractions)}`
147
+ }
148
+ );
149
+ return response.data.results;
150
+ }
151
+ async gptHandler(command) {
152
+ const driver = this.driver;
153
+ try {
154
+ let conditionSucceeded = false;
155
+ while (!conditionSucceeded) {
156
+ await delayIfNeeded(this.lastCommandExecutionTime);
157
+ const screenshot = await getScreenshot(driver, this.deviceSize);
158
+ console.log(">> Waiting for GTPDriver response...");
159
+ const response = await axios.request(
160
+ {
161
+ url: `${this.baseUrl}/sessions/${this.firestoreSessionId}/execute`,
162
+ method: "POST",
163
+ data: {
164
+ api_key: this.apiKey,
165
+ command,
166
+ base64_screenshot: screenshot
167
+ }
168
+ }
169
+ );
170
+ const executeStatus = response.data.status;
171
+ if (executeStatus === "failed") {
172
+ throw new Error("Execution failed");
173
+ }
174
+ conditionSucceeded = executeStatus !== "inProgress";
175
+ const executeResponse = response.data;
176
+ for (const command2 of executeResponse.commands) {
177
+ await this.executeCommand(command2);
178
+ }
179
+ this.lastCommandExecutionTime = Date.now();
180
+ }
181
+ } catch (e) {
182
+ await this.stopSession({ status: "failed" });
183
+ throw e;
184
+ }
185
+ }
186
+ async executeCommand(command) {
187
+ const firstAction = command.data.actions?.at(0);
188
+ if (firstAction?.type === "pause" && firstAction.duration != null) {
189
+ await delay(firstAction * 1e3);
190
+ } else {
191
+ await axios.request({
192
+ url: command.url,
193
+ method: command.method,
194
+ data: command.data
195
+ });
196
+ }
197
+ }
198
+ }
199
+
200
+ export { GptDriver as default };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gpt-driver-node",
3
- "version": "1.0.0-alpha.3",
3
+ "version": "1.0.0-alpha.4",
4
4
  "main": "./dist/index.cjs",
5
5
  "module": "./dist/index.mjs",
6
6
  "types": "./dist/index.d.cts",