@modelprofile.com/browser-runtime 1.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 +34 -0
- package/changelog.md +11 -0
- package/dist_ts/00_commitinfo_data.d.ts +8 -0
- package/dist_ts/00_commitinfo_data.js +9 -0
- package/dist_ts/actions.d.ts +3 -0
- package/dist_ts/actions.js +215 -0
- package/dist_ts/classes.artifactstore.d.ts +38 -0
- package/dist_ts/classes.artifactstore.js +344 -0
- package/dist_ts/classes.egressproxy.d.ts +67 -0
- package/dist_ts/classes.egressproxy.js +830 -0
- package/dist_ts/classes.flexprovider.d.ts +9 -0
- package/dist_ts/classes.flexprovider.js +117 -0
- package/dist_ts/classes.framed.d.ts +52 -0
- package/dist_ts/classes.framed.js +557 -0
- package/dist_ts/classes.runtime.d.ts +202 -0
- package/dist_ts/classes.runtime.js +1667 -0
- package/dist_ts/confinement.d.ts +2 -0
- package/dist_ts/confinement.js +63 -0
- package/dist_ts/errors.d.ts +7 -0
- package/dist_ts/errors.js +40 -0
- package/dist_ts/index.d.ts +11 -0
- package/dist_ts/index.js +9 -0
- package/dist_ts/interfaces.d.ts +287 -0
- package/dist_ts/interfaces.js +2 -0
- package/dist_ts/internal.testing.d.ts +9 -0
- package/dist_ts/internal.testing.js +2 -0
- package/dist_ts/mcp.d.ts +4 -0
- package/dist_ts/mcp.js +196 -0
- package/dist_ts/plugins.d.ts +20 -0
- package/dist_ts/plugins.js +24 -0
- package/dist_ts/utils.d.ts +25 -0
- package/dist_ts/utils.js +143 -0
- package/license.md +21 -0
- package/package.json +59 -0
- package/readme.hints.md +35 -0
- package/readme.md +181 -0
- package/ts/00_commitinfo_data.ts +8 -0
- package/ts/actions.ts +241 -0
- package/ts/classes.artifactstore.ts +432 -0
- package/ts/classes.egressproxy.ts +1005 -0
- package/ts/classes.flexprovider.ts +134 -0
- package/ts/classes.framed.ts +649 -0
- package/ts/classes.runtime.ts +2135 -0
- package/ts/confinement.ts +90 -0
- package/ts/errors.ts +63 -0
- package/ts/index.ts +52 -0
- package/ts/interfaces.ts +375 -0
- package/ts/internal.testing.ts +18 -0
- package/ts/mcp.ts +230 -0
- package/ts/plugins.ts +28 -0
- package/ts/utils.ts +188 -0
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import * as plugins from './plugins.js';
|
|
2
|
+
import type {
|
|
3
|
+
IBrowserRuntimeFlexToolProviderOptions,
|
|
4
|
+
} from './interfaces.js';
|
|
5
|
+
import { BrowserRuntimeError } from './errors.js';
|
|
6
|
+
import { validateBoundedString, validateExactKeys } from './utils.js';
|
|
7
|
+
|
|
8
|
+
const allowedActions = [
|
|
9
|
+
'navigate',
|
|
10
|
+
'snapshot',
|
|
11
|
+
'screenshot',
|
|
12
|
+
'click',
|
|
13
|
+
'fill',
|
|
14
|
+
'press',
|
|
15
|
+
] as const satisfies readonly plugins.smartagent.TBrowserToolAction[];
|
|
16
|
+
|
|
17
|
+
export class BrowserRuntimeFlexToolProvider<TScope>
|
|
18
|
+
implements plugins.flexharness.IFlexToolProvider<TScope> {
|
|
19
|
+
private readonly client: IBrowserRuntimeFlexToolProviderOptions<TScope>['client'];
|
|
20
|
+
private readonly resolveCapability: IBrowserRuntimeFlexToolProviderOptions<TScope>['resolveCapability'];
|
|
21
|
+
private state: 'unused' | 'provisioning' | 'active' | 'closed' = 'unused';
|
|
22
|
+
|
|
23
|
+
constructor(options: IBrowserRuntimeFlexToolProviderOptions<TScope>) {
|
|
24
|
+
if (!options || typeof options !== 'object') throw new BrowserRuntimeError('INVALID_INPUT');
|
|
25
|
+
if (!options.client || typeof options.resolveCapability !== 'function') {
|
|
26
|
+
throw new BrowserRuntimeError('INVALID_INPUT');
|
|
27
|
+
}
|
|
28
|
+
this.client = options.client;
|
|
29
|
+
this.resolveCapability = options.resolveCapability;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
public async provideTools(
|
|
33
|
+
context: plugins.flexharness.IFlexToolProviderContext<TScope>,
|
|
34
|
+
): Promise<plugins.flexharness.IFlexToolHandle> {
|
|
35
|
+
if (this.state !== 'unused') throw new BrowserRuntimeError('BUSY');
|
|
36
|
+
this.state = 'provisioning';
|
|
37
|
+
const scopeId = validateBoundedString(context.scopeId, 'scopeId', 1, 128);
|
|
38
|
+
const sessionId = validateBoundedString(context.sessionId, 'sessionId', 1, 128);
|
|
39
|
+
if (this.client.scopeId !== scopeId || this.client.sessionId !== sessionId) {
|
|
40
|
+
this.state = 'closed';
|
|
41
|
+
throw new BrowserRuntimeError('CAPABILITY_INVALID');
|
|
42
|
+
}
|
|
43
|
+
try {
|
|
44
|
+
context.signal.throwIfAborted();
|
|
45
|
+
const resolved = validateExactKeys(
|
|
46
|
+
await this.resolveCapability(context),
|
|
47
|
+
['capabilityToken'],
|
|
48
|
+
'resolved capability',
|
|
49
|
+
);
|
|
50
|
+
const capabilityToken = validateBoundedString(
|
|
51
|
+
resolved.capabilityToken,
|
|
52
|
+
'capabilityToken',
|
|
53
|
+
16,
|
|
54
|
+
512,
|
|
55
|
+
);
|
|
56
|
+
context.signal.throwIfAborted();
|
|
57
|
+
await this.client.acquire(capabilityToken, context.signal);
|
|
58
|
+
context.signal.throwIfAborted();
|
|
59
|
+
} catch (error) {
|
|
60
|
+
await this.client.release().catch(() => undefined);
|
|
61
|
+
await this.client.close().catch(() => undefined);
|
|
62
|
+
this.state = 'closed';
|
|
63
|
+
throw error;
|
|
64
|
+
}
|
|
65
|
+
this.state = 'active';
|
|
66
|
+
|
|
67
|
+
const tools = plugins.smartagent.createBrowserTools({
|
|
68
|
+
abortSignal: context.signal,
|
|
69
|
+
browser: this.client.asToolBrowserContext(),
|
|
70
|
+
requestPermission: async (request) => {
|
|
71
|
+
const metadata = request.metadata ?? {};
|
|
72
|
+
const actionValue = metadata.action;
|
|
73
|
+
if (typeof actionValue !== 'string' || !allowedActions.includes(
|
|
74
|
+
actionValue as (typeof allowedActions)[number],
|
|
75
|
+
)) {
|
|
76
|
+
throw new BrowserRuntimeError('INVALID_INPUT');
|
|
77
|
+
}
|
|
78
|
+
const action = actionValue as (typeof allowedActions)[number];
|
|
79
|
+
const toolCallId = request.toolCallId === undefined
|
|
80
|
+
? undefined
|
|
81
|
+
: validateBoundedString(request.toolCallId, 'toolCallId', 1, 128);
|
|
82
|
+
const permission = context.requestPermission({
|
|
83
|
+
kind: `browser.${action}`,
|
|
84
|
+
description: `Allow the browser to perform the ${action} action.`,
|
|
85
|
+
...(toolCallId ? { toolCallId } : {}),
|
|
86
|
+
metadata: { action },
|
|
87
|
+
});
|
|
88
|
+
const signal = AbortSignal.any([
|
|
89
|
+
context.signal,
|
|
90
|
+
request.abortSignal ?? new AbortController().signal,
|
|
91
|
+
]);
|
|
92
|
+
signal.throwIfAborted();
|
|
93
|
+
let onAbort!: () => void;
|
|
94
|
+
const aborted = new Promise<never>((_resolve, reject) => {
|
|
95
|
+
onAbort = () => reject(new BrowserRuntimeError('ABORTED'));
|
|
96
|
+
signal.addEventListener('abort', onAbort, { once: true });
|
|
97
|
+
});
|
|
98
|
+
try {
|
|
99
|
+
await Promise.race([permission, aborted]);
|
|
100
|
+
signal.throwIfAborted();
|
|
101
|
+
} finally {
|
|
102
|
+
signal.removeEventListener('abort', onAbort);
|
|
103
|
+
}
|
|
104
|
+
},
|
|
105
|
+
}, {
|
|
106
|
+
allowedActions,
|
|
107
|
+
maxLines: 2000,
|
|
108
|
+
maxBytes: 128 * 1024,
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
let closePromise: Promise<void> | undefined;
|
|
112
|
+
const close = (): Promise<void> => {
|
|
113
|
+
if (closePromise) return closePromise;
|
|
114
|
+
closePromise = (async () => {
|
|
115
|
+
context.signal.removeEventListener('abort', onRunAbort);
|
|
116
|
+
let releaseError: unknown;
|
|
117
|
+
await this.client.release().catch((error) => { releaseError = error; });
|
|
118
|
+
await this.client.close();
|
|
119
|
+
this.state = 'closed';
|
|
120
|
+
if (releaseError) throw releaseError;
|
|
121
|
+
})();
|
|
122
|
+
return closePromise;
|
|
123
|
+
};
|
|
124
|
+
const onRunAbort = (): void => {
|
|
125
|
+
void close().catch(() => undefined);
|
|
126
|
+
};
|
|
127
|
+
context.signal.addEventListener('abort', onRunAbort, { once: true });
|
|
128
|
+
return {
|
|
129
|
+
tools,
|
|
130
|
+
close,
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
}
|