@limrun/api 0.5.2 → 0.6.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 (54) hide show
  1. package/CHANGELOG.md +23 -112
  2. package/client.d.mts +6 -6
  3. package/client.d.mts.map +1 -1
  4. package/client.d.ts +6 -6
  5. package/client.d.ts.map +1 -1
  6. package/client.js +12 -6
  7. package/client.js.map +1 -1
  8. package/client.mjs +12 -6
  9. package/client.mjs.map +1 -1
  10. package/index.d.mts +0 -1
  11. package/index.d.ts +0 -1
  12. package/index.js +0 -2
  13. package/index.js.map +1 -1
  14. package/index.mjs +0 -1
  15. package/package.json +1 -4
  16. package/resources/android-instances-helpers.d.mts +51 -24
  17. package/resources/android-instances-helpers.d.mts.map +1 -1
  18. package/resources/android-instances-helpers.d.ts +51 -24
  19. package/resources/android-instances-helpers.d.ts.map +1 -1
  20. package/resources/android-instances-helpers.js +199 -107
  21. package/resources/android-instances-helpers.js.map +1 -1
  22. package/resources/android-instances-helpers.mjs +198 -103
  23. package/resources/android-instances-helpers.mjs.map +1 -1
  24. package/resources/android-instances.d.mts +4 -7
  25. package/resources/android-instances.d.mts.map +1 -1
  26. package/resources/android-instances.d.ts +4 -7
  27. package/resources/android-instances.d.ts.map +1 -1
  28. package/resources/android-instances.js.map +1 -1
  29. package/resources/android-instances.mjs.map +1 -1
  30. package/resources/index.d.mts +1 -1
  31. package/resources/index.d.mts.map +1 -1
  32. package/resources/index.d.ts +1 -1
  33. package/resources/index.d.ts.map +1 -1
  34. package/resources/index.js.map +1 -1
  35. package/resources/index.mjs.map +1 -1
  36. package/resources/tunnel.d.mts +25 -0
  37. package/resources/tunnel.d.mts.map +1 -0
  38. package/resources/tunnel.d.ts +25 -0
  39. package/resources/tunnel.d.ts.map +1 -0
  40. package/resources/tunnel.js +102 -0
  41. package/resources/tunnel.js.map +1 -0
  42. package/resources/tunnel.mjs +98 -0
  43. package/resources/tunnel.mjs.map +1 -0
  44. package/src/client.ts +18 -14
  45. package/src/index.ts +0 -2
  46. package/src/resources/android-instances-helpers.ts +318 -127
  47. package/src/resources/android-instances.ts +3 -8
  48. package/src/resources/index.ts +0 -1
  49. package/src/resources/tunnel.ts +126 -0
  50. package/src/version.ts +1 -1
  51. package/version.d.mts +1 -1
  52. package/version.d.ts +1 -1
  53. package/version.js +1 -1
  54. package/version.mjs +1 -1
