@limrun/api 0.48.4 → 0.49.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +8 -0
- package/build-settings.d.mts.map +1 -1
- package/build-settings.d.ts.map +1 -1
- package/build-settings.js +4 -5
- package/build-settings.js.map +1 -1
- package/build-settings.mjs +4 -5
- package/build-settings.mjs.map +1 -1
- package/client.d.mts +8 -5
- package/client.d.mts.map +1 -1
- package/client.d.ts +8 -5
- package/client.d.ts.map +1 -1
- package/client.js +4 -4
- package/client.js.map +1 -1
- package/client.mjs +2 -2
- package/client.mjs.map +1 -1
- package/destination-tunnel-dialer.d.mts +30 -2
- package/destination-tunnel-dialer.d.mts.map +1 -1
- package/destination-tunnel-dialer.d.ts +30 -2
- package/destination-tunnel-dialer.d.ts.map +1 -1
- package/destination-tunnel-dialer.js +349 -65
- package/destination-tunnel-dialer.js.map +1 -1
- package/destination-tunnel-dialer.mjs +345 -64
- package/destination-tunnel-dialer.mjs.map +1 -1
- package/destination-tunnel-har.d.mts +23 -0
- package/destination-tunnel-har.d.mts.map +1 -0
- package/destination-tunnel-har.d.ts +23 -0
- package/destination-tunnel-har.d.ts.map +1 -0
- package/destination-tunnel-har.js +164 -0
- package/destination-tunnel-har.js.map +1 -0
- package/destination-tunnel-har.mjs +160 -0
- package/destination-tunnel-har.mjs.map +1 -0
- package/destination-tunnel-inspection.d.mts +71 -0
- package/destination-tunnel-inspection.d.mts.map +1 -0
- package/destination-tunnel-inspection.d.ts +71 -0
- package/destination-tunnel-inspection.d.ts.map +1 -0
- package/destination-tunnel-inspection.js +267 -0
- package/destination-tunnel-inspection.js.map +1 -0
- package/destination-tunnel-inspection.mjs +261 -0
- package/destination-tunnel-inspection.mjs.map +1 -0
- package/destination-tunnel.d.mts +119 -14
- package/destination-tunnel.d.mts.map +1 -1
- package/destination-tunnel.d.ts +119 -14
- package/destination-tunnel.d.ts.map +1 -1
- package/destination-tunnel.js +407 -98
- package/destination-tunnel.js.map +1 -1
- package/destination-tunnel.mjs +374 -73
- package/destination-tunnel.mjs.map +1 -1
- package/exec-client.d.mts +9 -0
- package/exec-client.d.mts.map +1 -1
- package/exec-client.d.ts +9 -0
- package/exec-client.d.ts.map +1 -1
- package/exec-client.js.map +1 -1
- package/exec-client.mjs.map +1 -1
- package/index.d.mts +8 -1
- package/index.d.mts.map +1 -1
- package/index.d.ts +8 -1
- package/index.d.ts.map +1 -1
- package/index.js +26 -2
- package/index.js.map +1 -1
- package/index.mjs +7 -1
- package/index.mjs.map +1 -1
- package/instance-client.d.mts +107 -1
- package/instance-client.d.mts.map +1 -1
- package/instance-client.d.ts +107 -1
- package/instance-client.d.ts.map +1 -1
- package/instance-client.js +83 -1
- package/instance-client.js.map +1 -1
- package/instance-client.mjs +83 -1
- package/instance-client.mjs.map +1 -1
- package/internal/destination-tunnel-management.d.mts +4 -3
- package/internal/destination-tunnel-management.d.mts.map +1 -1
- package/internal/destination-tunnel-management.d.ts +4 -3
- package/internal/destination-tunnel-management.d.ts.map +1 -1
- package/internal/destination-tunnel-management.js +64 -46
- package/internal/destination-tunnel-management.js.map +1 -1
- package/internal/destination-tunnel-management.mjs +55 -37
- package/internal/destination-tunnel-management.mjs.map +1 -1
- package/internal/destination-tunnel-url.d.mts +2 -0
- package/internal/destination-tunnel-url.d.mts.map +1 -1
- package/internal/destination-tunnel-url.d.ts +2 -0
- package/internal/destination-tunnel-url.d.ts.map +1 -1
- package/internal/destination-tunnel-url.js +45 -5
- package/internal/destination-tunnel-url.js.map +1 -1
- package/internal/destination-tunnel-url.mjs +44 -5
- package/internal/destination-tunnel-url.mjs.map +1 -1
- package/internal/destination-tunnel-wire-reader.d.mts +23 -0
- package/internal/destination-tunnel-wire-reader.d.mts.map +1 -0
- package/internal/destination-tunnel-wire-reader.d.ts +23 -0
- package/internal/destination-tunnel-wire-reader.d.ts.map +1 -0
- package/internal/destination-tunnel-wire-reader.js +110 -0
- package/internal/destination-tunnel-wire-reader.js.map +1 -0
- package/internal/destination-tunnel-wire-reader.mjs +93 -0
- package/internal/destination-tunnel-wire-reader.mjs.map +1 -0
- package/internal/persist-option.d.mts +14 -0
- package/internal/persist-option.d.mts.map +1 -0
- package/internal/persist-option.d.ts +14 -0
- package/internal/persist-option.d.ts.map +1 -0
- package/internal/persist-option.js +18 -0
- package/internal/persist-option.js.map +1 -0
- package/internal/persist-option.mjs +15 -0
- package/internal/persist-option.mjs.map +1 -0
- package/internal/proxy-transport.d.mts +27 -0
- package/internal/proxy-transport.d.mts.map +1 -1
- package/internal/proxy-transport.d.ts +27 -0
- package/internal/proxy-transport.d.ts.map +1 -1
- package/internal/proxy-transport.js +206 -0
- package/internal/proxy-transport.js.map +1 -1
- package/internal/proxy-transport.mjs +205 -0
- package/internal/proxy-transport.mjs.map +1 -1
- package/internal/session-stream.d.mts +39 -0
- package/internal/session-stream.d.mts.map +1 -0
- package/internal/session-stream.d.ts +39 -0
- package/internal/session-stream.d.ts.map +1 -0
- package/internal/session-stream.js +144 -0
- package/internal/session-stream.js.map +1 -0
- package/internal/session-stream.mjs +139 -0
- package/internal/session-stream.mjs.map +1 -0
- package/internal/tslib.js +4 -4
- package/ios-client.d.mts +63 -4
- package/ios-client.d.mts.map +1 -1
- package/ios-client.d.ts +63 -4
- package/ios-client.d.ts.map +1 -1
- package/ios-client.js +48 -2
- package/ios-client.js.map +1 -1
- package/ios-client.mjs +48 -2
- package/ios-client.mjs.map +1 -1
- package/package.json +22 -1
- package/resources/android-instances-helpers.d.mts +25 -0
- package/resources/android-instances-helpers.d.mts.map +1 -0
- package/resources/android-instances-helpers.d.ts +25 -0
- package/resources/android-instances-helpers.d.ts.map +1 -0
- package/resources/android-instances-helpers.js +37 -0
- package/resources/android-instances-helpers.js.map +1 -0
- package/resources/android-instances-helpers.mjs +33 -0
- package/resources/android-instances-helpers.mjs.map +1 -0
- package/resources/index.d.mts +6 -3
- package/resources/index.d.mts.map +1 -1
- package/resources/index.d.ts +6 -3
- package/resources/index.d.ts.map +1 -1
- package/resources/index.js +6 -5
- package/resources/index.js.map +1 -1
- package/resources/index.mjs +3 -3
- package/resources/index.mjs.map +1 -1
- package/resources/ios-instances-helpers.d.mts +25 -0
- package/resources/ios-instances-helpers.d.mts.map +1 -0
- package/resources/ios-instances-helpers.d.ts +25 -0
- package/resources/ios-instances-helpers.d.ts.map +1 -0
- package/resources/ios-instances-helpers.js +37 -0
- package/resources/ios-instances-helpers.js.map +1 -0
- package/resources/ios-instances-helpers.mjs +33 -0
- package/resources/ios-instances-helpers.mjs.map +1 -0
- package/resources/session-artifacts.d.mts +70 -0
- package/resources/session-artifacts.d.mts.map +1 -0
- package/resources/session-artifacts.d.ts +70 -0
- package/resources/session-artifacts.d.ts.map +1 -0
- package/resources/session-artifacts.js +8 -0
- package/resources/session-artifacts.js.map +1 -0
- package/resources/session-artifacts.mjs +7 -0
- package/resources/session-artifacts.mjs.map +1 -0
- package/resources/xcode-instances-helpers.d.mts +55 -0
- package/resources/xcode-instances-helpers.d.mts.map +1 -1
- package/resources/xcode-instances-helpers.d.ts +55 -0
- package/resources/xcode-instances-helpers.d.ts.map +1 -1
- package/resources/xcode-instances-helpers.js +49 -3
- package/resources/xcode-instances-helpers.js.map +1 -1
- package/resources/xcode-instances-helpers.mjs +47 -2
- package/resources/xcode-instances-helpers.mjs.map +1 -1
- package/src/build-settings.ts +4 -5
- package/src/client.ts +12 -2
- package/src/destination-tunnel-dialer.ts +403 -64
- package/src/destination-tunnel-har.ts +200 -0
- package/src/destination-tunnel-inspection.ts +367 -0
- package/src/destination-tunnel.ts +510 -88
- package/src/exec-client.ts +9 -0
- package/src/index.ts +58 -0
- package/src/instance-client.ts +273 -5
- package/src/internal/destination-tunnel-management.ts +67 -43
- package/src/internal/destination-tunnel-url.ts +61 -5
- package/src/internal/destination-tunnel-wire-reader.ts +109 -0
- package/src/internal/persist-option.ts +26 -0
- package/src/internal/proxy-transport.ts +248 -0
- package/src/internal/session-stream.ts +157 -0
- package/src/ios-client.ts +129 -8
- package/src/resources/android-instances-helpers.ts +45 -0
- package/src/resources/index.ts +14 -2
- package/src/resources/ios-instances-helpers.ts +45 -0
- package/src/resources/session-artifacts.ts +95 -0
- package/src/resources/xcode-instances-helpers.ts +116 -5
- package/src/version.ts +1 -1
- package/version.d.mts +1 -1
- package/version.d.ts +1 -1
- package/version.js +1 -1
- package/version.mjs +1 -1
package/src/ios-client.ts
CHANGED
|
@@ -6,7 +6,7 @@ import { WebSocket, Data } from 'ws';
|
|
|
6
6
|
import { EventEmitter } from 'events';
|
|
7
7
|
import { assertPort, isNonRetryableError, startReverseTcpTunnel, type ReverseTunnel } from './tunnel';
|
|
8
8
|
import { startDestinationTcpTunnel, type DestinationTcpTunnel } from './destination-tunnel-dialer';
|
|
9
|
-
import type
|
|
9
|
+
import { disabledDestinationTunnelInspection, type DestinationTunnelSelectors } from './destination-tunnel';
|
|
10
10
|
import { type SyncFolderResult, type FolderSyncOptions, syncFolder } from './folder-sync';
|
|
11
11
|
import { createIgnoreFn } from './folder-sync-ignore';
|
|
12
12
|
import { prepareAppBundlePath, watchAppArchive } from './app-archive';
|
|
@@ -25,6 +25,11 @@ import {
|
|
|
25
25
|
type StartForwardHttpProxyOptions,
|
|
26
26
|
} from './http-proxy';
|
|
27
27
|
import { startXcrunShim as startClientXcrunShim } from './ios-shim';
|
|
28
|
+
import { persistFields, type PersistOption } from './internal/persist-option';
|
|
29
|
+
import { streamSessionEntries } from './internal/session-stream';
|
|
30
|
+
import type { SessionLogLine, SessionEvent } from './resources/session-artifacts';
|
|
31
|
+
|
|
32
|
+
export { type PersistOption } from './internal/persist-option';
|
|
28
33
|
|
|
29
34
|
/**
|
|
30
35
|
* Connection state of the instance client
|
|
@@ -65,8 +70,8 @@ export function deriveReverseTunnelUrl(apiUrl: string, remotePort: number): stri
|
|
|
65
70
|
export type { ReverseTunnel } from './tunnel';
|
|
66
71
|
export type Tunnel = DestinationTcpTunnel;
|
|
67
72
|
export type TunnelOptions = {
|
|
68
|
-
/** Exact
|
|
69
|
-
|
|
73
|
+
/** Exact endpoint (host:port) and domain selector values; the server asks this client to dial exact endpoints and intercepted domains. */
|
|
74
|
+
selectors: DestinationTunnelSelectors;
|
|
70
75
|
/** Controls tunnel logging verbosity. Defaults to the instance client's log level. */
|
|
71
76
|
logLevel?: LogLevel;
|
|
72
77
|
};
|
|
@@ -236,6 +241,10 @@ export type InstalledApp = {
|
|
|
236
241
|
bundleId: string;
|
|
237
242
|
name: string;
|
|
238
243
|
installType: string;
|
|
244
|
+
/** App icon as a base64-encoded 64px PNG; absent when the runtime could not extract one. */
|
|
245
|
+
icon?: string;
|
|
246
|
+
/** Install time as epoch milliseconds; absent when the runtime cannot tell (e.g. iOS system apps). */
|
|
247
|
+
installedAt?: number;
|
|
239
248
|
};
|
|
240
249
|
|
|
241
250
|
export type LsofEntry = {
|
|
@@ -677,7 +686,8 @@ export type InstanceClient = {
|
|
|
677
686
|
|
|
678
687
|
/**
|
|
679
688
|
* List installed apps on the simulator
|
|
680
|
-
* @returns Array of installed apps with bundleId, name, and
|
|
689
|
+
* @returns Array of installed apps with bundleId, name, installType, and,
|
|
690
|
+
* when available, a small base64 PNG icon
|
|
681
691
|
*/
|
|
682
692
|
listApps: () => Promise<InstalledApp[]>;
|
|
683
693
|
|
|
@@ -758,8 +768,11 @@ export type InstanceClient = {
|
|
|
758
768
|
* Start recording simulator video. Use stopRecording() to stop the recording.
|
|
759
769
|
* When provided, `quality` must be one of `5`, `6`, `7`, `8`, `9`, or `10`.
|
|
760
770
|
* The server default is `5`.
|
|
771
|
+
* With `persist`, the completed recording is uploaded to Limrun's bucket
|
|
772
|
+
* when the recording stops or the instance terminates; list it with
|
|
773
|
+
* `iosInstances.listRecordings`.
|
|
761
774
|
*/
|
|
762
|
-
startRecording: (options?: { quality?: RecordingQuality }) => Promise<void>;
|
|
775
|
+
startRecording: (options?: { quality?: RecordingQuality; persist?: PersistOption }) => Promise<void>;
|
|
763
776
|
|
|
764
777
|
/**
|
|
765
778
|
* Stop the active server-side recording.
|
|
@@ -771,6 +784,54 @@ export type InstanceClient = {
|
|
|
771
784
|
*/
|
|
772
785
|
stopRecording: (saveTo: { presignedUrl?: string; localPath?: string }) => Promise<string>;
|
|
773
786
|
|
|
787
|
+
/**
|
|
788
|
+
* Start capturing an app's logs (one JSONL object per line) for the given
|
|
789
|
+
* bundle id. One app log capture runs at a time; the server rejects a
|
|
790
|
+
* second start. With `persist`, the capture is uploaded to Limrun's bucket
|
|
791
|
+
* when it stops or the instance terminates; list it with
|
|
792
|
+
* `iosInstances.listAppLogs`.
|
|
793
|
+
*/
|
|
794
|
+
startAppLogCapture: (options: { bundleId: string; persist?: PersistOption }) => Promise<void>;
|
|
795
|
+
|
|
796
|
+
/** Stop the active app log capture. */
|
|
797
|
+
stopAppLogCapture: () => Promise<void>;
|
|
798
|
+
|
|
799
|
+
/**
|
|
800
|
+
* Start capturing the coalesced event log of user and agent actions (one
|
|
801
|
+
* JSONL object per line). One event capture runs at a time; the server
|
|
802
|
+
* rejects a second start. With `persist`, the capture is uploaded to
|
|
803
|
+
* Limrun's bucket when it stops or the instance terminates; list it with
|
|
804
|
+
* `iosInstances.listEvents`.
|
|
805
|
+
*/
|
|
806
|
+
startEventCapture: (options?: { persist?: PersistOption }) => Promise<void>;
|
|
807
|
+
|
|
808
|
+
/** Stop the active event capture. */
|
|
809
|
+
stopEventCapture: () => Promise<void>;
|
|
810
|
+
|
|
811
|
+
/**
|
|
812
|
+
* Tail the live app log stream fed by {@link startAppLogCapture}. On
|
|
813
|
+
* connect the instance replays its recent buffer (up to 1000 lines), then
|
|
814
|
+
* delivers lines as they are captured; a dropped connection reconnects
|
|
815
|
+
* and resumes exactly where it left off, so every line is delivered once.
|
|
816
|
+
* The stream is independent of this client's websocket and survives
|
|
817
|
+
* {@link disconnect}; `onError` fires at most once, after the stream has
|
|
818
|
+
* kept failing for several minutes (e.g. the instance was deleted), and
|
|
819
|
+
* the stream is closed. Returns a function that closes the stream.
|
|
820
|
+
*/
|
|
821
|
+
streamAppLogCapture: (handlers: {
|
|
822
|
+
onLine: (line: SessionLogLine) => void;
|
|
823
|
+
onError?: (error: Error) => void;
|
|
824
|
+
}) => () => void;
|
|
825
|
+
|
|
826
|
+
/**
|
|
827
|
+
* Tail the live coalesced event log fed by {@link startEventCapture}.
|
|
828
|
+
* Same delivery and lifecycle semantics as {@link streamAppLogCapture}.
|
|
829
|
+
*/
|
|
830
|
+
streamEventCapture: (handlers: {
|
|
831
|
+
onEvent: (event: SessionEvent) => void;
|
|
832
|
+
onError?: (error: Error) => void;
|
|
833
|
+
}) => () => void;
|
|
834
|
+
|
|
774
835
|
/**
|
|
775
836
|
* Play an audio file as the simulator's microphone input. Stage the file on
|
|
776
837
|
* the instance first with `pushFile` (no bundle ID); `path` is the same
|
|
@@ -1861,6 +1922,10 @@ export async function createInstanceClient(options: InstanceClientOptions): Prom
|
|
|
1861
1922
|
}),
|
|
1862
1923
|
startVideoRecordingResult: () => undefined,
|
|
1863
1924
|
stopVideoRecordingResult: () => undefined,
|
|
1925
|
+
startAppLogCaptureResult: () => undefined,
|
|
1926
|
+
stopAppLogCaptureResult: () => undefined,
|
|
1927
|
+
startEventCaptureResult: () => undefined,
|
|
1928
|
+
stopEventCaptureResult: () => undefined,
|
|
1864
1929
|
cameraControlResult: () => undefined,
|
|
1865
1930
|
playOnMicrophoneResult: (msg): IosPlayOnMicrophoneResult => ({
|
|
1866
1931
|
duration: msg.duration ?? 0,
|
|
@@ -2055,6 +2120,12 @@ export async function createInstanceClient(options: InstanceClientOptions): Prom
|
|
|
2055
2120
|
performActions,
|
|
2056
2121
|
startRecording,
|
|
2057
2122
|
stopRecording,
|
|
2123
|
+
startAppLogCapture,
|
|
2124
|
+
stopAppLogCapture,
|
|
2125
|
+
startEventCapture,
|
|
2126
|
+
stopEventCapture,
|
|
2127
|
+
streamAppLogCapture,
|
|
2128
|
+
streamEventCapture,
|
|
2058
2129
|
playOnMicrophone,
|
|
2059
2130
|
stopMicrophonePlayback,
|
|
2060
2131
|
microphoneStatus,
|
|
@@ -2390,8 +2461,13 @@ export async function createInstanceClient(options: InstanceClientOptions): Prom
|
|
|
2390
2461
|
return sendRequest<PerformActionsResult>('performActions', { actions }, undefined, timeoutMs);
|
|
2391
2462
|
};
|
|
2392
2463
|
|
|
2393
|
-
const startRecording = async (opts?: {
|
|
2394
|
-
|
|
2464
|
+
const startRecording = async (opts?: {
|
|
2465
|
+
quality?: RecordingQuality;
|
|
2466
|
+
persist?: PersistOption;
|
|
2467
|
+
}): Promise<void> => {
|
|
2468
|
+
const request: { quality?: RecordingQuality; persist?: boolean; ttlSeconds?: number } = {
|
|
2469
|
+
...persistFields(opts?.persist),
|
|
2470
|
+
};
|
|
2395
2471
|
if (opts?.quality !== undefined) {
|
|
2396
2472
|
if (!Number.isInteger(opts.quality) || opts.quality < 5 || opts.quality > 10) {
|
|
2397
2473
|
throw new Error('quality must be one of: 5, 6, 7, 8, 9, 10');
|
|
@@ -2401,6 +2477,50 @@ export async function createInstanceClient(options: InstanceClientOptions): Prom
|
|
|
2401
2477
|
await sendRequest<void>('startVideoRecording', request);
|
|
2402
2478
|
};
|
|
2403
2479
|
|
|
2480
|
+
const startAppLogCapture = async (opts: { bundleId: string; persist?: PersistOption }): Promise<void> => {
|
|
2481
|
+
if (!opts.bundleId) {
|
|
2482
|
+
throw new Error('bundleId must be a non-empty string');
|
|
2483
|
+
}
|
|
2484
|
+
await sendRequest<void>('startAppLogCapture', {
|
|
2485
|
+
bundleId: opts.bundleId,
|
|
2486
|
+
...persistFields(opts.persist),
|
|
2487
|
+
});
|
|
2488
|
+
};
|
|
2489
|
+
|
|
2490
|
+
const stopAppLogCapture = async (): Promise<void> => {
|
|
2491
|
+
await sendRequest<void>('stopAppLogCapture');
|
|
2492
|
+
};
|
|
2493
|
+
|
|
2494
|
+
const startEventCapture = async (opts?: { persist?: PersistOption }): Promise<void> => {
|
|
2495
|
+
await sendRequest<void>('startEventCapture', { ...persistFields(opts?.persist) });
|
|
2496
|
+
};
|
|
2497
|
+
|
|
2498
|
+
const stopEventCapture = async (): Promise<void> => {
|
|
2499
|
+
await sendRequest<void>('stopEventCapture');
|
|
2500
|
+
};
|
|
2501
|
+
|
|
2502
|
+
const streamAppLogCapture = (handlers: {
|
|
2503
|
+
onLine: (line: SessionLogLine) => void;
|
|
2504
|
+
onError?: (error: Error) => void;
|
|
2505
|
+
}): (() => void) =>
|
|
2506
|
+
streamSessionEntries<SessionLogLine>({
|
|
2507
|
+
url: `${options.apiUrl}/session/applogs/events`,
|
|
2508
|
+
token: options.token,
|
|
2509
|
+
onEntry: handlers.onLine,
|
|
2510
|
+
onError: handlers.onError,
|
|
2511
|
+
});
|
|
2512
|
+
|
|
2513
|
+
const streamEventCapture = (handlers: {
|
|
2514
|
+
onEvent: (event: SessionEvent) => void;
|
|
2515
|
+
onError?: (error: Error) => void;
|
|
2516
|
+
}): (() => void) =>
|
|
2517
|
+
streamSessionEntries<SessionEvent>({
|
|
2518
|
+
url: `${options.apiUrl}/session/events/events`,
|
|
2519
|
+
token: options.token,
|
|
2520
|
+
onEntry: handlers.onEvent,
|
|
2521
|
+
onError: handlers.onError,
|
|
2522
|
+
});
|
|
2523
|
+
|
|
2404
2524
|
const stopRecording = async (saveTo: { presignedUrl?: string; localPath?: string }): Promise<string> => {
|
|
2405
2525
|
await sendRequest<void>('stopVideoRecording', {
|
|
2406
2526
|
upload: saveTo.presignedUrl ? { presignedUrl: saveTo.presignedUrl } : undefined,
|
|
@@ -2858,7 +2978,8 @@ export async function createInstanceClient(options: InstanceClientOptions): Prom
|
|
|
2858
2978
|
|
|
2859
2979
|
const startTunnel = async (tunnelOptions: TunnelOptions): Promise<Tunnel> => {
|
|
2860
2980
|
return startDestinationTcpTunnel(deriveDestinationTunnelURL(options.apiUrl), options.token, {
|
|
2861
|
-
|
|
2981
|
+
selectors: tunnelOptions.selectors,
|
|
2982
|
+
inspection: disabledDestinationTunnelInspection(),
|
|
2862
2983
|
logLevel: tunnelOptions.logLevel ?? logLevel,
|
|
2863
2984
|
});
|
|
2864
2985
|
};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { AndroidInstances as GeneratedAndroidInstances } from './android-instances';
|
|
2
|
+
import { type SessionArtifact, type SessionArtifactKind } from './session-artifacts';
|
|
3
|
+
import { APIPromise } from '../core/api-promise';
|
|
4
|
+
import { RequestOptions } from '../internal/request-options';
|
|
5
|
+
import { path } from '../internal/utils/path';
|
|
6
|
+
|
|
7
|
+
export type { SessionArtifact, SessionArtifactKind } from './session-artifacts';
|
|
8
|
+
|
|
9
|
+
export class AndroidInstances extends GeneratedAndroidInstances {
|
|
10
|
+
/**
|
|
11
|
+
* List the instance's persisted session artifacts, optionally narrowed to
|
|
12
|
+
* one kind.
|
|
13
|
+
*/
|
|
14
|
+
listSessionArtifacts(
|
|
15
|
+
id: string,
|
|
16
|
+
kind?: SessionArtifactKind,
|
|
17
|
+
options?: RequestOptions,
|
|
18
|
+
): APIPromise<SessionArtifact[]> {
|
|
19
|
+
return this._client.get(path`/v1/android_instances/${id}/session_artifacts`, {
|
|
20
|
+
query: kind ? { kind } : undefined,
|
|
21
|
+
...options,
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* List the instance's persisted session recordings.
|
|
27
|
+
*/
|
|
28
|
+
listRecordings(id: string, options?: RequestOptions): APIPromise<SessionArtifact[]> {
|
|
29
|
+
return this.listSessionArtifacts(id, 'recording', options);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* List the instance's persisted app log captures.
|
|
34
|
+
*/
|
|
35
|
+
listAppLogs(id: string, options?: RequestOptions): APIPromise<SessionArtifact[]> {
|
|
36
|
+
return this.listSessionArtifacts(id, 'appLog', options);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* List the instance's persisted event log captures.
|
|
41
|
+
*/
|
|
42
|
+
listEvents(id: string, options?: RequestOptions): APIPromise<SessionArtifact[]> {
|
|
43
|
+
return this.listSessionArtifacts(id, 'eventLog', options);
|
|
44
|
+
}
|
|
45
|
+
}
|
package/src/resources/index.ts
CHANGED
|
@@ -8,7 +8,6 @@ export {
|
|
|
8
8
|
type AnalyticsGetInstancesParams,
|
|
9
9
|
} from './analytics';
|
|
10
10
|
export {
|
|
11
|
-
AndroidInstances,
|
|
12
11
|
type AndroidInstance,
|
|
13
12
|
type AndroidInstanceCreateParams,
|
|
14
13
|
type AndroidInstanceListParams,
|
|
@@ -32,7 +31,6 @@ export {
|
|
|
32
31
|
type GradleInstancesItems,
|
|
33
32
|
} from './gradle-instances';
|
|
34
33
|
export {
|
|
35
|
-
IosInstances,
|
|
36
34
|
type IosInstance,
|
|
37
35
|
type IosInstanceCreateParams,
|
|
38
36
|
type IosInstanceListParams,
|
|
@@ -46,10 +44,24 @@ export {
|
|
|
46
44
|
type XcodeInstancesItems,
|
|
47
45
|
} from './xcode-instances';
|
|
48
46
|
export { Assets, AssetGetOrUploadParams, AssetGetOrUploadResponse } from './assets-helpers';
|
|
47
|
+
export { IosInstances } from './ios-instances-helpers';
|
|
48
|
+
export { AndroidInstances } from './android-instances-helpers';
|
|
49
|
+
export {
|
|
50
|
+
type SessionArtifact,
|
|
51
|
+
type SessionArtifactKind,
|
|
52
|
+
type SessionLogLine,
|
|
53
|
+
type SessionEvent,
|
|
54
|
+
type SessionEventType,
|
|
55
|
+
} from './session-artifacts';
|
|
49
56
|
export {
|
|
50
57
|
XcodeInstances,
|
|
51
58
|
RbeUnsupportedError,
|
|
59
|
+
XcodeSelectionUnsupportedError,
|
|
52
60
|
DEFAULT_RBE_TUNNEL_PORT,
|
|
61
|
+
type XcodeInfo,
|
|
62
|
+
type XcodeStatus,
|
|
63
|
+
type XcodeSelectResult,
|
|
64
|
+
type SandboxInfo,
|
|
53
65
|
type XcodeCreateClientParams,
|
|
54
66
|
type XcodeClient,
|
|
55
67
|
type XcodeProjectConfig,
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { IosInstances as GeneratedIosInstances } from './ios-instances';
|
|
2
|
+
import { type SessionArtifact, type SessionArtifactKind } from './session-artifacts';
|
|
3
|
+
import { APIPromise } from '../core/api-promise';
|
|
4
|
+
import { RequestOptions } from '../internal/request-options';
|
|
5
|
+
import { path } from '../internal/utils/path';
|
|
6
|
+
|
|
7
|
+
export type { SessionArtifact, SessionArtifactKind } from './session-artifacts';
|
|
8
|
+
|
|
9
|
+
export class IosInstances extends GeneratedIosInstances {
|
|
10
|
+
/**
|
|
11
|
+
* List the instance's persisted session artifacts, optionally narrowed to
|
|
12
|
+
* one kind.
|
|
13
|
+
*/
|
|
14
|
+
listSessionArtifacts(
|
|
15
|
+
id: string,
|
|
16
|
+
kind?: SessionArtifactKind,
|
|
17
|
+
options?: RequestOptions,
|
|
18
|
+
): APIPromise<SessionArtifact[]> {
|
|
19
|
+
return this._client.get(path`/v1/ios_instances/${id}/session_artifacts`, {
|
|
20
|
+
query: kind ? { kind } : undefined,
|
|
21
|
+
...options,
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* List the instance's persisted session recordings.
|
|
27
|
+
*/
|
|
28
|
+
listRecordings(id: string, options?: RequestOptions): APIPromise<SessionArtifact[]> {
|
|
29
|
+
return this.listSessionArtifacts(id, 'recording', options);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* List the instance's persisted app log captures.
|
|
34
|
+
*/
|
|
35
|
+
listAppLogs(id: string, options?: RequestOptions): APIPromise<SessionArtifact[]> {
|
|
36
|
+
return this.listSessionArtifacts(id, 'appLog', options);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* List the instance's persisted event log captures.
|
|
41
|
+
*/
|
|
42
|
+
listEvents(id: string, options?: RequestOptions): APIPromise<SessionArtifact[]> {
|
|
43
|
+
return this.listSessionArtifacts(id, 'eventLog', options);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
// Hand-written: the session-artifact list endpoint is not in the generated
|
|
2
|
+
// OpenAPI surface yet. Shared by the iOS and Android instance helpers.
|
|
3
|
+
// SessionLogLine and SessionEvent mirror pkg/artifacts/contract.go in the
|
|
4
|
+
// limrun repo; the runtimes emit the same JSON over live SSE and into the
|
|
5
|
+
// persisted JSONL artifacts.
|
|
6
|
+
|
|
7
|
+
/** The kind of a persisted session capture. */
|
|
8
|
+
export type SessionArtifactKind = 'recording' | 'appLog' | 'eventLog' | 'networkLog';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* One persisted session capture of an instance: a video recording, an app log
|
|
12
|
+
* capture, a coalesced event log, or a HAR network log. Returned by the
|
|
13
|
+
* director's GET /v1/{ios,android}_instances/{id}/session_artifacts endpoint,
|
|
14
|
+
* optionally narrowed with its kind query parameter.
|
|
15
|
+
*/
|
|
16
|
+
export interface SessionArtifact {
|
|
17
|
+
id: string;
|
|
18
|
+
|
|
19
|
+
kind: SessionArtifactKind;
|
|
20
|
+
|
|
21
|
+
/** Bundle id (iOS) or package name (Android) for app log captures. */
|
|
22
|
+
bundleId?: string;
|
|
23
|
+
|
|
24
|
+
/** Destination tunnel that produced a networkLog artifact. */
|
|
25
|
+
tunnelId?: string;
|
|
26
|
+
|
|
27
|
+
/** Canonical selectors captured by a networkLog artifact. */
|
|
28
|
+
selectors?: string[];
|
|
29
|
+
|
|
30
|
+
startedAt?: string;
|
|
31
|
+
|
|
32
|
+
stoppedAt?: string;
|
|
33
|
+
|
|
34
|
+
/** When the artifact is deleted from storage. */
|
|
35
|
+
expiresAt?: string;
|
|
36
|
+
|
|
37
|
+
sizeBytes?: number;
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Short-lived presigned GET URL for the raw artifact: MP4 for recordings,
|
|
41
|
+
* JSONL for app logs and event logs, HAR for network logs.
|
|
42
|
+
*/
|
|
43
|
+
downloadUrl: string;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* One captured app log line, delivered live by `streamAppLogCapture` and stored
|
|
48
|
+
* one-per-line in `appLog` artifacts.
|
|
49
|
+
*/
|
|
50
|
+
export interface SessionLogLine {
|
|
51
|
+
/**
|
|
52
|
+
* Epoch milliseconds: logcat's own timestamp on Android, receipt time on
|
|
53
|
+
* iOS. Shares the clock of the recording artifact's `startedAt`, so lines
|
|
54
|
+
* can be aligned with video playback offsets.
|
|
55
|
+
*/
|
|
56
|
+
ts: number;
|
|
57
|
+
|
|
58
|
+
line: string;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export type SessionEventType = 'tap' | 'drag' | 'scroll' | 'key' | 'text' | 'command';
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* One coalesced user or agent action, delivered live by `streamEventCapture` and
|
|
65
|
+
* stored one-per-line in `eventLog` artifacts. The runtime logs semantic
|
|
66
|
+
* actions, never raw HID frames: a pointer sequence becomes one tap or drag,
|
|
67
|
+
* scroll and keystroke bursts merge into one event, and typed text is never
|
|
68
|
+
* included.
|
|
69
|
+
*/
|
|
70
|
+
export interface SessionEvent {
|
|
71
|
+
/** The action's start in epoch milliseconds, same clock as SessionLogLine. */
|
|
72
|
+
ts: number;
|
|
73
|
+
|
|
74
|
+
type: SessionEventType;
|
|
75
|
+
|
|
76
|
+
/** Touch point for tap, start point for drag, anchor for scroll. */
|
|
77
|
+
x?: number;
|
|
78
|
+
y?: number;
|
|
79
|
+
|
|
80
|
+
/** Drag end point; for scroll they carry the burst's summed deltas instead. */
|
|
81
|
+
toX?: number;
|
|
82
|
+
toY?: number;
|
|
83
|
+
|
|
84
|
+
/** Duration of the whole gesture or burst. */
|
|
85
|
+
durationMs?: number;
|
|
86
|
+
|
|
87
|
+
/** Number of merged actions in a scroll or text burst. */
|
|
88
|
+
count?: number;
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Short human-readable label, e.g. "launchApp com.example". Never carries
|
|
92
|
+
* typed text or request payloads.
|
|
93
|
+
*/
|
|
94
|
+
summary?: string;
|
|
95
|
+
}
|
|
@@ -373,6 +373,44 @@ export type RbeStatus = {
|
|
|
373
373
|
/** Default local TCP port the RBE tunnel listens on. */
|
|
374
374
|
export const DEFAULT_RBE_TUNNEL_PORT = 8980;
|
|
375
375
|
|
|
376
|
+
/** One Xcode installed on the sandbox's node, as reported by limbuild's /xcode. */
|
|
377
|
+
export type XcodeInfo = {
|
|
378
|
+
/** Major version, e.g. "27". Empty on nodes that predate the Xcode inventory. */
|
|
379
|
+
major: string;
|
|
380
|
+
/** Short version, e.g. "27.0". */
|
|
381
|
+
version: string;
|
|
382
|
+
/** Build, e.g. "27A5252f". */
|
|
383
|
+
build: string;
|
|
384
|
+
/**
|
|
385
|
+
* The version key builds and caches are stamped with (major.minor.patch.build,
|
|
386
|
+
* e.g. 27.0.0.27A5252f). Empty until resolved on a cold node.
|
|
387
|
+
*/
|
|
388
|
+
versionKey: string;
|
|
389
|
+
developerDir: string;
|
|
390
|
+
/** Whether this is the Xcode the sandbox binds to when nothing was selected. */
|
|
391
|
+
nodeDefault: boolean;
|
|
392
|
+
};
|
|
393
|
+
|
|
394
|
+
/** The sandbox's Xcode binding and the Xcodes it can switch to. */
|
|
395
|
+
export type XcodeStatus = {
|
|
396
|
+
bound: XcodeInfo;
|
|
397
|
+
installed: XcodeInfo[];
|
|
398
|
+
};
|
|
399
|
+
|
|
400
|
+
/** Result of binding the sandbox to an Xcode major. */
|
|
401
|
+
export type XcodeSelectResult = {
|
|
402
|
+
bound: XcodeInfo;
|
|
403
|
+
alreadyBound: boolean;
|
|
404
|
+
/** Whether synced projects lost their DerivedData because the Xcode changed. */
|
|
405
|
+
derivedDataReset: boolean;
|
|
406
|
+
};
|
|
407
|
+
|
|
408
|
+
/** limbuild's GET /info: the sandbox home and, on daemons that support selection, its Xcode. */
|
|
409
|
+
export type SandboxInfo = {
|
|
410
|
+
homeDir: string;
|
|
411
|
+
xcode?: XcodeInfo;
|
|
412
|
+
};
|
|
413
|
+
|
|
376
414
|
export type RbeStartOptions = {
|
|
377
415
|
/** Max size of the content-addressable store (CAS), in bytes. */
|
|
378
416
|
casMaxBytes?: number;
|
|
@@ -498,6 +536,22 @@ export type XcodeClient = {
|
|
|
498
536
|
*/
|
|
499
537
|
getSimulator: () => Promise<SimulatorStatus>;
|
|
500
538
|
|
|
539
|
+
/**
|
|
540
|
+
* Return the Xcode this sandbox builds with and the Xcodes installed on its node.
|
|
541
|
+
* Throws XcodeSelectionUnsupportedError on a sandbox whose daemon predates selection.
|
|
542
|
+
*/
|
|
543
|
+
getXcode: () => Promise<XcodeStatus>;
|
|
544
|
+
|
|
545
|
+
/**
|
|
546
|
+
* Bind the sandbox to an installed Xcode major (e.g. "27"). Switching resets every
|
|
547
|
+
* synced project's DerivedData. Refused (HTTP 409) while a build, command, sync, or
|
|
548
|
+
* the RBE stack is running, and (HTTP 400) when the major is not installed.
|
|
549
|
+
*/
|
|
550
|
+
setXcode: (major: string) => Promise<XcodeSelectResult>;
|
|
551
|
+
|
|
552
|
+
/** Return the sandbox's home directory and, when the daemon reports it, its Xcode. */
|
|
553
|
+
getInfo: () => Promise<SandboxInfo>;
|
|
554
|
+
|
|
501
555
|
/**
|
|
502
556
|
* Start the instance's embedded Bazel Remote Build Execution stack.
|
|
503
557
|
* Idempotent: returns the running status when already up.
|
|
@@ -591,19 +645,20 @@ function normalizeWorkspaceRelativePath(remotePath: string): string {
|
|
|
591
645
|
return parts.join('/');
|
|
592
646
|
}
|
|
593
647
|
|
|
594
|
-
async function fetchSandboxInfo(apiUrl: string, token: string): Promise<
|
|
648
|
+
async function fetchSandboxInfo(apiUrl: string, token: string): Promise<SandboxInfo> {
|
|
595
649
|
const res = await nodeProxyTransport.fetch(`${apiUrl}/info`, {
|
|
596
650
|
method: 'GET',
|
|
597
651
|
headers: {
|
|
598
652
|
Authorization: `Bearer ${token}`,
|
|
599
653
|
},
|
|
600
654
|
});
|
|
601
|
-
const body = await readJsonResponse<{ homeDir?: string }>(res, 'GET /info');
|
|
655
|
+
const body = await readJsonResponse<{ homeDir?: string; xcode?: XcodeInfo }>(res, 'GET /info');
|
|
602
656
|
if (!body.homeDir) {
|
|
603
657
|
throw new Error('GET /info response is missing homeDir');
|
|
604
658
|
}
|
|
605
659
|
return {
|
|
606
660
|
homeDir: normalizeWorkspaceRelativePath(body.homeDir),
|
|
661
|
+
...(body.xcode ? { xcode: body.xcode } : {}),
|
|
607
662
|
};
|
|
608
663
|
}
|
|
609
664
|
|
|
@@ -646,14 +701,46 @@ export class RbeUnsupportedError extends LimrunError {
|
|
|
646
701
|
}
|
|
647
702
|
}
|
|
648
703
|
|
|
704
|
+
/**
|
|
705
|
+
* Raised when an `/xcode` request returns 404: the sandbox's daemon predates Xcode
|
|
706
|
+
* version selection. Distinct from NotFoundError for the same reason as
|
|
707
|
+
* RbeUnsupportedError: the instance exists, only the feature is missing.
|
|
708
|
+
*/
|
|
709
|
+
export class XcodeSelectionUnsupportedError extends LimrunError {
|
|
710
|
+
constructor(operation: string) {
|
|
711
|
+
super(
|
|
712
|
+
`Xcode version selection is not available on this sandbox (${operation} returned 404): ` +
|
|
713
|
+
'its daemon predates Xcode selection. Create a new sandbox, or omit the Xcode version to build with its default.',
|
|
714
|
+
);
|
|
715
|
+
this.name = 'XcodeSelectionUnsupportedError';
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
/** Reads an `/xcode` JSON response, mapping a 404 to XcodeSelectionUnsupportedError. */
|
|
720
|
+
function readXcodeResponse<T>(res: Response, operation: string): Promise<T> {
|
|
721
|
+
return readOrUnsupported<T>(res, operation, (op) => new XcodeSelectionUnsupportedError(op));
|
|
722
|
+
}
|
|
723
|
+
|
|
649
724
|
/**
|
|
650
725
|
* Reads an `/rbe` JSON response, mapping a 404 to RbeUnsupportedError (a route
|
|
651
726
|
* 404 means RBE is not supported here, never a missing instance).
|
|
652
727
|
*/
|
|
653
|
-
|
|
728
|
+
function readRbeResponse<T>(res: Response, operation: string): Promise<T> {
|
|
729
|
+
return readOrUnsupported<T>(res, operation, (op) => new RbeUnsupportedError(op));
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
/**
|
|
733
|
+
* Reads a JSON response from a limbuild route that only newer daemons serve: a 404 there means
|
|
734
|
+
* the feature is missing, never that the instance is (the caller's error class says which).
|
|
735
|
+
*/
|
|
736
|
+
async function readOrUnsupported<T>(
|
|
737
|
+
res: Response,
|
|
738
|
+
operation: string,
|
|
739
|
+
unsupported: (operation: string) => Error,
|
|
740
|
+
): Promise<T> {
|
|
654
741
|
if (res.status === 404) {
|
|
655
742
|
await res.text().catch(() => undefined);
|
|
656
|
-
throw
|
|
743
|
+
throw unsupported(operation);
|
|
657
744
|
}
|
|
658
745
|
return readJsonResponse<T>(res, operation);
|
|
659
746
|
}
|
|
@@ -777,7 +864,7 @@ export class XcodeInstances extends GeneratedXcodeInstances {
|
|
|
777
864
|
|
|
778
865
|
const log = createDaemonLogger('[XcodeInstance]', params.logLevel ?? 'info');
|
|
779
866
|
const client = this._client;
|
|
780
|
-
let sandboxInfoPromise: Promise<
|
|
867
|
+
let sandboxInfoPromise: Promise<SandboxInfo> | undefined;
|
|
781
868
|
const getSandboxInfo = () => {
|
|
782
869
|
sandboxInfoPromise ??= fetchSandboxInfo(apiUrl, token);
|
|
783
870
|
return sandboxInfoPromise;
|
|
@@ -996,6 +1083,30 @@ export class XcodeInstances extends GeneratedXcodeInstances {
|
|
|
996
1083
|
attachNewSimulator: attachNewSimulatorImpl,
|
|
997
1084
|
deleteSimulator: deleteSimulatorImpl,
|
|
998
1085
|
|
|
1086
|
+
async getXcode(): Promise<XcodeStatus> {
|
|
1087
|
+
const res = await nodeProxyTransport.fetch(`${apiUrl}/xcode`, {
|
|
1088
|
+
method: 'GET',
|
|
1089
|
+
headers: {
|
|
1090
|
+
Authorization: `Bearer ${token}`,
|
|
1091
|
+
},
|
|
1092
|
+
});
|
|
1093
|
+
return readXcodeResponse<XcodeStatus>(res, 'GET /xcode');
|
|
1094
|
+
},
|
|
1095
|
+
|
|
1096
|
+
async setXcode(major: string): Promise<XcodeSelectResult> {
|
|
1097
|
+
const res = await nodeProxyTransport.fetch(`${apiUrl}/xcode`, {
|
|
1098
|
+
method: 'POST',
|
|
1099
|
+
headers: {
|
|
1100
|
+
'Content-Type': 'application/json',
|
|
1101
|
+
Authorization: `Bearer ${token}`,
|
|
1102
|
+
},
|
|
1103
|
+
body: JSON.stringify({ version: major }),
|
|
1104
|
+
});
|
|
1105
|
+
return readXcodeResponse<XcodeSelectResult>(res, 'POST /xcode');
|
|
1106
|
+
},
|
|
1107
|
+
|
|
1108
|
+
getInfo: () => fetchSandboxInfo(apiUrl, token),
|
|
1109
|
+
|
|
999
1110
|
async startRbe(opts?: RbeStartOptions): Promise<RbeStatus> {
|
|
1000
1111
|
const res = await nodeProxyTransport.fetch(`${apiUrl}/rbe`, {
|
|
1001
1112
|
method: 'POST',
|
package/src/version.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const VERSION = '0.
|
|
1
|
+
export const VERSION = '0.49.1'; // x-release-please-version
|
package/version.d.mts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const VERSION = "0.
|
|
1
|
+
export declare const VERSION = "0.49.1";
|
|
2
2
|
//# sourceMappingURL=version.d.mts.map
|
package/version.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const VERSION = "0.
|
|
1
|
+
export declare const VERSION = "0.49.1";
|
|
2
2
|
//# sourceMappingURL=version.d.ts.map
|
package/version.js
CHANGED
package/version.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export const VERSION = '0.
|
|
1
|
+
export const VERSION = '0.49.1'; // x-release-please-version
|
|
2
2
|
//# sourceMappingURL=version.mjs.map
|