android-midscene-automation 0.1.0
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 +160 -0
- package/bin/android-midscene-automation.js +27 -0
- package/index.html +12 -0
- package/package.json +49 -0
- package/remote-agent/index.ts +206 -0
- package/server/appium-recorder/appium-runner.ts +427 -0
- package/server/appium-recorder/repository.ts +228 -0
- package/server/appium-recorder/routes.ts +219 -0
- package/server/config-store.ts +167 -0
- package/server/config.ts +130 -0
- package/server/device-locks/repository.ts +147 -0
- package/server/device-locks/service.ts +72 -0
- package/server/device-locks/types.ts +22 -0
- package/server/device-sessions/repository.ts +169 -0
- package/server/device-sessions/service.ts +59 -0
- package/server/device-sessions/types.ts +24 -0
- package/server/http-api.ts +1389 -0
- package/server/model-call-usage-importer.ts +108 -0
- package/server/model-tester.ts +104 -0
- package/server/model-usage-repository.ts +131 -0
- package/server/operations/repository.ts +218 -0
- package/server/operations/service.ts +84 -0
- package/server/operations/types.ts +27 -0
- package/server/paths.ts +27 -0
- package/server/remote-agents/protocol.ts +38 -0
- package/server/remote-agents/registry.ts +136 -0
- package/server/remote-agents/routes.ts +89 -0
- package/server/script-agent.ts +284 -0
- package/server/script-db.ts +281 -0
- package/server/script-runner.ts +551 -0
- package/server/storage/sqlite.ts +49 -0
- package/server/test-case-import/formatter.ts +28 -0
- package/server/test-case-import/parsers/excel.ts +69 -0
- package/server/test-case-import/parsers/txt.ts +11 -0
- package/server/test-case-import/parsers/word.ts +9 -0
- package/server/test-case-import/service.ts +58 -0
- package/server/test-case-import/text-normalizer.ts +98 -0
- package/server/test-case-import/types.ts +24 -0
- package/server/test-case-import/validator.ts +34 -0
- package/src/App.vue +1450 -0
- package/src/api.ts +290 -0
- package/src/appium-recorder/AppiumPage.vue +894 -0
- package/src/appium-recorder/api.ts +64 -0
- package/src/appium-recorder/components/ComponentTree.vue +44 -0
- package/src/appium-recorder/components/NodeDetail.vue +152 -0
- package/src/appium-recorder/components/RecordedSteps.vue +79 -0
- package/src/appium-recorder/tree.ts +129 -0
- package/src/appium-recorder/types.ts +88 -0
- package/src/assets/device-actions/back.svg +5 -0
- package/src/assets/device-actions/home.svg +3 -0
- package/src/assets/device-actions/power.svg +5 -0
- package/src/assets/device-actions/tasks.svg +3 -0
- package/src/assets/device-actions/volume-down.svg +3 -0
- package/src/assets/device-actions/volume-up.svg +3 -0
- package/src/components/config/ModelUsageChart.vue +188 -0
- package/src/components/device/DevicePreviewPanel.vue +266 -0
- package/src/components/generator/GeneratedCodePanel.vue +70 -0
- package/src/components/generator/TestCaseFileUpload.vue +97 -0
- package/src/config/midscene-model-presets.ts +75 -0
- package/src/config/prompt-example.ts +6 -0
- package/src/main.ts +7 -0
- package/src/pages/AiGeneratorPage.vue +90 -0
- package/src/pages/AutomationPage.vue +161 -0
- package/src/pages/ConfigPage.vue +273 -0
- package/src/pages/GeneratorPage.vue +97 -0
- package/src/pages/ManualStepsPage.vue +179 -0
- package/src/script-generator/codegen.ts +126 -0
- package/src/script-generator/index.ts +4 -0
- package/src/script-generator/presets.ts +37 -0
- package/src/script-generator/step-options.ts +52 -0
- package/src/script-generator/types.ts +27 -0
- package/src/style.css +1983 -0
- package/src/types.ts +157 -0
- package/src/vite-env.d.ts +1 -0
- package/tsconfig.app.json +8 -0
- package/tsconfig.json +11 -0
- package/tsconfig.node.json +16 -0
- package/vite.config.ts +28 -0
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
import { execFileSync } from 'node:child_process';
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { appPath } from './paths';
|
|
5
|
+
|
|
6
|
+
export type ScriptStepRecord = {
|
|
7
|
+
id?: string;
|
|
8
|
+
type: string;
|
|
9
|
+
label: string;
|
|
10
|
+
prompt: string;
|
|
11
|
+
outputVar?: string;
|
|
12
|
+
value?: string;
|
|
13
|
+
observePrompt?: string;
|
|
14
|
+
repeat?: number;
|
|
15
|
+
enabled?: boolean;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export type ScriptRecord = {
|
|
19
|
+
id: string;
|
|
20
|
+
name: string;
|
|
21
|
+
promptTitle: string;
|
|
22
|
+
sourcePrompt: string;
|
|
23
|
+
code: string;
|
|
24
|
+
filePath: string;
|
|
25
|
+
steps: ScriptStepRecord[];
|
|
26
|
+
createdAt: string;
|
|
27
|
+
updatedAt: string;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
type ScriptRow = {
|
|
31
|
+
id: string;
|
|
32
|
+
name: string;
|
|
33
|
+
prompt_title: string;
|
|
34
|
+
source_prompt: string;
|
|
35
|
+
code: string;
|
|
36
|
+
file_path: string;
|
|
37
|
+
steps_json: string;
|
|
38
|
+
created_at: string;
|
|
39
|
+
updated_at: string;
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
const dbPath = appPath('.midscene-app', 'script-cache.sqlite');
|
|
43
|
+
const legacyJsonPath = appPath('midscene_run', 'script-db.json');
|
|
44
|
+
const sqliteBin = process.env.SQLITE3_BIN || '/usr/bin/sqlite3';
|
|
45
|
+
let initialized = false;
|
|
46
|
+
|
|
47
|
+
function createId() {
|
|
48
|
+
return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function ensureDbDir() {
|
|
52
|
+
fs.mkdirSync(path.dirname(dbPath), { recursive: true });
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function sqlString(value: string) {
|
|
56
|
+
return `'${value.replace(/'/g, "''")}'`;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function runSql(sql: string) {
|
|
60
|
+
ensureDbDir();
|
|
61
|
+
execFileSync(sqliteBin, [dbPath], {
|
|
62
|
+
input: sql,
|
|
63
|
+
maxBuffer: 20 * 1024 * 1024,
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function querySql<T>(sql: string) {
|
|
68
|
+
ensureDbDir();
|
|
69
|
+
const output = execFileSync(sqliteBin, ['-json', dbPath, sql], {
|
|
70
|
+
encoding: 'utf8',
|
|
71
|
+
maxBuffer: 20 * 1024 * 1024,
|
|
72
|
+
});
|
|
73
|
+
return JSON.parse(output || '[]') as T[];
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function rowToRecord(row: ScriptRow): ScriptRecord {
|
|
77
|
+
return {
|
|
78
|
+
id: row.id,
|
|
79
|
+
name: row.name,
|
|
80
|
+
promptTitle: row.prompt_title,
|
|
81
|
+
sourcePrompt: row.source_prompt,
|
|
82
|
+
code: row.code,
|
|
83
|
+
filePath: row.file_path,
|
|
84
|
+
steps: JSON.parse(row.steps_json || '[]') as ScriptStepRecord[],
|
|
85
|
+
createdAt: row.created_at,
|
|
86
|
+
updatedAt: row.updated_at,
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function migrateLegacyJson() {
|
|
91
|
+
if (!fs.existsSync(legacyJsonPath)) {
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const count = querySql<{ count: number }>('SELECT COUNT(*) AS count FROM scripts;')[0]?.count || 0;
|
|
96
|
+
if (count > 0) {
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const parsed = JSON.parse(fs.readFileSync(legacyJsonPath, 'utf8')) as {
|
|
101
|
+
scripts?: ScriptRecord[];
|
|
102
|
+
};
|
|
103
|
+
const scripts = Array.isArray(parsed.scripts) ? parsed.scripts : [];
|
|
104
|
+
|
|
105
|
+
for (const script of scripts) {
|
|
106
|
+
upsertScriptRecord({
|
|
107
|
+
name: script.name,
|
|
108
|
+
promptTitle: script.promptTitle,
|
|
109
|
+
sourcePrompt: script.sourcePrompt,
|
|
110
|
+
code: script.code,
|
|
111
|
+
filePath: script.filePath,
|
|
112
|
+
steps: script.steps,
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function initDb() {
|
|
118
|
+
if (initialized) return;
|
|
119
|
+
initialized = true;
|
|
120
|
+
runSql(`
|
|
121
|
+
PRAGMA journal_mode = WAL;
|
|
122
|
+
CREATE TABLE IF NOT EXISTS scripts (
|
|
123
|
+
id TEXT PRIMARY KEY,
|
|
124
|
+
name TEXT NOT NULL UNIQUE,
|
|
125
|
+
prompt_title TEXT NOT NULL,
|
|
126
|
+
source_prompt TEXT NOT NULL DEFAULT '',
|
|
127
|
+
code TEXT NOT NULL,
|
|
128
|
+
file_path TEXT NOT NULL,
|
|
129
|
+
steps_json TEXT NOT NULL DEFAULT '[]',
|
|
130
|
+
created_at TEXT NOT NULL,
|
|
131
|
+
updated_at TEXT NOT NULL
|
|
132
|
+
);
|
|
133
|
+
CREATE INDEX IF NOT EXISTS idx_scripts_updated_at ON scripts(updated_at DESC);
|
|
134
|
+
`);
|
|
135
|
+
migrateLegacyJson();
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
export function listScriptRecords() {
|
|
139
|
+
initDb();
|
|
140
|
+
return querySql<ScriptRow>(`
|
|
141
|
+
SELECT
|
|
142
|
+
id,
|
|
143
|
+
name,
|
|
144
|
+
prompt_title,
|
|
145
|
+
source_prompt,
|
|
146
|
+
code,
|
|
147
|
+
file_path,
|
|
148
|
+
steps_json,
|
|
149
|
+
created_at,
|
|
150
|
+
updated_at
|
|
151
|
+
FROM scripts
|
|
152
|
+
ORDER BY updated_at DESC;
|
|
153
|
+
`).map(rowToRecord);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
export function getScriptRecord(id: string) {
|
|
157
|
+
initDb();
|
|
158
|
+
const row = querySql<ScriptRow>(`
|
|
159
|
+
SELECT
|
|
160
|
+
id,
|
|
161
|
+
name,
|
|
162
|
+
prompt_title,
|
|
163
|
+
source_prompt,
|
|
164
|
+
code,
|
|
165
|
+
file_path,
|
|
166
|
+
steps_json,
|
|
167
|
+
created_at,
|
|
168
|
+
updated_at
|
|
169
|
+
FROM scripts
|
|
170
|
+
WHERE id = ${sqlString(id)}
|
|
171
|
+
LIMIT 1;
|
|
172
|
+
`)[0];
|
|
173
|
+
|
|
174
|
+
return row ? rowToRecord(row) : null;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
export function upsertScriptRecord(input: {
|
|
178
|
+
name: string;
|
|
179
|
+
promptTitle: string;
|
|
180
|
+
sourcePrompt: string;
|
|
181
|
+
code: string;
|
|
182
|
+
filePath: string;
|
|
183
|
+
steps?: ScriptStepRecord[];
|
|
184
|
+
}) {
|
|
185
|
+
initDb();
|
|
186
|
+
const now = new Date().toISOString();
|
|
187
|
+
const id = createId();
|
|
188
|
+
const stepsJson = JSON.stringify(input.steps || []);
|
|
189
|
+
|
|
190
|
+
runSql(`
|
|
191
|
+
INSERT INTO scripts (
|
|
192
|
+
id,
|
|
193
|
+
name,
|
|
194
|
+
prompt_title,
|
|
195
|
+
source_prompt,
|
|
196
|
+
code,
|
|
197
|
+
file_path,
|
|
198
|
+
steps_json,
|
|
199
|
+
created_at,
|
|
200
|
+
updated_at
|
|
201
|
+
)
|
|
202
|
+
VALUES (
|
|
203
|
+
${sqlString(id)},
|
|
204
|
+
${sqlString(input.name)},
|
|
205
|
+
${sqlString(input.promptTitle || input.name)},
|
|
206
|
+
${sqlString(input.sourcePrompt || '')},
|
|
207
|
+
${sqlString(input.code)},
|
|
208
|
+
${sqlString(input.filePath)},
|
|
209
|
+
${sqlString(stepsJson)},
|
|
210
|
+
${sqlString(now)},
|
|
211
|
+
${sqlString(now)}
|
|
212
|
+
)
|
|
213
|
+
ON CONFLICT(name) DO UPDATE SET
|
|
214
|
+
prompt_title = excluded.prompt_title,
|
|
215
|
+
source_prompt = excluded.source_prompt,
|
|
216
|
+
code = excluded.code,
|
|
217
|
+
file_path = excluded.file_path,
|
|
218
|
+
steps_json = excluded.steps_json,
|
|
219
|
+
updated_at = excluded.updated_at;
|
|
220
|
+
`);
|
|
221
|
+
|
|
222
|
+
const row = querySql<ScriptRow>(`
|
|
223
|
+
SELECT
|
|
224
|
+
id,
|
|
225
|
+
name,
|
|
226
|
+
prompt_title,
|
|
227
|
+
source_prompt,
|
|
228
|
+
code,
|
|
229
|
+
file_path,
|
|
230
|
+
steps_json,
|
|
231
|
+
created_at,
|
|
232
|
+
updated_at
|
|
233
|
+
FROM scripts
|
|
234
|
+
WHERE name = ${sqlString(input.name)}
|
|
235
|
+
LIMIT 1;
|
|
236
|
+
`)[0];
|
|
237
|
+
|
|
238
|
+
return rowToRecord(row);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
export function updateScriptRecordCode(input: { id: string; code: string; filePath?: string }) {
|
|
242
|
+
initDb();
|
|
243
|
+
const now = new Date().toISOString();
|
|
244
|
+
|
|
245
|
+
runSql(`
|
|
246
|
+
UPDATE scripts
|
|
247
|
+
SET
|
|
248
|
+
code = ${sqlString(input.code)},
|
|
249
|
+
file_path = COALESCE(${input.filePath ? sqlString(input.filePath) : 'NULL'}, file_path),
|
|
250
|
+
steps_json = '[]',
|
|
251
|
+
updated_at = ${sqlString(now)}
|
|
252
|
+
WHERE id = ${sqlString(input.id)};
|
|
253
|
+
`);
|
|
254
|
+
|
|
255
|
+
return getScriptRecord(input.id);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
export function removeScriptRecord(id: string) {
|
|
259
|
+
initDb();
|
|
260
|
+
const row = querySql<ScriptRow>(`
|
|
261
|
+
SELECT
|
|
262
|
+
id,
|
|
263
|
+
name,
|
|
264
|
+
prompt_title,
|
|
265
|
+
source_prompt,
|
|
266
|
+
code,
|
|
267
|
+
file_path,
|
|
268
|
+
steps_json,
|
|
269
|
+
created_at,
|
|
270
|
+
updated_at
|
|
271
|
+
FROM scripts
|
|
272
|
+
WHERE id = ${sqlString(id)}
|
|
273
|
+
LIMIT 1;
|
|
274
|
+
`)[0];
|
|
275
|
+
runSql(`DELETE FROM scripts WHERE id = ${sqlString(id)};`);
|
|
276
|
+
return row ? rowToRecord(row) : null;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
export function getScriptDbPath() {
|
|
280
|
+
return dbPath;
|
|
281
|
+
}
|