package/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["src/index.ts"],"names":[],"mappings":";AAAA,sFAAsF;;;;AAEtF,sCAA6C;AAApC,iGAAA,MAAM,OAAW;AAE1B,6CAAyD;AAA/B,iGAAA,MAAM,OAAA;AAChC,qDAAgD;AAAvC,yGAAA,UAAU,OAAA;AACnB,sCAAsD;AAA7C,gGAAA,MAAM,OAAA;AACf,yCAcsB;AAbpB,oGAAA,WAAW,OAAA;AACX,iGAAA,QAAQ,OAAA;AACR,2GAAA,kBAAkB,OAAA;AAClB,kHAAA,yBAAyB,OAAA;AACzB,0GAAA,iBAAiB,OAAA;AACjB,sGAAA,aAAa,OAAA;AACb,sGAAA,aAAa,OAAA;AACb,uGAAA,cAAc,OAAA;AACd,wGAAA,eAAe,OAAA;AACf,4GAAA,mBAAmB,OAAA;AACnB,4GAAA,mBAAmB,OAAA;AACnB,8GAAA,qBAAqB,OAAA;AACrB,iHAAA,wBAAwB,OAAA;AAG1B,mFAAsD"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["src/index.ts"],"names":[],"mappings":";AAAA,sFAAsF;;;AAEtF,sCAA6C;AAApC,iGAAA,MAAM,OAAW;AAE1B,6CAAyD;AAA/B,iGAAA,MAAM,OAAA;AAChC,qDAAgD;AAAvC,yGAAA,UAAU,OAAA;AACnB,sCAAsD;AAA7C,gGAAA,MAAM,OAAA;AACf,yCAcsB;AAbpB,oGAAA,WAAW,OAAA;AACX,iGAAA,QAAQ,OAAA;AACR,2GAAA,kBAAkB,OAAA;AAClB,kHAAA,yBAAyB,OAAA;AACzB,0GAAA,iBAAiB,OAAA;AACjB,sGAAA,aAAa,OAAA;AACb,sGAAA,aAAa,OAAA;AACb,uGAAA,cAAc,OAAA;AACd,wGAAA,eAAe,OAAA;AACf,4GAAA,mBAAmB,OAAA;AACnB,4GAAA,mBAAmB,OAAA;AACnB,8GAAA,qBAAqB,OAAA;AACrB,iHAAA,wBAAwB,OAAA"}
package/index.mjs CHANGED
@@ -4,5 +4,4 @@ export { toFile } from "./core/uploads.mjs";
4
4
  export { APIPromise } from "./core/api-promise.mjs";
5
5
  export { Limrun } from "./client.mjs";
6
6
  export { LimrunError, APIError, APIConnectionError, APIConnectionTimeoutError, APIUserAbortError, NotFoundError, ConflictError, RateLimitError, BadRequestError, AuthenticationError, InternalServerError, PermissionDeniedError, UnprocessableEntityError, } from "./core/error.mjs";
7
- export * from "./resources/android-instances-helpers.mjs";
8
7
  //# sourceMappingURL=index.mjs.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@limrun/api",
3
- "version": "0.5.2",
3
+ "version": "0.6.2",
4
4
  "description": "The official TypeScript library for the Limrun API",
5
5
  "author": "Limrun <contact@limrun.com>",
6
6
  "types": "./index.d.ts",
@@ -136,8 +136,5 @@
136
136
  "./version.mjs": {
137
137
  "default": "./version.mjs"
138
138
  }
139
- },
140
- "optionalDependencies": {
141
- "bufferutil": "^4.0.9"
142
139
  }
143
140
  }
@@ -1,30 +1,57 @@
1
- import * as net from 'net';
1
+ import type { Tunnel } from "./tunnel.mjs";
2
+ export type { Tunnel } from "./tunnel.mjs";
2
3
  import { AndroidInstance } from "./android-instances.mjs";
3
4
  /**
4
- * Opens a WebSocket TCP proxy for the ADB port and connects the local adb
5
- * client to it.
5
+ * A client for interacting with a Limbar instance
6
6
  */
7
- export declare const startAdbTunnel: (androidInstance: AndroidInstance, hostname?: string, port?: number) => Promise<Proxy>;
8
- /** Returned by `startTcpProxy` – holds the chosen localhost port and a close callback. */
9
- export interface Proxy {
10
- address: net.AddressInfo;
11
- close: () => void;
12
- }
7
+ export type InstanceClient = {
8
+ /**
9
+ * Take a screenshot of the current screen
10
+ * @returns A promise that resolves to the screenshot data
11
+ */
12
+ screenshot: () => Promise<ScreenshotData>;
13
+ /**
14
+ * Disconnect from the Limbar instance
15
+ */
16
+ disconnect: () => void;
17
+ /**
18
+ * Establish an ADB tunnel to the instance.
19
+ * Returns the local TCP port and a cleanup function.
20
+ */
21
+ startAdbTunnel: () => Promise<Tunnel>;
22
+ /**
23
+ * Send an asset URL to the instance. The instance will download the asset
24
+ * and process it (currently APK install is supported). Resolves on success,
25
+ * rejects with an Error on failure.
26
+ */
27
+ sendAsset: (url: string) => Promise<void>;
28
+ };
13
29
  /**
14
- * Starts a one-shot TCP WebSocket proxy.
15
- *
16
- * The function creates a local TCP server that listens on an ephemeral port on
17
- * 127.0.0.1. As soon as the **first** TCP client connects the server stops
18
- * accepting further connections and forwards all traffic between that client
19
- * and `remoteURL` through an authenticated WebSocket. If you need to proxy
20
- * more than one TCP connection, call `startTcpProxy` again to create a new
21
- * proxy instance.
22
- *
23
- * @param remoteURL Remote WebSocket endpoint (e.g. wss://example.com/instance)
24
- * @param token Bearer token sent as `Authorization` header
25
- * @param hostname Optional IP address to listen on. Default is 127.0.0.1
26
- * @param port Optional port number to listen on. Default is to ask Node.js
27
- * to find an available non-privileged port.
30
+ * Controls the verbosity of logging in the client
28
31
  */
