@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.
Files changed (67) hide show
  1. package/README.md +239 -2
  2. package/dist/ScriptStudio-DaCqOF5H.d.ts +43 -0
  3. package/dist/agent-runner-V60B0is1.d.ts +57 -0
  4. package/dist/automation-queue-8HqRJV_K.d.ts +72 -0
  5. package/dist/chunk-536WVCTB.js +5744 -0
  6. package/dist/chunk-5BMFMDQR.js +18 -0
  7. package/dist/chunk-64Q7BBIQ.js +1347 -0
  8. package/dist/chunk-6YUAACW6.js +866 -0
  9. package/dist/chunk-AIUOLJZA.js +412 -0
  10. package/dist/chunk-CVV7FOMQ.js +175 -0
  11. package/dist/chunk-EHF2WQUR.js +2042 -0
  12. package/dist/chunk-EVHTAXQG.js +273 -0
  13. package/dist/chunk-FW23FLAF.js +56 -0
  14. package/dist/chunk-GKEPMDXO.js +410 -0
  15. package/dist/chunk-H7MHGYF3.js +341 -0
  16. package/dist/chunk-MRQ7VJFJ.js +1074 -0
  17. package/dist/chunk-NWFYTFMI.js +65 -0
  18. package/dist/chunk-OEZFYKL6.js +1 -0
  19. package/dist/chunk-PCYERBNZ.js +1564 -0
  20. package/dist/chunk-PUPVZRKZ.js +668 -0
  21. package/dist/chunk-SUKFJ73W.js +217 -0
  22. package/dist/config-Bm1hmXhD.d.ts +1380 -0
  23. package/dist/coords-LPR_MPyp.d.ts +22 -0
  24. package/dist/core/index.d.ts +135 -0
  25. package/dist/core/index.js +5 -0
  26. package/dist/device-normalize-BaThgnH7.d.ts +43 -0
  27. package/dist/device-session-KZVGLPOI.js +2 -0
  28. package/dist/i18n/index.d.ts +30 -0
  29. package/dist/i18n/index.js +1 -0
  30. package/dist/mirror-session-C_1vFkRV.d.ts +108 -0
  31. package/dist/nav/index.d.ts +19 -0
  32. package/dist/nav/index.js +1 -0
  33. package/dist/react/index.d.ts +487 -0
  34. package/dist/react/index.js +7 -0
  35. package/dist/script-debugger-Cw5jVRoz.d.ts +114 -0
  36. package/dist/task-agent-runner-eD9-SCJz.d.ts +160 -0
  37. package/dist/types-C2WXVcam.d.ts +1005 -0
  38. package/dist/ui/billing.d.ts +48 -0
  39. package/dist/ui/billing.js +425 -0
  40. package/dist/ui/device-create.d.ts +55 -0
  41. package/dist/ui/device-create.js +9 -0
  42. package/dist/ui/device-mirror.d.ts +42 -0
  43. package/dist/ui/device-mirror.js +10 -0
  44. package/dist/ui/devices.d.ts +223 -0
  45. package/dist/ui/devices.js +15 -0
  46. package/dist/ui/editor.d.ts +64 -0
  47. package/dist/ui/editor.js +344 -0
  48. package/dist/ui/index.d.ts +104 -0
  49. package/dist/ui/index.js +676 -0
  50. package/dist/ui/inspector.d.ts +5 -0
  51. package/dist/ui/inspector.js +1250 -0
  52. package/dist/ui/market.d.ts +10 -0
  53. package/dist/ui/market.js +60 -0
  54. package/dist/ui/mirror.d.ts +52 -0
  55. package/dist/ui/mirror.js +3 -0
  56. package/dist/ui/primitives/index.d.ts +101 -0
  57. package/dist/ui/primitives/index.js +1 -0
  58. package/dist/ui/script-studio.d.ts +85 -0
  59. package/dist/ui/script-studio.js +10 -0
  60. package/dist/ui/scripts.d.ts +18 -0
  61. package/dist/ui/scripts.js +210 -0
  62. package/dist/ui/tasks.d.ts +10 -0
  63. package/dist/ui/tasks.js +1039 -0
  64. package/dist/use-script-run-Diay7ibY.d.ts +50 -0
  65. package/package.json +76 -4
  66. package/styles.css +97 -0
  67. package/tailwind-preset.cjs +84 -0
