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 +202 -0
- package/dist/index.d.cts +39 -0
- package/dist/index.mjs +200 -0
- package/package.json +1 -1
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;
|
package/dist/index.d.cts
ADDED
|
@@ -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 };
|