29
- export declare function startTcpProxy(remoteURL: string, token: string, hostname: string, port: number): Promise<Proxy>;
32
+ export type LogLevel = 'none' | 'error' | 'warn' | 'info' | 'debug';
33
+ /**
34
+ * Configuration options for creating an Instance API client
35
+ */
36
+ export type InstanceClientOptions = {
37
+ /**
38
+ * Path to the ADB executable.
39
+ * @default 'adb'
40
+ */
41
+ adbPath?: string;
42
+ /**
43
+ * Controls logging verbosity
44
+ * @default 'info'
45
+ */
46
+ logLevel?: LogLevel;
47
+ };
48
+ type ScreenshotData = {
49
+ dataUri: string;
50
+ };
51
+ /**
52
+ * Creates a client for interacting with a Limbar instance
53
+ * @param options Configuration options including webrtcUrl, token and log level
54
+ * @returns An InstanceClient for controlling the instance
55
+ */
56
+ export declare function createInstanceClient(androidInstance: AndroidInstance, options?: InstanceClientOptions): Promise<InstanceClient>;
30
57
  //# sourceMappingURL=android-instances-helpers.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"android-instances-helpers.d.mts","sourceRoot":"","sources":["../src/resources/android-instances-helpers.ts"],"names":[],"mappings":"OACO,KAAK,GAAG,MAAM,KAAK;OAGnB,EAAE,eAAe,EAAE;AAE1B;;;GAGG;AACH,eAAO,MAAM,cAAc,GACzB,iBAAiB,eAAe,EAChC,WAAW,MAAM,EACjB,OAAO,MAAM,KACZ,OAAO,CAAC,KAAK,CAsBf,CAAC;AAEF,0FAA0F;AAC1F,MAAM,WAAW,KAAK;IACpB,OAAO,EAAE,GAAG,CAAC,WAAW,CAAC;IACzB,KAAK,EAAE,MAAM,IAAI,CAAC;CACnB;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,aAAa,CACjC,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,KAAK,CAAC,CA6FhB"}
1
+ {"version":3,"file":"android-instances-helpers.d.mts","sourceRoot":"","sources":["../src/resources/android-instances-helpers.ts"],"names":[],"mappings":"OAIO,KAAK,EAAE,MAAM,EAAE;YACV,EAAE,MAAM,EAAE;OACf,EAAE,eAAe,EAAE;AAE1B;;GAEG;AACH,MAAM,MAAM,cAAc,GAAG;IAC3B;;;OAGG;IACH,UAAU,EAAE,MAAM,OAAO,CAAC,cAAc,CAAC,CAAC;IAC1C;;OAEG;IACH,UAAU,EAAE,MAAM,IAAI,CAAC;IAEvB;;;OAGG;IACH,cAAc,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC;IACtC;;;;OAIG;IACH,SAAS,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CAC3C,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,QAAQ,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;AAEpE;;GAEG;AACH,MAAM,MAAM,qBAAqB,GAAG;IAClC;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;OAGG;IACH,QAAQ,CAAC,EAAE,QAAQ,CAAC;CACrB,CAAC;AAaF,KAAK,cAAc,GAAG;IACpB,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC;AA0BF;;;;GAIG;AACH,wBAAsB,oBAAoB,CACxC,eAAe,EAAE,eAAe,EAChC,OAAO,GAAE,qBAGR,GACA,OAAO,CAAC,cAAc,CAAC,CAmPzB"}
@@ -1,30 +1,57 @@
1
- import * as net from 'net';
1
+ import type { Tunnel } from "./tunnel.js";
2
+ export type { Tunnel } from "./tunnel.js";
2
3
  import { AndroidInstance } from "./android-instances.js";
