@modelprofile.com/browser-runtime 1.0.1 → 2.0.0
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/.smartconfig.json +1 -1
- package/changelog.md +10 -0
- package/dist_ts/00_commitinfo_data.js +3 -3
- package/dist_ts/actions.js +12 -3
- package/dist_ts/classes.artifactstore.d.ts +16 -7
- package/dist_ts/classes.artifactstore.js +167 -88
- package/dist_ts/classes.egressproxy.d.ts +2 -0
- package/dist_ts/classes.egressproxy.js +7 -2
- package/dist_ts/classes.flexprovider.js +4 -2
- package/dist_ts/classes.framed.d.ts +30 -3
- package/dist_ts/classes.framed.js +205 -30
- package/dist_ts/classes.runtime.d.ts +80 -22
- package/dist_ts/classes.runtime.js +876 -196
- package/dist_ts/errors.d.ts +1 -1
- package/dist_ts/errors.js +6 -3
- package/dist_ts/index.d.ts +2 -2
- package/dist_ts/interfaces.d.ts +102 -40
- package/dist_ts/internal.testing.d.ts +10 -1
- package/dist_ts/internal.testing.js +1 -1
- package/dist_ts/mcp.js +45 -11
- package/package.json +2 -2
- package/readme.hints.md +22 -18
- package/readme.md +69 -101
- package/ts/00_commitinfo_data.ts +2 -2
- package/ts/actions.ts +11 -2
- package/ts/classes.artifactstore.ts +239 -107
- package/ts/classes.egressproxy.ts +12 -1
- package/ts/classes.flexprovider.ts +5 -1
- package/ts/classes.framed.ts +246 -38
- package/ts/classes.runtime.ts +1145 -281
- package/ts/errors.ts +8 -2
- package/ts/index.ts +18 -6
- package/ts/interfaces.ts +127 -38
- package/ts/internal.testing.ts +12 -0
- package/ts/mcp.ts +67 -16
package/ts/errors.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export type TBrowserRuntimeErrorCode =
|
|
2
2
|
| 'ABORTED'
|
|
3
3
|
| 'AUTHORIZATION_DENIED'
|
|
4
|
+
| 'ATTACHMENT_CONFLICT'
|
|
4
5
|
| 'BUSY'
|
|
5
6
|
| 'CAPABILITY_EXPIRED'
|
|
6
7
|
| 'CAPABILITY_INVALID'
|
|
@@ -14,24 +15,29 @@ export type TBrowserRuntimeErrorCode =
|
|
|
14
15
|
| 'NOT_RUNNING'
|
|
15
16
|
| 'PROTOCOL_ERROR'
|
|
16
17
|
| 'QUOTA_EXCEEDED'
|
|
18
|
+
| 'RESOURCE_NOT_FOUND'
|
|
19
|
+
| 'RESOURCE_RETIRED'
|
|
17
20
|
| 'TIMEOUT';
|
|
18
21
|
|
|
19
22
|
const publicMessages: Record<TBrowserRuntimeErrorCode, string> = {
|
|
20
23
|
ABORTED: 'The operation was aborted.',
|
|
21
24
|
AUTHORIZATION_DENIED: 'Capability authorization was denied.',
|
|
22
|
-
|
|
25
|
+
ATTACHMENT_CONFLICT: 'The attachment binding conflicts with the current revision.',
|
|
26
|
+
BUSY: 'The browser resource is busy.',
|
|
23
27
|
CAPABILITY_EXPIRED: 'The capability has expired.',
|
|
24
28
|
CAPABILITY_INVALID: 'The capability is invalid.',
|
|
25
29
|
CAPABILITY_REVOKED: 'The capability has been revoked.',
|
|
26
30
|
CONFINEMENT_FAILED: 'Browser confinement could not be confirmed.',
|
|
27
31
|
EGRESS_DENIED: 'The egress target was denied.',
|
|
28
|
-
FENCED: 'The browser
|
|
32
|
+
FENCED: 'The browser resource is permanently fenced.',
|
|
29
33
|
FRAME_TOO_LARGE: 'The browser frame exceeded its limit.',
|
|
30
34
|
INVALID_INPUT: 'The request is invalid.',
|
|
31
35
|
LOCKED: 'The browser runtime is already locked.',
|
|
32
36
|
NOT_RUNNING: 'The browser runtime is not running.',
|
|
33
37
|
PROTOCOL_ERROR: 'The framed protocol request is invalid.',
|
|
34
38
|
QUOTA_EXCEEDED: 'A browser runtime quota was exceeded.',
|
|
39
|
+
RESOURCE_NOT_FOUND: 'The browser resource is not registered.',
|
|
40
|
+
RESOURCE_RETIRED: 'The browser resource has been retired.',
|
|
35
41
|
TIMEOUT: 'The operation timed out.',
|
|
36
42
|
};
|
|
37
43
|
|
package/ts/index.ts
CHANGED
|
@@ -10,37 +10,48 @@ export { BrowserRuntime, BrowserRuntimeLease } from './classes.runtime.js';
|
|
|
10
10
|
export { BrowserRuntimeError } from './errors.js';
|
|
11
11
|
export { createBrowserRuntimeMcpHttpHandler } from './mcp.js';
|
|
12
12
|
|
|
13
|
-
export type {
|
|
13
|
+
export type { TAcquireBrowserRuntimeLeaseOptions } from './classes.runtime.js';
|
|
14
14
|
export type { TBrowserRuntimeErrorCode } from './errors.js';
|
|
15
15
|
export type {
|
|
16
16
|
IAttachTrustedFramedPeerOptions,
|
|
17
|
+
IApplyBrowserAttachmentBindingRequest,
|
|
18
|
+
IBrowserAttachmentBinding,
|
|
17
19
|
IBrowserActionStateResult,
|
|
18
20
|
IBrowserArtifactMetadata,
|
|
19
21
|
IBrowserArtifactStoreOptions,
|
|
20
|
-
|
|
21
|
-
|
|
22
|
+
IBrowserMcpAuthenticatedBinding,
|
|
23
|
+
TBrowserCapabilityAuthorizationRequest,
|
|
24
|
+
TBrowserCapabilityDescriptor,
|
|
22
25
|
IBrowserClickAction,
|
|
23
26
|
IBrowserEgressConnectionOptions,
|
|
24
27
|
IBrowserEgressProxyOptions,
|
|
25
28
|
IBrowserEgressProxyStats,
|
|
29
|
+
IBrowserFlexCapabilityBinding,
|
|
30
|
+
IBrowserFlexSessionId,
|
|
26
31
|
IBrowserFillAction,
|
|
27
32
|
IBrowserNavigateAction,
|
|
28
33
|
IBrowserObservationResult,
|
|
34
|
+
IBrowserOpenCodeSessionId,
|
|
29
35
|
IBrowserPressAction,
|
|
30
|
-
|
|
36
|
+
TBrowserRuntimeAuditEvent,
|
|
31
37
|
IBrowserRuntimeFlexToolProviderOptions,
|
|
32
38
|
IBrowserRuntimeFrameSubscription,
|
|
33
39
|
IBrowserRuntimeFramedClientOptions,
|
|
34
40
|
IBrowserRuntimeMcpOptions,
|
|
35
41
|
IBrowserRuntimeOperationOptions,
|
|
36
42
|
IBrowserRuntimeOptions,
|
|
43
|
+
IBrowserResourceKey,
|
|
44
|
+
IBrowserResourceRegistration,
|
|
37
45
|
IBrowserRuntimeState,
|
|
38
46
|
IBrowserRuntimeTabState,
|
|
39
47
|
IBrowserScreenshotAction,
|
|
40
48
|
IBrowserScreenshotResult,
|
|
41
49
|
IBrowserSnapshotAction,
|
|
42
|
-
|
|
43
|
-
|
|
50
|
+
ICreateBrowserResourceRequest,
|
|
51
|
+
TIssueBrowserCapabilityRequest,
|
|
52
|
+
TIssuedBrowserCapability,
|
|
53
|
+
TQualifiedBrowserSessionId,
|
|
54
|
+
IRegisterBrowserResourceRequest,
|
|
44
55
|
IResolvedBrowserFlexCapability,
|
|
45
56
|
TBrowserActorRole,
|
|
46
57
|
TBrowserAgentAction,
|
|
@@ -48,5 +59,6 @@ export type {
|
|
|
48
59
|
TBrowserCapabilitySource,
|
|
49
60
|
TBrowserDnsResolver,
|
|
50
61
|
TBrowserEgressConnector,
|
|
62
|
+
TBrowserHarnessId,
|
|
51
63
|
TBrowserRuntimeEvent,
|
|
52
64
|
} from './interfaces.js';
|
package/ts/interfaces.ts
CHANGED
|
@@ -1,49 +1,119 @@
|
|
|
1
1
|
import type * as plugins from './plugins.js';
|
|
2
|
+
import type { BrowserEgressProxy } from './classes.egressproxy.js';
|
|
3
|
+
import type { BrowserRuntimeFramedClient } from './classes.framed.js';
|
|
2
4
|
|
|
3
5
|
export type TBrowserActorRole = 'human' | 'agent';
|
|
4
6
|
export type TBrowserCapabilitySource = 'human' | 'flex' | 'mcp';
|
|
7
|
+
export type TBrowserHarnessId = 'opencode' | 'flex';
|
|
5
8
|
|
|
6
|
-
export interface
|
|
9
|
+
export interface IBrowserOpenCodeSessionId {
|
|
10
|
+
harnessId: 'opencode';
|
|
11
|
+
nativeId: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface IBrowserFlexSessionId {
|
|
15
|
+
harnessId: 'flex';
|
|
16
|
+
nativeId: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export type TQualifiedBrowserSessionId = IBrowserOpenCodeSessionId | IBrowserFlexSessionId;
|
|
20
|
+
|
|
21
|
+
export interface IBrowserResourceKey {
|
|
22
|
+
projectId: string;
|
|
23
|
+
browserResourceId: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface IBrowserAttachmentBinding {
|
|
27
|
+
attachmentAuthorityId: string;
|
|
28
|
+
attachmentRevision: number;
|
|
29
|
+
sessionId: TQualifiedBrowserSessionId | null;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface ICreateBrowserResourceRequest {
|
|
7
33
|
projectId: string;
|
|
34
|
+
attachmentBinding: IBrowserAttachmentBinding;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export interface IRegisterBrowserResourceRequest extends IBrowserResourceKey {
|
|
38
|
+
attachmentBinding: IBrowserAttachmentBinding;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface IApplyBrowserAttachmentBindingRequest extends IBrowserResourceKey {
|
|
42
|
+
attachmentBinding: IBrowserAttachmentBinding;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export interface IBrowserResourceRegistration extends IBrowserResourceKey {
|
|
46
|
+
registeredAt: number;
|
|
47
|
+
attachmentBinding: IBrowserAttachmentBinding;
|
|
48
|
+
incarnationStatus: 'absent' | 'running' | 'fenced';
|
|
49
|
+
incarnationGeneration: number;
|
|
50
|
+
retirementPending: boolean;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export interface IBrowserCapabilityBindingBase extends IBrowserResourceKey {
|
|
54
|
+
attachmentAuthorityId: string;
|
|
55
|
+
attachmentRevision: number;
|
|
8
56
|
actorId: string;
|
|
9
|
-
role: TBrowserActorRole;
|
|
10
57
|
peerId: string;
|
|
11
|
-
source: TBrowserCapabilitySource;
|
|
12
|
-
scopeId?: string;
|
|
13
|
-
sessionId?: string;
|
|
14
58
|
}
|
|
15
59
|
|
|
16
|
-
export interface
|
|
60
|
+
export interface IBrowserHumanCapabilityBinding extends IBrowserCapabilityBindingBase {
|
|
61
|
+
role: 'human';
|
|
62
|
+
source: 'human';
|
|
63
|
+
sessionId?: never;
|
|
64
|
+
scopeId?: never;
|
|
65
|
+
channelId?: never;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export interface IBrowserFlexCapabilityBinding extends IBrowserCapabilityBindingBase {
|
|
69
|
+
role: 'agent';
|
|
70
|
+
source: 'flex';
|
|
71
|
+
sessionId: IBrowserFlexSessionId;
|
|
72
|
+
scopeId: string;
|
|
73
|
+
channelId: string;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export interface IBrowserMcpCapabilityBinding extends IBrowserCapabilityBindingBase {
|
|
77
|
+
role: 'agent';
|
|
78
|
+
source: 'mcp';
|
|
79
|
+
sessionId: IBrowserOpenCodeSessionId;
|
|
80
|
+
scopeId?: never;
|
|
81
|
+
channelId?: never;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export type TBrowserCapabilityAuthorizationRequest =
|
|
85
|
+
| IBrowserHumanCapabilityBinding
|
|
86
|
+
| IBrowserFlexCapabilityBinding
|
|
87
|
+
| IBrowserMcpCapabilityBinding;
|
|
88
|
+
|
|
89
|
+
export type TIssueBrowserCapabilityRequest = TBrowserCapabilityAuthorizationRequest & {
|
|
17
90
|
expiresInMs?: number;
|
|
18
91
|
disconnect?: (signal: AbortSignal) => Promise<void> | void;
|
|
19
|
-
}
|
|
92
|
+
};
|
|
20
93
|
|
|
21
|
-
export
|
|
94
|
+
export type TBrowserCapabilityDescriptor = TBrowserCapabilityAuthorizationRequest & {
|
|
22
95
|
capabilityId: string;
|
|
23
96
|
expiresAt: number;
|
|
24
|
-
}
|
|
97
|
+
};
|
|
25
98
|
|
|
26
|
-
export
|
|
99
|
+
export type TIssuedBrowserCapability = TBrowserCapabilityDescriptor & {
|
|
27
100
|
capabilityToken: string;
|
|
28
|
-
}
|
|
101
|
+
};
|
|
29
102
|
|
|
30
103
|
export interface IResolvedBrowserFlexCapability {
|
|
31
104
|
capabilityToken: string;
|
|
32
105
|
}
|
|
33
106
|
|
|
34
|
-
export
|
|
107
|
+
export type TBrowserRuntimeAuditEvent = TBrowserCapabilityAuthorizationRequest & {
|
|
35
108
|
operationId: string;
|
|
36
109
|
capabilityId: string;
|
|
37
|
-
|
|
38
|
-
actorId: string;
|
|
39
|
-
role: TBrowserActorRole;
|
|
40
|
-
source: TBrowserCapabilitySource;
|
|
110
|
+
leaseId: string;
|
|
41
111
|
action: string;
|
|
42
112
|
phase: 'completed' | 'failed';
|
|
43
113
|
startedAt: number;
|
|
44
114
|
finishedAt: number;
|
|
45
115
|
errorCode?: string;
|
|
46
|
-
}
|
|
116
|
+
};
|
|
47
117
|
|
|
48
118
|
export interface ILiveBrowserSessionLike {
|
|
49
119
|
start(options?: plugins.smartpuppeteer.ILiveBrowserOperationOptions): Promise<void>;
|
|
@@ -120,9 +190,9 @@ export interface ILiveBrowserSessionLike {
|
|
|
120
190
|
): Promise<void>;
|
|
121
191
|
}
|
|
122
192
|
|
|
123
|
-
export interface IBrowserConfinementProbeContext {
|
|
193
|
+
export interface IBrowserConfinementProbeContext extends IBrowserResourceKey {
|
|
124
194
|
session: ILiveBrowserSessionLike;
|
|
125
|
-
proxy:
|
|
195
|
+
proxy: BrowserEgressProxy;
|
|
126
196
|
signal: AbortSignal;
|
|
127
197
|
}
|
|
128
198
|
|
|
@@ -134,27 +204,31 @@ export interface IBrowserRuntimeEnvironment {
|
|
|
134
204
|
export interface IBrowserRuntimeOptions {
|
|
135
205
|
runtimeDirectory: string;
|
|
136
206
|
authorizeCapability(
|
|
137
|
-
request:
|
|
207
|
+
request: TBrowserCapabilityAuthorizationRequest,
|
|
138
208
|
signal: AbortSignal,
|
|
139
209
|
): Promise<boolean> | boolean;
|
|
140
|
-
audit?(event:
|
|
141
|
-
|
|
210
|
+
audit?(event: TBrowserRuntimeAuditEvent, signal: AbortSignal): Promise<void> | void;
|
|
211
|
+
maxResources?: number;
|
|
212
|
+
maxResourcesPerProject?: number;
|
|
213
|
+
maxRunningResources?: number;
|
|
214
|
+
maxRunningResourcesPerProject?: number;
|
|
142
215
|
maxCapabilities?: number;
|
|
216
|
+
maxCapabilitiesPerResource?: number;
|
|
143
217
|
maxCapabilitiesPerProject?: number;
|
|
144
218
|
capabilityDefaultTtlMs?: number;
|
|
145
219
|
capabilityMaximumTtlMs?: number;
|
|
146
220
|
authorizationTimeoutMs?: number;
|
|
147
|
-
|
|
221
|
+
maxTabsPerResource?: number;
|
|
148
222
|
operationTimeoutMs?: number;
|
|
149
223
|
quiescenceTimeoutMs?: number;
|
|
150
224
|
terminationGraceMs?: number;
|
|
151
225
|
terminationForceMs?: number;
|
|
152
226
|
auditTimeoutMs?: number;
|
|
153
227
|
confinementProbeTimeoutMs?: number;
|
|
154
|
-
|
|
228
|
+
idleTerminationMs?: number;
|
|
155
229
|
maxFrameBytes?: number;
|
|
156
230
|
frameAcknowledgementTimeoutMs?: number;
|
|
157
|
-
egress?: Omit<IBrowserEgressProxyOptions, 'projectId'>;
|
|
231
|
+
egress?: Omit<IBrowserEgressProxyOptions, 'projectId' | 'browserResourceId'>;
|
|
158
232
|
artifacts?: Omit<IBrowserArtifactStoreOptions, 'rootDirectory'>;
|
|
159
233
|
}
|
|
160
234
|
|
|
@@ -186,9 +260,8 @@ export interface IBrowserRuntimeState {
|
|
|
186
260
|
};
|
|
187
261
|
}
|
|
188
262
|
|
|
189
|
-
export interface IBrowserArtifactMetadata {
|
|
263
|
+
export interface IBrowserArtifactMetadata extends IBrowserResourceKey {
|
|
190
264
|
artifactId: string;
|
|
191
|
-
projectId: string;
|
|
192
265
|
mimeType: string;
|
|
193
266
|
size: number;
|
|
194
267
|
createdAt: number;
|
|
@@ -198,6 +271,8 @@ export interface IBrowserArtifactMetadata {
|
|
|
198
271
|
export interface IBrowserArtifactStoreOptions {
|
|
199
272
|
rootDirectory: string;
|
|
200
273
|
maxFileBytes?: number;
|
|
274
|
+
maxArtifactsPerResource?: number;
|
|
275
|
+
maxResourceBytes?: number;
|
|
201
276
|
maxArtifactsPerProject?: number;
|
|
202
277
|
maxProjectBytes?: number;
|
|
203
278
|
maxProjects?: number;
|
|
@@ -315,8 +390,7 @@ export type TBrowserEgressConnector = (
|
|
|
315
390
|
options: IBrowserEgressConnectionOptions,
|
|
316
391
|
) => plugins.net.Socket;
|
|
317
392
|
|
|
318
|
-
export interface IBrowserEgressProxyOptions {
|
|
319
|
-
projectId: string;
|
|
393
|
+
export interface IBrowserEgressProxyOptions extends IBrowserResourceKey {
|
|
320
394
|
allowedPorts?: number[];
|
|
321
395
|
maxConnections?: number;
|
|
322
396
|
maxActiveRequests?: number;
|
|
@@ -340,17 +414,24 @@ export interface IBrowserEgressProxyStats {
|
|
|
340
414
|
activeConnections: number;
|
|
341
415
|
}
|
|
342
416
|
|
|
343
|
-
export interface IAttachTrustedFramedPeerOptions
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
417
|
+
export interface IAttachTrustedFramedPeerOptions
|
|
418
|
+
extends Omit<
|
|
419
|
+
IBrowserFlexCapabilityBinding,
|
|
420
|
+
'role' | 'source'
|
|
421
|
+
> {
|
|
422
|
+
role: 'agent';
|
|
423
|
+
source: 'flex';
|
|
347
424
|
readable: plugins.stream.Readable;
|
|
348
425
|
writable: plugins.stream.Writable;
|
|
349
426
|
}
|
|
350
427
|
|
|
351
|
-
export interface IBrowserRuntimeFramedClientOptions
|
|
352
|
-
|
|
353
|
-
|
|
428
|
+
export interface IBrowserRuntimeFramedClientOptions
|
|
429
|
+
extends Omit<
|
|
430
|
+
IBrowserFlexCapabilityBinding,
|
|
431
|
+
'role' | 'source'
|
|
432
|
+
> {
|
|
433
|
+
role: 'agent';
|
|
434
|
+
source: 'flex';
|
|
354
435
|
readable: plugins.stream.Readable;
|
|
355
436
|
writable: plugins.stream.Writable;
|
|
356
437
|
maxFrameBytes?: number;
|
|
@@ -358,8 +439,16 @@ export interface IBrowserRuntimeFramedClientOptions {
|
|
|
358
439
|
requestTimeoutMs?: number;
|
|
359
440
|
}
|
|
360
441
|
|
|
442
|
+
export interface IBrowserMcpAuthenticatedBinding extends IBrowserCapabilityBindingBase {
|
|
443
|
+
role: 'agent';
|
|
444
|
+
source: 'mcp';
|
|
445
|
+
sessionId: IBrowserOpenCodeSessionId;
|
|
446
|
+
scopeId?: never;
|
|
447
|
+
channelId?: never;
|
|
448
|
+
}
|
|
449
|
+
|
|
361
450
|
export interface IBrowserRuntimeMcpOptions {
|
|
362
|
-
authenticateMcpRequest(request: Request): Promise<
|
|
451
|
+
authenticateMcpRequest(request: Request): Promise<IBrowserMcpAuthenticatedBinding>;
|
|
363
452
|
trustedOrigins?: string[];
|
|
364
453
|
allowedHosts?: string[];
|
|
365
454
|
allowMissingOrigin?: boolean;
|
|
@@ -368,7 +457,7 @@ export interface IBrowserRuntimeMcpOptions {
|
|
|
368
457
|
}
|
|
369
458
|
|
|
370
459
|
export interface IBrowserRuntimeFlexToolProviderOptions<TScope> {
|
|
371
|
-
client:
|
|
460
|
+
client: BrowserRuntimeFramedClient;
|
|
372
461
|
resolveCapability(
|
|
373
462
|
context: plugins.flexharness.IFlexToolProviderContext<TScope>,
|
|
374
463
|
): Promise<IResolvedBrowserFlexCapability> | IResolvedBrowserFlexCapability;
|
package/ts/internal.testing.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type * as plugins from './plugins.js';
|
|
2
2
|
import type {
|
|
3
3
|
IBrowserConfinementProbeContext,
|
|
4
|
+
IBrowserRuntimeFramedClientOptions,
|
|
4
5
|
IBrowserRuntimeOptions,
|
|
5
6
|
IBrowserRuntimeEnvironment,
|
|
6
7
|
ILiveBrowserSessionLike,
|
|
@@ -15,4 +16,15 @@ export interface IBrowserRuntimeTestingOptions extends IBrowserRuntimeOptions {
|
|
|
15
16
|
options: plugins.smartpuppeteer.ILiveBrowserSessionOptions,
|
|
16
17
|
): ILiveBrowserSessionLike;
|
|
17
18
|
confinementProbe(context: IBrowserConfinementProbeContext): Promise<void>;
|
|
19
|
+
beforeLeasePublication?(): Promise<void>;
|
|
20
|
+
beforeIdleTermination?(): Promise<void>;
|
|
21
|
+
beforeFrameFailureTermination?(): Promise<void>;
|
|
22
|
+
afterFrameFailureFence?(): Promise<void>;
|
|
23
|
+
beforeFramedPeerLeasePublication?(): Promise<void>;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface IBrowserRuntimeFramedClientTestingOptions
|
|
27
|
+
extends IBrowserRuntimeFramedClientOptions {
|
|
28
|
+
[browserRuntimeTesting]: true;
|
|
29
|
+
afterAcquireResponse?(): Promise<void>;
|
|
18
30
|
}
|
package/ts/mcp.ts
CHANGED
|
@@ -2,7 +2,10 @@ import * as plugins from './plugins.js';
|
|
|
2
2
|
import type { BrowserRuntime, BrowserRuntimeLease } from './classes.runtime.js';
|
|
3
3
|
import { BrowserRuntimeError } from './errors.js';
|
|
4
4
|
import { commitinfo } from './00_commitinfo_data.js';
|
|
5
|
-
import type {
|
|
5
|
+
import type {
|
|
6
|
+
IBrowserMcpAuthenticatedBinding,
|
|
7
|
+
IBrowserRuntimeMcpOptions,
|
|
8
|
+
} from './interfaces.js';
|
|
6
9
|
import { validateAgentActionResult } from './actions.js';
|
|
7
10
|
import {
|
|
8
11
|
validateBoundedString,
|
|
@@ -51,19 +54,17 @@ export const createBrowserRuntimeMcpHttpHandler = (
|
|
|
51
54
|
context: plugins.smartmcp.ISmartMcpToolContext,
|
|
52
55
|
action: (lease: BrowserRuntimeLease, signal: AbortSignal) => Promise<T>,
|
|
53
56
|
): Promise<T> => {
|
|
54
|
-
const
|
|
57
|
+
const binding = parseMcpAuthBinding(context.authInfo?.scopes);
|
|
55
58
|
const capabilityToken = parseBearerToken(context.request);
|
|
56
59
|
const signal = AbortSignal.any([context.extra.signal, context.request.signal]);
|
|
57
60
|
signal.throwIfAborted();
|
|
58
|
-
const descriptor = runtime.authorizeCapability(capabilityToken,
|
|
61
|
+
const descriptor = runtime.authorizeCapability(capabilityToken, binding);
|
|
59
62
|
if (descriptor.capabilityId !== context.authInfo?.token) {
|
|
60
63
|
throw new BrowserRuntimeError('CAPABILITY_INVALID');
|
|
61
64
|
}
|
|
62
65
|
const lease = await runtime.acquireLease({
|
|
63
66
|
capabilityToken,
|
|
64
|
-
|
|
65
|
-
expectedRole: 'agent',
|
|
66
|
-
expectedSource: 'mcp',
|
|
67
|
+
...binding,
|
|
67
68
|
signal,
|
|
68
69
|
});
|
|
69
70
|
try {
|
|
@@ -203,22 +204,16 @@ export const createBrowserRuntimeMcpHttpHandler = (
|
|
|
203
204
|
maxToolResultBytes,
|
|
204
205
|
redactError: () => 'Browser tool execution failed.',
|
|
205
206
|
authorize: async ({ request }) => {
|
|
206
|
-
let peerId: string;
|
|
207
207
|
let capabilityToken: string;
|
|
208
208
|
try {
|
|
209
209
|
const authentication = await options.authenticateMcpRequest(request);
|
|
210
|
-
|
|
210
|
+
const binding = validateMcpBinding(authentication);
|
|
211
211
|
capabilityToken = parseBearerToken(request);
|
|
212
|
-
const descriptor = runtime.authorizeCapability(
|
|
213
|
-
capabilityToken,
|
|
214
|
-
peerId,
|
|
215
|
-
'agent',
|
|
216
|
-
'mcp',
|
|
217
|
-
);
|
|
212
|
+
const descriptor = runtime.authorizeCapability(capabilityToken, binding);
|
|
218
213
|
return {
|
|
219
214
|
token: descriptor.capabilityId,
|
|
220
|
-
clientId: peerId,
|
|
221
|
-
scopes: [
|
|
215
|
+
clientId: binding.peerId,
|
|
216
|
+
scopes: [serializeMcpAuthBinding(binding)],
|
|
222
217
|
expiresAt: descriptor.expiresAt,
|
|
223
218
|
};
|
|
224
219
|
} catch {
|
|
@@ -228,3 +223,59 @@ export const createBrowserRuntimeMcpHttpHandler = (
|
|
|
228
223
|
tools,
|
|
229
224
|
});
|
|
230
225
|
};
|
|
226
|
+
|
|
227
|
+
const validateMcpBinding = (value: unknown): IBrowserMcpAuthenticatedBinding => {
|
|
228
|
+
if (!value || typeof value !== 'object') throw new BrowserRuntimeError('CAPABILITY_INVALID');
|
|
229
|
+
const binding = value as Record<string, unknown>;
|
|
230
|
+
if (binding.role !== 'agent' || binding.source !== 'mcp') {
|
|
231
|
+
throw new BrowserRuntimeError('CAPABILITY_INVALID');
|
|
232
|
+
}
|
|
233
|
+
const session = binding.sessionId as Record<string, unknown> | undefined;
|
|
234
|
+
if (session?.harnessId !== 'opencode') throw new BrowserRuntimeError('CAPABILITY_INVALID');
|
|
235
|
+
return {
|
|
236
|
+
projectId: validateBoundedString(binding.projectId, 'projectId', 1, 256),
|
|
237
|
+
browserResourceId: validateBoundedString(
|
|
238
|
+
binding.browserResourceId,
|
|
239
|
+
'browserResourceId',
|
|
240
|
+
16,
|
|
241
|
+
256,
|
|
242
|
+
),
|
|
243
|
+
attachmentAuthorityId: validateBoundedString(
|
|
244
|
+
binding.attachmentAuthorityId,
|
|
245
|
+
'attachmentAuthorityId',
|
|
246
|
+
1,
|
|
247
|
+
256,
|
|
248
|
+
),
|
|
249
|
+
attachmentRevision: validateInteger(
|
|
250
|
+
binding.attachmentRevision,
|
|
251
|
+
'attachmentRevision',
|
|
252
|
+
0,
|
|
253
|
+
Number.MAX_SAFE_INTEGER,
|
|
254
|
+
),
|
|
255
|
+
actorId: validateBoundedString(binding.actorId, 'actorId', 1, 256),
|
|
256
|
+
role: 'agent',
|
|
257
|
+
peerId: validateBoundedString(binding.peerId, 'peerId', 1, 256),
|
|
258
|
+
source: 'mcp',
|
|
259
|
+
sessionId: {
|
|
260
|
+
harnessId: 'opencode',
|
|
261
|
+
nativeId: validateBoundedString(session.nativeId, 'sessionId.nativeId', 1, 256),
|
|
262
|
+
},
|
|
263
|
+
};
|
|
264
|
+
};
|
|
265
|
+
|
|
266
|
+
const serializeMcpAuthBinding = (
|
|
267
|
+
binding: IBrowserMcpAuthenticatedBinding,
|
|
268
|
+
): string => plugins.Buffer.from(JSON.stringify(binding), 'utf8').toString('base64url');
|
|
269
|
+
|
|
270
|
+
const parseMcpAuthBinding = (scopes: string[] | undefined): IBrowserMcpAuthenticatedBinding => {
|
|
271
|
+
if (!Array.isArray(scopes) || scopes.length !== 1) {
|
|
272
|
+
throw new BrowserRuntimeError('CAPABILITY_INVALID');
|
|
273
|
+
}
|
|
274
|
+
try {
|
|
275
|
+
return validateMcpBinding(JSON.parse(
|
|
276
|
+
plugins.Buffer.from(scopes[0]!, 'base64url').toString('utf8'),
|
|
277
|
+
));
|
|
278
|
+
} catch {
|
|
279
|
+
throw new BrowserRuntimeError('CAPABILITY_INVALID');
|
|
280
|
+
}
|
|
281
|
+
};
|