@modelprofile.com/browser-runtime 2.0.0 → 2.1.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/ts/confinement.ts CHANGED
@@ -28,18 +28,32 @@ const listenOnUnsupportedPort = async (
28
28
  export const runProductionConfinementProbe = async (
29
29
  context: IBrowserConfinementProbeContext,
30
30
  ): Promise<void> => {
31
+ const initialState = context.session.getState();
32
+ const initialStateJson = JSON.stringify(initialState);
33
+ let probeTabId: string | undefined;
34
+ let probeFailed = false;
35
+ let probeError: unknown;
31
36
  let originHits = 0;
32
37
  const fixture = await listenOnUnsupportedPort(
33
38
  new Set(context.proxy.allowedTargetPorts),
34
39
  () => { originHits += 1; },
35
40
  );
36
- const closeFixture = async (): Promise<void> => new Promise((resolve) => {
37
- fixture.server.close(() => resolve());
41
+ const closeFixture = async (): Promise<void> => new Promise((resolve, reject) => {
42
+ fixture.server.close((error) => error ? reject(error) : resolve());
38
43
  });
39
44
  try {
45
+ const probeTab = await context.session.createTab(
46
+ { activate: false },
47
+ { signal: context.signal },
48
+ );
49
+ probeTabId = probeTab.id;
40
50
  const beforeLoopback = context.proxy.getStats();
41
51
  await context.session.navigate(
42
- { url: `http://127.0.0.1:${fixture.port}/browser-runtime-probe`, timeoutMs: 3000 },
52
+ {
53
+ url: `http://127.0.0.1:${fixture.port}/browser-runtime-probe`,
54
+ tabId: probeTab.id,
55
+ timeoutMs: 3000,
56
+ },
43
57
  { signal: context.signal },
44
58
  ).catch(() => undefined);
45
59
  const afterLoopback = context.proxy.getStats();
@@ -53,7 +67,11 @@ export const runProductionConfinementProbe = async (
53
67
 
54
68
  const beforeInvalid = context.proxy.getStats();
55
69
  await context.session.navigate(
56
- { url: 'http://browser-runtime-confinement.invalid/', timeoutMs: 3000 },
70
+ {
71
+ url: 'http://browser-runtime-confinement.invalid/',
72
+ tabId: probeTab.id,
73
+ timeoutMs: 3000,
74
+ },
57
75
  { signal: context.signal },
58
76
  ).catch(() => undefined);
59
77
  const afterInvalid = context.proxy.getStats();
@@ -73,7 +91,7 @@ export const runProductionConfinementProbe = async (
73
91
  try { await pc.setLocalDescription(await pc.createOffer()); } catch {}
74
92
  setTimeout(() => { pc.close(); resolve({ candidateEmitted: emitted }); }, 1000);
75
93
  })`,
76
- { timeoutMs: 2500, maxOutputBytes: 1024 },
94
+ { tabId: probeTab.id, timeoutMs: 2500, maxOutputBytes: 1024 },
77
95
  { signal: context.signal },
78
96
  );
79
97
  if (
@@ -84,7 +102,32 @@ export const runProductionConfinementProbe = async (
84
102
  ) {
85
103
  throw new BrowserRuntimeError('CONFINEMENT_FAILED');
86
104
  }
105
+ } catch (error) {
106
+ probeFailed = true;
107
+ probeError = error instanceof Error ? error : new BrowserRuntimeError('CONFINEMENT_FAILED');
87
108
  } finally {
88
- await closeFixture();
109
+ const cleanupErrors: unknown[] = [];
110
+ if (probeTabId) {
111
+ await context.session.closeTab(probeTabId).catch((error) => cleanupErrors.push(error));
112
+ }
113
+ await closeFixture().catch((error) => cleanupErrors.push(error));
114
+ try {
115
+ if (JSON.stringify(context.session.getState()) !== initialStateJson) {
116
+ cleanupErrors.push(new BrowserRuntimeError('CONFINEMENT_FAILED'));
117
+ }
118
+ } catch (error) {
119
+ cleanupErrors.push(error);
120
+ }
121
+ if (probeFailed && cleanupErrors.length > 0) {
122
+ throw new AggregateError(
123
+ [probeError, ...cleanupErrors],
124
+ 'Browser confinement probe and cleanup failed.',
125
+ );
126
+ }
127
+ if (cleanupErrors.length === 1) throw cleanupErrors[0];
128
+ if (cleanupErrors.length > 1) {
129
+ throw new AggregateError(cleanupErrors, 'Browser confinement probe cleanup failed.');
130
+ }
89
131
  }
132
+ if (probeFailed) throw probeError;
90
133
  };
package/ts/index.ts CHANGED
@@ -34,6 +34,7 @@ export type {
34
34
  IBrowserOpenCodeSessionId,
35
35
  IBrowserPressAction,
36
36
  TBrowserRuntimeAuditEvent,
37
+ TBrowserRuntimeOperationIdentity,
37
38
  IBrowserRuntimeFlexToolProviderOptions,
38
39
  IBrowserRuntimeFrameSubscription,
39
40
  IBrowserRuntimeFramedClientOptions,
package/ts/interfaces.ts CHANGED
@@ -104,15 +104,27 @@ export interface IResolvedBrowserFlexCapability {
104
104
  capabilityToken: string;
105
105
  }
106
106
 
107
- export type TBrowserRuntimeAuditEvent = TBrowserCapabilityAuthorizationRequest & {
108
- operationId: string;
109
- capabilityId: string;
110
- leaseId: string;
111
- action: string;
112
- phase: 'completed' | 'failed';
113
- startedAt: number;
114
- finishedAt: number;
115
- errorCode?: string;
107
+ type TReadonlyBrowserCapabilityAuthorizationRequest =
108
+ | Readonly<IBrowserHumanCapabilityBinding>
109
+ | (Omit<Readonly<IBrowserFlexCapabilityBinding>, 'sessionId'> & {
110
+ readonly sessionId: Readonly<IBrowserFlexSessionId>;
111
+ })
112
+ | (Omit<Readonly<IBrowserMcpCapabilityBinding>, 'sessionId'> & {
113
+ readonly sessionId: Readonly<IBrowserOpenCodeSessionId>;
114
+ });
115
+
116
+ export type TBrowserRuntimeOperationIdentity = TReadonlyBrowserCapabilityAuthorizationRequest & {
117
+ readonly operationId: string;
118
+ readonly capabilityId: string;
119
+ readonly leaseId: string;
120
+ readonly action: string;
121
+ readonly startedAt: number;
122
+ };
123
+
124
+ export type TBrowserRuntimeAuditEvent = TBrowserRuntimeOperationIdentity & {
125
+ readonly phase: 'completed' | 'failed';
126
+ readonly finishedAt: number;
127
+ readonly errorCode?: string;
116
128
  };
117
129
 
118
130
  export interface ILiveBrowserSessionLike {
@@ -207,6 +219,10 @@ export interface IBrowserRuntimeOptions {
207
219
  request: TBrowserCapabilityAuthorizationRequest,
208
220
  signal: AbortSignal,
209
221
  ): Promise<boolean> | boolean;
222
+ beforeOperation?(
223
+ operation: TBrowserRuntimeOperationIdentity,
224
+ signal: AbortSignal,
225
+ ): Promise<void> | void;
210
226
  audit?(event: TBrowserRuntimeAuditEvent, signal: AbortSignal): Promise<void> | void;
211
227
  maxResources?: number;
212
228
  maxResourcesPerProject?: number;
@@ -218,6 +234,7 @@ export interface IBrowserRuntimeOptions {
218
234
  capabilityDefaultTtlMs?: number;
219
235
  capabilityMaximumTtlMs?: number;
220
236
  authorizationTimeoutMs?: number;
237
+ beforeOperationTimeoutMs?: number;
221
238
  maxTabsPerResource?: number;
222
239
  operationTimeoutMs?: number;
223
240
  quiescenceTimeoutMs?: number;