3
4
  /**
4
- * Opens a WebSocket TCP proxy for the ADB port and connects the local adb
5
- * client to it.
5
+ * A client for interacting with a Limbar instance
6
6
  */
7
- export declare const startAdbTunnel: (androidInstance: AndroidInstance, hostname?: string, port?: number) => Promise<Proxy>;
8
- /** Returned by `startTcpProxy` – holds the chosen localhost port and a close callback. */
9
- export interface Proxy {
10
- address: net.AddressInfo;
11
- close: () => void;
12
- }
7
+ export type InstanceClient = {
8
+ /**
9
+ * Take a screenshot of the current screen
10
+ * @returns A promise that resolves to the screenshot data
11
+ */
12
+ screenshot: () => Promise<ScreenshotData>;
13
+ /**
14
+ * Disconnect from the Limbar instance
15
+ */
16
+ disconnect: () => void;
17
+ /**
18
+ * Establish an ADB tunnel to the instance.
19
+ * Returns the local TCP port and a cleanup function.
20
+ */
21
+ startAdbTunnel: () => Promise<Tunnel>;
22
+ /**
23
+ * Send an asset URL to the instance. The instance will download the asset
24
+ * and process it (currently APK install is supported). Resolves on success,
25
+ * rejects with an Error on failure.
26
+ */
27
+ sendAsset: (url: string) => Promise<void>;
28
+ };
13
29
  /**
14
- * Starts a one-shot TCP WebSocket proxy.
15
- *
16
- * The function creates a local TCP server that listens on an ephemeral port on
17
- * 127.0.0.1. As soon as the **first** TCP client connects the server stops
18
- * accepting further connections and forwards all traffic between that client
19
- * and `remoteURL` through an authenticated WebSocket. If you need to proxy
20
- * more than one TCP connection, call `startTcpProxy` again to create a new
21
- * proxy instance.
22
- *
23
- * @param remoteURL Remote WebSocket endpoint (e.g. wss://example.com/instance)
24
- * @param token Bearer token sent as `Authorization` header
25
- * @param hostname Optional IP address to listen on. Default is 127.0.0.1
26
- * @param port Optional port number to listen on. Default is to ask Node.js
27
- * to find an available non-privileged port.
30
+ * Controls the verbosity of logging in the client
28
31
  */
29
- export declare function startTcpProxy(remoteURL: string, token: string, hostname: string, port: number): Promise<Proxy>;
32
+ export type LogLevel = 'none' | 'error' | 'warn' | 'info' | 'debug';
33
+ /**
34
+ * Configuration options for creating an Instance API client
35
+ */
36
+ export type InstanceClientOptions = {
37
+ /**
38
+ * Path to the ADB executable.
39
+ * @default 'adb'
40
+ */
41
+ adbPath?: string;
42
+ /**
43
+ * Controls logging verbosity
44
+ * @default 'info'
45
+ */
46
+ logLevel?: LogLevel;
47
+ };
48
+ type ScreenshotData = {
49
+ dataUri: string;
50
+ };
51
+ /**
52
+ * Creates a client for interacting with a Limbar instance
53
+ * @param options Configuration options including webrtcUrl, token and log level
54
+ * @returns An InstanceClient for controlling the instance
55
+ */
56
+ export declare function createInstanceClient(androidInstance: AndroidInstance, options?: InstanceClientOptions): Promise<InstanceClient>;
30
57
  //# sourceMappingURL=android-instances-helpers.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"android-instances-helpers.d.ts","sourceRoot":"","sources":["../src/resources/android-instances-helpers.ts"],"names":[],"mappings":"OACO,KAAK,GAAG,MAAM,KAAK;OAGnB,EAAE,eAAe,EAAE;AAE1B;;;GAGG;AACH,eAAO,MAAM,cAAc,GACzB,iBAAiB,eAAe,EAChC,WAAW,MAAM,EACjB,OAAO,MAAM,KACZ,OAAO,CAAC,KAAK,CAsBf,CAAC;AAEF,0FAA0F;AAC1F,MAAM,WAAW,KAAK;IACpB,OAAO,EAAE,GAAG,CAAC,WAAW,CAAC;IACzB,KAAK,EAAE,MAAM,IAAI,CAAC;CACnB;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,aAAa,CACjC,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,KAAK,CAAC,CA6FhB"}
