misoai-web 1.0.3 → 1.0.5
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/README.md +349 -5
- package/dist/es/agent.js +2676 -0
- package/dist/es/agent.js.map +1 -0
- package/dist/es/bridge-mode-browser.js +955 -0
- package/dist/es/bridge-mode-browser.js.map +1 -0
- package/dist/es/bridge-mode.js +3037 -0
- package/dist/es/bridge-mode.js.map +1 -0
- package/dist/es/chrome-extension.js +3424 -0
- package/dist/es/chrome-extension.js.map +1 -0
- package/dist/es/index.js +3292 -0
- package/dist/es/index.js.map +1 -0
- package/dist/es/midscene-playground.js +3012 -0
- package/dist/es/midscene-playground.js.map +1 -0
- package/dist/es/midscene-server.js +247 -0
- package/dist/es/midscene-server.js.map +1 -0
- package/dist/es/playground.js +2783 -0
- package/dist/es/playground.js.map +1 -0
- package/dist/es/playwright-report.js +120 -0
- package/dist/es/playwright-report.js.map +1 -0
- package/dist/es/playwright.js +3237 -0
- package/dist/es/playwright.js.map +1 -0
- package/dist/es/puppeteer-agent-launcher.js +3187 -0
- package/dist/es/puppeteer-agent-launcher.js.map +1 -0
- package/dist/es/puppeteer.js +3034 -0
- package/dist/es/puppeteer.js.map +1 -0
- package/dist/es/ui-utils.js +106 -0
- package/dist/es/ui-utils.js.map +1 -0
- package/dist/es/utils.js +197 -0
- package/dist/es/utils.js.map +1 -0
- package/dist/es/yaml.js +351 -0
- package/dist/es/yaml.js.map +1 -0
- package/dist/lib/agent.js +2691 -0
- package/dist/lib/agent.js.map +1 -0
- package/dist/lib/bridge-mode-browser.js +989 -0
- package/dist/lib/bridge-mode-browser.js.map +1 -0
- package/dist/lib/bridge-mode.js +3057 -0
- package/dist/lib/bridge-mode.js.map +1 -0
- package/dist/lib/chrome-extension.js +3441 -0
- package/dist/lib/chrome-extension.js.map +1 -0
- package/dist/lib/index.js +3308 -0
- package/dist/lib/index.js.map +1 -0
- package/dist/lib/midscene-playground.js +3016 -0
- package/dist/lib/midscene-playground.js.map +1 -0
- package/dist/lib/midscene-server.js +273 -0
- package/dist/lib/midscene-server.js.map +1 -0
- package/dist/lib/playground.js +2802 -0
- package/dist/lib/playground.js.map +1 -0
- package/dist/lib/playwright-report.js +148 -0
- package/dist/lib/playwright-report.js.map +1 -0
- package/dist/lib/playwright.js +3254 -0
- package/dist/lib/playwright.js.map +1 -0
- package/dist/lib/puppeteer-agent-launcher.js +3200 -0
- package/dist/lib/puppeteer-agent-launcher.js.map +1 -0
- package/dist/lib/puppeteer.js +3045 -0
- package/dist/lib/puppeteer.js.map +1 -0
- package/dist/lib/ui-utils.js +137 -0
- package/dist/lib/ui-utils.js.map +1 -0
- package/dist/lib/utils.js +235 -0
- package/dist/lib/utils.js.map +1 -0
- package/dist/lib/yaml.js +390 -0
- package/dist/lib/yaml.js.map +1 -0
- package/dist/types/agent.d.ts +290 -0
- package/dist/types/bridge-mode-browser.d.ts +9 -0
- package/dist/types/bridge-mode.d.ts +40 -0
- package/dist/types/browser-a1877d18.d.ts +37 -0
- package/dist/types/chrome-extension.d.ts +18 -0
- package/dist/types/index.d.ts +16 -0
- package/dist/types/midscene-playground.d.ts +2 -0
- package/dist/types/midscene-server.d.ts +31 -0
- package/dist/types/page-663ece08.d.ts +333 -0
- package/dist/types/playground.d.ts +17 -0
- package/dist/types/playwright-report.d.ts +11 -0
- package/dist/types/playwright.d.ts +87 -0
- package/dist/types/puppeteer-agent-launcher.d.ts +40 -0
- package/dist/types/puppeteer.d.ts +17 -0
- package/dist/types/ui-utils.d.ts +14 -0
- package/dist/types/utils-badc824e.d.ts +34 -0
- package/dist/types/utils.d.ts +8 -0
- package/dist/types/yaml.d.ts +15 -0
- package/iife-script/htmlElement.js +99 -37
- package/iife-script/htmlElementDebug.js +92 -9
- package/package.json +2 -2
package/dist/es/yaml.js
ADDED
@@ -0,0 +1,351 @@
|
|
1
|
+
// src/yaml/player.ts
|
2
|
+
import { existsSync, mkdirSync, writeFileSync } from "fs";
|
3
|
+
import { dirname, join, resolve } from "path";
|
4
|
+
import { assert, ifInBrowser } from "misoai-shared/utils";
|
5
|
+
import { getMidsceneRunSubDir } from "misoai-shared/common";
|
6
|
+
var ScriptPlayer = class {
|
7
|
+
constructor(script, setupAgent, onTaskStatusChange) {
|
8
|
+
this.script = script;
|
9
|
+
this.setupAgent = setupAgent;
|
10
|
+
this.onTaskStatusChange = onTaskStatusChange;
|
11
|
+
this.taskStatusList = [];
|
12
|
+
this.status = "init";
|
13
|
+
this.unnamedResultIndex = 0;
|
14
|
+
this.pageAgent = null;
|
15
|
+
this.result = {};
|
16
|
+
if (ifInBrowser) {
|
17
|
+
this.output = void 0;
|
18
|
+
} else if (script.target?.output) {
|
19
|
+
this.output = resolve(process.cwd(), script.target.output);
|
20
|
+
} else {
|
21
|
+
this.output = join(getMidsceneRunSubDir("output"), `${process.pid}.json`);
|
22
|
+
}
|
23
|
+
this.taskStatusList = (script.tasks || []).map((task, taskIndex) => ({
|
24
|
+
...task,
|
25
|
+
index: taskIndex,
|
26
|
+
status: "init",
|
27
|
+
totalSteps: task.flow?.length || 0
|
28
|
+
}));
|
29
|
+
}
|
30
|
+
setResult(key, value) {
|
31
|
+
const keyToUse = key || this.unnamedResultIndex++;
|
32
|
+
if (this.result[keyToUse]) {
|
33
|
+
console.warn(`result key ${keyToUse} already exists, will overwrite`);
|
34
|
+
}
|
35
|
+
this.result[keyToUse] = value;
|
36
|
+
this.flushResult();
|
37
|
+
}
|
38
|
+
setPlayerStatus(status, error) {
|
39
|
+
this.status = status;
|
40
|
+
this.errorInSetup = error;
|
41
|
+
}
|
42
|
+
notifyCurrentTaskStatusChange(taskIndex) {
|
43
|
+
const taskIndexToNotify = typeof taskIndex === "number" ? taskIndex : this.currentTaskIndex;
|
44
|
+
if (typeof taskIndexToNotify !== "number") {
|
45
|
+
return;
|
46
|
+
}
|
47
|
+
const taskStatus = this.taskStatusList[taskIndexToNotify];
|
48
|
+
if (this.onTaskStatusChange) {
|
49
|
+
this.onTaskStatusChange(taskStatus);
|
50
|
+
}
|
51
|
+
}
|
52
|
+
async setTaskStatus(index, statusValue, error) {
|
53
|
+
this.taskStatusList[index].status = statusValue;
|
54
|
+
if (error) {
|
55
|
+
this.taskStatusList[index].error = error;
|
56
|
+
}
|
57
|
+
this.notifyCurrentTaskStatusChange(index);
|
58
|
+
}
|
59
|
+
setTaskIndex(taskIndex) {
|
60
|
+
this.currentTaskIndex = taskIndex;
|
61
|
+
}
|
62
|
+
flushResult() {
|
63
|
+
if (Object.keys(this.result).length && this.output) {
|
64
|
+
const output = resolve(process.cwd(), this.output);
|
65
|
+
const outputDir = dirname(output);
|
66
|
+
if (!existsSync(outputDir)) {
|
67
|
+
mkdirSync(outputDir, { recursive: true });
|
68
|
+
}
|
69
|
+
writeFileSync(output, JSON.stringify(this.result, void 0, 2));
|
70
|
+
}
|
71
|
+
}
|
72
|
+
async playTask(taskStatus, agent) {
|
73
|
+
const { flow } = taskStatus;
|
74
|
+
assert(flow, "missing flow in task");
|
75
|
+
for (const flowItemIndex in flow) {
|
76
|
+
const currentStep = Number.parseInt(flowItemIndex, 10);
|
77
|
+
taskStatus.currentStep = currentStep;
|
78
|
+
const flowItem = flow[flowItemIndex];
|
79
|
+
if ("aiAction" in flowItem || "ai" in flowItem) {
|
80
|
+
const actionTask = flowItem;
|
81
|
+
const prompt = actionTask.aiAction || actionTask.ai;
|
82
|
+
assert(prompt, "missing prompt for ai (aiAction)");
|
83
|
+
assert(
|
84
|
+
typeof prompt === "string",
|
85
|
+
"prompt for aiAction must be a string"
|
86
|
+
);
|
87
|
+
await agent.aiAction(prompt, {
|
88
|
+
cacheable: actionTask.cacheable
|
89
|
+
});
|
90
|
+
} else if ("aiAssert" in flowItem) {
|
91
|
+
const assertTask = flowItem;
|
92
|
+
const prompt = assertTask.aiAssert;
|
93
|
+
assert(prompt, "missing prompt for aiAssert");
|
94
|
+
assert(
|
95
|
+
typeof prompt === "string",
|
96
|
+
"prompt for aiAssert must be a string"
|
97
|
+
);
|
98
|
+
await agent.aiAssert(prompt);
|
99
|
+
} else if ("aiQuery" in flowItem) {
|
100
|
+
const queryTask = flowItem;
|
101
|
+
const prompt = queryTask.aiQuery;
|
102
|
+
assert(prompt, "missing prompt for aiQuery");
|
103
|
+
assert(
|
104
|
+
typeof prompt === "string",
|
105
|
+
"prompt for aiQuery must be a string"
|
106
|
+
);
|
107
|
+
const queryResult = await agent.aiQuery(prompt);
|
108
|
+
this.setResult(queryTask.name, queryResult);
|
109
|
+
} else if ("aiNumber" in flowItem) {
|
110
|
+
const numberTask = flowItem;
|
111
|
+
const prompt = numberTask.aiNumber;
|
112
|
+
assert(prompt, "missing prompt for number");
|
113
|
+
assert(
|
114
|
+
typeof prompt === "string",
|
115
|
+
"prompt for number must be a string"
|
116
|
+
);
|
117
|
+
const numberResult = await agent.aiNumber(prompt);
|
118
|
+
this.setResult(numberTask.name, numberResult);
|
119
|
+
} else if ("aiString" in flowItem) {
|
120
|
+
const stringTask = flowItem;
|
121
|
+
const prompt = stringTask.aiString;
|
122
|
+
assert(prompt, "missing prompt for string");
|
123
|
+
assert(
|
124
|
+
typeof prompt === "string",
|
125
|
+
"prompt for string must be a string"
|
126
|
+
);
|
127
|
+
const stringResult = await agent.aiString(prompt);
|
128
|
+
this.setResult(stringTask.name, stringResult);
|
129
|
+
} else if ("aiBoolean" in flowItem) {
|
130
|
+
const booleanTask = flowItem;
|
131
|
+
const prompt = booleanTask.aiBoolean;
|
132
|
+
assert(prompt, "missing prompt for boolean");
|
133
|
+
assert(
|
134
|
+
typeof prompt === "string",
|
135
|
+
"prompt for boolean must be a string"
|
136
|
+
);
|
137
|
+
const booleanResult = await agent.aiBoolean(prompt);
|
138
|
+
this.setResult(booleanTask.name, booleanResult);
|
139
|
+
} else if ("aiLocate" in flowItem) {
|
140
|
+
const locateTask = flowItem;
|
141
|
+
const prompt = locateTask.aiLocate;
|
142
|
+
assert(prompt, "missing prompt for aiLocate");
|
143
|
+
assert(
|
144
|
+
typeof prompt === "string",
|
145
|
+
"prompt for aiLocate must be a string"
|
146
|
+
);
|
147
|
+
const locateResult = await agent.aiLocate(prompt);
|
148
|
+
this.setResult(locateTask.name, locateResult);
|
149
|
+
} else if ("aiWaitFor" in flowItem) {
|
150
|
+
const waitForTask = flowItem;
|
151
|
+
const prompt = waitForTask.aiWaitFor;
|
152
|
+
assert(prompt, "missing prompt for aiWaitFor");
|
153
|
+
assert(
|
154
|
+
typeof prompt === "string",
|
155
|
+
"prompt for aiWaitFor must be a string"
|
156
|
+
);
|
157
|
+
const timeout = waitForTask.timeout;
|
158
|
+
await agent.aiWaitFor(prompt, { timeoutMs: timeout });
|
159
|
+
} else if ("sleep" in flowItem) {
|
160
|
+
const sleepTask = flowItem;
|
161
|
+
const ms = sleepTask.sleep;
|
162
|
+
let msNumber = ms;
|
163
|
+
if (typeof ms === "string") {
|
164
|
+
msNumber = Number.parseInt(ms, 10);
|
165
|
+
}
|
166
|
+
assert(
|
167
|
+
msNumber && msNumber > 0,
|
168
|
+
`ms for sleep must be greater than 0, but got ${ms}`
|
169
|
+
);
|
170
|
+
await new Promise((resolve2) => setTimeout(resolve2, msNumber));
|
171
|
+
} else if ("aiTap" in flowItem) {
|
172
|
+
const tapTask = flowItem;
|
173
|
+
await agent.aiTap(tapTask.aiTap, tapTask);
|
174
|
+
} else if ("aiHover" in flowItem) {
|
175
|
+
const hoverTask = flowItem;
|
176
|
+
await agent.aiHover(hoverTask.aiHover, hoverTask);
|
177
|
+
} else if ("aiInput" in flowItem) {
|
178
|
+
const inputTask = flowItem;
|
179
|
+
await agent.aiInput(inputTask.aiInput, inputTask.locate, inputTask);
|
180
|
+
} else if ("aiKeyboardPress" in flowItem) {
|
181
|
+
const keyboardPressTask = flowItem;
|
182
|
+
await agent.aiKeyboardPress(
|
183
|
+
keyboardPressTask.aiKeyboardPress,
|
184
|
+
keyboardPressTask.locate,
|
185
|
+
keyboardPressTask
|
186
|
+
);
|
187
|
+
} else if ("aiScroll" in flowItem) {
|
188
|
+
const scrollTask = flowItem;
|
189
|
+
await agent.aiScroll(scrollTask, scrollTask.locate, scrollTask);
|
190
|
+
} else if ("javascript" in flowItem) {
|
191
|
+
const evaluateJavaScriptTask = flowItem;
|
192
|
+
const result = await agent.evaluateJavaScript(
|
193
|
+
evaluateJavaScriptTask.javascript
|
194
|
+
);
|
195
|
+
this.setResult(evaluateJavaScriptTask.name, result);
|
196
|
+
} else {
|
197
|
+
throw new Error(`unknown flowItem: ${JSON.stringify(flowItem)}`);
|
198
|
+
}
|
199
|
+
}
|
200
|
+
this.reportFile = agent.reportFile;
|
201
|
+
}
|
202
|
+
async run() {
|
203
|
+
const { target, web, android, tasks } = this.script;
|
204
|
+
const webEnv = web || target;
|
205
|
+
const androidEnv = android;
|
206
|
+
const platform = webEnv || androidEnv;
|
207
|
+
this.setPlayerStatus("running");
|
208
|
+
let agent = null;
|
209
|
+
let freeFn = [];
|
210
|
+
try {
|
211
|
+
const { agent: newAgent, freeFn: newFreeFn } = await this.setupAgent(
|
212
|
+
platform
|
213
|
+
);
|
214
|
+
agent = newAgent;
|
215
|
+
const originalOnTaskStartTip = agent.onTaskStartTip;
|
216
|
+
agent.onTaskStartTip = (tip) => {
|
217
|
+
if (this.status === "running") {
|
218
|
+
this.agentStatusTip = tip;
|
219
|
+
}
|
220
|
+
originalOnTaskStartTip?.(tip);
|
221
|
+
};
|
222
|
+
freeFn = [
|
223
|
+
...newFreeFn || [],
|
224
|
+
{
|
225
|
+
name: "restore-agent-onTaskStartTip",
|
226
|
+
fn: () => {
|
227
|
+
if (agent) {
|
228
|
+
agent.onTaskStartTip = originalOnTaskStartTip;
|
229
|
+
}
|
230
|
+
}
|
231
|
+
}
|
232
|
+
];
|
233
|
+
} catch (e) {
|
234
|
+
this.setPlayerStatus("error", e);
|
235
|
+
return;
|
236
|
+
}
|
237
|
+
this.pageAgent = agent;
|
238
|
+
let taskIndex = 0;
|
239
|
+
this.setPlayerStatus("running");
|
240
|
+
let errorFlag = false;
|
241
|
+
while (taskIndex < tasks.length) {
|
242
|
+
const taskStatus = this.taskStatusList[taskIndex];
|
243
|
+
this.setTaskStatus(taskIndex, "running");
|
244
|
+
this.setTaskIndex(taskIndex);
|
245
|
+
try {
|
246
|
+
await this.playTask(taskStatus, this.pageAgent);
|
247
|
+
this.setTaskStatus(taskIndex, "done");
|
248
|
+
} catch (e) {
|
249
|
+
this.setTaskStatus(taskIndex, "error", e);
|
250
|
+
if (taskStatus.continueOnError) {
|
251
|
+
} else {
|
252
|
+
this.reportFile = agent.reportFile;
|
253
|
+
errorFlag = true;
|
254
|
+
break;
|
255
|
+
}
|
256
|
+
}
|
257
|
+
this.reportFile = agent.reportFile;
|
258
|
+
taskIndex++;
|
259
|
+
}
|
260
|
+
if (errorFlag) {
|
261
|
+
this.setPlayerStatus("error");
|
262
|
+
} else {
|
263
|
+
this.setPlayerStatus("done");
|
264
|
+
}
|
265
|
+
this.agentStatusTip = "";
|
266
|
+
for (const fn of freeFn) {
|
267
|
+
try {
|
268
|
+
await fn.fn();
|
269
|
+
} catch (e) {
|
270
|
+
}
|
271
|
+
}
|
272
|
+
}
|
273
|
+
};
|
274
|
+
|
275
|
+
// src/yaml/builder.ts
|
276
|
+
import yaml from "js-yaml";
|
277
|
+
function buildYaml(env, tasks) {
|
278
|
+
const result = {
|
279
|
+
target: env,
|
280
|
+
tasks
|
281
|
+
};
|
282
|
+
return yaml.dump(result, {
|
283
|
+
indent: 2
|
284
|
+
});
|
285
|
+
}
|
286
|
+
|
287
|
+
// src/yaml/utils.ts
|
288
|
+
import { assert as assert2 } from "misoai-shared/utils";
|
289
|
+
import yaml2 from "js-yaml";
|
290
|
+
function interpolateEnvVars(content) {
|
291
|
+
return content.replace(/\$\{([^}]+)\}/g, (_, envVar) => {
|
292
|
+
const value = process.env[envVar.trim()];
|
293
|
+
if (value === void 0) {
|
294
|
+
throw new Error(`Environment variable "${envVar.trim()}" is not defined`);
|
295
|
+
}
|
296
|
+
return value;
|
297
|
+
});
|
298
|
+
}
|
299
|
+
function parseYamlScript(content, filePath, ignoreCheckingTarget) {
|
300
|
+
let processedContent = content;
|
301
|
+
if (content.indexOf("android") !== -1 && content.match(/deviceId:\s*(\d+)/)) {
|
302
|
+
let matchedDeviceId;
|
303
|
+
processedContent = content.replace(
|
304
|
+
/deviceId:\s*(\d+)/g,
|
305
|
+
(match, deviceId) => {
|
306
|
+
matchedDeviceId = deviceId;
|
307
|
+
return `deviceId: '${deviceId}'`;
|
308
|
+
}
|
309
|
+
);
|
310
|
+
console.warn(
|
311
|
+
`please use string-style deviceId in yaml script, for example: deviceId: "${matchedDeviceId}"`
|
312
|
+
);
|
313
|
+
}
|
314
|
+
const interpolatedContent = interpolateEnvVars(processedContent);
|
315
|
+
const obj = yaml2.load(interpolatedContent, {
|
316
|
+
schema: yaml2.JSON_SCHEMA
|
317
|
+
});
|
318
|
+
const pathTip = filePath ? `, failed to load ${filePath}` : "";
|
319
|
+
const android = typeof obj.android !== "undefined" ? Object.assign({}, obj.android || {}) : void 0;
|
320
|
+
const webConfig = obj.web || obj.target;
|
321
|
+
const web = typeof webConfig !== "undefined" ? Object.assign({}, webConfig || {}) : void 0;
|
322
|
+
if (!ignoreCheckingTarget) {
|
323
|
+
assert2(
|
324
|
+
web || android,
|
325
|
+
`at least one of "target", "web", or "android" properties is required in yaml script${pathTip}`
|
326
|
+
);
|
327
|
+
assert2(
|
328
|
+
web && !android || !web && android,
|
329
|
+
`only one of "target", "web", or "android" properties is allowed in yaml script${pathTip}`
|
330
|
+
);
|
331
|
+
if (web || android) {
|
332
|
+
assert2(
|
333
|
+
typeof web === "object" || typeof android === "object",
|
334
|
+
`property "target/web/android" must be an object${pathTip}`
|
335
|
+
);
|
336
|
+
}
|
337
|
+
}
|
338
|
+
assert2(obj.tasks, `property "tasks" is required in yaml script ${pathTip}`);
|
339
|
+
assert2(
|
340
|
+
Array.isArray(obj.tasks),
|
341
|
+
`property "tasks" must be an array in yaml script, but got ${obj.tasks}`
|
342
|
+
);
|
343
|
+
return obj;
|
344
|
+
}
|
345
|
+
export {
|
346
|
+
ScriptPlayer,
|
347
|
+
buildYaml,
|
348
|
+
parseYamlScript
|
349
|
+
};
|
350
|
+
|
351
|
+
//# sourceMappingURL=yaml.js.map
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"mappings":"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","names":["resolve","assert","yaml"],"ignoreList":[],"sources":["../../src/yaml/player.ts","../../src/yaml/builder.ts","../../src/yaml/utils.ts"],"sourcesContent":["import { existsSync, mkdirSync, writeFileSync } from 'node:fs';\r\nimport { dirname, join, resolve } from 'node:path';\r\nimport { assert, ifInBrowser } from 'misoai-shared/utils';\r\n\r\nimport type { PageAgent } from '@/common/agent';\r\nimport type {\r\n FreeFn,\r\n MidsceneYamlFlowItemAIAction,\r\n MidsceneYamlFlowItemAIAssert,\r\n MidsceneYamlFlowItemAIBoolean,\r\n MidsceneYamlFlowItemAIHover,\r\n MidsceneYamlFlowItemAIInput,\r\n MidsceneYamlFlowItemAIKeyboardPress,\r\n MidsceneYamlFlowItemAILocate,\r\n MidsceneYamlFlowItemAINString,\r\n MidsceneYamlFlowItemAINumber,\r\n MidsceneYamlFlowItemAIQuery,\r\n MidsceneYamlFlowItemAIScroll,\r\n MidsceneYamlFlowItemAITap,\r\n MidsceneYamlFlowItemAIWaitFor,\r\n MidsceneYamlFlowItemEvaluateJavaScript,\r\n MidsceneYamlFlowItemSleep,\r\n MidsceneYamlScript,\r\n MidsceneYamlScriptEnv,\r\n ScriptPlayerStatusValue,\r\n ScriptPlayerTaskStatus,\r\n} from 'misoai-core';\r\nimport { getMidsceneRunSubDir } from 'misoai-shared/common';\r\n\r\nexport class ScriptPlayer<T extends MidsceneYamlScriptEnv> {\r\n public currentTaskIndex?: number;\r\n public taskStatusList: ScriptPlayerTaskStatus[] = [];\r\n public status: ScriptPlayerStatusValue = 'init';\r\n public reportFile?: string | null;\r\n public result: Record<string, any>;\r\n private unnamedResultIndex = 0;\r\n public output?: string | null;\r\n public errorInSetup?: Error;\r\n private pageAgent: PageAgent | null = null;\r\n public agentStatusTip?: string;\r\n constructor(\r\n private script: MidsceneYamlScript,\r\n private setupAgent: (platform: T) => Promise<{\r\n agent: PageAgent;\r\n freeFn: FreeFn[];\r\n }>,\r\n public onTaskStatusChange?: (taskStatus: ScriptPlayerTaskStatus) => void,\r\n ) {\r\n this.result = {};\r\n\r\n if (ifInBrowser) {\r\n this.output = undefined;\r\n } else if (script.target?.output) {\r\n this.output = resolve(process.cwd(), script.target.output);\r\n } else {\r\n this.output = join(getMidsceneRunSubDir('output'), `${process.pid}.json`);\r\n }\r\n\r\n this.taskStatusList = (script.tasks || []).map((task, taskIndex) => ({\r\n ...task,\r\n index: taskIndex,\r\n status: 'init',\r\n totalSteps: task.flow?.length || 0,\r\n }));\r\n }\r\n\r\n private setResult(key: string | undefined, value: any) {\r\n const keyToUse = key || this.unnamedResultIndex++;\r\n if (this.result[keyToUse]) {\r\n console.warn(`result key ${keyToUse} already exists, will overwrite`);\r\n }\r\n this.result[keyToUse] = value;\r\n\r\n this.flushResult();\r\n }\r\n\r\n private setPlayerStatus(status: ScriptPlayerStatusValue, error?: Error) {\r\n this.status = status;\r\n this.errorInSetup = error;\r\n }\r\n\r\n private notifyCurrentTaskStatusChange(taskIndex?: number) {\r\n const taskIndexToNotify =\r\n typeof taskIndex === 'number' ? taskIndex : this.currentTaskIndex;\r\n\r\n if (typeof taskIndexToNotify !== 'number') {\r\n return;\r\n }\r\n\r\n const taskStatus = this.taskStatusList[taskIndexToNotify];\r\n if (this.onTaskStatusChange) {\r\n this.onTaskStatusChange(taskStatus);\r\n }\r\n }\r\n\r\n private async setTaskStatus(\r\n index: number,\r\n statusValue: ScriptPlayerStatusValue,\r\n error?: Error,\r\n ) {\r\n this.taskStatusList[index].status = statusValue;\r\n if (error) {\r\n this.taskStatusList[index].error = error;\r\n }\r\n\r\n this.notifyCurrentTaskStatusChange(index);\r\n }\r\n\r\n private setTaskIndex(taskIndex: number) {\r\n this.currentTaskIndex = taskIndex;\r\n }\r\n\r\n private flushResult() {\r\n if (Object.keys(this.result).length && this.output) {\r\n const output = resolve(process.cwd(), this.output);\r\n const outputDir = dirname(output);\r\n if (!existsSync(outputDir)) {\r\n mkdirSync(outputDir, { recursive: true });\r\n }\r\n writeFileSync(output, JSON.stringify(this.result, undefined, 2));\r\n }\r\n }\r\n\r\n async playTask(taskStatus: ScriptPlayerTaskStatus, agent: PageAgent) {\r\n const { flow } = taskStatus;\r\n assert(flow, 'missing flow in task');\r\n\r\n for (const flowItemIndex in flow) {\r\n const currentStep = Number.parseInt(flowItemIndex, 10);\r\n taskStatus.currentStep = currentStep;\r\n const flowItem = flow[flowItemIndex];\r\n if (\r\n 'aiAction' in (flowItem as MidsceneYamlFlowItemAIAction) ||\r\n 'ai' in (flowItem as MidsceneYamlFlowItemAIAction)\r\n ) {\r\n const actionTask = flowItem as MidsceneYamlFlowItemAIAction;\r\n const prompt = actionTask.aiAction || actionTask.ai;\r\n assert(prompt, 'missing prompt for ai (aiAction)');\r\n assert(\r\n typeof prompt === 'string',\r\n 'prompt for aiAction must be a string',\r\n );\r\n await agent.aiAction(prompt, {\r\n cacheable: actionTask.cacheable,\r\n });\r\n } else if ('aiAssert' in (flowItem as MidsceneYamlFlowItemAIAssert)) {\r\n const assertTask = flowItem as MidsceneYamlFlowItemAIAssert;\r\n const prompt = assertTask.aiAssert;\r\n assert(prompt, 'missing prompt for aiAssert');\r\n assert(\r\n typeof prompt === 'string',\r\n 'prompt for aiAssert must be a string',\r\n );\r\n await agent.aiAssert(prompt);\r\n } else if ('aiQuery' in (flowItem as MidsceneYamlFlowItemAIQuery)) {\r\n const queryTask = flowItem as MidsceneYamlFlowItemAIQuery;\r\n const prompt = queryTask.aiQuery;\r\n assert(prompt, 'missing prompt for aiQuery');\r\n assert(\r\n typeof prompt === 'string',\r\n 'prompt for aiQuery must be a string',\r\n );\r\n const queryResult = await agent.aiQuery(prompt);\r\n this.setResult(queryTask.name, queryResult);\r\n } else if ('aiNumber' in (flowItem as MidsceneYamlFlowItemAINumber)) {\r\n const numberTask = flowItem as MidsceneYamlFlowItemAINumber;\r\n const prompt = numberTask.aiNumber;\r\n assert(prompt, 'missing prompt for number');\r\n assert(\r\n typeof prompt === 'string',\r\n 'prompt for number must be a string',\r\n );\r\n const numberResult = await agent.aiNumber(prompt);\r\n this.setResult(numberTask.name, numberResult);\r\n } else if ('aiString' in (flowItem as MidsceneYamlFlowItemAINString)) {\r\n const stringTask = flowItem as MidsceneYamlFlowItemAINString;\r\n const prompt = stringTask.aiString;\r\n assert(prompt, 'missing prompt for string');\r\n assert(\r\n typeof prompt === 'string',\r\n 'prompt for string must be a string',\r\n );\r\n const stringResult = await agent.aiString(prompt);\r\n this.setResult(stringTask.name, stringResult);\r\n } else if ('aiBoolean' in (flowItem as MidsceneYamlFlowItemAIBoolean)) {\r\n const booleanTask = flowItem as MidsceneYamlFlowItemAIBoolean;\r\n const prompt = booleanTask.aiBoolean;\r\n assert(prompt, 'missing prompt for boolean');\r\n assert(\r\n typeof prompt === 'string',\r\n 'prompt for boolean must be a string',\r\n );\r\n const booleanResult = await agent.aiBoolean(prompt);\r\n this.setResult(booleanTask.name, booleanResult);\r\n } else if ('aiLocate' in (flowItem as MidsceneYamlFlowItemAILocate)) {\r\n const locateTask = flowItem as MidsceneYamlFlowItemAILocate;\r\n const prompt = locateTask.aiLocate;\r\n assert(prompt, 'missing prompt for aiLocate');\r\n assert(\r\n typeof prompt === 'string',\r\n 'prompt for aiLocate must be a string',\r\n );\r\n const locateResult = await agent.aiLocate(prompt);\r\n this.setResult(locateTask.name, locateResult);\r\n } else if ('aiWaitFor' in (flowItem as MidsceneYamlFlowItemAIWaitFor)) {\r\n const waitForTask = flowItem as MidsceneYamlFlowItemAIWaitFor;\r\n const prompt = waitForTask.aiWaitFor;\r\n assert(prompt, 'missing prompt for aiWaitFor');\r\n assert(\r\n typeof prompt === 'string',\r\n 'prompt for aiWaitFor must be a string',\r\n );\r\n const timeout = waitForTask.timeout;\r\n await agent.aiWaitFor(prompt, { timeoutMs: timeout });\r\n } else if ('sleep' in (flowItem as MidsceneYamlFlowItemSleep)) {\r\n const sleepTask = flowItem as MidsceneYamlFlowItemSleep;\r\n const ms = sleepTask.sleep;\r\n let msNumber = ms;\r\n if (typeof ms === 'string') {\r\n msNumber = Number.parseInt(ms, 10);\r\n }\r\n assert(\r\n msNumber && msNumber > 0,\r\n `ms for sleep must be greater than 0, but got ${ms}`,\r\n );\r\n await new Promise((resolve) => setTimeout(resolve, msNumber));\r\n } else if ('aiTap' in (flowItem as MidsceneYamlFlowItemAITap)) {\r\n const tapTask = flowItem as MidsceneYamlFlowItemAITap;\r\n await agent.aiTap(tapTask.aiTap, tapTask);\r\n } else if ('aiHover' in (flowItem as MidsceneYamlFlowItemAIHover)) {\r\n const hoverTask = flowItem as MidsceneYamlFlowItemAIHover;\r\n await agent.aiHover(hoverTask.aiHover, hoverTask);\r\n } else if ('aiInput' in (flowItem as MidsceneYamlFlowItemAIInput)) {\r\n // may be input empty string ''\r\n const inputTask = flowItem as MidsceneYamlFlowItemAIInput;\r\n await agent.aiInput(inputTask.aiInput, inputTask.locate, inputTask);\r\n } else if (\r\n 'aiKeyboardPress' in (flowItem as MidsceneYamlFlowItemAIKeyboardPress)\r\n ) {\r\n const keyboardPressTask =\r\n flowItem as MidsceneYamlFlowItemAIKeyboardPress;\r\n await agent.aiKeyboardPress(\r\n keyboardPressTask.aiKeyboardPress,\r\n keyboardPressTask.locate,\r\n keyboardPressTask,\r\n );\r\n } else if ('aiScroll' in (flowItem as MidsceneYamlFlowItemAIScroll)) {\r\n const scrollTask = flowItem as MidsceneYamlFlowItemAIScroll;\r\n await agent.aiScroll(scrollTask, scrollTask.locate, scrollTask);\r\n } else if (\r\n 'javascript' in (flowItem as MidsceneYamlFlowItemEvaluateJavaScript)\r\n ) {\r\n const evaluateJavaScriptTask =\r\n flowItem as MidsceneYamlFlowItemEvaluateJavaScript;\r\n\r\n const result = await agent.evaluateJavaScript(\r\n evaluateJavaScriptTask.javascript,\r\n );\r\n this.setResult(evaluateJavaScriptTask.name, result);\r\n } else {\r\n throw new Error(`unknown flowItem: ${JSON.stringify(flowItem)}`);\r\n }\r\n }\r\n this.reportFile = agent.reportFile;\r\n }\r\n\r\n async run() {\r\n const { target, web, android, tasks } = this.script;\r\n const webEnv = web || target;\r\n const androidEnv = android;\r\n const platform = webEnv || androidEnv;\r\n\r\n this.setPlayerStatus('running');\r\n\r\n let agent: PageAgent | null = null;\r\n let freeFn: FreeFn[] = [];\r\n try {\r\n const { agent: newAgent, freeFn: newFreeFn } = await this.setupAgent(\r\n platform as T,\r\n );\r\n agent = newAgent;\r\n const originalOnTaskStartTip = agent.onTaskStartTip;\r\n agent.onTaskStartTip = (tip) => {\r\n if (this.status === 'running') {\r\n this.agentStatusTip = tip;\r\n }\r\n originalOnTaskStartTip?.(tip);\r\n };\r\n freeFn = [\r\n ...(newFreeFn || []),\r\n {\r\n name: 'restore-agent-onTaskStartTip',\r\n fn: () => {\r\n if (agent) {\r\n agent.onTaskStartTip = originalOnTaskStartTip;\r\n }\r\n },\r\n },\r\n ];\r\n } catch (e) {\r\n this.setPlayerStatus('error', e as Error);\r\n return;\r\n }\r\n this.pageAgent = agent;\r\n\r\n let taskIndex = 0;\r\n this.setPlayerStatus('running');\r\n let errorFlag = false;\r\n while (taskIndex < tasks.length) {\r\n const taskStatus = this.taskStatusList[taskIndex];\r\n this.setTaskStatus(taskIndex, 'running' as any);\r\n this.setTaskIndex(taskIndex);\r\n\r\n try {\r\n await this.playTask(taskStatus, this.pageAgent);\r\n this.setTaskStatus(taskIndex, 'done' as any);\r\n } catch (e) {\r\n this.setTaskStatus(taskIndex, 'error' as any, e as Error);\r\n\r\n if (taskStatus.continueOnError) {\r\n // nothing more to do\r\n } else {\r\n this.reportFile = agent.reportFile;\r\n errorFlag = true;\r\n break;\r\n }\r\n }\r\n this.reportFile = agent.reportFile;\r\n taskIndex++;\r\n }\r\n\r\n if (errorFlag) {\r\n this.setPlayerStatus('error');\r\n } else {\r\n this.setPlayerStatus('done');\r\n }\r\n this.agentStatusTip = '';\r\n\r\n // free the resources\r\n for (const fn of freeFn) {\r\n try {\r\n // console.log('freeing', fn.name);\r\n await fn.fn();\r\n // console.log('freed', fn.name);\r\n } catch (e) {\r\n // console.error('error freeing', fn.name, e);\r\n }\r\n }\r\n }\r\n}\r\n","import type {\r\n MidsceneYamlScript,\r\n MidsceneYamlScriptWebEnv,\r\n MidsceneYamlTask,\r\n} from 'misoai-core';\r\nimport yaml from 'js-yaml';\r\n\r\nexport function buildYaml(\r\n env: MidsceneYamlScriptWebEnv,\r\n tasks: MidsceneYamlTask[],\r\n) {\r\n const result: MidsceneYamlScript = {\r\n target: env,\r\n tasks,\r\n };\r\n\r\n return yaml.dump(result, {\r\n indent: 2,\r\n });\r\n}\r\n","import { assert } from 'misoai-shared/utils';\r\nimport yaml from 'js-yaml';\r\n\r\nimport type { MidsceneYamlScript } from 'misoai-core';\r\n\r\nfunction interpolateEnvVars(content: string): string {\r\n return content.replace(/\\$\\{([^}]+)\\}/g, (_, envVar) => {\r\n const value = process.env[envVar.trim()];\r\n if (value === undefined) {\r\n throw new Error(`Environment variable \"${envVar.trim()}\" is not defined`);\r\n }\r\n return value;\r\n });\r\n}\r\n\r\nexport function parseYamlScript(\r\n content: string,\r\n filePath?: string,\r\n ignoreCheckingTarget?: boolean,\r\n): MidsceneYamlScript {\r\n let processedContent = content;\r\n if (content.indexOf('android') !== -1 && content.match(/deviceId:\\s*(\\d+)/)) {\r\n let matchedDeviceId;\r\n processedContent = content.replace(\r\n /deviceId:\\s*(\\d+)/g,\r\n (match, deviceId) => {\r\n matchedDeviceId = deviceId;\r\n return `deviceId: '${deviceId}'`;\r\n },\r\n );\r\n console.warn(\r\n `please use string-style deviceId in yaml script, for example: deviceId: \"${matchedDeviceId}\"`,\r\n );\r\n }\r\n const interpolatedContent = interpolateEnvVars(processedContent);\r\n const obj = yaml.load(interpolatedContent, {\r\n schema: yaml.JSON_SCHEMA,\r\n }) as MidsceneYamlScript;\r\n\r\n const pathTip = filePath ? `, failed to load ${filePath}` : '';\r\n const android =\r\n typeof obj.android !== 'undefined'\r\n ? Object.assign({}, obj.android || {})\r\n : undefined;\r\n const webConfig = obj.web || obj.target; // no need to handle null case, because web has required parameters url\r\n const web =\r\n typeof webConfig !== 'undefined'\r\n ? Object.assign({}, webConfig || {})\r\n : undefined;\r\n\r\n if (!ignoreCheckingTarget) {\r\n // make sure at least one of target/web/android is provided\r\n assert(\r\n web || android,\r\n `at least one of \"target\", \"web\", or \"android\" properties is required in yaml script${pathTip}`,\r\n );\r\n\r\n // make sure only one of target/web/android is provided\r\n assert(\r\n (web && !android) || (!web && android),\r\n `only one of \"target\", \"web\", or \"android\" properties is allowed in yaml script${pathTip}`,\r\n );\r\n\r\n // make sure the config is valid\r\n if (web || android) {\r\n assert(\r\n typeof web === 'object' || typeof android === 'object',\r\n `property \"target/web/android\" must be an object${pathTip}`,\r\n );\r\n }\r\n }\r\n\r\n assert(obj.tasks, `property \"tasks\" is required in yaml script ${pathTip}`);\r\n assert(\r\n Array.isArray(obj.tasks),\r\n `property \"tasks\" must be an array in yaml script, but got ${obj.tasks}`,\r\n );\r\n return obj;\r\n}\r\n"]}
|