@aixnn/aixautots 0.0.0-stage → 0.5.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/README.md +239 -2
- package/dist/ScriptStudio-DaCqOF5H.d.ts +43 -0
- package/dist/agent-runner-V60B0is1.d.ts +57 -0
- package/dist/automation-queue-8HqRJV_K.d.ts +72 -0
- package/dist/chunk-536WVCTB.js +5744 -0
- package/dist/chunk-5BMFMDQR.js +18 -0
- package/dist/chunk-64Q7BBIQ.js +1347 -0
- package/dist/chunk-6YUAACW6.js +866 -0
- package/dist/chunk-AIUOLJZA.js +412 -0
- package/dist/chunk-CVV7FOMQ.js +175 -0
- package/dist/chunk-EHF2WQUR.js +2042 -0
- package/dist/chunk-EVHTAXQG.js +273 -0
- package/dist/chunk-FW23FLAF.js +56 -0
- package/dist/chunk-GKEPMDXO.js +410 -0
- package/dist/chunk-H7MHGYF3.js +341 -0
- package/dist/chunk-MRQ7VJFJ.js +1074 -0
- package/dist/chunk-NWFYTFMI.js +65 -0
- package/dist/chunk-OEZFYKL6.js +1 -0
- package/dist/chunk-PCYERBNZ.js +1564 -0
- package/dist/chunk-PUPVZRKZ.js +668 -0
- package/dist/chunk-SUKFJ73W.js +217 -0
- package/dist/config-Bm1hmXhD.d.ts +1380 -0
- package/dist/coords-LPR_MPyp.d.ts +22 -0
- package/dist/core/index.d.ts +135 -0
- package/dist/core/index.js +5 -0
- package/dist/device-normalize-BaThgnH7.d.ts +43 -0
- package/dist/device-session-KZVGLPOI.js +2 -0
- package/dist/i18n/index.d.ts +30 -0
- package/dist/i18n/index.js +1 -0
- package/dist/mirror-session-C_1vFkRV.d.ts +108 -0
- package/dist/nav/index.d.ts +19 -0
- package/dist/nav/index.js +1 -0
- package/dist/react/index.d.ts +487 -0
- package/dist/react/index.js +7 -0
- package/dist/script-debugger-Cw5jVRoz.d.ts +114 -0
- package/dist/task-agent-runner-eD9-SCJz.d.ts +160 -0
- package/dist/types-C2WXVcam.d.ts +1005 -0
- package/dist/ui/billing.d.ts +48 -0
- package/dist/ui/billing.js +425 -0
- package/dist/ui/device-create.d.ts +55 -0
- package/dist/ui/device-create.js +9 -0
- package/dist/ui/device-mirror.d.ts +42 -0
- package/dist/ui/device-mirror.js +10 -0
- package/dist/ui/devices.d.ts +223 -0
- package/dist/ui/devices.js +15 -0
- package/dist/ui/editor.d.ts +64 -0
- package/dist/ui/editor.js +344 -0
- package/dist/ui/index.d.ts +104 -0
- package/dist/ui/index.js +676 -0
- package/dist/ui/inspector.d.ts +5 -0
- package/dist/ui/inspector.js +1250 -0
- package/dist/ui/market.d.ts +10 -0
- package/dist/ui/market.js +60 -0
- package/dist/ui/mirror.d.ts +52 -0
- package/dist/ui/mirror.js +3 -0
- package/dist/ui/primitives/index.d.ts +101 -0
- package/dist/ui/primitives/index.js +1 -0
- package/dist/ui/script-studio.d.ts +85 -0
- package/dist/ui/script-studio.js +10 -0
- package/dist/ui/scripts.d.ts +18 -0
- package/dist/ui/scripts.js +210 -0
- package/dist/ui/tasks.d.ts +10 -0
- package/dist/ui/tasks.js +1039 -0
- package/dist/use-script-run-Diay7ibY.d.ts +50 -0
- package/package.json +76 -4
- package/styles.css +97 -0
- package/tailwind-preset.cjs +84 -0
|
@@ -0,0 +1,410 @@
|
|
|
1
|
+
import { joinUrl, AixAuthError, AixLocked, AixError, resolveConfig, ReconnectingSocket, AixTimeout, parseIncoming, isRetFrame, errorFromRet, isFridaEventFrame, isScriptletEventFrame, isStatusFrame, executionWsTicketUrl, isExecutionFrame, executionStreamEventsAfter } from './chunk-PCYERBNZ.js';
|
|
2
|
+
|
|
3
|
+
// src/core/ws-access.ts
|
|
4
|
+
async function mintDeviceAccess(cfg, deviceId) {
|
|
5
|
+
const url = joinUrl(cfg.baseUrl, `/api/devices/${encodeURIComponent(deviceId)}/ws-access`);
|
|
6
|
+
const headers = { ...await cfg.auth.headers("caller"), "content-type": "application/json" };
|
|
7
|
+
const resp = await cfg.fetchFn(url, { method: "POST", headers });
|
|
8
|
+
if (!resp.ok) {
|
|
9
|
+
const body = await resp.text().catch(() => "");
|
|
10
|
+
if (resp.status === 401 || resp.status === 403) throw new AixAuthError(`ws-access ${resp.status}: ${body}`);
|
|
11
|
+
if (resp.status === 423) throw new AixLocked(`device locked: ${body}`);
|
|
12
|
+
throw new AixError(`ws-access ${resp.status}: ${body}`);
|
|
13
|
+
}
|
|
14
|
+
return await resp.json();
|
|
15
|
+
}
|
|
16
|
+
function wsUrlFromAccess(a, prefer = "direct") {
|
|
17
|
+
const base = prefer === "direct" && a.direct_ws ? a.direct_ws : a.relay_ws;
|
|
18
|
+
const sep = base.includes("?") ? "&" : "?";
|
|
19
|
+
return `${base}${sep}token=${encodeURIComponent(a.token)}`;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// src/core/device-channel.ts
|
|
23
|
+
function bytesToB64(b) {
|
|
24
|
+
let bin = "";
|
|
25
|
+
for (let i = 0; i < b.length; i++) bin += String.fromCharCode(b[i]);
|
|
26
|
+
if (typeof btoa === "function") return btoa(bin);
|
|
27
|
+
return globalThis.Buffer?.from(b)?.toString("base64") ?? btoa(bin);
|
|
28
|
+
}
|
|
29
|
+
function b64ToBytes(s) {
|
|
30
|
+
const bin = typeof atob === "function" ? atob(s) : globalThis.Buffer?.from(s, "base64")?.toString("binary") ?? atob(s);
|
|
31
|
+
const out = new Uint8Array(bin.length);
|
|
32
|
+
for (let i = 0; i < bin.length; i++) out[i] = bin.charCodeAt(i);
|
|
33
|
+
return out;
|
|
34
|
+
}
|
|
35
|
+
function fallbackUuid() {
|
|
36
|
+
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
|
|
37
|
+
const r = Math.random() * 16 | 0;
|
|
38
|
+
const v = c === "x" ? r : r & 3 | 8;
|
|
39
|
+
return v.toString(16);
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
var DeviceTunnel = class {
|
|
43
|
+
constructor(streamId, sink) {
|
|
44
|
+
this.streamId = streamId;
|
|
45
|
+
this.sink = sink;
|
|
46
|
+
this.dataListeners = /* @__PURE__ */ new Set();
|
|
47
|
+
}
|
|
48
|
+
send(bytes) {
|
|
49
|
+
this.sink({ t: "data", stream: this.streamId, b64: bytesToB64(bytes) });
|
|
50
|
+
}
|
|
51
|
+
onData(cb) {
|
|
52
|
+
this.dataListeners.add(cb);
|
|
53
|
+
return () => this.dataListeners.delete(cb);
|
|
54
|
+
}
|
|
55
|
+
_deliver(b) {
|
|
56
|
+
for (const cb of this.dataListeners) cb(b);
|
|
57
|
+
}
|
|
58
|
+
close() {
|
|
59
|
+
this.sink({ t: "close", stream: this.streamId });
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
var DEFAULT_TIMEOUT_MS = 3e4;
|
|
63
|
+
var DeviceChannel = class _DeviceChannel {
|
|
64
|
+
constructor(cfg, deviceId) {
|
|
65
|
+
this.cfg = cfg;
|
|
66
|
+
this.deviceId = deviceId;
|
|
67
|
+
this.pending = /* @__PURE__ */ new Map();
|
|
68
|
+
this.corr = 0;
|
|
69
|
+
this.channelClosed = false;
|
|
70
|
+
this.fridaListeners = /* @__PURE__ */ new Set();
|
|
71
|
+
this.scriptletListeners = /* @__PURE__ */ new Set();
|
|
72
|
+
this.statusListeners = /* @__PURE__ */ new Set();
|
|
73
|
+
this.tunnels = /* @__PURE__ */ new Map();
|
|
74
|
+
}
|
|
75
|
+
static async open(cfg, deviceId, opts) {
|
|
76
|
+
const resolved = "fetchFn" in cfg ? cfg : resolveConfig(cfg);
|
|
77
|
+
const prefer = opts?.prefer ?? "direct";
|
|
78
|
+
const ch = new _DeviceChannel(resolved, deviceId);
|
|
79
|
+
const socket = new ReconnectingSocket({
|
|
80
|
+
urlProvider: async () => {
|
|
81
|
+
const access = await mintDeviceAccess(resolved, deviceId);
|
|
82
|
+
return wsUrlFromAccess(access, prefer);
|
|
83
|
+
},
|
|
84
|
+
wsFactory: resolved.wsFactory,
|
|
85
|
+
onMessage: (text) => ch.onMessage(text)
|
|
86
|
+
});
|
|
87
|
+
ch.socketRef = socket;
|
|
88
|
+
await socket.start();
|
|
89
|
+
return ch;
|
|
90
|
+
}
|
|
91
|
+
nextId() {
|
|
92
|
+
return `c${++this.corr}`;
|
|
93
|
+
}
|
|
94
|
+
request(frame, timeoutMs = DEFAULT_TIMEOUT_MS) {
|
|
95
|
+
if (this.channelClosed) return Promise.reject(new AixError("channel is closed", "offline"));
|
|
96
|
+
const id = this.nextId();
|
|
97
|
+
const full = { ...frame, id };
|
|
98
|
+
return new Promise((resolve, reject) => {
|
|
99
|
+
const timer = setTimeout(() => {
|
|
100
|
+
this.pending.delete(id);
|
|
101
|
+
reject(new AixTimeout(`command ${full.t} timed out after ${timeoutMs}ms`));
|
|
102
|
+
}, timeoutMs);
|
|
103
|
+
this.pending.set(id, { resolve, reject, timer });
|
|
104
|
+
try {
|
|
105
|
+
this.socketRef.send(JSON.stringify(full));
|
|
106
|
+
} catch (e) {
|
|
107
|
+
clearTimeout(timer);
|
|
108
|
+
this.pending.delete(id);
|
|
109
|
+
reject(new AixError(String(e)));
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
on(event, cb) {
|
|
114
|
+
if (event === "frida_event") {
|
|
115
|
+
this.fridaListeners.add(cb);
|
|
116
|
+
return () => this.fridaListeners.delete(cb);
|
|
117
|
+
}
|
|
118
|
+
if (event === "scriptlet_event") {
|
|
119
|
+
this.scriptletListeners.add(cb);
|
|
120
|
+
return () => this.scriptletListeners.delete(cb);
|
|
121
|
+
}
|
|
122
|
+
this.statusListeners.add(cb);
|
|
123
|
+
return () => this.statusListeners.delete(cb);
|
|
124
|
+
}
|
|
125
|
+
fridaRun(script, target, spawn = false) {
|
|
126
|
+
return this.request({ t: "frida_run", script, target, spawn });
|
|
127
|
+
}
|
|
128
|
+
fridaCall(runId, method, args) {
|
|
129
|
+
return this.request({ t: "frida_call", run_id: runId, method, args });
|
|
130
|
+
}
|
|
131
|
+
fridaCancel(runId) {
|
|
132
|
+
return this.request({ t: "frida_cancel", run_id: runId });
|
|
133
|
+
}
|
|
134
|
+
scriptletRun(script, vars) {
|
|
135
|
+
return this.request({ t: "scriptlet_run", script, vars });
|
|
136
|
+
}
|
|
137
|
+
scriptletCall(runId, method, args) {
|
|
138
|
+
return this.request({ t: "scriptlet_call", run_id: runId, method, args });
|
|
139
|
+
}
|
|
140
|
+
scriptletCancel(runId) {
|
|
141
|
+
return this.request({ t: "scriptlet_cancel", run_id: runId });
|
|
142
|
+
}
|
|
143
|
+
onMessage(text) {
|
|
144
|
+
const frame = parseIncoming(text);
|
|
145
|
+
if (isRetFrame(frame) && typeof frame.id === "string") {
|
|
146
|
+
const p = this.pending.get(frame.id);
|
|
147
|
+
if (!p) return;
|
|
148
|
+
this.pending.delete(frame.id);
|
|
149
|
+
clearTimeout(p.timer);
|
|
150
|
+
if (frame.ok) p.resolve(frame.value);
|
|
151
|
+
else p.reject(errorFromRet(frame.error ?? "unknown error"));
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
if (frame.t === "data") {
|
|
155
|
+
const raw = frame;
|
|
156
|
+
if (typeof raw.stream === "string") {
|
|
157
|
+
const b64 = typeof raw.b64 === "string" ? raw.b64 : "";
|
|
158
|
+
this.tunnels.get(raw.stream)?._deliver(b64ToBytes(b64));
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
if (isFridaEventFrame(frame)) {
|
|
163
|
+
const { t: _t, ...payload } = frame;
|
|
164
|
+
for (const cb of this.fridaListeners) cb(payload);
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
if (isScriptletEventFrame(frame)) {
|
|
168
|
+
const { t: _t, ...payload } = frame;
|
|
169
|
+
for (const cb of this.scriptletListeners) cb(payload);
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
if (isStatusFrame(frame)) {
|
|
173
|
+
const { t: _t, ...payload } = frame;
|
|
174
|
+
for (const cb of this.statusListeners) cb(payload);
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
openTunnel(kind, addr) {
|
|
179
|
+
if (this.channelClosed) throw new AixError("channel is closed", "offline");
|
|
180
|
+
if (!this.socketRef.isConnected()) throw new AixError("socket not connected", "offline");
|
|
181
|
+
const streamId = globalThis.crypto?.randomUUID?.() ?? fallbackUuid();
|
|
182
|
+
const send = (frame) => this.socketRef.send(JSON.stringify(frame));
|
|
183
|
+
const tun = new DeviceTunnel(streamId, send);
|
|
184
|
+
this.tunnels.set(streamId, tun);
|
|
185
|
+
send({ t: "open", stream: streamId, kind, addr });
|
|
186
|
+
return tun;
|
|
187
|
+
}
|
|
188
|
+
adbExec(argv, o) {
|
|
189
|
+
return this.request({ t: "adb_exec", argv, timeout_ms: o?.timeoutMs }, o?.timeoutMs ?? DEFAULT_TIMEOUT_MS);
|
|
190
|
+
}
|
|
191
|
+
wdaReq(method, path, body) {
|
|
192
|
+
return this.request({ t: "wda_req", method, path, body });
|
|
193
|
+
}
|
|
194
|
+
close() {
|
|
195
|
+
this.channelClosed = true;
|
|
196
|
+
for (const tun of this.tunnels.values()) {
|
|
197
|
+
try {
|
|
198
|
+
tun.close();
|
|
199
|
+
} catch {
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
this.tunnels.clear();
|
|
203
|
+
for (const [, p] of this.pending) {
|
|
204
|
+
clearTimeout(p.timer);
|
|
205
|
+
p.reject(new AixError("channel closed", "offline"));
|
|
206
|
+
}
|
|
207
|
+
this.pending.clear();
|
|
208
|
+
this.socketRef.close();
|
|
209
|
+
}
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
// src/core/flow-run.ts
|
|
213
|
+
var FlowRun = class _FlowRun {
|
|
214
|
+
constructor(taskId, targets, socket) {
|
|
215
|
+
this.taskId = taskId;
|
|
216
|
+
this.targets = targets;
|
|
217
|
+
this.socket = socket;
|
|
218
|
+
this.eventListeners = /* @__PURE__ */ new Set();
|
|
219
|
+
this.doneListeners = /* @__PURE__ */ new Set();
|
|
220
|
+
this.finished = /* @__PURE__ */ new Set();
|
|
221
|
+
this.doneFired = false;
|
|
222
|
+
this.cursor = 0;
|
|
223
|
+
}
|
|
224
|
+
static async submit(cfg, body) {
|
|
225
|
+
const resolved = "fetchFn" in cfg ? cfg : resolveConfig(cfg);
|
|
226
|
+
const url = joinUrl(resolved.baseUrl, "/api/webhook/tasks");
|
|
227
|
+
const payload = { device_ids: body.deviceIds };
|
|
228
|
+
if (body.flow !== void 0) payload.flow = body.flow;
|
|
229
|
+
if (body.scriptId !== void 0) payload.script_id = body.scriptId;
|
|
230
|
+
if (body.params !== void 0) payload.params = body.params;
|
|
231
|
+
if (body.mode !== void 0) payload.mode = body.mode;
|
|
232
|
+
if (body.expiresIn !== void 0) payload.expires_in = body.expiresIn;
|
|
233
|
+
const headers = { ...await resolved.auth.headers("caller"), "content-type": "application/json" };
|
|
234
|
+
const resp = await resolved.fetchFn(url, { method: "POST", headers, body: JSON.stringify(payload) });
|
|
235
|
+
if (!resp.ok) {
|
|
236
|
+
const t = await resp.text().catch(() => "");
|
|
237
|
+
if (resp.status === 401 || resp.status === 403) throw new AixAuthError(`tasks ${resp.status}: ${t}`);
|
|
238
|
+
throw new AixError(`tasks ${resp.status}: ${t}`);
|
|
239
|
+
}
|
|
240
|
+
const data = await resp.json();
|
|
241
|
+
const targets = (data.targets ?? []).map((x) => x.device_id);
|
|
242
|
+
let fr = null;
|
|
243
|
+
let cursor = 0;
|
|
244
|
+
let processing = Promise.resolve();
|
|
245
|
+
let processFrame;
|
|
246
|
+
const fetchCatchup = async (after) => {
|
|
247
|
+
let nextAfter = after;
|
|
248
|
+
do {
|
|
249
|
+
const page = await executionStreamEventsAfter(resolved, data.task_id, nextAfter);
|
|
250
|
+
for (const event of page.events) await processFrame(event);
|
|
251
|
+
nextAfter = page.next_cursor ?? cursor;
|
|
252
|
+
if (!page.has_more) break;
|
|
253
|
+
} while (true);
|
|
254
|
+
};
|
|
255
|
+
processFrame = async (frame) => {
|
|
256
|
+
if (frame.run_id !== data.task_id) return;
|
|
257
|
+
if (typeof frame.cursor !== "number") {
|
|
258
|
+
fr?.onMessage(JSON.stringify({ run_id: frame.run_id, device_id: frame.device_id, event: frame.event }));
|
|
259
|
+
return;
|
|
260
|
+
}
|
|
261
|
+
if (frame.cursor <= cursor) return;
|
|
262
|
+
if (frame.cursor > cursor + 1) {
|
|
263
|
+
await fetchCatchup(cursor);
|
|
264
|
+
return processFrame(frame);
|
|
265
|
+
}
|
|
266
|
+
cursor = frame.cursor;
|
|
267
|
+
fr?.onMessage(JSON.stringify({ run_id: frame.run_id, device_id: frame.device_id, cursor: frame.cursor, event: frame.event }));
|
|
268
|
+
};
|
|
269
|
+
const socket = new ReconnectingSocket({
|
|
270
|
+
// urlProvider runs again after a close, minting a fresh one-time ticket
|
|
271
|
+
// and resuming after the latest durable event cursor.
|
|
272
|
+
urlProvider: async () => executionWsTicketUrl(resolved, data.task_id, cursor),
|
|
273
|
+
wsFactory: resolved.wsFactory,
|
|
274
|
+
onMessage: (text) => {
|
|
275
|
+
let frame;
|
|
276
|
+
try {
|
|
277
|
+
frame = JSON.parse(text);
|
|
278
|
+
} catch {
|
|
279
|
+
return;
|
|
280
|
+
}
|
|
281
|
+
if (typeof frame.cursor !== "number") {
|
|
282
|
+
fr?.onMessage(text);
|
|
283
|
+
return;
|
|
284
|
+
}
|
|
285
|
+
processing = processing.then(() => processFrame(frame)).catch(() => void 0);
|
|
286
|
+
}
|
|
287
|
+
});
|
|
288
|
+
fr = new _FlowRun(data.task_id, targets, socket);
|
|
289
|
+
await socket.start();
|
|
290
|
+
if (targets.length === 0) fr.fireDoneIfEmpty();
|
|
291
|
+
return fr;
|
|
292
|
+
}
|
|
293
|
+
on(event, cb) {
|
|
294
|
+
if (event === "event") {
|
|
295
|
+
this.eventListeners.add(cb);
|
|
296
|
+
return () => this.eventListeners.delete(cb);
|
|
297
|
+
}
|
|
298
|
+
const doneCb = cb;
|
|
299
|
+
this.doneListeners.add(doneCb);
|
|
300
|
+
if (this.doneFired) queueMicrotask(() => doneCb());
|
|
301
|
+
return () => this.doneListeners.delete(doneCb);
|
|
302
|
+
}
|
|
303
|
+
/** Called (via microtask) when submit() returns targets:[]. */
|
|
304
|
+
fireDoneIfEmpty() {
|
|
305
|
+
if (!this.doneFired && this.targets.length === 0) {
|
|
306
|
+
this.doneFired = true;
|
|
307
|
+
for (const cb of this.doneListeners) cb();
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
onMessage(text) {
|
|
311
|
+
let frame;
|
|
312
|
+
try {
|
|
313
|
+
frame = JSON.parse(text);
|
|
314
|
+
} catch {
|
|
315
|
+
return;
|
|
316
|
+
}
|
|
317
|
+
if (!isExecutionFrame(frame)) return;
|
|
318
|
+
const f = frame;
|
|
319
|
+
if (typeof f.cursor === "number") {
|
|
320
|
+
if (f.cursor <= this.cursor) return;
|
|
321
|
+
this.cursor = f.cursor;
|
|
322
|
+
}
|
|
323
|
+
for (const cb of this.eventListeners) cb({ deviceId: f.device_id, event: f.event });
|
|
324
|
+
if (f.event.kind === "finished") {
|
|
325
|
+
this.finished.add(f.device_id);
|
|
326
|
+
if (!this.doneFired && this.targets.length > 0 && this.finished.size >= this.targets.length) {
|
|
327
|
+
this.doneFired = true;
|
|
328
|
+
for (const cb of this.doneListeners) cb();
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
close() {
|
|
333
|
+
this.socket.close();
|
|
334
|
+
}
|
|
335
|
+
};
|
|
336
|
+
|
|
337
|
+
// src/core/script-debugger.ts
|
|
338
|
+
var ScriptDebugger = class {
|
|
339
|
+
constructor(opts) {
|
|
340
|
+
this.opts = opts;
|
|
341
|
+
this.runId = null;
|
|
342
|
+
this.unsub = null;
|
|
343
|
+
this.flow = null;
|
|
344
|
+
this.listeners = /* @__PURE__ */ new Set();
|
|
345
|
+
}
|
|
346
|
+
get engine() {
|
|
347
|
+
return this.opts.engine;
|
|
348
|
+
}
|
|
349
|
+
get currentRunId() {
|
|
350
|
+
return this.runId;
|
|
351
|
+
}
|
|
352
|
+
on(_event, cb) {
|
|
353
|
+
this.listeners.add(cb);
|
|
354
|
+
return () => this.listeners.delete(cb);
|
|
355
|
+
}
|
|
356
|
+
async run(input) {
|
|
357
|
+
if (this.opts.engine === "flow") {
|
|
358
|
+
this.flow = await FlowRun.submit(this.opts.cfg, {
|
|
359
|
+
flow: input.flow,
|
|
360
|
+
scriptId: input.scriptId,
|
|
361
|
+
deviceIds: input.deviceIds ?? [],
|
|
362
|
+
params: input.params
|
|
363
|
+
});
|
|
364
|
+
this.runId = this.flow.taskId;
|
|
365
|
+
this.unsub = this.flow.on("event", (e) => {
|
|
366
|
+
for (const cb of this.listeners) cb({ runId: this.runId ?? this.flow.taskId, name: e.event.kind, data: { deviceId: e.deviceId, ...e.event } });
|
|
367
|
+
});
|
|
368
|
+
return { runId: this.flow.taskId };
|
|
369
|
+
}
|
|
370
|
+
const res = this.opts.engine === "frida" ? await this.opts.channel.fridaRun(input.script ?? "", input.target, input.spawn) : await this.opts.channel.scriptletRun(input.script ?? "", input.vars);
|
|
371
|
+
this.runId = res.run_id;
|
|
372
|
+
this.attachChannel();
|
|
373
|
+
return { runId: res.run_id };
|
|
374
|
+
}
|
|
375
|
+
async call(method, args) {
|
|
376
|
+
if (this.opts.engine === "flow") throw new AixError("flow engine has no call(); it runs a whole flow");
|
|
377
|
+
const id = this.runId;
|
|
378
|
+
if (!id) throw new AixError("no active run; call run() first");
|
|
379
|
+
return this.opts.engine === "frida" ? this.opts.channel.fridaCall(id, method, args) : this.opts.channel.scriptletCall(id, method, args);
|
|
380
|
+
}
|
|
381
|
+
async cancel() {
|
|
382
|
+
const id = this.runId;
|
|
383
|
+
this.detach();
|
|
384
|
+
this.runId = null;
|
|
385
|
+
if (this.opts.engine === "flow") {
|
|
386
|
+
this.flow?.close();
|
|
387
|
+
this.flow = null;
|
|
388
|
+
return;
|
|
389
|
+
}
|
|
390
|
+
if (!id) return;
|
|
391
|
+
if (this.opts.engine === "frida") await this.opts.channel.fridaCancel(id);
|
|
392
|
+
else await this.opts.channel.scriptletCancel(id);
|
|
393
|
+
}
|
|
394
|
+
attachChannel() {
|
|
395
|
+
if (this.opts.engine === "flow") return;
|
|
396
|
+
this.detach();
|
|
397
|
+
const handler = (e) => {
|
|
398
|
+
if (!this.runId || e.run_id !== this.runId) return;
|
|
399
|
+
for (const cb of this.listeners) cb({ runId: e.run_id, name: e.name, data: e.data });
|
|
400
|
+
};
|
|
401
|
+
if (this.opts.engine === "frida") this.unsub = this.opts.channel.on("frida_event", handler);
|
|
402
|
+
else this.unsub = this.opts.channel.on("scriptlet_event", handler);
|
|
403
|
+
}
|
|
404
|
+
detach() {
|
|
405
|
+
this.unsub?.();
|
|
406
|
+
this.unsub = null;
|
|
407
|
+
}
|
|
408
|
+
};
|
|
409
|
+
|
|
410
|
+
export { DeviceChannel, DeviceTunnel, FlowRun, ScriptDebugger, mintDeviceAccess, wsUrlFromAccess };
|