1
+ {"version":3,"file":"android-instances-helpers.d.ts","sourceRoot":"","sources":["../src/resources/android-instances-helpers.ts"],"names":[],"mappings":"OAIO,KAAK,EAAE,MAAM,EAAE;YACV,EAAE,MAAM,EAAE;OACf,EAAE,eAAe,EAAE;AAE1B;;GAEG;AACH,MAAM,MAAM,cAAc,GAAG;IAC3B;;;OAGG;IACH,UAAU,EAAE,MAAM,OAAO,CAAC,cAAc,CAAC,CAAC;IAC1C;;OAEG;IACH,UAAU,EAAE,MAAM,IAAI,CAAC;IAEvB;;;OAGG;IACH,cAAc,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC;IACtC;;;;OAIG;IACH,SAAS,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CAC3C,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,QAAQ,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;AAEpE;;GAEG;AACH,MAAM,MAAM,qBAAqB,GAAG;IAClC;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;OAGG;IACH,QAAQ,CAAC,EAAE,QAAQ,CAAC;CACrB,CAAC;AAaF,KAAK,cAAc,GAAG;IACpB,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC;AA0BF;;;;GAIG;AACH,wBAAsB,oBAAoB,CACxC,eAAe,EAAE,eAAe,EAChC,OAAO,GAAE,qBAGR,GACA,OAAO,CAAC,cAAc,CAAC,CAmPzB"}
@@ -1,129 +1,221 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.startAdbTunnel = void 0;
4
- exports.startTcpProxy = startTcpProxy;
5
- const tslib_1 = require("../internal/tslib.js");
6
- const child_process_1 = require("child_process");
7
- const net = tslib_1.__importStar(require("net"));
3
+ exports.createInstanceClient = createInstanceClient;
8
4
  const ws_1 = require("ws");
5
+ const node_child_process_1 = require("node:child_process");
6
+ const tunnel_js_1 = require("./tunnel.js");
9
7
  /**
10
- * Opens a WebSocket TCP proxy for the ADB port and connects the local adb
11
- * client to it.
8
+ * Creates a client for interacting with a Limbar instance
9
+ * @param options Configuration options including webrtcUrl, token and log level
10
+ * @returns An InstanceClient for controlling the instance
12
11
  */
