@trusty-squire/mcp 1.1.13 → 1.1.14-rc.10
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/README.md +94 -110
- package/{LICENSE → assets/licenses/browser-use-MIT.txt} +1 -1
- package/dist/api-client.d.ts +9 -0
- package/dist/api-client.d.ts.map +1 -1
- package/dist/api-client.js +11 -0
- package/dist/api-client.js.map +1 -1
- package/dist/bin.js +7 -1
- package/dist/bin.js.map +1 -1
- package/dist/bot/broker/admission-context.d.ts +11 -0
- package/dist/bot/broker/admission-context.d.ts.map +1 -0
- package/dist/bot/broker/admission-context.js +17 -0
- package/dist/bot/broker/admission-context.js.map +1 -0
- package/dist/bot/broker/authority.d.ts +76 -0
- package/dist/bot/broker/authority.d.ts.map +1 -0
- package/dist/bot/broker/authority.js +528 -0
- package/dist/bot/broker/authority.js.map +1 -0
- package/dist/bot/broker/custody.d.ts +20 -0
- package/dist/bot/broker/custody.d.ts.map +1 -0
- package/dist/bot/broker/custody.js +10 -0
- package/dist/bot/broker/custody.js.map +1 -0
- package/dist/bot/broker/daemon.d.ts +24 -0
- package/dist/bot/broker/daemon.d.ts.map +1 -0
- package/dist/bot/broker/daemon.js +371 -0
- package/dist/bot/broker/daemon.js.map +1 -0
- package/dist/bot/broker/discovery.d.ts +9 -0
- package/dist/bot/broker/discovery.d.ts.map +1 -0
- package/dist/bot/broker/discovery.js +167 -0
- package/dist/bot/broker/discovery.js.map +1 -0
- package/dist/bot/broker/dispatch-journal.d.ts +71 -0
- package/dist/bot/broker/dispatch-journal.d.ts.map +1 -0
- package/dist/bot/broker/dispatch-journal.js +319 -0
- package/dist/bot/broker/dispatch-journal.js.map +1 -0
- package/dist/bot/broker/forwarder.d.ts +35 -0
- package/dist/bot/broker/forwarder.d.ts.map +1 -0
- package/dist/bot/broker/forwarder.js +150 -0
- package/dist/bot/broker/forwarder.js.map +1 -0
- package/dist/bot/broker/identity-lane.d.ts +3 -0
- package/dist/bot/broker/identity-lane.d.ts.map +1 -0
- package/dist/bot/broker/identity-lane.js +15 -0
- package/dist/bot/broker/identity-lane.js.map +1 -0
- package/dist/bot/broker/lineage.d.ts +3 -0
- package/dist/bot/broker/lineage.d.ts.map +1 -0
- package/dist/bot/broker/lineage.js +16 -0
- package/dist/bot/broker/lineage.js.map +1 -0
- package/dist/bot/broker/maintenance.d.ts +4 -0
- package/dist/bot/broker/maintenance.d.ts.map +1 -0
- package/dist/bot/broker/maintenance.js +49 -0
- package/dist/bot/broker/maintenance.js.map +1 -0
- package/dist/bot/broker/operator.d.ts +48 -0
- package/dist/bot/broker/operator.d.ts.map +1 -0
- package/dist/bot/broker/operator.js +523 -0
- package/dist/bot/broker/operator.js.map +1 -0
- package/dist/bot/broker/qualification.d.ts +5 -0
- package/dist/bot/broker/qualification.d.ts.map +1 -0
- package/dist/bot/broker/qualification.js +67 -0
- package/dist/bot/broker/qualification.js.map +1 -0
- package/dist/bot/broker/runtime.d.ts +36 -0
- package/dist/bot/broker/runtime.d.ts.map +1 -0
- package/dist/bot/broker/runtime.js +202 -0
- package/dist/bot/broker/runtime.js.map +1 -0
- package/dist/bot/broker/scheduler.d.ts +20 -0
- package/dist/bot/broker/scheduler.d.ts.map +1 -0
- package/dist/bot/broker/scheduler.js +94 -0
- package/dist/bot/broker/scheduler.js.map +1 -0
- package/dist/bot/broker/transport.d.ts +27 -0
- package/dist/bot/broker/transport.d.ts.map +1 -0
- package/dist/bot/broker/transport.js +265 -0
- package/dist/bot/broker/transport.js.map +1 -0
- package/dist/bot/browser-process-owner.d.ts +75 -0
- package/dist/bot/browser-process-owner.d.ts.map +1 -0
- package/dist/bot/browser-process-owner.js +939 -0
- package/dist/bot/browser-process-owner.js.map +1 -0
- package/dist/bot/browser-process-runtime.d.ts +168 -0
- package/dist/bot/browser-process-runtime.d.ts.map +1 -0
- package/dist/bot/browser-process-runtime.js +940 -0
- package/dist/bot/browser-process-runtime.js.map +1 -0
- package/dist/bot/browser-use-capture.d.ts +15 -0
- package/dist/bot/browser-use-capture.d.ts.map +1 -0
- package/dist/bot/browser-use-capture.js +975 -0
- package/dist/bot/browser-use-capture.js.map +1 -0
- package/dist/bot/browser-use-paint-order.d.ts +12 -0
- package/dist/bot/browser-use-paint-order.d.ts.map +1 -0
- package/dist/bot/browser-use-paint-order.js +73 -0
- package/dist/bot/browser-use-paint-order.js.map +1 -0
- package/dist/bot/browser-use-serializer.d.ts +67 -0
- package/dist/bot/browser-use-serializer.d.ts.map +1 -0
- package/dist/bot/browser-use-serializer.js +1027 -0
- package/dist/bot/browser-use-serializer.js.map +1 -0
- package/dist/bot/browser.d.ts +158 -264
- package/dist/bot/browser.d.ts.map +1 -1
- package/dist/bot/browser.js +1681 -2830
- package/dist/bot/browser.js.map +1 -1
- package/dist/bot/compact-observation-v2.d.ts +54 -45
- package/dist/bot/compact-observation-v2.d.ts.map +1 -1
- package/dist/bot/compact-observation-v2.js +454 -165
- package/dist/bot/compact-observation-v2.js.map +1 -1
- package/dist/bot/credential-shape.d.ts.map +1 -1
- package/dist/bot/credential-shape.js +7 -0
- package/dist/bot/credential-shape.js.map +1 -1
- package/dist/bot/identity-runtime.d.ts +56 -0
- package/dist/bot/identity-runtime.d.ts.map +1 -0
- package/dist/bot/identity-runtime.js +141 -0
- package/dist/bot/identity-runtime.js.map +1 -0
- package/dist/bot/mutation-dispatch-evidence.d.ts +13 -0
- package/dist/bot/mutation-dispatch-evidence.d.ts.map +1 -0
- package/dist/bot/mutation-dispatch-evidence.js +19 -0
- package/dist/bot/mutation-dispatch-evidence.js.map +1 -0
- package/dist/bot/owned-pages.d.ts +16 -0
- package/dist/bot/owned-pages.d.ts.map +1 -0
- package/dist/bot/owned-pages.js +58 -0
- package/dist/bot/owned-pages.js.map +1 -0
- package/dist/bot/page-driver.d.ts +37 -0
- package/dist/bot/page-driver.d.ts.map +1 -0
- package/dist/bot/page-driver.js +303 -0
- package/dist/bot/page-driver.js.map +1 -0
- package/dist/bot/pay-operator.d.ts +1 -0
- package/dist/bot/pay-operator.d.ts.map +1 -1
- package/dist/bot/pay-operator.js +10 -0
- package/dist/bot/pay-operator.js.map +1 -1
- package/dist/bot/profile.d.ts +9 -0
- package/dist/bot/profile.d.ts.map +1 -1
- package/dist/bot/profile.js +16 -11
- package/dist/bot/profile.js.map +1 -1
- package/dist/bot/provision-session.d.ts +32 -7
- package/dist/bot/provision-session.d.ts.map +1 -1
- package/dist/bot/provision-session.js +1008 -471
- package/dist/bot/provision-session.js.map +1 -1
- package/dist/bot/session/lifecycle.d.ts +5 -2
- package/dist/bot/session/lifecycle.d.ts.map +1 -1
- package/dist/bot/session/lifecycle.js +303 -34
- package/dist/bot/session/lifecycle.js.map +1 -1
- package/dist/bot/session/multisession-flag.d.ts +2 -0
- package/dist/bot/session/multisession-flag.d.ts.map +1 -0
- package/dist/bot/session/multisession-flag.js +23 -0
- package/dist/bot/session/multisession-flag.js.map +1 -0
- package/dist/install/cli.d.ts.map +1 -1
- package/dist/install/cli.js +2 -1
- package/dist/install/cli.js.map +1 -1
- package/dist/server-instance-registry.d.ts +18 -6
- package/dist/server-instance-registry.d.ts.map +1 -1
- package/dist/server-instance-registry.js +49 -28
- package/dist/server-instance-registry.js.map +1 -1
- package/dist/server.d.ts +6 -3
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +122 -29
- package/dist/server.js.map +1 -1
- package/dist/tools/audit-log.d.ts.map +1 -1
- package/dist/tools/audit-log.js +1 -5
- package/dist/tools/audit-log.js.map +1 -1
- package/dist/tools/audit-rollup.d.ts +2 -5
- package/dist/tools/audit-rollup.d.ts.map +1 -1
- package/dist/tools/audit-rollup.js +0 -0
- package/dist/tools/audit-rollup.js.map +1 -1
- package/dist/tools/credential-mutations.d.ts +16 -16
- package/dist/tools/index.js +2 -2
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/operate-pay.d.ts +18 -18
- package/dist/tools/provision-drive.d.ts +263 -555
- package/dist/tools/provision-drive.d.ts.map +1 -1
- package/dist/tools/provision-drive.js +404 -1139
- package/dist/tools/provision-drive.js.map +1 -1
- package/dist/tools/use-credential.d.ts +6 -6
- package/package.json +1 -1
|
@@ -4,7 +4,7 @@ import type { OAuthProviderId } from "../oauth-providers.js";
|
|
|
4
4
|
import type { Session } from "./model.js";
|
|
5
5
|
import type { CompactV2StartMetadata, Observation } from "../provision-session.js";
|
|
6
6
|
export interface SessionStartPorts {
|
|
7
|
-
observeSession: (session: Session,
|
|
7
|
+
observeSession: (session: Session, format: "compact" | "full", startMetadata?: CompactV2StartMetadata) => Promise<Observation>;
|
|
8
8
|
compactV2StartMetadata: (registryHint: string | undefined, loginHint: string, userEmail: string | null) => CompactV2StartMetadata;
|
|
9
9
|
}
|
|
10
10
|
export declare function audit(sessionId: string, event: string, detail?: Record<string, unknown>): void;
|
|
@@ -15,6 +15,8 @@ export declare function withPaymentSessionCall<T>(sessionId: string | undefined,
|
|
|
15
15
|
export declare function observeSnapshotDir(sessionId: string): string;
|
|
16
16
|
export interface StartOptions {
|
|
17
17
|
serviceUrl: string;
|
|
18
|
+
/** Observation shape returned by this start. Compact is the public default. */
|
|
19
|
+
format?: "compact" | "full";
|
|
18
20
|
profileDir?: string;
|
|
19
21
|
proxyUrl?: string;
|
|
20
22
|
extraAllowedHosts?: readonly string[];
|
|
@@ -24,7 +26,7 @@ export interface StartOptions {
|
|
|
24
26
|
}
|
|
25
27
|
export interface HarnessStartOptions extends Omit<StartOptions, "profileDir" | "proxyUrl"> {
|
|
26
28
|
browser: BrowserController;
|
|
27
|
-
observationFormat?: "v1" | "
|
|
29
|
+
observationFormat?: "v1" | "browser-use-dom";
|
|
28
30
|
}
|
|
29
31
|
export interface NeedsUserLogin {
|
|
30
32
|
wall: "google_session";
|
|
@@ -51,6 +53,7 @@ export interface PreparedFinishResult<T> {
|
|
|
51
53
|
}
|
|
52
54
|
export declare function finishProvisionSessionWithPreparation<T>(sessionId: string, prepare: () => Promise<T>, successfulOutcome?: () => boolean): Promise<PreparedFinishResult<T>>;
|
|
53
55
|
export declare function finishProvisionSession(sessionId: string): Promise<FinishResult>;
|
|
56
|
+
export declare function forceFinishProvisionSession(sessionId: string): Promise<boolean>;
|
|
54
57
|
export declare function closeAllProvisionSessions(): Promise<void>;
|
|
55
58
|
export declare function activeSessionCount(): number;
|
|
56
59
|
//# sourceMappingURL=lifecycle.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lifecycle.d.ts","sourceRoot":"","sources":["../../../src/bot/session/lifecycle.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"lifecycle.d.ts","sourceRoot":"","sources":["../../../src/bot/session/lifecycle.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { reserveBrokerAdmission } from "../broker/admission-context.js";
|
|
2
|
+
import { brokerBrowserCustody } from "../broker/custody.js";
|
|
1
3
|
// Phase 2 of the operator session-management restructure: the session
|
|
2
4
|
// lifecycle, moved out of provision-session.ts as ONE transaction.
|
|
3
5
|
//
|
|
@@ -30,6 +32,8 @@ import { compactV2AuditValue } from "../compact-observation-v2.js";
|
|
|
30
32
|
import { acquireProfileOperationGuard, CHROME_PROFILE_DIR, ProfileBusyError, waitForProfileFree, } from "../profile.js";
|
|
31
33
|
import { loginSessionGuidance } from "../skill-hint.js";
|
|
32
34
|
import { OperatorBrowserWatchdog, } from "../operator-browser-watchdog.js";
|
|
35
|
+
import { IdentityRuntime, IncompatibleIdentityRuntimeSettingsError, } from "../identity-runtime.js";
|
|
36
|
+
import { experimentalMultiSessionEnabled } from "./multisession-flag.js";
|
|
33
37
|
import { createSession } from "./model.js";
|
|
34
38
|
import { hostStrings, merchantSiblingSeedHosts, registrableHost, requestScopeHostStrings, } from "./hosts.js";
|
|
35
39
|
const sessions = new Map();
|
|
@@ -37,6 +41,9 @@ const sessions = new Map();
|
|
|
37
41
|
// but it never creates a browser session. Its terminal acknowledgement is a
|
|
38
42
|
// no-op rather than an "unknown session" error.
|
|
39
43
|
const refusedStartSessionIds = new Set();
|
|
44
|
+
// At most one live group per process today (one shared operator profile).
|
|
45
|
+
let sharedIdentityGroup = null;
|
|
46
|
+
const operatorIdentityRuntime = new IdentityRuntime();
|
|
40
47
|
const leasedBrowsers = new Map();
|
|
41
48
|
const startingBrowsers = new Set();
|
|
42
49
|
let shutdownGeneration = 0;
|
|
@@ -109,16 +116,120 @@ function assertProvisionStartAdmitted(generation) {
|
|
|
109
116
|
throw new Error("operate_start cancelled: operator server is shutting down");
|
|
110
117
|
}
|
|
111
118
|
}
|
|
119
|
+
// Experimental (TRUSTY_SQUIRE_EXPERIMENTAL_MULTISESSION) — a second (or Nth)
|
|
120
|
+
// operate_start joining an identity that is already live or mid-launch IN
|
|
121
|
+
// THIS PROCESS. Returns null when there is nothing of ours to join (the busy
|
|
122
|
+
// signal is from an unrelated process, or a genuinely different profile), so
|
|
123
|
+
// the caller re-throws the original ProfileBusyError instead of silently
|
|
124
|
+
// swallowing a real cross-process busy condition.
|
|
125
|
+
//
|
|
126
|
+
// Deliberately does NOT re-acquire the profile-operation guard: that lease is
|
|
127
|
+
// the group's, held once by whichever session became primary (see
|
|
128
|
+
// acquireWarmBrowser) and released only when the group empties (see
|
|
129
|
+
// releaseWarmBrowserPage). Joining here only ever reuses operatorIdentityRuntime's
|
|
130
|
+
// already-live handle — its own single-flight is what makes this race-free
|
|
131
|
+
// against a primary launch still in flight.
|
|
132
|
+
async function tryAcquireSatelliteBrowser(opts, profileDir, generation, sessionId) {
|
|
133
|
+
if (!operatorIdentityRuntime.isLive() && !operatorIdentityRuntime.isLaunching())
|
|
134
|
+
return null;
|
|
135
|
+
const identitySettings = {
|
|
136
|
+
profileDir,
|
|
137
|
+
...(opts.proxyUrl !== undefined ? { proxyUrl: opts.proxyUrl } : {}),
|
|
138
|
+
};
|
|
139
|
+
let acquired;
|
|
140
|
+
try {
|
|
141
|
+
acquired = await operatorIdentityRuntime.acquire(identitySettings, async () => {
|
|
142
|
+
throw new Error("operate_start (TRUSTY_SQUIRE_EXPERIMENTAL_MULTISESSION): expected to join an already-live " +
|
|
143
|
+
"identity but the runtime attempted a fresh launch instead — this should be unreachable");
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
catch (err) {
|
|
147
|
+
if (err instanceof IncompatibleIdentityRuntimeSettingsError)
|
|
148
|
+
return null;
|
|
149
|
+
throw err;
|
|
150
|
+
}
|
|
151
|
+
const joinable = (group) => group !== null && group.profileDir === profileDir && group.refCount > 0;
|
|
152
|
+
if (!acquired.reused || !joinable(sharedIdentityGroup)) {
|
|
153
|
+
acquired.releaseTabs();
|
|
154
|
+
return null;
|
|
155
|
+
}
|
|
156
|
+
const group = sharedIdentityGroup;
|
|
157
|
+
try {
|
|
158
|
+
assertProvisionStartAdmitted(generation);
|
|
159
|
+
}
|
|
160
|
+
catch (err) {
|
|
161
|
+
acquired.releaseTabs();
|
|
162
|
+
throw err;
|
|
163
|
+
}
|
|
164
|
+
let satellite;
|
|
165
|
+
try {
|
|
166
|
+
satellite = await BrowserController.attachSatellite(group.primary, {
|
|
167
|
+
profileDir,
|
|
168
|
+
...(opts.proxyUrl !== undefined ? { proxyUrl: opts.proxyUrl } : {}),
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
catch (err) {
|
|
172
|
+
acquired.releaseTabs();
|
|
173
|
+
throw err;
|
|
174
|
+
}
|
|
175
|
+
// The last live session's teardown may have emptied the group while the
|
|
176
|
+
// attach was in flight; a satellite must never be committed onto it.
|
|
177
|
+
if (sharedIdentityGroup !== group || !joinable(group)) {
|
|
178
|
+
await satellite.closeOwnPagesOnly().catch(() => undefined);
|
|
179
|
+
acquired.releaseTabs();
|
|
180
|
+
return null;
|
|
181
|
+
}
|
|
182
|
+
group.refCount += 1;
|
|
183
|
+
audit(sessionId, "multisession_satellite_attach", { profile_dir: profileDir });
|
|
184
|
+
leasedBrowsers.set(satellite, {
|
|
185
|
+
controller: satellite,
|
|
186
|
+
profileDir,
|
|
187
|
+
lease: group.lease,
|
|
188
|
+
shutdownGeneration: generation,
|
|
189
|
+
identityLease: acquired.releaseTabs,
|
|
190
|
+
identityGroup: group,
|
|
191
|
+
...(opts.proxyUrl === undefined ? {} : { proxyUrl: opts.proxyUrl }),
|
|
192
|
+
});
|
|
193
|
+
return { controller: satellite, profileDir, shutdownGeneration: generation };
|
|
194
|
+
}
|
|
112
195
|
async function acquireWarmBrowser(opts, sessionId) {
|
|
113
196
|
const generation = provisionStartGeneration();
|
|
197
|
+
const custody = brokerBrowserCustody();
|
|
198
|
+
if (custody !== undefined) {
|
|
199
|
+
const acquired = await custody.acquire(opts);
|
|
200
|
+
try {
|
|
201
|
+
assertProvisionStartAdmitted(generation);
|
|
202
|
+
}
|
|
203
|
+
catch (error) {
|
|
204
|
+
await custody.release(acquired.browser);
|
|
205
|
+
throw error;
|
|
206
|
+
}
|
|
207
|
+
return {
|
|
208
|
+
controller: acquired.browser,
|
|
209
|
+
profileDir: acquired.profileDir,
|
|
210
|
+
shutdownGeneration: generation,
|
|
211
|
+
};
|
|
212
|
+
}
|
|
114
213
|
if ((process.env.BOT_CDP_ENDPOINT ?? "").trim().length > 0) {
|
|
115
214
|
throw new Error("operate_start does not support remote CDP with the local Chrome profile");
|
|
116
215
|
}
|
|
117
216
|
const profileDir = opts.profileDir ?? CHROME_PROFILE_DIR;
|
|
217
|
+
const multiSession = experimentalMultiSessionEnabled();
|
|
118
218
|
// This lease is held for the complete operate session. Its on-disk owner
|
|
119
219
|
// records host + pid + process birth time, so a crashed holder is reclaimed
|
|
120
220
|
// while a live or indeterminate holder is never stolen.
|
|
121
|
-
|
|
221
|
+
let lease;
|
|
222
|
+
try {
|
|
223
|
+
lease = acquireProfileOperationGuard(profileDir);
|
|
224
|
+
}
|
|
225
|
+
catch (err) {
|
|
226
|
+
if (multiSession && err instanceof ProfileBusyError) {
|
|
227
|
+
const satellite = await tryAcquireSatelliteBrowser(opts, profileDir, generation, sessionId);
|
|
228
|
+
if (satellite !== null)
|
|
229
|
+
return satellite;
|
|
230
|
+
}
|
|
231
|
+
throw err;
|
|
232
|
+
}
|
|
122
233
|
if (!(await waitForProfileFree(profileDir, { deadlineMs: 0 }))) {
|
|
123
234
|
lease.release();
|
|
124
235
|
throw new ProfileBusyError("another Trusty Squire session is already using the browser — close it first");
|
|
@@ -134,42 +245,70 @@ async function acquireWarmBrowser(opts, sessionId) {
|
|
|
134
245
|
};
|
|
135
246
|
startingBrowsers.add(pending);
|
|
136
247
|
let controller = null;
|
|
248
|
+
let identityLease = null;
|
|
137
249
|
try {
|
|
138
250
|
if (pending.cancelRequested) {
|
|
139
251
|
throw new Error("operate_start cancelled: operator server is shutting down");
|
|
140
252
|
}
|
|
141
|
-
|
|
253
|
+
const identitySettings = {
|
|
142
254
|
profileDir,
|
|
143
255
|
...(opts.proxyUrl !== undefined ? { proxyUrl: opts.proxyUrl } : {}),
|
|
256
|
+
};
|
|
257
|
+
// Single-flighted through operatorIdentityRuntime rather than constructed
|
|
258
|
+
// inline: today it always launches fresh (production still forgets this
|
|
259
|
+
// identity at every session finish, below), but the acquisition itself
|
|
260
|
+
// now goes through the one place that will later decide whether to reuse
|
|
261
|
+
// a still-live Chrome instead of relaunching it.
|
|
262
|
+
const acquired = await operatorIdentityRuntime.acquire(identitySettings, async (settings) => {
|
|
263
|
+
const launched = new BrowserController({
|
|
264
|
+
profileDir: settings.profileDir,
|
|
265
|
+
...(settings.proxyUrl !== undefined ? { proxyUrl: settings.proxyUrl } : {}),
|
|
266
|
+
});
|
|
267
|
+
pending.controller = launched;
|
|
268
|
+
pending.launch = startBrowserBounded(launched, sessionId, async () => {
|
|
269
|
+
await cancelStartingBrowser(pending);
|
|
270
|
+
});
|
|
271
|
+
await pending.launch;
|
|
272
|
+
return launched;
|
|
144
273
|
});
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
await cancelStartingBrowser(pending);
|
|
148
|
-
});
|
|
149
|
-
await pending.launch;
|
|
274
|
+
controller = acquired.handle;
|
|
275
|
+
identityLease = acquired.releaseTabs;
|
|
150
276
|
if (pending.cancelRequested) {
|
|
151
277
|
throw new Error("operate_start cancelled: operator server is shutting down");
|
|
152
278
|
}
|
|
153
279
|
assertProvisionStartAdmitted(generation);
|
|
154
280
|
}
|
|
155
281
|
catch (err) {
|
|
156
|
-
|
|
157
|
-
|
|
282
|
+
const constructed = controller ?? pending.controller;
|
|
283
|
+
if (constructed !== null) {
|
|
284
|
+
identityLease?.();
|
|
285
|
+
await constructed.close().catch(() => undefined);
|
|
286
|
+
operatorIdentityRuntime.forgetAfterShutdown();
|
|
287
|
+
}
|
|
158
288
|
lease.release();
|
|
159
289
|
throw err;
|
|
160
290
|
}
|
|
161
291
|
finally {
|
|
162
292
|
startingBrowsers.delete(pending);
|
|
163
293
|
}
|
|
164
|
-
if (controller === null) {
|
|
294
|
+
if (controller === null || identityLease === null) {
|
|
165
295
|
throw new Error("operate_start cancelled before browser initialization");
|
|
166
296
|
}
|
|
297
|
+
// This session became primary: form the shared group so a subsequent
|
|
298
|
+
// busy-guard failure (above) knows there is something of ours to join, and
|
|
299
|
+
// so releaseWarmBrowserPage/forceReleaseWarmBrowserPage know who actually
|
|
300
|
+
// owns the profile-operation lease and the real teardown.
|
|
301
|
+
const identityGroup = multiSession
|
|
302
|
+
? (sharedIdentityGroup = { primary: controller, profileDir, lease, refCount: 1 })
|
|
303
|
+
: undefined;
|
|
167
304
|
leasedBrowsers.set(controller, {
|
|
168
305
|
controller,
|
|
169
306
|
profileDir,
|
|
170
307
|
lease,
|
|
171
308
|
shutdownGeneration: generation,
|
|
309
|
+
identityLease,
|
|
172
310
|
...(opts.proxyUrl === undefined ? {} : { proxyUrl: opts.proxyUrl }),
|
|
311
|
+
...(identityGroup !== undefined ? { identityGroup } : {}),
|
|
173
312
|
});
|
|
174
313
|
return {
|
|
175
314
|
controller,
|
|
@@ -178,25 +317,118 @@ async function acquireWarmBrowser(opts, sessionId) {
|
|
|
178
317
|
};
|
|
179
318
|
}
|
|
180
319
|
async function releaseWarmBrowserPage(browser, _persistState, owner) {
|
|
320
|
+
const custody = brokerBrowserCustody();
|
|
321
|
+
if (custody !== undefined) {
|
|
322
|
+
await custody.release(browser);
|
|
323
|
+
return;
|
|
324
|
+
}
|
|
181
325
|
const leased = leasedBrowsers.get(browser);
|
|
326
|
+
const group = leased?.identityGroup;
|
|
327
|
+
// A forced teardown of a GROUPED session belongs wholly to
|
|
328
|
+
// forceReleaseWarmBrowserPage, which the forcing path runs next on this same
|
|
329
|
+
// session: it performs the group's one decrement and, when last, the
|
|
330
|
+
// primary close. Touching the refcount, the lease, or the leasedBrowsers
|
|
331
|
+
// entry here first would discard the group before anything closed the
|
|
332
|
+
// shared Chrome. The ungrouped (flag-off) path is unchanged.
|
|
333
|
+
if (group !== undefined && owner?.forced) {
|
|
334
|
+
throw new Error("operator browser terminal teardown was forced");
|
|
335
|
+
}
|
|
336
|
+
if (group !== undefined)
|
|
337
|
+
group.refCount -= 1;
|
|
338
|
+
const emptiedGroup = group !== undefined && group.refCount <= 0;
|
|
182
339
|
try {
|
|
183
340
|
if (owner?.forced)
|
|
184
341
|
throw new Error("operator browser terminal teardown was forced");
|
|
185
|
-
|
|
342
|
+
if (group === undefined) {
|
|
343
|
+
// Production still tears the identity's Chrome down at every session
|
|
344
|
+
// finish (flag off, or no multisession group formed).
|
|
345
|
+
await browser.close();
|
|
346
|
+
}
|
|
347
|
+
else if (!emptiedGroup) {
|
|
348
|
+
await browser.closeOwnPagesOnly();
|
|
349
|
+
}
|
|
350
|
+
else {
|
|
351
|
+
if (browser !== group.primary)
|
|
352
|
+
await browser.closeOwnPagesOnly().catch(() => undefined);
|
|
353
|
+
await group.primary.close();
|
|
354
|
+
}
|
|
186
355
|
}
|
|
187
356
|
finally {
|
|
188
357
|
leasedBrowsers.delete(browser);
|
|
189
|
-
leased
|
|
358
|
+
if (leased !== undefined) {
|
|
359
|
+
if (group === undefined) {
|
|
360
|
+
leased.lease.release();
|
|
361
|
+
leased.identityLease();
|
|
362
|
+
operatorIdentityRuntime.forgetAfterShutdown();
|
|
363
|
+
}
|
|
364
|
+
else {
|
|
365
|
+
leased.identityLease();
|
|
366
|
+
if (emptiedGroup) {
|
|
367
|
+
group.lease.release();
|
|
368
|
+
operatorIdentityRuntime.forgetAfterShutdown();
|
|
369
|
+
if (sharedIdentityGroup === group)
|
|
370
|
+
sharedIdentityGroup = null;
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
}
|
|
190
374
|
}
|
|
191
375
|
}
|
|
192
376
|
async function forceReleaseWarmBrowserPage(browser, owner) {
|
|
377
|
+
const custody = brokerBrowserCustody();
|
|
378
|
+
if (custody !== undefined) {
|
|
379
|
+
await custody.release(browser);
|
|
380
|
+
return;
|
|
381
|
+
}
|
|
193
382
|
const leased = leasedBrowsers.get(browser);
|
|
194
|
-
|
|
383
|
+
const group = leased?.identityGroup;
|
|
384
|
+
// The group's one decrement for a forced session happens here, never in a
|
|
385
|
+
// preempted releaseWarmBrowserPage (see there), so whichever session's
|
|
386
|
+
// teardown empties the group — primary or satellite, graceful or forced —
|
|
387
|
+
// is the one that closes the shared Chrome.
|
|
388
|
+
if (group !== undefined)
|
|
389
|
+
group.refCount -= 1;
|
|
390
|
+
const emptiedGroup = group !== undefined && group.refCount <= 0;
|
|
391
|
+
try {
|
|
392
|
+
if (group === undefined) {
|
|
393
|
+
await closeBrowserUntilProven(browser, false, "operator browser force-close timed out", () => owner?.requireProvenBrowserClose === true);
|
|
394
|
+
}
|
|
395
|
+
else if (!emptiedGroup) {
|
|
396
|
+
await closeOwnPagesBounded(browser);
|
|
397
|
+
}
|
|
398
|
+
else {
|
|
399
|
+
if (browser !== group.primary)
|
|
400
|
+
await closeOwnPagesBounded(browser);
|
|
401
|
+
await closeBrowserUntilProven(group.primary, false, "operator browser force-close timed out", () => owner?.requireProvenBrowserClose === true);
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
finally {
|
|
405
|
+
if (leased !== undefined) {
|
|
406
|
+
leased.identityLease();
|
|
407
|
+
if (group === undefined) {
|
|
408
|
+
operatorIdentityRuntime.forgetAfterShutdown();
|
|
409
|
+
}
|
|
410
|
+
else if (emptiedGroup) {
|
|
411
|
+
operatorIdentityRuntime.forgetAfterShutdown();
|
|
412
|
+
if (sharedIdentityGroup === group)
|
|
413
|
+
sharedIdentityGroup = null;
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
}
|
|
195
417
|
if (leased === undefined)
|
|
196
418
|
return;
|
|
197
|
-
|
|
419
|
+
if (group === undefined)
|
|
420
|
+
leased.lease.release();
|
|
421
|
+
else if (emptiedGroup)
|
|
422
|
+
group.lease.release();
|
|
198
423
|
leasedBrowsers.delete(browser);
|
|
199
424
|
}
|
|
425
|
+
// The forced-teardown counterpart of closeOwnPagesOnly: page.close() can hang
|
|
426
|
+
// indefinitely on a wedged Chrome, so the grouped forced path keeps the same
|
|
427
|
+
// bounded contract closeBrowserBounded gives the ungrouped one.
|
|
428
|
+
async function closeOwnPagesBounded(browser) {
|
|
429
|
+
const timeoutMs = positiveTimeout("TRUSTY_SQUIRE_OPERATOR_FORCE_CLOSE_TIMEOUT_MS", DEFAULT_OPERATOR_FORCE_CLOSE_TIMEOUT_MS);
|
|
430
|
+
await withTerminalTimeout(browser.closeOwnPagesOnly(), timeoutMs, "operator browser own-page force-close timed out").catch(() => undefined);
|
|
431
|
+
}
|
|
200
432
|
async function closeBrowserBounded(browser, cancelStart, timeoutMessage, maximumTimeoutMs) {
|
|
201
433
|
const forceClose = browser.forceCloseOwnedProcessTree;
|
|
202
434
|
const ordinaryClose = browser
|
|
@@ -410,7 +642,10 @@ function startSessionWatchdog(session) {
|
|
|
410
642
|
const watchdog = new OperatorBrowserWatchdog({
|
|
411
643
|
startedAt: session.startedAt,
|
|
412
644
|
lastActivityAt: () => session.lastActivityAt,
|
|
413
|
-
hasActiveCall: () =>
|
|
645
|
+
hasActiveCall: () => brokerBrowserCustody() !== undefined ||
|
|
646
|
+
session.initializing ||
|
|
647
|
+
session.callCount > 0 ||
|
|
648
|
+
session.paymentCallCount > 0,
|
|
414
649
|
processMarker: () => session.browser.operatorBrowserMarker?.() ?? null,
|
|
415
650
|
onTerminate: async (reason) => await terminateExpiredProvisionSession(session, reason),
|
|
416
651
|
});
|
|
@@ -578,15 +813,28 @@ async function ensureProvisionPrimaryProviderSession(browser) {
|
|
|
578
813
|
});
|
|
579
814
|
}
|
|
580
815
|
export async function startProvisionSession(opts, ports) {
|
|
581
|
-
const id = randomUUID();
|
|
816
|
+
const id = reserveBrokerAdmission([opts.serviceUrl, ...(opts.extraAllowedHosts ?? [])]) ?? randomUUID();
|
|
582
817
|
const compactV2Mode = configuredCompactV2Mode();
|
|
818
|
+
const requestedFormat = opts.format ?? (compactV2Mode === "on" ? "full" : "compact");
|
|
583
819
|
let browser;
|
|
584
820
|
let liveProviders;
|
|
585
821
|
let workerEmail = null;
|
|
586
822
|
const acquired = await acquireWarmBrowser(opts, id);
|
|
587
823
|
browser = acquired.controller;
|
|
588
824
|
try {
|
|
589
|
-
|
|
825
|
+
const probe = async () => {
|
|
826
|
+
const providers = await ensureProvisionPrimaryProviderSession(browser);
|
|
827
|
+
workerEmail =
|
|
828
|
+
typeof browser.detectGoogleAccountEmail === "function"
|
|
829
|
+
? await browser.detectGoogleAccountEmail().catch(() => null)
|
|
830
|
+
: null;
|
|
831
|
+
return providers;
|
|
832
|
+
};
|
|
833
|
+
const custody = brokerBrowserCustody();
|
|
834
|
+
liveProviders =
|
|
835
|
+
custody === undefined
|
|
836
|
+
? await ensureProvisionPrimaryProviderSession(browser)
|
|
837
|
+
: await custody.identity(probe);
|
|
590
838
|
assertProvisionStartAdmitted(acquired.shutdownGeneration);
|
|
591
839
|
const gate = googleSessionGate(liveProviders);
|
|
592
840
|
if (!gate.ok) {
|
|
@@ -594,20 +842,29 @@ export async function startProvisionSession(opts, ports) {
|
|
|
594
842
|
await releaseWarmBrowserPage(browser, false);
|
|
595
843
|
refusedStartSessionIds.add(id);
|
|
596
844
|
return compactV2Mode === "on"
|
|
597
|
-
?
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
845
|
+
? requestedFormat === "full"
|
|
846
|
+
? {
|
|
847
|
+
session_id: id,
|
|
848
|
+
format: "browser-use-dom",
|
|
849
|
+
stage: "auth",
|
|
850
|
+
url: "",
|
|
851
|
+
needs_user: gate.needs_user,
|
|
852
|
+
}
|
|
853
|
+
: {
|
|
854
|
+
session_id: id,
|
|
855
|
+
format: "browser-use-control-query",
|
|
856
|
+
stage: "auth",
|
|
857
|
+
url: "",
|
|
858
|
+
safe_table: [],
|
|
859
|
+
needs_user: gate.needs_user,
|
|
860
|
+
}
|
|
605
861
|
: { session_id: id, url: "", text: "", elements: [], needs_user: gate.needs_user };
|
|
606
862
|
}
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
863
|
+
if (custody === undefined)
|
|
864
|
+
workerEmail =
|
|
865
|
+
typeof browser.detectGoogleAccountEmail === "function"
|
|
866
|
+
? await browser.detectGoogleAccountEmail().catch(() => null)
|
|
867
|
+
: null;
|
|
611
868
|
}
|
|
612
869
|
catch (error) {
|
|
613
870
|
await releaseWarmBrowserPage(browser, false);
|
|
@@ -669,10 +926,11 @@ export async function startProvisionSession(opts, ports) {
|
|
|
669
926
|
// doesn't have to guess. Composed with the skill route hint (if any).
|
|
670
927
|
const loginHint = loginSessionGuidance(liveProviders);
|
|
671
928
|
const hintParts = [loginHint, ...(opts.hint !== undefined ? [opts.hint] : [])];
|
|
672
|
-
const observation = await ports.observeSession(session,
|
|
929
|
+
const observation = await ports.observeSession(session, requestedFormat, ports.compactV2StartMetadata(opts.hint, loginHint, session.userEmail));
|
|
673
930
|
session.initializing = false;
|
|
674
931
|
session.lastActivityAt = Date.now();
|
|
675
|
-
if (observation.format === "
|
|
932
|
+
if (observation.format === "browser-use-dom" ||
|
|
933
|
+
observation.format === "browser-use-control-query")
|
|
676
934
|
return observation;
|
|
677
935
|
return {
|
|
678
936
|
...observation,
|
|
@@ -690,6 +948,7 @@ export async function startProvisionSession(opts, ports) {
|
|
|
690
948
|
/** Start a normal guarded session on a caller-owned harness page. */
|
|
691
949
|
export async function startHarnessProvisionSession(opts, ports) {
|
|
692
950
|
const id = randomUUID();
|
|
951
|
+
const requestedFormat = opts.format ?? (opts.observationFormat === "browser-use-dom" ? "full" : "compact");
|
|
693
952
|
const targetHost = registrableHost(opts.serviceUrl);
|
|
694
953
|
const allowedHosts = [
|
|
695
954
|
...(targetHost === null ? [] : [targetHost]),
|
|
@@ -704,7 +963,7 @@ export async function startHarnessProvisionSession(opts, ports) {
|
|
|
704
963
|
id,
|
|
705
964
|
browser: opts.browser,
|
|
706
965
|
allowedHosts,
|
|
707
|
-
compactV2Mode: opts.observationFormat === "
|
|
966
|
+
compactV2Mode: opts.observationFormat === "browser-use-dom" ? "on" : "off",
|
|
708
967
|
startUrl: opts.serviceUrl,
|
|
709
968
|
hintServed: opts.hint !== undefined,
|
|
710
969
|
consentInboxRead: opts.consentInboxRead !== false,
|
|
@@ -719,10 +978,11 @@ export async function startHarnessProvisionSession(opts, ports) {
|
|
|
719
978
|
allowed_hosts: hostStrings(session),
|
|
720
979
|
});
|
|
721
980
|
await opts.browser.goto(opts.serviceUrl);
|
|
722
|
-
const observation = await ports.observeSession(session,
|
|
981
|
+
const observation = await ports.observeSession(session, requestedFormat, ports.compactV2StartMetadata(opts.hint, "", null));
|
|
723
982
|
session.initializing = false;
|
|
724
983
|
session.lastActivityAt = Date.now();
|
|
725
|
-
if (observation.format === "
|
|
984
|
+
if (observation.format === "browser-use-dom" ||
|
|
985
|
+
observation.format === "browser-use-control-query")
|
|
726
986
|
return observation;
|
|
727
987
|
return { ...observation, hint: opts.hint ?? "" };
|
|
728
988
|
}
|
|
@@ -922,6 +1182,15 @@ export async function finishProvisionSession(sessionId) {
|
|
|
922
1182
|
}
|
|
923
1183
|
return (await finishProvisionSessionWithPreparation(sessionId, async () => undefined)).finish;
|
|
924
1184
|
}
|
|
1185
|
+
export async function forceFinishProvisionSession(sessionId) {
|
|
1186
|
+
const session = sessionForCall(sessionId);
|
|
1187
|
+
if (session === undefined)
|
|
1188
|
+
return true;
|
|
1189
|
+
const terminalError = await forceTerminateProvisionSession(session, "broker_detached_expiry", {
|
|
1190
|
+
reason: "stuck_dispatched_operation",
|
|
1191
|
+
});
|
|
1192
|
+
return terminalError === undefined && sessionForCall(sessionId) === undefined;
|
|
1193
|
+
}
|
|
925
1194
|
// Test/teardown helper — close every live session (used by the dev shim on exit).
|
|
926
1195
|
export async function closeAllProvisionSessions() {
|
|
927
1196
|
shutdownGeneration += 1;
|