@@ -0,0 +1,1380 @@
1
+ import { w as AuthResult, aH as SendCodeResult, a$ as VerifyCodeResult, ar as ProjectUserResult, ap as Project, I as CreateProjectInput, J as CreateProjectResult, as as ProvisionUserInput, at as ProvisionUserResult, D as DeviceRow, P as ProvisionSpec, a as ProvisionResult, C as CatalogBrand, R as DeviceOp, aG as ScreenshotResult, X as ElementNode, am as PowerAction, r as AppCandidate, aD as RecycledDeviceRow, aw as ProxyPatchResult, ax as ProxyProbe, T as DeviceProfileView, aK as SetProfileResult, ao as ProfileOverrides, a3 as InstallAntiDetectResult, aJ as SetDisguiseResult, N as NodeRow, aE as RegisterNodeInput, aF as RegisterNodeResult, aZ as UpdateNodeInput, aI as SetDataRootResult, o as AgentTaskSubmitInput, p as AgentTaskSubmitResult, m as AgentTaskReadResult, l as AgentTaskEventsResult, b as DiscoveredDevice, i as AdbScanResult, a8 as IosScanResult, a4 as IosAccountState, a7 as IosLoginResult, a6 as IosLibraryApp, E as Cluster, G as CreateClusterInput, f as ScriptSummary, S as Script, aL as Shortcut, ae as NewShortcut, ab as Locator, H as CreateLocatorInput, $ as FleetRunSubmit, Z as FleetRunDetail, aT as TaskStatus, aR as TaskListFilter, aS as TaskListResult, aQ as TaskEventsResult, b0 as WebhookConfig, L as CreateWebhookInput, aa as LlmTestResult, U as DeviceRate, aB as RateInput, y as BillingPlan, aj as PlanInput, V as DeviceUsageSession, aC as RebateLedgerRow, ak as PlatformOem, al as PlatformOemInput, a0 as ImageArtifact, a1 as ImageInput, a2 as ImageOption, x as BalanceResp, aq as ProjectSubscription, aO as SubscribeInput, ai as OwnNodeDailyCharge, aX as TopupInput, aY as TopupResult, q as ApiTokenRow, K as CreateTokenInput, M as CreatedToken, t as AppTemplate, ad as NewAppTemplate, ah as NodeScope } from './types-C2WXVcam.js';
2
+
3
+ type OutgoingFrame = {
4
+ t: 'adb_exec';
5
+ id: string;
6
+ argv: string[];
7
+ timeout_ms?: number;
8
+ } | {
9
+ t: 'wda_req';
10
+ id: string;
11
+ method: string;
12
+ path: string;
13
+ body?: unknown;
14
+ } | {
15
+ t: 'frida_run';
16
+ id: string;
17
+ script: string;
18
+ target?: unknown;
19
+ spawn?: boolean;
20
+ } | {
21
+ t: 'frida_call';
22
+ id: string;
23
+ run_id: string;
24
+ method: string;
25
+ args?: unknown;
26
+ } | {
27
+ t: 'frida_cancel';
28
+ id: string;
29
+ run_id: string;
30
+ } | {
31
+ t: 'scriptlet_run';
32
+ id: string;
33
+ script: string;
34
+ vars?: unknown;
35
+ } | {
36
+ t: 'scriptlet_call';
37
+ id: string;
38
+ run_id: string;
39
+ method: string;
40
+ args?: unknown;
41
+ } | {
42
+ t: 'scriptlet_cancel';
43
+ id: string;
44
+ run_id: string;
45
+ } | {
46
+ t: 'open';
47
+ stream: string;
48
+ kind: 'adb' | 'wda' | 'frida' | 'tcp';
49
+ addr?: string;
50
+ } | {
51
+ t: 'data';
52
+ stream: string;
53
+ b64: string;
54
+ } | {
55
+ t: 'close';
56
+ stream: string;
57
+ };
58
+ interface RetFrame {
59
+ t: 'ret';
60
+ id: string | null;
61
+ ok: boolean;
62
+ value?: unknown;
63
+ error?: string;
64
+ }
65
+ interface FridaEventFrame {
66
+ t: 'frida_event';
67
+ run_id: string;
68
+ name: string;
69
+ data: unknown;
70
+ }
71
+ interface ScriptletEventFrame {
72
+ t: 'scriptlet_event';
73
+ run_id: string;
74
+ name: string;
75
+ data: unknown;
76
+ }
77
+ interface StatusFrame {
78
+ t: 'status';
79
+ state: string;
80
+ detail?: string;
81
+ stream?: string;
82
+ }
83
+ type RunEventKind = {
84
+ kind: 'started';
85
+ total: number;
86
+ } | {
87
+ kind: 'step';
88
+ index: number;
89
+ status: string;
90
+ } | {
91
+ kind: 'finished';
92
+ status: string;
93
+ error?: string;
94
+ };
95
+ interface ExecutionFrame {
96
+ run_id: string;
97
+ device_id: string;
98
+ event: RunEventKind;
99
+ }
100
+ interface EnvStepMeta {
101
+ id: string;
102
+ label: string;
103
+ }
104
+ interface EnvResultDto {
105
+ vm: string;
106
+ arch: string;
107
+ docker_socket: string;
108
+ env: Record<string, string>;
109
+ }
110
+ type EnvEventKind = {
111
+ kind: 'started';
112
+ total: number;
113
+ steps: EnvStepMeta[];
114
+ } | {
115
+ kind: 'step';
116
+ index: number;
117
+ id: string;
118
+ state: string;
119
+ pct: number;
120
+ detail?: string | null;
121
+ } | {
122
+ kind: 'log';
123
+ index: number;
124
+ id: string;
125
+ line: string;
126
+ } | {
127
+ kind: 'finished';
128
+ ok: boolean;
129
+ result?: EnvResultDto | null;
130
+ failed_id?: string | null;
131
+ code?: string | null;
132
+ error?: string | null;
133
+ hint?: string | null;
134
+ };
135
+ type IncomingFrame = RetFrame | FridaEventFrame | ScriptletEventFrame | StatusFrame | ExecutionFrame | {
136
+ t: string;
137
+ [k: string]: unknown;
138
+ };
139
+ declare function parseIncoming(text: string): IncomingFrame;
140
+ declare function isRetFrame(f: IncomingFrame): f is RetFrame;
141
+ declare function isFridaEventFrame(f: IncomingFrame): f is FridaEventFrame;
142
+ declare function isScriptletEventFrame(f: IncomingFrame): f is ScriptletEventFrame;
143
+ declare function isStatusFrame(f: IncomingFrame): f is StatusFrame;
144
+ declare function isExecutionFrame(f: IncomingFrame): f is ExecutionFrame;
145
+ /** An env-base event frame: top-level `kind` ∈ started|step|log|finished, and
146
+ * (unlike ExecutionFrame) no `run_id`/`device_id`. */
147
+ declare function isEnvEventFrame(f: unknown): f is EnvEventKind;
148
+
149
+ /** Per-request auth scope. Mirrors the backend extractors:
150
+ * - `caller` → CallerScope (project key/secret OR user bearer)
151
+ * - `developer` → DeveloperAuth (dev bearer)
152
+ * - `admin` → PlatformAdminAuth (dev bearer whose developer is an admin)
153
+ * - `public` → no credentials (login / send-code / etc). */
154
+ type Scope = 'caller' | 'developer' | 'admin' | 'public';
155
+ /** A single credential the SDK can authenticate with. A config may carry
156
+ * several (e.g. the in-house app holds a developer token AND a project-scoped
157
+ * user token); the provider picks the right one per scope. */
158
+ type Credential = {
159
+ kind: 'project';
160
+ key: string;
161
+ secret: string;
162
+ } | {
163
+ kind: 'user';
164
+ getToken: () => string | Promise<string>;
165
+ } | {
166
+ kind: 'developer';
167
+ getToken: () => string | Promise<string>;
168
+ };
169
+ interface AuthProvider {
170
+ /** Resolve the HTTP headers required for a request at the given scope.
171
+ * Throws AixAuthError when no credential satisfies the scope. */
172
+ headers(scope: Scope): Promise<Record<string, string>>;
173
+ /** The user/project caller credential as a query-param token where one
174
+ * exists (used for WS urls that cannot carry headers). Null otherwise. */
175
+ callerBearer(): Promise<string | null>;
176
+ }
177
+ /** Build an AuthProvider from one or more credentials. Resolution per scope:
178
+ * - caller: project headers first (third-party path), else user bearer.
179
+ * - developer: developer bearer.
180
+ * - admin: developer bearer (admin gate is enforced server-side).
181
+ * - public: no headers. */
182
+ declare function buildAuthProvider(input: Credential | Credential[]): AuthProvider;
183
+ interface CachedUserCredentialOpts {
184
+ /** Mint a fresh user JWT — typically a `fetch` to your own backend's
185
+ * token endpoint. Return the raw token, or `{ token, expiresIn }` (seconds). */
186
+ mint: () => Promise<string | {
187
+ token: string;
188
+ expiresIn?: number;
189
+ }>;
190
+ /** Refresh this many ms before expiry (default 60_000). */
191
+ refreshSkewMs?: number;
192
+ /** TTL when expiry can't be derived (no `expiresIn`, no JWT `exp`); default 10 min. */
193
+ fallbackTtlMs?: number;
194
+ /** Injectable clock (tests). Defaults to `Date.now`. */
195
+ now?: () => number;
196
+ }
197
+ /** A `user` credential that mints a JWT via `opts.mint` and caches it until
198
+ * (near) expiry — derived from `expiresIn` or the JWT `exp` claim. Concurrent
199
+ * `getToken()` calls share one in-flight mint (no stampede).
200
+ *
201
+ * The mint itself stays with the caller (e.g. a third-party frontend fetching
202
+ * from its own backend, which holds the project key/secret) — the SDK never
203
+ * needs the project secret in the browser. */
204
+ declare function createCachedUserCredential(opts: CachedUserCredentialOpts): Extract<Credential, {
205
+ kind: 'user';
206
+ }>;
207
+
208
+ /** 客户端 → 节点的请求帧(均带自增 `id`)。 */
209
+ type DeviceReq = {
210
+ id: string;
211
+ t: 'list';
212
+ path: string;
213
+ } | {
214
+ id: string;
215
+ t: 'delete';
216
+ path: string;
217
+ recursive?: boolean;
218
+ } | {
219
+ id: string;
220
+ t: 'mkdir';
221
+ path: string;
222
+ } | {
223
+ id: string;
224
+ t: 'rename';
225
+ from: string;
226
+ to: string;
227
+ } | {
228
+ id: string;
229
+ t: 'exec';
230
+ cmd: string;
231
+ } | {
232
+ id: string;
233
+ t: 'put';
234
+ name: string;
235
+ size: number;
236
+ sha256: string;
237
+ dest: string;
238
+ } | {
239
+ id: string;
240
+ t: 'chunk';
241
+ offset: number;
242
+ bytes: string;
243
+ } | {
244
+ id: string;
245
+ t: 'eof';
246
+ } | {
247
+ id: string;
248
+ t: 'get';
249
+ src: string;
250
+ } | {
251
+ id: string;
252
+ t: 'open';
253
+ kind: 'tcp';
254
+ host: string;
255
+ port: number;
256
+ } | {
257
+ id: string;
258
+ t: 'open';
259
+ kind: 'exec';
260
+ cmd: string;
261
+ } | {
262
+ id: string;
263
+ t: 'data';
264
+ bytes: string;
265
+ } | {
266
+ id: string;
267
+ t: 'close';
268
+ };
269
+ /** 节点 → 客户端的响应帧(均带原 `id`)。 */
270
+ type DeviceResp = {
271
+ id: string;
272
+ t: 'list_ok';
273
+ entries: FileEntry[];
274
+ } | {
275
+ id: string;
276
+ t: 'ok';
277
+ } | {
278
+ id: string;
279
+ t: 'err';
280
+ message: string;
281
+ } | {
282
+ id: string;
283
+ t: 'stdout';
284
+ data: string;
285
+ } | {
286
+ id: string;
287
+ t: 'stderr';
288
+ data: string;
289
+ } | {
290
+ id: string;
291
+ t: 'exit';
292
+ code: number;
293
+ } | {
294
+ id: string;
295
+ t: 'ack';
296
+ offset: number;
297
+ } | {
298
+ id: string;
299
+ t: 'chunk';
300
+ offset: number;
301
+ bytes: string;
302
+ size?: number;
303
+ } | {
304
+ id: string;
305
+ t: 'done';
306
+ sha256?: string;
307
+ size?: number;
308
+ } | {
309
+ id: string;
310
+ t: 'opened';
311
+ } | {
312
+ id: string;
313
+ t: 'data';
314
+ bytes: string;
315
+ } | {
316
+ id: string;
317
+ t: 'close';
318
+ };
319
+ /** 通用流入参:TCP 端口转发(host:port 为**节点可达**地址)或交互式 exec。 */
320
+ type StreamTarget = {
321
+ kind: 'tcp';
322
+ host: string;
323
+ port: number;
324
+ } | {
325
+ kind: 'exec';
326
+ cmd: string;
327
+ };
328
+ /**
329
+ * 双工字节流:`openStream` / `shell` 的返回。把"终端"泛化成通用字节流,
330
+ * 端口转发也复用它。语义:
331
+ * - `write(bytes)`:向流写字节(TCP write / 子进程 stdin)。
332
+ * - `onData(cb)`:订阅流的出站字节(TCP 读 / 子进程 stdout+stderr 合并)。
333
+ * - `onClose(cb)`:流被对端关闭(TCP 关 / 子进程退出)或本地 `close()` 后触发(一次)。
334
+ * - `close()`:主动关闭流(发 `{id,t:"close"}`)。
335
+ */
336
+ interface DcStream {
337
+ write(bytes: Uint8Array): void;
338
+ onData(cb: (bytes: Uint8Array) => void): void;
339
+ onClose(cb: () => void): void;
340
+ close(): void;
341
+ }
342
+ /** 目录项(节点 `ls` 解析结果)。 */
343
+ interface FileEntry {
344
+ name: string;
345
+ size: number;
346
+ is_dir: boolean;
347
+ mode?: string;
348
+ mtime?: string;
349
+ is_link?: boolean;
350
+ }
351
+ interface TransferProgress {
352
+ /** 已传输字节。 */
353
+ loaded: number;
354
+ /** 总字节(pull 时若未知则为 0,直到收到首个 chunk/done)。 */
355
+ total: number;
356
+ /** 当前所用通道:'dc' = WebRTC 直连,'r2' = R2 中转兜底。 */
357
+ channel: 'dc' | 'r2';
358
+ }
359
+ type ProgressFn = (p: TransferProgress) => void;
360
+ /** 传输结果:实际成功所用通道 + 是否走了兜底。 */
361
+ interface TransferResult {
362
+ channel: 'dc' | 'r2';
363
+ /** 是否走了兜底(true 时 UI 可提示"已用中转通道")。 */
364
+ fellBack: boolean;
365
+ }
366
+ /** push 入参:通用字节 + 文件名 + 设备目标路径(**不接受浏览器 File**)。 */
367
+ interface PushInput {
368
+ /** 待上传的字节。 */
369
+ data: Uint8Array | ArrayBuffer;
370
+ /** 文件名(用于 put 元数据 / R2 兜底的 upload-url)。 */
371
+ filename: string;
372
+ /** 设备目标路径,如 /sdcard/Download/x.apk。 */
373
+ dest: string;
374
+ }
375
+ /** pull 结果:通用字节 + 文件名(下载触发由上层做)。 */
376
+ interface PullResult extends TransferResult {
377
+ /** 拉回的字节。 */
378
+ data: Uint8Array;
379
+ /** 文件名(取自 src 末段)。 */
380
+ filename: string;
381
+ }
382
+ interface TransferOpts {
383
+ onProgress?: ProgressFn;
384
+ /** answerer 建链超时(ms),默认 15000。 */
385
+ dcTimeoutMs?: number;
386
+ }
387
+ /**
388
+ * 设备会话:一条 "ctrl" DataChannel 上多路复用文件管理(list/delete/mkdir/rename)、
389
+ * 终端(exec 流式)、文件传输(put/get)。每个操作分配自增 `id`,节点回应按 `id`
390
+ * 路由,故可并发(一边传文件一边 list)。
391
+ *
392
+ * 用法:`const s = await DeviceSession.open(client, deviceId); ...; s.close()`。
393
+ */
394
+ declare class DeviceSession {
395
+ readonly session: string;
396
+ private readonly dc;
397
+ private readonly dispose;
398
+ private nextId;
399
+ /** id → 该操作的响应路由回调(多路复用核心)。 */
400
+ private readonly pending;
401
+ private closed;
402
+ private constructor();
403
+ /**
404
+ * 打开一个设备会话:dcSession 握手 → answerer 建链 → 等 "ctrl" 通道 open。
405
+ * @param source `AixClient` 或 `AixConfig | ResolvedConfig`
406
+ * @param deviceId 设备 id
407
+ * @param opts.dcTimeoutMs 建链超时(ms),默认 15000
408
+ */
409
+ static open(source: AixClient | AixConfig | ResolvedConfig, deviceId: string, opts?: {
410
+ dcTimeoutMs?: number;
411
+ }): Promise<DeviceSession>;
412
+ private allocId;
413
+ private ensureOpen;
414
+ private sendReq;
415
+ /** 列目录:返回 entries。节点 `ls -la` 解析后回 `list_ok`。 */
416
+ listFiles(path: string): Promise<FileEntry[]>;
417
+ /** 删除文件/目录(recursive=true 走 `rm -rf`)。 */
418
+ deleteFile(path: string, recursive?: boolean): Promise<void>;
419
+ /** 新建目录(`mkdir -p`)。 */
420
+ mkdir(path: string): Promise<void>;
421
+ /** 重命名/移动(`mv from to`)。 */
422
+ rename(from: string, to: string): Promise<void>;
423
+ /** 共享:发一条请求(由 id 构造完整帧),等节点回 `ok` / `err`。 */
424
+ private simpleOk;
425
+ /** 执行命令并收齐输出:返回 `{stdout, stderr, exitCode}`。 */
426
+ exec(cmd: string): Promise<{
427
+ stdout: string;
428
+ stderr: string;
429
+ exitCode: number;
430
+ }>;
431
+ /** 流式执行命令:stdout/stderr 分块回调,resolve 退出码。 */
432
+ execStream(cmd: string, hooks?: {
433
+ onStdout?: (d: string) => void;
434
+ onStderr?: (d: string) => void;
435
+ }): Promise<number>;
436
+ /** 推字节到设备 `dest`:put → chunk... → eof,节点逐块回 `ack` 推进进度。 */
437
+ pushFile(input: PushInput, onProgress?: ProgressFn): Promise<void>;
438
+ /** 从设备 `src` 拉字节,收齐后返回 Uint8Array。节点流式回 chunk + done。 */
439
+ pullFile(src: string, onProgress?: ProgressFn): Promise<Uint8Array>;
440
+ /**
441
+ * 打开一条通用双工字节流(多路复用,沿用同一 DC)。
442
+ * - `{kind:'tcp',host,port}`:节点 `TcpStream::connect(host:port)`,host 是
443
+ * **节点可达**的地址(设备 adb-forward 出的端口、节点本机/局域网服务等)。
444
+ * - `{kind:'exec',cmd}`:节点 spawn `adb shell <cmd>`(交互式:write→stdin,
445
+ * stdout/stderr 合并→onData)。
446
+ * 返回的 `DcStream` 在收到节点 `opened` 后即可用;若节点回 `err` 则 reject。
447
+ */
448
+ openStream(target: StreamTarget): Promise<DcStream>;
449
+ /** 交互式终端:`= openStream({kind:'exec',cmd})`,cmd 默认 `sh`(交互式 shell)。 */
450
+ shell(cmd?: string): Promise<DcStream>;
451
+ /**
452
+ * 本地端口转发(**仅 Node 环境**):用 `node:net` 监听 `localPort`,每个进来的
453
+ * 连接都开一条 `openStream({kind:'tcp',host,port})` 并双向 pipe。返回停止函数
454
+ * (关闭监听 + 现有连接)。浏览器环境无 `node:net`,调用会抛"仅 node 可用"。
455
+ *
456
+ * 用动态 import('node:net') + try,避免把 node:net 打进浏览器 bundle
457
+ * (frontend 不会调用本方法)。
458
+ */
459
+ forwardLocalPort(localPort: number, remote: {
460
+ host: string;
461
+ port: number;
462
+ }): Promise<() => void>;
463
+ /** 关闭会话:断开 DC + pc + 信令 socket,清空 pending。 */
464
+ close(): void;
465
+ }
466
+ /**
467
+ * 推文件到设备:先试通道 A(DeviceSession DC 直连);DC 建链超时或任意失败 →
468
+ * 无感回退通道 B(R2 中转)。**通用入参**:接收字节而非浏览器 File。
469
+ *
470
+ * @param source `AixClient` 或 `AixConfig | ResolvedConfig`
471
+ * @param deviceId 设备 id
472
+ * @param input `{ data: Uint8Array|ArrayBuffer, filename, dest }`
473
+ */
474
+ declare function pushFile(source: AixClient | AixConfig | ResolvedConfig, deviceId: string, input: PushInput, opts?: TransferOpts): Promise<TransferResult>;
475
+ /**
476
+ * 从设备拉文件:先试通道 A;DC 建链超时或失败则无感回退通道 B(R2)。返回
477
+ * `{ data: Uint8Array, filename, channel, fellBack }`——下载触发由上层做。
478
+ *
479
+ * @param source `AixClient` 或 `AixConfig | ResolvedConfig`
480
+ * @param deviceId 设备 id
481
+ * @param src 设备源路径,如 /sdcard/x.log
482
+ */
483
+ declare function pullFile(source: AixClient | AixConfig | ResolvedConfig, deviceId: string, src: string, opts?: TransferOpts): Promise<PullResult>;
484
+
485
+ interface RequestOpts {
486
+ /** Auth scope; defaults to 'caller'. */
487
+ scope?: Scope;
488
+ /** JSON body (serialized unless it is a string already). */
489
+ body?: unknown;
490
+ /** Query params (undefined values are skipped). */
491
+ query?: Record<string, string | number | boolean | undefined | null>;
492
+ /** Extra headers (override auth/content-type when set). */
493
+ headers?: Record<string, string>;
494
+ }
495
+ /** Thin REST client over `ResolvedConfig` (fetchFn + AuthProvider). Maps
496
+ * HTTP failures to the SDK's typed errors and decodes JSON / 204 / text. */
497
+ declare class RestClient {
498
+ private readonly cfg;
499
+ constructor(cfg: ResolvedConfig);
500
+ request<T>(method: string, path: string, opts?: RequestOpts): Promise<T>;
501
+ get<T>(path: string, opts?: RequestOpts): Promise<T>;
502
+ post<T>(path: string, body?: unknown, opts?: RequestOpts): Promise<T>;
503
+ put<T>(path: string, body?: unknown, opts?: RequestOpts): Promise<T>;
504
+ patch<T>(path: string, body?: unknown, opts?: RequestOpts): Promise<T>;
505
+ del<T>(path: string, opts?: RequestOpts): Promise<T>;
506
+ }
507
+ /** Build a RestClient from a resolved config. */
508
+ declare function restClient(cfg: ResolvedConfig): RestClient;
509
+
510
+ /** Developer + user auth flows. `login`/`register`/`send-code`/`verify-code`
511
+ * are public; `set-password`/`project-user` require the developer bearer. */
512
+ declare class AuthApi {
513
+ private readonly c;
514
+ constructor(c: RestClient);
515
+ login(email: string, password: string): Promise<AuthResult>;
516
+ register(email: string, password: string): Promise<AuthResult>;
517
+ sendCode(email: string): Promise<SendCodeResult>;
518
+ verifyCode(email: string, code: string): Promise<VerifyCodeResult>;
519
+ setPassword(password: string): Promise<void>;
520
+ /** Exchange the developer bearer for a project-scoped user token. */
521
+ projectUser(projectId: string): Promise<ProjectUserResult>;
522
+ }
523
+
524
+ /** Project management (developer-scoped). */
525
+ declare class ProjectsApi {
526
+ private readonly c;
527
+ constructor(c: RestClient);
528
+ list(): Promise<Project[]>;
529
+ create(input: CreateProjectInput): Promise<CreateProjectResult>;
530
+ remove(id: string): Promise<void>;
531
+ rotateSecret(id: string): Promise<{
532
+ secret: string;
533
+ }>;
534
+ }
535
+
536
+ /** End-user provisioning — the cloud token-exchange (project-credential scoped).
537
+ *
538
+ * `provision` upserts a user by email, joins them to the authenticated project,
539
+ * and returns a project-scoped USER JWT. This is the standard integration path:
540
+ * an app's own backend (which holds the project key/secret) exchanges an
541
+ * end-user identity for a short-lived user token, then hands that token to its
542
+ * frontend — so the project secret never reaches the browser. Pair the result
543
+ * with `createCachedUserCredential` to feed it back into an `AixClient`.
544
+ *
545
+ * Requires a `project` credential (key/secret): the backend authenticates this
546
+ * endpoint by project headers, so a user bearer alone cannot mint tokens. */
547
+ declare class UsersApi {
548
+ private readonly c;
549
+ constructor(c: RestClient);
550
+ /** POST /api/users — provision (or reuse) a user and mint their token. */
551
+ provision(input: ProvisionUserInput): Promise<ProvisionUserResult>;
552
+ }
553
+
554
+ /** Device listing, lifecycle, and live-op proxy (all caller-scoped).
555
+ * For a persistent WS command channel use `DeviceChannel.open()` instead. */
556
+ declare class DevicesApi {
557
+ private readonly c;
558
+ constructor(c: RestClient);
559
+ list(): Promise<DeviceRow[]>;
560
+ /** Register/upsert a device (`POST /api/devices`). */
561
+ register(body: {
562
+ external_id: string;
563
+ kind?: string;
564
+ name?: string;
565
+ meta?: Record<string, unknown>;
566
+ visibility?: string;
567
+ }): Promise<unknown>;
568
+ /** Provision a virtual device (`POST /api/devices/provision`). The spec is
569
+ * opaque; `node_id` (optional) routes to a specific usable node. */
570
+ provision(spec: ProvisionSpec): Promise<ProvisionResult>;
571
+ /** Brand→model identity catalog for the create-wizard's 品牌 → 机型 picker
572
+ * (`GET /api/devices/profile-catalog`). Static read-only data; the chosen
573
+ * `model` is passed back via `ProvisionSpec.model` to lock the fingerprint base. */
574
+ profileCatalog(): Promise<CatalogBrand[]>;
575
+ /** The single generic live-op entry point, proxied to the owning node. Any
576
+ * node-supported op works here without a dedicated method; the typed helpers
577
+ * below are thin wrappers over it. */
578
+ op<T = unknown>(deviceId: string, op: DeviceOp): Promise<T>;
579
+ screenshot(deviceId: string): Promise<ScreenshotResult>;
580
+ captureUiTree(deviceId: string): Promise<ElementNode>;
581
+ /** Power / lifecycle op (`POST /api/devices/:id/power`). Resolves with the
582
+ * node's power JSON receipt (e.g. `{ rebooted: true }`, `{ stopped: true }`,
583
+ * `{ device_id, reset: true }`) — the backend awaits the node response, so a
584
+ * resolved promise means the op actually ran. Rejects (RestClient throws on
585
+ * non-2xx) carry the node/backend error. */
586
+ power<T = Record<string, unknown>>(deviceId: string, action: PowerAction, extra?: Record<string, unknown>): Promise<T>;
587
+ /** Typed device-control op via the power lane (install / inject / screen-record). */
588
+ control(deviceId: string, op: Record<string, unknown>): Promise<unknown>;
589
+ installApkUrl(deviceId: string, url: string): Promise<unknown>;
590
+ /** Install an iOS App Store app by bundle id via the node's ipatool local
591
+ * library (downloads on miss). `control` lane, fire-and-forget. */
592
+ installIosBundle(deviceId: string, bundleId: string): Promise<unknown>;
593
+ /** Search APKPure for installable apps (`GET /api/apps/search?q=`). */
594
+ searchApps(q: string): Promise<AppCandidate[]>;
595
+ /** Install by APKPure package name; the backend resolves the download url and
596
+ * the node downloads + installs (apk or xapk). `POST /api/devices/:id/install-app`. */
597
+ installByPackage(deviceId: string, pkg: string, version?: string): Promise<unknown>;
598
+ /** Install an apk/xapk already uploaded to R2 (key from `filesUploadUrl`). */
599
+ installByKey(deviceId: string, key: string): Promise<unknown>;
600
+ lease(deviceId: string): Promise<unknown>;
601
+ release(deviceId: string): Promise<unknown>;
602
+ claim(deviceId: string): Promise<unknown>;
603
+ /** 软删除(移入回收站)—`DELETE /api/devices/:id`。在线节点即时销毁容器,
604
+ * 离线节点标记 pending_destroy,待节点重连对账。3 天后自动彻底清除。 */
605
+ delete(deviceId: string): Promise<{
606
+ deleted: boolean;
607
+ pending_destroy: boolean;
608
+ }>;
609
+ /** 列回收站内的设备(`GET /api/devices/recycle`)。 */
610
+ recycle(): Promise<RecycledDeviceRow[]>;
611
+ /** 从回收站恢复(`POST /api/devices/:id/restore`)。容器已销毁的虚拟设备会按存储 spec 重建。 */
612
+ restore(deviceId: string): Promise<{
613
+ restored: boolean;
614
+ rebuilt: boolean;
615
+ device_id?: string;
616
+ }>;
617
+ /** 彻底删除(`DELETE /api/devices/:id/purge`)。不可恢复。 */
618
+ purge(deviceId: string): Promise<{
619
+ purged: boolean;
620
+ }>;
621
+ /** 清空回收站(`DELETE /api/devices/recycle`)。彻底删除当前用户所有回收站设备。 */
622
+ purgeAll(): Promise<{
623
+ purged: number;
624
+ }>;
625
+ /** Update mutable device fields — `PATCH /api/devices/:id`.
626
+ * `ios_transport` switches an iOS device's live-mirror VIDEO source
627
+ * (`replaykit` device-app WebRTC, default | `iosusbadapter` host screenshot
628
+ * over usbmux, driven by go-ios); control is always host-mediated and
629
+ * unaffected. (Legacy value `pymobiledevice3` was renamed to `iosusbadapter`.)
630
+ * `sleep_timeout_min` sets the inactivity sleep timeout (0 = disabled). */
631
+ update(deviceId: string, patch: {
632
+ tags?: string[];
633
+ note?: string;
634
+ ios_transport?: 'replaykit' | 'iosusbadapter';
635
+ proxy?: string;
636
+ sleep_timeout_min?: number;
637
+ meta?: Record<string, unknown>;
638
+ }): Promise<unknown>;
639
+ /** Update only the device proxy and retain a typed patch receipt. */
640
+ updateProxy(deviceId: string, proxy: string): Promise<ProxyPatchResult>;
641
+ /** Probe the device's configured proxy and report its egress IP/connectivity. */
642
+ probeProxy(deviceId: string): Promise<ProxyProbe>;
643
+ /** 休眠设备 (`POST /api/devices/:id/sleep`)。
644
+ * 虚拟机:截图 + 快照 + 停机(关计费)。真机:锁屏(best-effort)。 */
645
+ sleep(deviceId: string): Promise<{
646
+ sleeping: boolean;
647
+ }>;
648
+ /** 唤醒设备 (`POST /api/devices/:id/wakeup`)。
649
+ * 虚拟机:恢复快照 + 重建计费。真机:发唤醒键(best-effort)。 */
650
+ wakeup(deviceId: string): Promise<{
651
+ sleeping: boolean;
652
+ }>;
653
+ /** Current fingerprint + curated editable subset + dropdown choices + apply
654
+ * semantics (`GET /api/devices/:id/profile`). */
655
+ getProfile(deviceId: string): Promise<DeviceProfileView>;
656
+ /** One-click regenerate the fingerprint (fresh identifiers). Optional
657
+ * `model`/`android_version` constrain the catalog base. */
658
+ regenerateProfile(deviceId: string, opts?: {
659
+ model?: string;
660
+ android_version?: string;
661
+ }): Promise<SetProfileResult>;
662
+ /** Apply curated edits onto the current fingerprint, keeping unique
663
+ * identifiers (`POST /api/devices/:id/profile`). */
664
+ setProfileOverrides(deviceId: string, overrides: ProfileOverrides): Promise<SetProfileResult>;
665
+ /** Real-device only: set which apps the LSPosed spoof module disguises (and
666
+ * force-stops on re-push for immediate effect). Persisted to `meta.spec.scope_pkgs`
667
+ * and pushed live with the current fingerprint (`POST /api/devices/:id/profile`). */
668
+ setProfileScope(deviceId: string, scopePkgs: string[]): Promise<SetProfileResult>;
669
+ /** Real-device only: **start** the first-time install of the LSPosed identity
670
+ * layer (+ spoof module) on a rooted device (`POST /api/devices/:id/anti-detect`,
671
+ * `202`). The multi-reboot install (~minutes) runs in the background — poll
672
+ * `getProfile().disguise_install_status` (`installing`→`installed`/`failed`).
673
+ * After install, fingerprint edits push live via `setProfileOverrides`/`regenerateProfile`. */
674
+ installAntiDetect(deviceId: string): Promise<InstallAntiDetectResult>;
675
+ /** Real-device only: toggle the disguise on/off WITHOUT uninstalling LSPosed (停用伪装).
676
+ * `enabled:false` empties the on-device spoof config + relaunches scoped apps so they read
677
+ * the genuine identity; `enabled:true` re-pushes the stored fingerprint. App-layer effect is
678
+ * immediate; `resetprop` props (model/hardware) fully revert only on the next device reboot.
679
+ * Requires the anti-detect layer installed (`POST /api/devices/:id/disguise`). */
680
+ setDisguiseEnabled(deviceId: string, enabled: boolean): Promise<SetDisguiseResult>;
681
+ /** 申请直传 R2 的 presigned PUT;返回 `key` 供后续 `filesPush` 引用。 */
682
+ filesUploadUrl(deviceId: string, filename: string): Promise<{
683
+ key: string;
684
+ upload_url: string;
685
+ }>;
686
+ /** 让节点从 R2 拉 `key`、adb push 到设备 `dest`;成功后后端删除 R2 对象。 */
687
+ filesPush(deviceId: string, key: string, dest: string): Promise<unknown>;
688
+ /** 让节点 adb pull 设备 `src`、PUT 到 R2;返回 presigned GET 供客户端下载。 */
689
+ filesPull(deviceId: string, src: string, filename: string): Promise<{
690
+ key: string;
691
+ download_url: string;
692
+ }>;
693
+ /** 开一条设备会话:让后端编排目标节点 join `/ws/signal`(节点 = offerer,建
694
+ * "ctrl" DataChannel),返回 `session_id`(SDK 据此 mint viewer token、作
695
+ * answerer 建链、经 `ondatachannel` 拿到 "ctrl" 通道)。这是 DataChannel 多路
696
+ * 复用会话(通道 A)的握手入口;push/pull 失败时由上层回退到 R2 中转
697
+ * (`filesUploadUrl` / `filesPush` / `filesPull`)。 */
698
+ dcSession(deviceId: string): Promise<{
699
+ session_id: string;
700
+ }>;
701
+ }
702
+
703
+ /** Which host the node runs (selects the daemon-side provider). */
704
+ type EnvOs = 'mac' | 'win';
705
+ /** win-only host route: WSL2 (default) or a full Hyper-V VM. */
706
+ type EnvRoute = 'wsl2' | 'full_vm';
707
+ type EnvAction = 'setup' | 'status' | 'teardown';
708
+ interface EnvSetupInput {
709
+ /** Defaults to 'mac' (matches backend: anything ≠ "win" → macOS). */
710
+ os?: EnvOs;
711
+ /** win-only; ignored for mac. Defaults to 'wsl2' server-side. */
712
+ route?: EnvRoute;
713
+ }
714
+ interface EnvTeardownInput {
715
+ os?: EnvOs;
716
+ /** Also delete the VM/distro, not just stop it. */
717
+ purge?: boolean;
718
+ }
719
+ /** A finished env event (the terminal frame). */
720
+ type EnvFinished = Extract<EnvEventKind, {
721
+ kind: 'finished';
722
+ }>;
723
+ /**
724
+ * A live env-base operation. Construct via `AixClient.nodes.env{Setup,Status,
725
+ * Teardown}` (or `EnvRun.start`). Subscribe with `.on('event', …)` for every
726
+ * progress frame and `.on('done', …)` for the terminal `finished` frame; the
727
+ * socket auto-closes once `finished` arrives (the stream is one-shot).
728
+ */
729
+ declare class EnvRun {
730
+ readonly reqId: string;
731
+ /** Relative stream path the backend advertised, e.g. `/ws/execution/<req_id>`. */
732
+ readonly stream: string;
733
+ private readonly socket;
734
+ private readonly eventListeners;
735
+ private readonly doneListeners;
736
+ private doneFired;
737
+ private last;
738
+ private cursor;
739
+ private pollTimer;
740
+ private closed;
741
+ private pendingEvents;
742
+ private constructor();
743
+ /** Trigger an env action on an owned node and start streaming its progress. */
744
+ static start(cfg: AixConfig | ResolvedConfig, nodeId: string, action: EnvAction, input?: EnvSetupInput & EnvTeardownInput): Promise<EnvRun>;
745
+ on(event: 'event', cb: (e: EnvEventKind) => void): () => void;
746
+ on(event: 'done', cb: (e: EnvFinished) => void): () => void;
747
+ private onMessage;
748
+ close(): void;
749
+ }
750
+ /** A render-ready view of one env step, folded from the event stream. */
751
+ interface EnvStepView {
752
+ index: number;
753
+ id: string;
754
+ label: string;
755
+ /** ok | running | skipped | missing | failed | … (daemon-reported). */
756
+ state: string;
757
+ pct: number;
758
+ detail?: string | null;
759
+ }
760
+ /** Folded summary of an env run/probe — what a progress UI renders. */
761
+ interface EnvSummary {
762
+ total: number;
763
+ steps: EnvStepView[];
764
+ logs: string[];
765
+ finished: EnvFinished | null;
766
+ /** For a STATUS probe: the base is fully configured. For a SETUP run: it
767
+ * completed ok. False while still running or when any step is unsatisfied. */
768
+ ready: boolean;
769
+ running: boolean;
770
+ }
771
+ /** Fold a stream of env events into a render-ready summary. Pure — safe to call
772
+ * on every event for live UI, or once on a completed STATUS probe to decide
773
+ * whether `setup` is needed (`ready`). */
774
+ declare function summarizeEnv(events: EnvEventKind[]): EnvSummary;
775
+ /** A setup blocker the host UI can auto-resolve (vs a generic failure to just
776
+ * retry). Currently only Windows WSL2-not-installed (`WSL_UNAVAILABLE`), which
777
+ * needs an elevated `wsl --install` + reboot the daemon can't do itself. */
778
+ declare function envBlocker(finished: EnvFinished | null): 'wsl-missing' | null;
779
+ /** Best-effort detection of the host OS the local node runs on, mapped to the
780
+ * env provider id the backend expects. Returns `null` on Linux/unknown — a
781
+ * Linux node is redroid-native and needs no env base, so callers skip the gate. */
782
+ declare function detectEnvOs(): EnvOs | null;
783
+ /** Trigger an env action and resolve once the terminal `finished` frame arrives,
784
+ * returning the full event list (for `summarizeEnv`). Rejects if the trigger
785
+ * POST fails. Used by headless callers (e.g. `LocalNodeController` auto-init). */
786
+ declare function runEnvToFinish(cfg: AixConfig | ResolvedConfig, nodeId: string, action: EnvAction, input?: EnvSetupInput & EnvTeardownInput): Promise<EnvSummary>;
787
+
788
+ /** Node registry (caller-scoped). A node belongs to the caller who registers it. */
789
+ declare class NodesApi {
790
+ private readonly c;
791
+ private readonly cfg;
792
+ constructor(c: RestClient, cfg: ResolvedConfig);
793
+ list(): Promise<NodeRow[]>;
794
+ /** Register a node. Accepts a name or a full input ({ name, scope?, region? }). */
795
+ register(input: string | RegisterNodeInput): Promise<RegisterNodeResult>;
796
+ /** Update name/scope/region of an owned node (`PATCH /api/nodes/:id`). */
797
+ update(nodeId: string, patch: UpdateNodeInput): Promise<NodeRow>;
798
+ /** Rotate the node token, returning a fresh one (`POST /api/nodes/:id/token`). */
799
+ rotateToken(nodeId: string): Promise<RegisterNodeResult>;
800
+ /** Delete (revoke) an owned node (`DELETE /api/nodes/:id`). */
801
+ remove(nodeId: string): Promise<void>;
802
+ /** Liveness/latency probe (`POST /api/nodes/:id/probe`). Round-trips a ping to
803
+ * the node; `online:false` when unreachable. */
804
+ probe(nodeId: string): Promise<{
805
+ online: boolean;
806
+ latency_ms?: number;
807
+ last_seen_at?: string | null;
808
+ error?: string;
809
+ }>;
810
+ /** Ask an owned node to restart itself (`POST /api/nodes/:id/restart`). */
811
+ restart(nodeId: string): Promise<{
812
+ ok: boolean;
813
+ }>;
814
+ /** Persist the only remotely configurable runtime value. The node validates
815
+ * the native absolute path and returns a restart-required receipt. */
816
+ setDataRoot(nodeId: string, dataRoot: string): Promise<SetDataRootResult>;
817
+ /** Submit a durable node-owned agent task (`POST /api/agent-tasks`). */
818
+ submitAgentTask(input: AgentTaskSubmitInput): Promise<AgentTaskSubmitResult>;
819
+ /** Reload a durable AgentTask, including each target's current attempt. */
820
+ getAgentTask(taskId: string): Promise<AgentTaskReadResult>;
821
+ /** Explicit read alias for callers that use CRUD-style naming. */
822
+ readAgentTask(taskId: string): Promise<AgentTaskReadResult>;
823
+ /** Stop one stable target without cancelling the other devices in its task. */
824
+ cancelAgentTaskTarget(taskId: string, targetId: string): Promise<void>;
825
+ /** Create a new immutable attempt for a failed/cancelled target. */
826
+ retryAgentTaskTarget(taskId: string, targetId: string): Promise<void>;
827
+ /** Resolve the target's current approval gate. */
828
+ approveAgentTaskTarget(taskId: string, targetId: string): Promise<void>;
829
+ /** Fetch cursor-ordered events missed while the task stream was disconnected. */
830
+ agentTaskEvents(taskId: string, after: number): Promise<AgentTaskEventsResult>;
831
+ /** Devices the node currently reports, with claim status (`POST /:id/devices/discover`). */
832
+ discoverDevices(nodeId: string): Promise<{
833
+ devices: DiscoveredDevice[];
834
+ }>;
835
+ /** Enumerate adb mDNS services on a node (`POST /api/nodes/:id/adb/scan`). */
836
+ adbScan(nodeId: string): Promise<AdbScanResult>;
837
+ /** Pair a wireless device (`POST /api/nodes/:id/adb/pair`). */
838
+ adbPair(nodeId: string, addr: string, code: string): Promise<unknown>;
839
+ /** Connect an already-paired wireless device (`POST /api/nodes/:id/adb/connect`). */
840
+ adbConnect(nodeId: string, addr: string): Promise<unknown>;
841
+ /** List connected iOS devices + their usbmux lanes (`{op:'scan'}`). */
842
+ iosScan(nodeId: string): Promise<IosScanResult>;
843
+ /** Trigger the on-device Trust dialog for a udid (`{op:'pair'}`). */
844
+ iosPair(nodeId: string, udid: string): Promise<unknown>;
845
+ /** Toggle wifi-connections for a trusted udid (`{op:'wifi'}`). */
846
+ iosWifi(nodeId: string, udid: string, enable: boolean): Promise<unknown>;
847
+ /** 节点 iOS App Store 账号状态(`{op:'account_info'}`)。 */
848
+ iosAccount(nodeId: string): Promise<IosAccountState>;
849
+ /** 登录节点 iOS 账号;`needs_2fa` 时带 `auth_code` 重试(`{op:'account_login'}`)。 */
850
+ iosAccountLogin(nodeId: string, body: {
851
+ email: string;
852
+ password: string;
853
+ auth_code?: string;
854
+ }): Promise<IosLoginResult>;
855
+ iosAccountLogout(nodeId: string): Promise<{
856
+ ok: boolean;
857
+ }>;
858
+ /** App Store 搜索(`{op:'store_search'}`)。 */
859
+ iosStoreSearch(nodeId: string, term: string, limit?: number): Promise<{
860
+ apps: IosLibraryApp[];
861
+ }>;
862
+ /** 下载入节点本地库(`{op:'download'}`)。 */
863
+ iosDownload(nodeId: string, bundleId: string, name?: string): Promise<{
864
+ ok: boolean;
865
+ app: IosLibraryApp;
866
+ }>;
867
+ /** 节点本地 IPA 库(已下载,模版多选来源)(`{op:'apps'}`)。 */
868
+ iosApps(nodeId: string): Promise<{
869
+ apps: IosLibraryApp[];
870
+ }>;
871
+ /** Configure the redroid host base on an owned node, streaming progress.
872
+ * `POST /api/nodes/:id/env/setup` → subscribe `/ws/execution/:req_id`. */
873
+ envSetup(nodeId: string, input?: EnvSetupInput): Promise<EnvRun>;
874
+ /** Probe the host base without changing it (`POST /api/nodes/:id/env/status`). */
875
+ envStatus(nodeId: string, input?: {
876
+ os?: EnvSetupInput['os'];
877
+ }): Promise<EnvRun>;
878
+ /** Stop (or `purge`) the host base (`POST /api/nodes/:id/env/teardown`). */
879
+ envTeardown(nodeId: string, input?: EnvTeardownInput): Promise<EnvRun>;
880
+ }
881
+
882
+ /** Device clusters (caller-scoped). */
883
+ declare class ClustersApi {
884
+ private readonly c;
885
+ constructor(c: RestClient);
886
+ list(): Promise<Cluster[]>;
887
+ create(input: CreateClusterInput): Promise<Cluster>;
888
+ remove(id: string): Promise<void>;
889
+ setDevices(id: string, deviceIds: string[]): Promise<Cluster>;
890
+ run(id: string, flow: unknown): Promise<{
891
+ run_id: string;
892
+ }>;
893
+ }
894
+
895
+ /** Stored script library (caller-scoped). */
896
+ declare class ScriptsApi {
897
+ private readonly c;
898
+ constructor(c: RestClient);
899
+ list(): Promise<ScriptSummary[]>;
900
+ get(id: string): Promise<Script>;
901
+ create(script: Script): Promise<ScriptSummary>;
902
+ update(id: string, script: Script): Promise<Script>;
903
+ /** Create when `script.id` is unset, else update. Returns the saved entity so
904
+ * callers can capture the server-assigned `id` on first create — otherwise a
905
+ * new script's id is lost and every later autosave re-creates it. */
906
+ save(script: Script): Promise<Script | ScriptSummary>;
907
+ remove(id: string): Promise<void>;
908
+ /** Change a script's visibility (`user` | `project` | `system`). */
909
+ setVisibility(id: string, visibility: string): Promise<ScriptSummary>;
910
+ /** Manual: ask the text LLM for a title + description, written back to the script. */
911
+ generateMeta(id: string): Promise<Script>;
912
+ /** Manual: generate a cover/icon image, store it, and write back stable URLs. */
913
+ generateCover(id: string): Promise<Script>;
914
+ duplicate(id: string): Promise<ScriptSummary>;
915
+ }
916
+
917
+ /** Device quick-action registry (caller-scoped: system built-ins + own user rows). */
918
+ declare class ShortcutsApi {
919
+ private readonly c;
920
+ constructor(c: RestClient);
921
+ list(platform?: 'android' | 'ios' | 'any'): Promise<Shortcut[]>;
922
+ create(body: NewShortcut): Promise<Shortcut>;
923
+ update(id: string, body: NewShortcut): Promise<Shortcut>;
924
+ remove(id: string): Promise<void>;
925
+ }
926
+
927
+ /** Reusable element locators (caller-scoped). */
928
+ declare class LocatorsApi {
929
+ private readonly c;
930
+ constructor(c: RestClient);
931
+ list(): Promise<Locator[]>;
932
+ create(input: CreateLocatorInput): Promise<Locator>;
933
+ remove(id: string): Promise<void>;
934
+ }
935
+
936
+ /** A fleet run is settled when the run row is terminal OR every dispatched
937
+ * device reached a terminal status. Treats an empty/absent device list as
938
+ * not-yet-settled so we keep polling until rows appear. */
939
+ declare function isFleetRunTerminal(detail: FleetRunDetail): boolean;
940
+ /** Fleet runs — the app-side execution path (caller-scoped). Distinct from
941
+ * `FlowRun` (third-party webhook path via `/api/webhook/tasks`). */
942
+ declare class FleetApi {
943
+ private readonly c;
944
+ constructor(c: RestClient);
945
+ start(body: FleetRunSubmit): Promise<{
946
+ run_id: string;
947
+ }>;
948
+ get(runId: string): Promise<FleetRunDetail>;
949
+ /** Poll `get(runId)` until the run is terminal (see {@link isFleetRunTerminal})
950
+ * or `timeoutMs` elapses. Always resolves with the latest `FleetRunDetail`
951
+ * (never rejects on timeout); inspect `isFleetRunTerminal(detail)` to tell a
952
+ * finished run from a polling-cap hit. Used to turn the fire-and-forget
953
+ * `start()` into a real receipt for shortcut/copy runs. */
954
+ waitForRun(runId: string, opts?: {
955
+ timeoutMs?: number;
956
+ intervalMs?: number;
957
+ }): Promise<FleetRunDetail>;
958
+ cancel(runId: string): Promise<unknown>;
959
+ pause(runId: string): Promise<unknown>;
960
+ resume(runId: string): Promise<unknown>;
961
+ }
962
+
963
+ /** Webhook task lane (`/api/webhook/tasks`) — the third-party submit/track path,
964
+ * distinct from `FleetApi` (`/api/fleet/runs`, the app's interactive run path).
965
+ * Submission lives in `FlowRun`; this reads/lists/cancels + reads event history. */
966
+ declare class TasksApi {
967
+ private readonly c;
968
+ constructor(c: RestClient);
969
+ /** Current status of one task (`GET /api/webhook/tasks/:id`). */
970
+ get(id: string): Promise<TaskStatus>;
971
+ /** List the caller-project's tasks, newest first (paginated, filterable). */
972
+ list(filter?: TaskListFilter): Promise<TaskListResult>;
973
+ /** Cancel a task (DB-side: not-yet-terminal targets → cancelled). */
974
+ cancel(id: string): Promise<{
975
+ task_id: string;
976
+ cancelled: number;
977
+ }>;
978
+ /** Pause a task: still-`pending` targets → `paused` so nodes skip them
979
+ * (`pending_for_external` only pulls `pending`). In-flight targets keep
980
+ * running — pause only holds back not-yet-started work. */
981
+ pause(id: string): Promise<{
982
+ task_id: string;
983
+ paused: number;
984
+ }>;
985
+ /** Resume a paused task: `paused` targets → `pending` so nodes pick them up again. */
986
+ resume(id: string): Promise<{
987
+ task_id: string;
988
+ resumed: number;
989
+ }>;
990
+ /** Persisted event history for a task, newest first. */
991
+ events(id: string, opts?: {
992
+ limit?: number;
993
+ offset?: number;
994
+ }): Promise<TaskEventsResult>;
995
+ }
996
+
997
+ /** Webhook configs + manual trigger (developer-scoped). */
998
+ declare class WebhooksApi {
999
+ private readonly c;
1000
+ constructor(c: RestClient);
1001
+ list(): Promise<WebhookConfig[]>;
1002
+ create(input: CreateWebhookInput): Promise<{
1003
+ status: string;
1004
+ id: string;
1005
+ }>;
1006
+ setEnabled(id: string, enabled: boolean): Promise<WebhookConfig>;
1007
+ remove(id: string): Promise<void>;
1008
+ trigger(eventType: string, payload: unknown): Promise<unknown>;
1009
+ }
1010
+
1011
+ /** LLM connectivity test (developer-scoped). */
1012
+ declare class LlmApi {
1013
+ private readonly c;
1014
+ constructor(c: RestClient);
1015
+ test(opts?: {
1016
+ token?: string;
1017
+ model?: string;
1018
+ baseUrl?: string;
1019
+ }): Promise<LlmTestResult>;
1020
+ }
1021
+
1022
+ /** Platform-admin + billing + image catalog (admin-scoped, except images which
1023
+ * also gate on PlatformAdminAuth server-side). */
1024
+ declare class AdminApi {
1025
+ private readonly c;
1026
+ constructor(c: RestClient);
1027
+ listRates(): Promise<DeviceRate[]>;
1028
+ upsertRate(body: RateInput): Promise<DeviceRate>;
1029
+ setRebate(projectId: string, ratio: number): Promise<unknown>;
1030
+ setNodePlatform(nodeId: string, isPlatform: boolean): Promise<unknown>;
1031
+ listPlans(): Promise<BillingPlan[]>;
1032
+ createPlan(body: PlanInput): Promise<BillingPlan>;
1033
+ usage(): Promise<DeviceUsageSession[]>;
1034
+ ledger(): Promise<RebateLedgerRow[]>;
1035
+ getOem(): Promise<PlatformOem>;
1036
+ putOem(body: PlatformOemInput): Promise<PlatformOem>;
1037
+ listImages(): Promise<ImageArtifact[]>;
1038
+ upsertImage(body: ImageInput): Promise<ImageArtifact>;
1039
+ setImageActive(id: string, active: boolean): Promise<ImageArtifact>;
1040
+ deleteImage(id: string): Promise<void>;
1041
+ }
1042
+
1043
+ /** Image catalog — caller-facing reads. Admin CRUD lives on `AdminApi`
1044
+ * (`/api/images`); this exposes only the slim, provisionable-version list the
1045
+ * device-create wizard needs. */
1046
+ declare class ImagesApi {
1047
+ private readonly c;
1048
+ constructor(c: RestClient);
1049
+ /** Provisionable versions for a platform, drawn from the active catalog
1050
+ * (`GET /api/images/options`). Omit `platform` for all platforms. */
1051
+ options(platform?: 'android' | 'ios'): Promise<ImageOption[]>;
1052
+ }
1053
+
1054
+ /** 账单 / 钱包(caller 读自己的钱包;`topup` 为 admin 给指定用户钱包充值)。
1055
+ * 钱包身份恒为 user——项目 key/secret 调用解析到 representative user 的钱包。 */
1056
+ declare class BillingApi {
1057
+ private readonly c;
1058
+ constructor(c: RestClient);
1059
+ /** 当前钱包余额 + 最近流水(`GET /api/billing/balance`)。 */
1060
+ balance(): Promise<BalanceResp>;
1061
+ /** 名下项目订阅列表(`GET /api/billing/subscriptions`)。 */
1062
+ subscriptions(): Promise<ProjectSubscription[]>;
1063
+ /** 订阅一个套餐(`POST /api/billing/subscriptions`)。`plan_id` 由调用方传入。 */
1064
+ subscribe(input: SubscribeInput): Promise<ProjectSubscription>;
1065
+ /** 自有节点日费(`GET /api/billing/own-node-charges`)。 */
1066
+ ownNodeCharges(): Promise<OwnNodeDailyCharge[]>;
1067
+ /** 充值到指定用户钱包(`POST /api/admin/billing/balance/topup`,admin scope)。 */
1068
+ topup(input: TopupInput): Promise<TopupResult>;
1069
+ }
1070
+
1071
+ /** 用户 PAT(personal access token)管理,挂在 `/api/tokens`(caller scope)。
1072
+ * 令牌绑用户钱包/账号身份;明文仅在 create/rotate 时返回一次。 */
1073
+ declare class ApiTokensApi {
1074
+ private readonly c;
1075
+ constructor(c: RestClient);
1076
+ /** 列出名下令牌(无明文/hash;`GET /api/tokens`)。 */
1077
+ list(): Promise<ApiTokenRow[]>;
1078
+ /** 创建一个 PAT(`POST /api/tokens`);响应 `token` 明文仅此一次。 */
1079
+ create(input: CreateTokenInput): Promise<CreatedToken>;
1080
+ /** 撤销一个 PAT(`DELETE /api/tokens/:id`,204)。 */
1081
+ revoke(id: string): Promise<void>;
1082
+ /** 轮换一个 PAT(重签明文,旧 hash 失效;`POST /api/tokens/:id/rotate`)。 */
1083
+ rotate(id: string): Promise<CreatedToken>;
1084
+ }
1085
+
1086
+ /** 用户级应用模版(`/api/app-templates`,caller-scoped)。 */
1087
+ declare class AppTemplatesApi {
1088
+ private readonly c;
1089
+ constructor(c: RestClient);
1090
+ list(platform?: 'android' | 'ios'): Promise<AppTemplate[]>;
1091
+ create(body: NewAppTemplate): Promise<AppTemplate>;
1092
+ update(id: string, body: NewAppTemplate): Promise<AppTemplate>;
1093
+ remove(id: string): Promise<void>;
1094
+ }
1095
+
1096
+ /** One-stop typed REST facade over the whole backend `/api/*` surface.
1097
+ * Build once from an `AixConfig` and share it across the app. */
1098
+ declare class AixClient {
1099
+ readonly cfg: ResolvedConfig;
1100
+ readonly http: RestClient;
1101
+ readonly auth: AuthApi;
1102
+ readonly projects: ProjectsApi;
1103
+ readonly users: UsersApi;
1104
+ readonly devices: DevicesApi;
1105
+ readonly nodes: NodesApi;
1106
+ readonly clusters: ClustersApi;
1107
+ readonly scripts: ScriptsApi;
1108
+ readonly shortcuts: ShortcutsApi;
1109
+ readonly locators: LocatorsApi;
1110
+ readonly fleet: FleetApi;
1111
+ readonly tasks: TasksApi;
1112
+ readonly webhooks: WebhooksApi;
1113
+ readonly llm: LlmApi;
1114
+ readonly admin: AdminApi;
1115
+ readonly images: ImagesApi;
1116
+ readonly billing: BillingApi;
1117
+ readonly tokens: ApiTokensApi;
1118
+ readonly appTemplates: AppTemplatesApi;
1119
+ /** Desktop local-node controller — present only when `cfg.localNode` is set
1120
+ * (Tauri/Electron). `ensure()` binds this machine as the caller's node and
1121
+ * `listDevices()`/`onDevices()` surface its connected devices. */
1122
+ readonly localNode?: LocalNodeController;
1123
+ constructor(cfg: AixConfig | ResolvedConfig);
1124
+ /**
1125
+ * 打开一条设备会话(DataChannel 多路复用):文件管理 + 终端 + 传输 + 通用流/
1126
+ * 端口转发。返回的 `DeviceSession` 用完请 `close()`。
1127
+ *
1128
+ * 解耦说明:`device-session.ts` 静态依赖本文件(`AixClient`),为避免模块求值期
1129
+ * 循环依赖,这里用**动态 import** 拿 `DeviceSession`;并把 `this.cfg`
1130
+ * (ResolvedConfig)而非 `this` 传给 `open`(`open` 内部用 cfg + 自建 DevicesApi
1131
+ * 即可,不必回依赖 client 实例)。
1132
+ */
1133
+ deviceSession(deviceId: string, opts?: {
1134
+ dcTimeoutMs?: number;
1135
+ }): Promise<DeviceSession>;
1136
+ }
1137
+ declare function createClient(cfg: AixConfig | ResolvedConfig): AixClient;
1138
+
1139
+ /** Runtime status of the locally-running node process, reported by the host. */
1140
+ interface NodeHostStatus {
1141
+ running: boolean;
1142
+ hasToken: boolean;
1143
+ nodeId: string | null;
1144
+ }
1145
+ /** The host-app's adapter for managing the local `aixautobot node` process.
1146
+ * The SDK owns the *policy* (which node, which token); the host owns the
1147
+ * *mechanism* (spawning the process, persisting the token). Implement this for
1148
+ * Tauri (`invoke`), Electron (`child_process`), or any runtime — see examples. */
1149
+ interface NodeHost {
1150
+ status(): Promise<NodeHostStatus>;
1151
+ start(opts: {
1152
+ token: string | null;
1153
+ backendWs: string;
1154
+ requireSdkUpdate?: boolean;
1155
+ }): Promise<void>;
1156
+ stop(): Promise<void>;
1157
+ }
1158
+ interface LocalNodeOptions {
1159
+ host: NodeHost;
1160
+ /** Require a foreground SDK update check before starting the host process. */
1161
+ requireSdkUpdate?: boolean;
1162
+ /** Run `ensure()` automatically once the client is constructed (best-effort). */
1163
+ auto?: boolean;
1164
+ /** Node scope to create/reuse. Defaults to `'personal'`. */
1165
+ scope?: NodeScope;
1166
+ /** Name for a newly-created node. Defaults to `'desktop-node'`. */
1167
+ nameHint?: string;
1168
+ /** Override the backend ws URL handed to the host. Defaults to baseUrl→ws[s]. */
1169
+ backendWs?: string;
1170
+ /** Poll interval (ms) for `onDevices`. Defaults to 4000. */
1171
+ pollMs?: number;
1172
+ /** Redroid host-base (env) initialization on the bound node:
1173
+ * - `'manual'` (default): never auto-init. A UI app drives it via the
1174
+ * `useEnvGate` hook + `<EnvInitDialog>` so the user confirms (and watches
1175
+ * the ~GB download).
1176
+ * - `'auto'`: a HEADLESS/server consumer with no UI — after binding the node,
1177
+ * probe `env/status` and, if the base is missing, run `env/setup` to
1178
+ * completion automatically (no confirmation). Best-effort; never throws. */
1179
+ envInit?: 'manual' | 'auto';
1180
+ /** Host OS for env init (selects mac-env vs win-env). Defaults to
1181
+ * `detectEnvOs()`; `null`/Linux skips env init (redroid is native there). */
1182
+ envOs?: EnvOs | null;
1183
+ /** win-only env route handed to `env/setup` (`'wsl2'` default server-side). */
1184
+ envRoute?: EnvRoute;
1185
+ }
1186
+ /** Drives the local node lifecycle for a desktop app: ensure a personal node
1187
+ * owned by the signed-in caller exists, mint/rotate a runnable token, hand it
1188
+ * to the host to spawn, and surface THAT node's connected devices — so a
1189
+ * Tauri/Electron consumer auto-lists local devices instead of adding manually.
1190
+ *
1191
+ * Ported from the in-house desktop `ensureLocalNode()` so any SDK consumer gets
1192
+ * the same behaviour. Best-effort: `ensure()` never throws into the caller. */
1193
+ declare class LocalNodeController {
1194
+ private readonly client;
1195
+ private readonly opts;
1196
+ /** The node id this machine is bound to once `ensure()` has run. */
1197
+ nodeId: string | null;
1198
+ /** Last best-effort failure captured by `ensure()`, for host UIs that want to surface it. */
1199
+ lastError: unknown;
1200
+ private startHost;
1201
+ constructor(client: AixClient, opts: LocalNodeOptions);
1202
+ private get scope();
1203
+ private get backendWs();
1204
+ /** Ensure THIS machine runs as its OWN node owned by the signed-in caller,
1205
+ * starting it via the host. The node is keyed by machine name (`nameHint`), so
1206
+ * each of the user's machines gets a distinct node (no shared-node churn).
1207
+ * Idempotent: keeps a running match, else restarts the persisted/rotated node,
1208
+ * else registers one named after the machine. Returns the bound node id, or
1209
+ * null on failure (best-effort). */
1210
+ ensure(): Promise<string | null>;
1211
+ /** Record the bound node id and, in headless `'auto'` mode, ensure its redroid
1212
+ * host base is configured before returning. Always returns the id. */
1213
+ private bind;
1214
+ /** Resolve the env OS: explicit option wins, else auto-detect; `null` (Linux/
1215
+ * unknown) means "no base needed" and the gate is skipped. */
1216
+ get envOs(): EnvOs | null;
1217
+ /** HEADLESS auto env-init: probe `env/status` on the bound node and, if the
1218
+ * redroid base is missing, run `env/setup` to completion — no confirmation.
1219
+ * No-op unless `envInit: 'auto'` and the OS needs a base. Best-effort. */
1220
+ private maybeAutoEnv;
1221
+ /** Run an env action on the bound node to completion. For `'status'` returns
1222
+ * whether the base is already ready; for `'setup'` returns whether it ok'd. */
1223
+ private runEnv;
1224
+ /** Stop the local node (e.g. on logout). Best-effort. */
1225
+ stop(): Promise<void>;
1226
+ /** Devices connected to THIS local node — `devices.list()` filtered by node id.
1227
+ * Empty until `ensure()` has bound `nodeId`. */
1228
+ listDevices(): Promise<DeviceRow[]>;
1229
+ /** Subscribe to this node's device list (polled). Fires immediately, then every
1230
+ * `pollMs`. Returns an unsubscribe fn. */
1231
+ onDevices(cb: (devices: DeviceRow[]) => void): () => void;
1232
+ }
1233
+
1234
+ interface IWebSocket {
1235
+ send(data: string): void;
1236
+ close(code?: number, reason?: string): void;
1237
+ onopen: ((ev: unknown) => void) | null;
1238
+ onmessage: ((ev: {
1239
+ data: unknown;
1240
+ }) => void) | null;
1241
+ onclose: ((ev: unknown) => void) | null;
1242
+ onerror: ((ev: unknown) => void) | null;
1243
+ }
1244
+ type WebSocketFactory = (url: string) => IWebSocket;
1245
+ /** Minimal RTCPeerConnection surface MirrorSession + DeviceSession depend on
1246
+ * (injectable for tests). Both are **answerer-only**: the node is the offerer
1247
+ * (owns the video track / "control" / "ctrl" DataChannel and createOffer), so the
1248
+ * client just createAnswer + setRemoteDescription(offer) + ondatachannel. */
1249
+ interface IRTCPeerConnection {
1250
+ createAnswer(): Promise<{
1251
+ type: string;
1252
+ sdp?: string;
1253
+ }>;
1254
+ setLocalDescription(desc?: {
1255
+ type: string;
1256
+ sdp?: string;
1257
+ }): Promise<void>;
1258
+ setRemoteDescription(desc: {
1259
+ type: string;
1260
+ sdp?: string;
1261
+ }): Promise<void>;
1262
+ addIceCandidate(candidate: unknown): Promise<void>;
1263
+ close(): void;
1264
+ ontrack: ((ev: {
1265
+ streams: ReadonlyArray<MediaStream>;
1266
+ track?: unknown;
1267
+ receiver?: {
1268
+ playoutDelayHint?: number;
1269
+ };
1270
+ }) => void) | null;
1271
+ onicecandidate: ((ev: {
1272
+ candidate: unknown;
1273
+ }) => void) | null;
1274
+ ondatachannel: ((ev: {
1275
+ channel: IRTCDataChannel;
1276
+ }) => void) | null;
1277
+ onconnectionstatechange: (() => void) | null;
1278
+ connectionState: string;
1279
+ /** 主动建一条命名 DataChannel(offerer 路径,当前 SDK 不再走)。 */
1280
+ createDataChannel?(label: string, init?: unknown): IRTCDataChannel;
1281
+ /** 生成 offer(offerer 路径,当前 SDK 不再走)。 */
1282
+ createOffer?(): Promise<{
1283
+ type: string;
1284
+ sdp?: string;
1285
+ }>;
1286
+ /** WebRTC 连接统计(浏览器 RTCStatsReport,Map-like)。可选:mock 可不实现,
1287
+ * 消费方(自动测速选档)拿不到时降级为 null。 */
1288
+ getStats?(): Promise<unknown>;
1289
+ }
1290
+ interface IRTCDataChannel {
1291
+ readonly readyState: string;
1292
+ readonly label?: string;
1293
+ /** Send text (mirror/control + device-session path) — or binary (后续二进制帧优化).
1294
+ * 接受 string|ArrayBuffer|ArrayBufferView 向后兼容:当前 DeviceSession 协议为
1295
+ * JSON 文本帧,MirrorSession 也只传 string。 */
1296
+ send(data: string | ArrayBuffer | ArrayBufferView): void;
1297
+ close(): void;
1298
+ onopen: (() => void) | null;
1299
+ onmessage: ((ev: {
1300
+ data: unknown;
1301
+ }) => void) | null;
1302
+ onclose: (() => void) | null;
1303
+ /** 待发送字节数,用于背压判断。 */
1304
+ readonly bufferedAmount?: number;
1305
+ /** 低水位阈值;降到此值时触发 onbufferedamountlow。 */
1306
+ bufferedAmountLowThreshold?: number;
1307
+ /** bufferedAmount 回落到低水位的回调,用于背压续传。 */
1308
+ onbufferedamountlow?: (() => void) | null;
1309
+ /** 接收二进制类型("arraybuffer"),文件通道设为 arraybuffer。 */
1310
+ binaryType?: string;
1311
+ }
1312
+ type RTCFactory = (config: {
1313
+ iceServers?: unknown[];
1314
+ }) => IRTCPeerConnection;
1315
+ interface AixConfig {
1316
+ /** Backend base URL, e.g. "https://api.example.com". */
1317
+ baseUrl: string;
1318
+ /** Legacy single-user-token path. Equivalent to `auth: { kind: 'user', getToken }`.
1319
+ * Optional now that `auth` exists — provide this OR `auth` (or both). */
1320
+ getAuthToken?: () => string | Promise<string>;
1321
+ /** One or more credentials (project key/secret, user JWT, developer JWT).
1322
+ * The resource layer selects the right one per request scope. */
1323
+ auth?: Credential | Credential[];
1324
+ /** Override fetch (Tauri / tests). Defaults to global fetch. */
1325
+ fetchFn?: typeof fetch;
1326
+ /** Override WebSocket construction (tests). Defaults to global WebSocket. */
1327
+ wsFactory?: WebSocketFactory;
1328
+ /** Override RTCPeerConnection construction (tests / jsdom). Defaults to global RTCPeerConnection. */
1329
+ rtcFactory?: RTCFactory;
1330
+ /** Desktop (Tauri/Electron) local-node integration. When set, `client.localNode`
1331
+ * is available; `auto:true` runs `ensure()` on construction (best-effort). */
1332
+ localNode?: LocalNodeOptions;
1333
+ }
1334
+ interface ResolvedConfig {
1335
+ baseUrl: string;
1336
+ /** Resolved user-bearer accessor (kept for the legacy device/mirror/flow
1337
+ * paths). Throws if no user credential is configured. */
1338
+ getAuthToken: () => string | Promise<string>;
1339
+ /** Multi-credential, scope-aware header provider for the resource layer. */
1340
+ auth: AuthProvider;
1341
+ fetchFn: typeof fetch;
1342
+ wsFactory: WebSocketFactory;
1343
+ rtcFactory: RTCFactory;
1344
+ /** Desktop local-node integration (passed through from AixConfig). */
1345
+ localNode?: LocalNodeOptions;
1346
+ }
1347
+ declare function joinUrl(base: string, path: string): string;
1348
+ /** Convert the backend base (http[s]) to the generic `/ws/execution/:id` relay.
1349
+ * This is intentionally only URL construction; callers must use
1350
+ * `executionWsTicketUrl` before opening a browser WebSocket. */
1351
+ declare function executionWsUrl(baseUrl: string, id: string): string;
1352
+ /** Cursor-ordered generic execution event retained by the backend stream ledger. */
1353
+ interface ExecutionStreamEvent {
1354
+ cursor: number;
1355
+ run_id: string;
1356
+ target_id?: string;
1357
+ attempt_id?: string;
1358
+ device_id?: string;
1359
+ event: unknown;
1360
+ }
1361
+ interface ExecutionStreamEventsResult {
1362
+ events: ExecutionStreamEvent[];
1363
+ next_cursor?: number;
1364
+ has_more?: boolean;
1365
+ }
1366
+ /** Fetch a durable execution-stream page for live cursor-gap recovery. */
1367
+ declare function executionStreamEventsAfter(cfg: ResolvedConfig, id: string, after: number, limit?: number): Promise<ExecutionStreamEventsResult>;
1368
+ /**
1369
+ * Mint a short-lived, single-use execution WebSocket ticket through an
1370
+ * authenticated HTTP request. `ReconnectingSocket.urlProvider` must call this
1371
+ * on every connection attempt: tickets are deliberately not reusable and no
1372
+ * long-lived bearer credential is ever put in the WebSocket URL.
1373
+ */
1374
+ declare function executionWsTicketUrl(cfg: ResolvedConfig, id: string, after?: number): Promise<string>;
1375
+ declare function authHeader(getAuthToken: () => string | Promise<string>): Promise<{
1376
+ Authorization: string;
1377
+ }>;
1378
+ declare function resolveConfig(cfg: AixConfig): ResolvedConfig;
1379
+
1380
+ export { type PullResult as $, type AixConfig as A, BillingApi as B, type CachedUserCredentialOpts as C, type DcStream as D, type EnvOs as E, type FridaEventFrame as F, type ExecutionStreamEventsResult as G, type FileEntry as H, FleetApi as I, type IRTCDataChannel as J, type IRTCPeerConnection as K, type IWebSocket as L, ImagesApi as M, type IncomingFrame as N, type OutgoingFrame as O, LlmApi as P, LocalNodeController as Q, type ResolvedConfig as R, type ScriptletEventFrame as S, type LocalNodeOptions as T, LocatorsApi as U, type NodeHost as V, type WebSocketFactory as W, type NodeHostStatus as X, NodesApi as Y, type ProgressFn as Z, ProjectsApi as _, type RunEventKind as a, type PushInput as a0, type RTCFactory as a1, type RequestOpts as a2, RestClient as a3, type RetFrame as a4, type Scope as a5, ScriptsApi as a6, ShortcutsApi as a7, type StreamTarget as a8, TasksApi as a9, restClient as aA, runEnvToFinish as aB, summarizeEnv as aC, type TransferOpts as aa, type TransferProgress as ab, type TransferResult as ac, UsersApi as ad, WebhooksApi as ae, authHeader as af, buildAuthProvider as ag, createCachedUserCredential as ah, createClient as ai, detectEnvOs as aj, envBlocker as ak, executionStreamEventsAfter as al, executionWsTicketUrl as am, executionWsUrl as an, isEnvEventFrame as ao, isExecutionFrame as ap, isFleetRunTerminal as aq, isFridaEventFrame as ar, isRetFrame as as, isScriptletEventFrame as at, isStatusFrame as au, joinUrl as av, parseIncoming as aw, pullFile as ax, pushFile as ay, resolveConfig as az, type StatusFrame as b, AixClient as c, type EnvRoute as d, type EnvEventKind as e, AdminApi as f, ApiTokensApi as g, AuthApi as h, type AuthProvider as i, ClustersApi as j, type Credential as k, type DeviceReq as l, type DeviceResp as m, DeviceSession as n, DevicesApi as o, type EnvAction as p, type EnvFinished as q, type EnvResultDto as r, EnvRun as s, type EnvSetupInput as t, type EnvStepMeta as u, type EnvStepView as v, type EnvSummary as w, type EnvTeardownInput as x, type ExecutionFrame as y, type ExecutionStreamEvent as z };