13
- const startAdbTunnel = async (androidInstance, hostname, port) => {
14
- if (!androidInstance.status.adbWebSocketUrl) {
15
- throw new Error('ADB WebSocket URL is not available');
16
- }
17
- const { address, close } = await startTcpProxy(androidInstance.status.adbWebSocketUrl, androidInstance.status.token, hostname ?? '127.0.0.1', port ?? 0);
18
- try {
19
- await new Promise((resolve, reject) => {
20
- (0, child_process_1.exec)(`adb connect ${address.address}:${address.port}`, (err) => {
21
- if (err)
22
- return reject(err);
23
- resolve();
24
- });
25
- });
26
- }
27
- catch (err) {
28
- close();
29
- throw err;
30
- }
31
- return { address, close };
32
- };
33
- exports.startAdbTunnel = startAdbTunnel;
34
- /**
35
- * Starts a one-shot TCP → WebSocket proxy.
36
- *
37
- * The function creates a local TCP server that listens on an ephemeral port on
38
- * 127.0.0.1. As soon as the **first** TCP client connects the server stops
39
- * accepting further connections and forwards all traffic between that client
40
- * and `remoteURL` through an authenticated WebSocket. If you need to proxy
41
- * more than one TCP connection, call `startTcpProxy` again to create a new
42
- * proxy instance.
43
- *
44
- * @param remoteURL Remote WebSocket endpoint (e.g. wss://example.com/instance)
45
- * @param token Bearer token sent as `Authorization` header
46
- * @param hostname Optional IP address to listen on. Default is 127.0.0.1
47
- * @param port Optional port number to listen on. Default is to ask Node.js
48
- * to find an available non-privileged port.
49
- */
50
- async function startTcpProxy(remoteURL, token, hostname, port) {
51
- return new Promise((resolve, reject) => {
52
- const server = net.createServer();
53
- let ws;
54
- let pingInterval;
55
- // close helper
56
- const close = () => {
57
- if (pingInterval) {
58
- clearInterval(pingInterval);
59
- pingInterval = undefined;
12
+ async function createInstanceClient(androidInstance, options = {
13
+ adbPath: 'adb',
14
+ logLevel: 'info',
15
+ }) {
16
+ const token = androidInstance.status.token;
17
+ const serverAddress = `${androidInstance.status.endpointWebSocketUrl}?token=${token}`;
18
+ const logLevel = options.logLevel ?? 'info';
19
+ let ws = undefined;
20
+ const screenshotRequests = new Map();
21
+ const assetRequests = new Map();
22
+ // Logger functions
23
+ const logger = {
24
+ debug: (...args) => {
25
+ if (logLevel === 'debug')
26
+ console.log(...args);
27
+ },
28
+ info: (...args) => {
29
+ if (logLevel === 'info' || logLevel === 'debug')
30
+ console.log(...args);
31
+ },
32
+ warn: (...args) => {
33
+ if (logLevel === 'warn' || logLevel === 'info' || logLevel === 'debug')
34
+ console.warn(...args);
35
+ },
36
+ error: (...args) => {
37
+ if (logLevel !== 'none')
38
+ console.error(...args);
39
+ },
40
+ };
41
+ return new Promise((resolveConnection, rejectConnection) => {
42
+ logger.debug(`Attempting to connect to WebSocket server at ${serverAddress}...`);
43
+ ws = new ws_1.WebSocket(serverAddress);
44
+ ws.on('message', (data) => {
45
+ let message;
46
+ try {
47
+ message = JSON.parse(data.toString());
60
48
  }
61
- if (ws && ws.readyState === ws_1.WebSocket.OPEN) {
62
- ws.close(1000, 'close');
49
+ catch (e) {
50
+ logger.error({ data, error: e }, 'Failed to parse JSON message');
51
+ return;
63
52
  }
64
- if (server.listening) {
65
- server.close();
53
+ switch (message.type) {
54
+ case 'screenshot': {
55
+ if (!('dataUri' in message) || typeof message.dataUri !== 'string' || !('id' in message)) {
56
+ logger.warn('Received invalid screenshot message:', message);
57
+ break;
58
+ }
59
+ const screenshotMessage = message;
60
+ const request = screenshotRequests.get(screenshotMessage.id);
61
+ if (!request) {
62
+ logger.warn(`Received screenshot data for unknown or already handled session: ${screenshotMessage.id}`);
63
+ break;
64
+ }
65
+ logger.debug(`Received screenshot data URI for session ${screenshotMessage.id}.`);
66
+ request.resolver({ dataUri: screenshotMessage.dataUri });
67
+ screenshotRequests.delete(screenshotMessage.id);
68
+ break;
69
+ }
70
+ case 'screenshotError': {
71
+ if (!('message' in message) || !('id' in message)) {
72
+ logger.warn('Received invalid screenshot error message:', message);
73
+ break;
74
+ }
75
+ const errorMessage = message;
76
+ const request = screenshotRequests.get(errorMessage.id);
77
+ if (!request) {
78
+ logger.warn(`Received screenshot error for unknown or already handled session: ${errorMessage.id}`);
79
+ break;
80
+ }
81
+ logger.error(`Server reported an error capturing screenshot for session ${errorMessage.id}:`, errorMessage.message);
82
+ request.rejecter(new Error(errorMessage.message));
83
+ screenshotRequests.delete(errorMessage.id);
84
+ break;
85
+ }
86
+ case 'assetResult': {
87
+ logger.debug('Received assetResult:', message);
88
+ const request = assetRequests.get(message.url);
89
+ if (!request) {
90
+ logger.warn(`Received assetResult for unknown or already handled url: ${message.url}`);
91
+ break;
92
+ }
93
+ if (message.result === 'success') {
94
+ logger.debug('Asset result is success');
95
+ request.resolver();
96
+ assetRequests.delete(message.url);
97
+ break;
98
+ }
99
+ const errorMessage = typeof message.message === 'string' && message.message ?
100
+ message.message
101
+ : `Asset processing failed: ${JSON.stringify(message)}`;
102
+ logger.debug('Asset result is failure', errorMessage);
103
+ request.rejecter(new Error(errorMessage));
104
+ assetRequests.delete(message.url);
105
+ break;
106
+ }
107
+ default:
108
+ logger.warn(`Received unexpected message type: ${message.type}`);
109
+ break;
66
110
  }
67
- };
68
- // No AbortController support – proxy can be closed via the returned handle
69
- // TCP server error
70
- server.once('error', (err) => {
71
- close();
72
- reject(new Error(`TCP server error: ${err.message}`));
73
111
  });
74
- // Listening
75
- server.once('listening', () => {
76
- const address = server.address();
77
- if (!address || typeof address === 'string') {
78
- close();
79
- return reject(new Error('Failed to obtain listening address'));
112
+ ws.on('error', (err) => {
113
+ logger.error('WebSocket error:', err.message);
114
+ if (ws && (ws.readyState === ws_1.WebSocket.CONNECTING || ws.readyState === ws_1.WebSocket.OPEN)) {
115
+ rejectConnection(err);
80
116
  }
81
- resolve({ address, close });
117
+ screenshotRequests.forEach((request) => request.rejecter(err));
82
118
  });
83
- // On first TCP connection
84
- server.on('connection', (tcpSocket) => {
85
- // Single-connection proxy
86
- server.close();
87
- ws = new ws_1.WebSocket(remoteURL, {
88
- headers: { Authorization: `Bearer ${token}` },
89
- perMessageDeflate: false,
90
- });
91
- // WebSocket error
92
- ws.once('error', (err) => {
93
- console.error('WebSocket error:', err);
94
- tcpSocket.destroy();
95
- close();
96
- });
97
- ws.once('open', () => {
98
- const socket = ws; // non-undefined after open
99
- pingInterval = setInterval(() => {
100
- if (socket.readyState === ws_1.WebSocket.OPEN) {
101
- socket.ping();
102
- }
103
- }, 30000);
104
- // TCP → WS
105
- tcpSocket.on('data', (chunk) => {
106
- if (socket.readyState === ws_1.WebSocket.OPEN) {
107
- socket.send(chunk);
119
+ ws.on('close', () => {
120
+ logger.debug('Disconnected from server.');
121
+ screenshotRequests.forEach((request) => request.rejecter('Disconnected from server'));
122
+ });
123
+ const screenshot = async () => {
124
+ if (!ws || ws.readyState !== ws_1.WebSocket.OPEN) {
125
+ return Promise.reject(new Error('WebSocket is not connected or connection is not open.'));
126
+ }
127
+ const id = 'ts-client-' + Date.now();
128
+ const screenshotRequest = {
129
+ type: 'screenshot',
130
+ id,
131
+ };
132
+ return new Promise((resolve, reject) => {
133
+ logger.debug('Sending screenshot request:', screenshotRequest);
134
+ ws.send(JSON.stringify(screenshotRequest), (err) => {
135
+ if (err) {
136
+ logger.error('Failed to send screenshot request:', err);
137
+ reject(err);
108
138
  }
109
139
  });
110
- // WS TCP
111
- socket.on('message', (data) => {
112
- if (!tcpSocket.destroyed) {
113
- tcpSocket.write(data);
140
+ const timeout = setTimeout(() => {
141
+ if (screenshotRequests.has(id)) {
142
+ logger.error(`Screenshot request timed out for session ${id}`);
143
+ screenshotRequests.get(id)?.rejecter(new Error('Screenshot request timed out'));
144
+ screenshotRequests.delete(id);
114
145
  }
146
+ }, 30000);
147
+ screenshotRequests.set(id, {
148
+ resolver: (value) => {
149
+ clearTimeout(timeout);
150
+ resolve(value);
151
+ screenshotRequests.delete(id);
152
+ },
153
+ rejecter: (reason) => {
154
+ clearTimeout(timeout);
155
+ reject(reason);
156
+ screenshotRequests.delete(id);
157
+ },
115
158
  });
116
159
  });
117
- // Mutual close
118
- tcpSocket.on('close', close);
119
- tcpSocket.on('error', (err) => {
120
- console.error('TCP socket error:', err);
160
+ };
161
+ const disconnect = () => {
162
+ if (ws) {
163
+ logger.debug('Closing WebSocket connection.');
164
+ ws.close();
165
+ }
166
+ screenshotRequests.forEach((request) => request.rejecter('Websocket connection closed'));
167
+ };
168
+ /**
169
+ * Opens a WebSocket TCP proxy for the ADB port and connects the local adb
170
+ * client to it.
171
+ */
172
+ const startAdbTunnel = async () => {
173
+ if (!androidInstance.status.adbWebSocketUrl) {
174
+ return Promise.reject(new Error('ADB WebSocket URL is not set'));
175
+ }
176
+ const { address, close } = await (0, tunnel_js_1.startTcpTunnel)(androidInstance.status.adbWebSocketUrl, token, '127.0.0.1', 0);
177
+ try {
178
+ await new Promise((resolve, reject) => {
179
+ (0, node_child_process_1.exec)(`${options.adbPath ?? 'adb'} connect ${address.address}:${address.port}`, (err) => {
180
+ if (err)
181
+ return reject(err);
182
+ resolve();
183
+ });
184
+ });
185
+ logger.debug(`ADB connected on ${address.address}`);
186
+ }
187
+ catch (err) {
121
188
  close();
189
+ throw err;
190
+ }
191
+ return { address, close };
192
+ };
193
+ const sendAsset = async (url) => {
194
+ if (!ws || ws.readyState !== ws_1.WebSocket.OPEN) {
195
+ return Promise.reject(new Error('WebSocket is not connected or connection is not open.'));
196
+ }
197
+ const assetRequest = {
198
+ type: 'asset',
199
+ url,
200
+ };
201
+ ws.send(JSON.stringify(assetRequest), (err) => {
202
+ if (err) {
203
+ logger.error('Failed to send asset request:', err);
204
+ }
205
+ });
206
+ return new Promise((resolve, reject) => {
207
+ assetRequests.set(url, { resolver: resolve, rejecter: reject });
208
+ });
209
+ };
210
+ ws.on('open', () => {
211
+ logger.debug(`Connected to ${serverAddress}`);
212
+ resolveConnection({
213
+ screenshot,
214
+ disconnect,
215
+ startAdbTunnel,
216
+ sendAsset,
122
217
  });
123
- ws.on('close', () => tcpSocket.destroy());
124
218
  });
125
- // Start listening
126
- server.listen(port, hostname);
127
219
  });
128
220
  }
129
221
  //# sourceMappingURL=android-instances-helpers.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"android-instances-helpers.js","sourceRoot":"","sources":["../src/resources/android-instances-helpers.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"android-instances-helpers.js","sourceRoot":"","sources":["../src/resources/android-instances-helpers.ts"],"names":[],"mappings":"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"}