@oharness/engine 0.7.1 → 0.8.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/dist/agent/agent.d.ts +1 -0
- package/dist/agent/agent.js +5 -0
- package/dist/agent/agent.js.map +1 -1
- package/dist/agent/permissions.js +8 -0
- package/dist/agent/permissions.js.map +1 -1
- package/dist/browser/cdp.d.ts +75 -0
- package/dist/browser/cdp.js +136 -0
- package/dist/browser/cdp.js.map +1 -0
- package/dist/browser/chrome.d.ts +19 -0
- package/dist/browser/chrome.js +306 -0
- package/dist/browser/chrome.js.map +1 -0
- package/dist/browser/extension.d.ts +35 -0
- package/dist/browser/extension.js +297 -0
- package/dist/browser/extension.js.map +1 -0
- package/dist/browser/host-manifest.d.ts +78 -0
- package/dist/browser/host-manifest.js +166 -0
- package/dist/browser/host-manifest.js.map +1 -0
- package/dist/browser/host.d.ts +27 -0
- package/dist/browser/host.js +311 -0
- package/dist/browser/host.js.map +1 -0
- package/dist/browser/index.d.ts +26 -0
- package/dist/browser/index.js +15 -0
- package/dist/browser/index.js.map +1 -0
- package/dist/browser/login-import.d.ts +98 -0
- package/dist/browser/login-import.js +244 -0
- package/dist/browser/login-import.js.map +1 -0
- package/dist/browser/plugin.d.ts +109 -0
- package/dist/browser/plugin.js +22 -0
- package/dist/browser/plugin.js.map +1 -0
- package/dist/browser/presence.d.ts +29 -0
- package/dist/browser/presence.js +54 -0
- package/dist/browser/presence.js.map +1 -0
- package/dist/browser/provider.d.ts +17 -0
- package/dist/browser/provider.js +13 -0
- package/dist/browser/provider.js.map +1 -0
- package/dist/browser/screencast.d.ts +42 -0
- package/dist/browser/screencast.js +99 -0
- package/dist/browser/screencast.js.map +1 -0
- package/dist/browser/session.d.ts +320 -0
- package/dist/browser/session.js +837 -0
- package/dist/browser/session.js.map +1 -0
- package/dist/browser/snapshot.d.ts +49 -0
- package/dist/browser/snapshot.js +145 -0
- package/dist/browser/snapshot.js.map +1 -0
- package/dist/browser/socket.d.ts +15 -0
- package/dist/browser/socket.js +32 -0
- package/dist/browser/socket.js.map +1 -0
- package/dist/browser/target.d.ts +112 -0
- package/dist/browser/target.js +20 -0
- package/dist/browser/target.js.map +1 -0
- package/dist/browser/tools.d.ts +3 -0
- package/dist/browser/tools.js +537 -0
- package/dist/browser/tools.js.map +1 -0
- package/dist/config.d.ts +14 -0
- package/dist/config.js.map +1 -1
- package/dist/core/errors.d.ts +12 -0
- package/dist/core/errors.js +14 -1
- package/dist/core/errors.js.map +1 -1
- package/dist/harness.d.ts +49 -0
- package/dist/harness.js +97 -1
- package/dist/harness.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/providers/piai.js +7 -2
- package/dist/providers/piai.js.map +1 -1
- package/dist/tools/define.d.ts +1 -0
- package/dist/tools/define.js +3 -0
- package/dist/tools/define.js.map +1 -1
- package/dist/tools/types.d.ts +11 -0
- package/dist/tools/types.js.map +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export { BrowserSession, originOf } from "./session.ts";
|
|
2
|
+
export type { BrowserSessionOptions, ConsoleEntry, NetworkEntry } from "./session.ts";
|
|
3
|
+
export { createBrowserTools } from "./tools.ts";
|
|
4
|
+
export { findChrome, launchChrome } from "./chrome.ts";
|
|
5
|
+
export type { LaunchChromeOptions } from "./chrome.ts";
|
|
6
|
+
export { CdpConnection, NulFramer } from "./cdp.ts";
|
|
7
|
+
export type { CdpMessage } from "./cdp.ts";
|
|
8
|
+
export { buildSnapshot } from "./snapshot.ts";
|
|
9
|
+
export type { AxNode, Snapshot, SnapshotOptions } from "./snapshot.ts";
|
|
10
|
+
export type { BrowserInputEvent, BrowserPage, BrowserTarget, BrowserTargetFactory, FrameOptions, BrowserTargetContext, } from "./target.ts";
|
|
11
|
+
export { createBrowserUsePlugin } from "./plugin.ts";
|
|
12
|
+
export type { BrowserUsePlugin, BrowserUseSession, BrowserUseSessionContext, DefaultBrowserUsePluginOptions } from "./plugin.ts";
|
|
13
|
+
export { browserProviderFor, builtinBrowserProvider, extensionBrowserProvider } from "./provider.ts";
|
|
14
|
+
export type { BrowserProviderPlugin } from "./provider.ts";
|
|
15
|
+
export { connectExtensionChannel, extensionTarget } from "./extension.ts";
|
|
16
|
+
export { extensionHostReachable, extensionSocketPath } from "./socket.ts";
|
|
17
|
+
export { announcePresence } from "./presence.ts";
|
|
18
|
+
export type { Presence, PresenceInfo } from "./presence.ts";
|
|
19
|
+
export type { ExtensionChannel } from "./extension.ts";
|
|
20
|
+
export { NativeMessageFramer, encodeNativeMessage, runBrowserHost } from "./host.ts";
|
|
21
|
+
export type { HostOptions, RunningHost } from "./host.ts";
|
|
22
|
+
export { EXTENSION_ID, EXTENSION_INSTALL_URL, HOST_NAME, hostLauncher, installNativeHost, nativeHostManifest, nativeHostTargets, windowsRegistryKeys, } from "./host-manifest.ts";
|
|
23
|
+
export type { InstallOptions, InstallResult, NativeHostManifest } from "./host-manifest.ts";
|
|
24
|
+
export { ScreencastHub } from "./screencast.ts";
|
|
25
|
+
export { LoginImportMemory, cookieInSite, cookieParam, groupSites, hostInSite, normalizeSite, readSiteLoginState } from "./login-import.ts";
|
|
26
|
+
export type { CookieParam, ExternalSite, ImportedSite, SiteLoginState } from "./login-import.ts";
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export { BrowserSession, originOf } from "./session.js";
|
|
2
|
+
export { createBrowserTools } from "./tools.js";
|
|
3
|
+
export { findChrome, launchChrome } from "./chrome.js";
|
|
4
|
+
export { CdpConnection, NulFramer } from "./cdp.js";
|
|
5
|
+
export { buildSnapshot } from "./snapshot.js";
|
|
6
|
+
export { createBrowserUsePlugin } from "./plugin.js";
|
|
7
|
+
export { browserProviderFor, builtinBrowserProvider, extensionBrowserProvider } from "./provider.js";
|
|
8
|
+
export { connectExtensionChannel, extensionTarget } from "./extension.js";
|
|
9
|
+
export { extensionHostReachable, extensionSocketPath } from "./socket.js";
|
|
10
|
+
export { announcePresence } from "./presence.js";
|
|
11
|
+
export { NativeMessageFramer, encodeNativeMessage, runBrowserHost } from "./host.js";
|
|
12
|
+
export { EXTENSION_ID, EXTENSION_INSTALL_URL, HOST_NAME, hostLauncher, installNativeHost, nativeHostManifest, nativeHostTargets, windowsRegistryKeys, } from "./host-manifest.js";
|
|
13
|
+
export { ScreencastHub } from "./screencast.js";
|
|
14
|
+
export { LoginImportMemory, cookieInSite, cookieParam, groupSites, hostInSite, normalizeSite, readSiteLoginState } from "./login-import.js";
|
|
15
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/browser/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAExD,OAAO,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAChD,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEvD,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAEpD,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAU9C,OAAO,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC;AAErD,OAAO,EAAE,kBAAkB,EAAE,sBAAsB,EAAE,wBAAwB,EAAE,MAAM,eAAe,CAAC;AAErG,OAAO,EAAE,uBAAuB,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAC1E,OAAO,EAAE,sBAAsB,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAC1E,OAAO,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAGjD,OAAO,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAErF,OAAO,EACL,YAAY,EACZ,qBAAqB,EACrB,SAAS,EACT,YAAY,EACZ,iBAAiB,EACjB,kBAAkB,EAClB,iBAAiB,EACjB,mBAAmB,GACpB,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAE,iBAAiB,EAAE,YAAY,EAAE,WAAW,EAAE,UAAU,EAAE,UAAU,EAAE,aAAa,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC"}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import type { BrowserTarget } from "./target.ts";
|
|
2
|
+
/**
|
|
3
|
+
* Moving a site's sign-in from the person's own browser into the built-in one.
|
|
4
|
+
*
|
|
5
|
+
* The external browser is only ever reached through the OHarness extension —
|
|
6
|
+
* the same sanctioned relay extension mode drives — so nothing here reads
|
|
7
|
+
* another browser's cookie database off disk or asks the keychain for its key.
|
|
8
|
+
* Chrome shows its "being debugged" banner while a tab is read, which is the
|
|
9
|
+
* disclosure this deserves.
|
|
10
|
+
*
|
|
11
|
+
* The unit is a site, not a browser. Copying a whole cookie jar would hand the
|
|
12
|
+
* agent every account the person has, for the sake of the one it needed; a site
|
|
13
|
+
* is what the person can reason about in a confirmation dialog, and what the
|
|
14
|
+
* dialog names. Nothing in this module runs without that confirmation: the
|
|
15
|
+
* server only calls `readSiteLoginState` from the dialog's import action.
|
|
16
|
+
*/
|
|
17
|
+
/** A cookie as `Network.setCookies` accepts it. */
|
|
18
|
+
export type CookieParam = Record<string, unknown> & {
|
|
19
|
+
name: string;
|
|
20
|
+
value: string;
|
|
21
|
+
};
|
|
22
|
+
export interface SiteLoginState {
|
|
23
|
+
site: string;
|
|
24
|
+
cookies: CookieParam[];
|
|
25
|
+
/** localStorage per origin, read from the site's open tabs. */
|
|
26
|
+
origins: Array<{
|
|
27
|
+
origin: string;
|
|
28
|
+
url: string;
|
|
29
|
+
localStorage: Array<[string, string]>;
|
|
30
|
+
}>;
|
|
31
|
+
}
|
|
32
|
+
export interface ExternalSite {
|
|
33
|
+
site: string;
|
|
34
|
+
/** The most useful page to reopen in the built-in browser. */
|
|
35
|
+
url: string;
|
|
36
|
+
title?: string;
|
|
37
|
+
tabs: number;
|
|
38
|
+
}
|
|
39
|
+
export interface ImportedSite {
|
|
40
|
+
site: string;
|
|
41
|
+
cookies: number;
|
|
42
|
+
localStorage: number;
|
|
43
|
+
url?: string;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* The site a URL or typed domain names: lower-case host, `www.` dropped so
|
|
47
|
+
* `github.com` and `www.github.com` are one choice in the dialog.
|
|
48
|
+
*/
|
|
49
|
+
export declare function normalizeSite(input: string): string | undefined;
|
|
50
|
+
/** Whether a host belongs to a site: the site itself or any subdomain of it. */
|
|
51
|
+
export declare function hostInSite(host: string, site: string): boolean;
|
|
52
|
+
/**
|
|
53
|
+
* Whether a cookie is part of signing in to `site`.
|
|
54
|
+
*
|
|
55
|
+
* Its own and its subdomains' cookies, and the parent domain's too: signing in
|
|
56
|
+
* to `mail.example.com` often lives in a cookie set on `.example.com`. A public
|
|
57
|
+
* suffix can never carry a cookie, so the parent walk cannot reach `.com`.
|
|
58
|
+
*/
|
|
59
|
+
export declare function cookieInSite(domain: string, site: string): boolean;
|
|
60
|
+
/** The http(s) sites open in a browser, grouped the way the dialog lists them. */
|
|
61
|
+
export declare function groupSites(tabs: Array<{
|
|
62
|
+
url: string;
|
|
63
|
+
title?: string;
|
|
64
|
+
}>): ExternalSite[];
|
|
65
|
+
/** Which browser the extension lives in, for the dialog. Older extensions cannot say. */
|
|
66
|
+
export declare function externalBrowserName(target: BrowserTarget): Promise<string>;
|
|
67
|
+
export declare function listExternalSites(target: BrowserTarget): Promise<ExternalSite[]>;
|
|
68
|
+
/**
|
|
69
|
+
* Read one site's cookies and localStorage from the external browser.
|
|
70
|
+
*
|
|
71
|
+
* Cookies come from the browser-wide store, so any attached tab can answer;
|
|
72
|
+
* localStorage only exists inside a page of its own origin, so it is read from
|
|
73
|
+
* the site's open tabs and is simply absent for a site with none.
|
|
74
|
+
*/
|
|
75
|
+
export declare function readSiteLoginState(target: BrowserTarget, site: string): Promise<SiteLoginState>;
|
|
76
|
+
/**
|
|
77
|
+
* A read cookie, rewritten as one `Network.setCookies` will store identically.
|
|
78
|
+
*
|
|
79
|
+
* A host-only cookie is reported with a bare domain, and passing that back as
|
|
80
|
+
* `domain` would widen it to every subdomain (and make a `__Host-` cookie
|
|
81
|
+
* invalid). So host-only cookies are set by `url`, domain cookies by `domain`.
|
|
82
|
+
* Partitioning and source-port fields are left out: their shapes differ across
|
|
83
|
+
* Chromium versions, and the embedded one rejects what it does not know.
|
|
84
|
+
*/
|
|
85
|
+
export declare function cookieParam(cookie: Record<string, unknown>): CookieParam | undefined;
|
|
86
|
+
/**
|
|
87
|
+
* Sites the person already answered for — imported, or told the suggestion to
|
|
88
|
+
* go away. The built-in profile is shared and persistent, so the answer is too:
|
|
89
|
+
* asking again in the next conversation would be asking the same question.
|
|
90
|
+
*/
|
|
91
|
+
export declare class LoginImportMemory {
|
|
92
|
+
private readonly file;
|
|
93
|
+
private loaded?;
|
|
94
|
+
constructor(file?: string);
|
|
95
|
+
private sites;
|
|
96
|
+
has(site: string): Promise<boolean>;
|
|
97
|
+
add(site: string): Promise<void>;
|
|
98
|
+
}
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
import { readFile, writeFile, mkdir } from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { BrowserError } from "../core/errors.js";
|
|
4
|
+
import { userPath } from "../paths.js";
|
|
5
|
+
/** A page's localStorage can be large; the import is for sign-in, not caches. */
|
|
6
|
+
const LOCAL_STORAGE_LIMIT = 5 * 1024 * 1024;
|
|
7
|
+
/**
|
|
8
|
+
* The site a URL or typed domain names: lower-case host, `www.` dropped so
|
|
9
|
+
* `github.com` and `www.github.com` are one choice in the dialog.
|
|
10
|
+
*/
|
|
11
|
+
export function normalizeSite(input) {
|
|
12
|
+
const raw = input.trim().toLowerCase();
|
|
13
|
+
if (!raw)
|
|
14
|
+
return undefined;
|
|
15
|
+
let host;
|
|
16
|
+
try {
|
|
17
|
+
host = new URL(/^[a-z][a-z0-9+.-]*:\/\//.test(raw) ? raw : `https://${raw}`).hostname;
|
|
18
|
+
}
|
|
19
|
+
catch {
|
|
20
|
+
return undefined;
|
|
21
|
+
}
|
|
22
|
+
host = host.replace(/^\.+/, "").replace(/\.$/, "");
|
|
23
|
+
if (!host || /\s/.test(host))
|
|
24
|
+
return undefined;
|
|
25
|
+
// A bare label ("com", "localhost") would match every cookie under it.
|
|
26
|
+
if (!host.includes(".") && host !== "localhost")
|
|
27
|
+
return undefined;
|
|
28
|
+
return host.startsWith("www.") ? host.slice(4) : host;
|
|
29
|
+
}
|
|
30
|
+
/** Whether a host belongs to a site: the site itself or any subdomain of it. */
|
|
31
|
+
export function hostInSite(host, site) {
|
|
32
|
+
const value = host.toLowerCase().replace(/^\.+/, "");
|
|
33
|
+
return value === site || value.endsWith(`.${site}`);
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Whether a cookie is part of signing in to `site`.
|
|
37
|
+
*
|
|
38
|
+
* Its own and its subdomains' cookies, and the parent domain's too: signing in
|
|
39
|
+
* to `mail.example.com` often lives in a cookie set on `.example.com`. A public
|
|
40
|
+
* suffix can never carry a cookie, so the parent walk cannot reach `.com`.
|
|
41
|
+
*/
|
|
42
|
+
export function cookieInSite(domain, site) {
|
|
43
|
+
const value = domain.toLowerCase().replace(/^\.+/, "");
|
|
44
|
+
if (!value)
|
|
45
|
+
return false;
|
|
46
|
+
return value === site || value.endsWith(`.${site}`) || site.endsWith(`.${value}`);
|
|
47
|
+
}
|
|
48
|
+
/** The http(s) sites open in a browser, grouped the way the dialog lists them. */
|
|
49
|
+
export function groupSites(tabs) {
|
|
50
|
+
const sites = new Map();
|
|
51
|
+
for (const tab of tabs) {
|
|
52
|
+
let parsed;
|
|
53
|
+
try {
|
|
54
|
+
parsed = new URL(tab.url);
|
|
55
|
+
}
|
|
56
|
+
catch {
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
if (parsed.protocol !== "http:" && parsed.protocol !== "https:")
|
|
60
|
+
continue;
|
|
61
|
+
const site = normalizeSite(parsed.hostname);
|
|
62
|
+
if (!site)
|
|
63
|
+
continue;
|
|
64
|
+
const existing = sites.get(site);
|
|
65
|
+
if (existing) {
|
|
66
|
+
existing.tabs++;
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
sites.set(site, { site, url: parsed.href, ...(tab.title ? { title: tab.title } : {}), tabs: 1 });
|
|
70
|
+
}
|
|
71
|
+
return [...sites.values()];
|
|
72
|
+
}
|
|
73
|
+
/** Which browser the extension lives in, for the dialog. Older extensions cannot say. */
|
|
74
|
+
export async function externalBrowserName(target) {
|
|
75
|
+
const described = await target.describe?.().catch(() => undefined);
|
|
76
|
+
return described?.name || "Chrome";
|
|
77
|
+
}
|
|
78
|
+
async function tabInfos(target) {
|
|
79
|
+
if (target.pageInfos)
|
|
80
|
+
return target.pageInfos();
|
|
81
|
+
const pages = await target.pages();
|
|
82
|
+
return Promise.all(pages.map(async (page) => ({ id: page.id, url: await evaluateString(page, "location.href") })));
|
|
83
|
+
}
|
|
84
|
+
export async function listExternalSites(target) {
|
|
85
|
+
return groupSites(await tabInfos(target));
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Read one site's cookies and localStorage from the external browser.
|
|
89
|
+
*
|
|
90
|
+
* Cookies come from the browser-wide store, so any attached tab can answer;
|
|
91
|
+
* localStorage only exists inside a page of its own origin, so it is read from
|
|
92
|
+
* the site's open tabs and is simply absent for a site with none.
|
|
93
|
+
*/
|
|
94
|
+
export async function readSiteLoginState(target, site) {
|
|
95
|
+
const infos = await tabInfos(target);
|
|
96
|
+
const web = infos.filter((info) => /^https?:/i.test(info.url));
|
|
97
|
+
const matching = web.filter((info) => {
|
|
98
|
+
try {
|
|
99
|
+
return hostInSite(new URL(info.url).hostname, site);
|
|
100
|
+
}
|
|
101
|
+
catch {
|
|
102
|
+
return false;
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
const reader = matching[0] ?? web[0];
|
|
106
|
+
if (!reader)
|
|
107
|
+
throw new BrowserError(`open ${site} in your browser first, so its sign-in can be read`);
|
|
108
|
+
const readerPage = await pageFor(target, reader.id);
|
|
109
|
+
if (!readerPage)
|
|
110
|
+
throw new BrowserError("the browser tab closed before it could be read");
|
|
111
|
+
const cookies = (await readCookies(readerPage, matching.map((info) => info.url).concat(`https://${site}/`, `http://${site}/`)))
|
|
112
|
+
.filter((cookie) => typeof cookie["domain"] === "string" && cookieInSite(cookie["domain"], site))
|
|
113
|
+
.flatMap((cookie) => {
|
|
114
|
+
const param = cookieParam(cookie);
|
|
115
|
+
return param ? [param] : [];
|
|
116
|
+
});
|
|
117
|
+
const origins = [];
|
|
118
|
+
const seen = new Set();
|
|
119
|
+
let budget = LOCAL_STORAGE_LIMIT;
|
|
120
|
+
for (const info of matching) {
|
|
121
|
+
const origin = new URL(info.url).origin;
|
|
122
|
+
if (seen.has(origin))
|
|
123
|
+
continue;
|
|
124
|
+
seen.add(origin);
|
|
125
|
+
const page = info.id === reader.id ? readerPage : await pageFor(target, info.id);
|
|
126
|
+
if (!page)
|
|
127
|
+
continue;
|
|
128
|
+
const entries = await readLocalStorage(page).catch(() => []);
|
|
129
|
+
const kept = [];
|
|
130
|
+
for (const entry of entries) {
|
|
131
|
+
const size = entry[0].length + entry[1].length;
|
|
132
|
+
if (size > budget)
|
|
133
|
+
continue;
|
|
134
|
+
budget -= size;
|
|
135
|
+
kept.push(entry);
|
|
136
|
+
}
|
|
137
|
+
origins.push({ origin, url: info.url, localStorage: kept });
|
|
138
|
+
}
|
|
139
|
+
return { site, cookies, origins };
|
|
140
|
+
}
|
|
141
|
+
async function pageFor(target, id) {
|
|
142
|
+
if (target.pageById)
|
|
143
|
+
return target.pageById(id);
|
|
144
|
+
return (await target.pages()).find((page) => page.id === id);
|
|
145
|
+
}
|
|
146
|
+
async function readCookies(page, urls) {
|
|
147
|
+
const pick = (result) => Array.isArray(result["cookies"])
|
|
148
|
+
? result["cookies"].filter((cookie) => typeof cookie === "object" && cookie !== null)
|
|
149
|
+
: [];
|
|
150
|
+
// Every cookie, so a subdomain's is not missed; the per-URL form is the
|
|
151
|
+
// fallback for a relay that refuses the browser-wide call.
|
|
152
|
+
try {
|
|
153
|
+
return pick(await page.send("Network.getAllCookies"));
|
|
154
|
+
}
|
|
155
|
+
catch {
|
|
156
|
+
return pick(await page.send("Network.getCookies", { urls: [...new Set(urls)] }));
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
async function readLocalStorage(page) {
|
|
160
|
+
const result = await page.send("Runtime.evaluate", {
|
|
161
|
+
expression: "JSON.stringify(Object.entries(localStorage))",
|
|
162
|
+
returnByValue: true,
|
|
163
|
+
});
|
|
164
|
+
const value = (result["result"] ?? {})["value"];
|
|
165
|
+
if (typeof value !== "string")
|
|
166
|
+
return [];
|
|
167
|
+
const parsed = JSON.parse(value);
|
|
168
|
+
return Array.isArray(parsed)
|
|
169
|
+
? parsed.flatMap((entry) => Array.isArray(entry) && typeof entry[0] === "string" && typeof entry[1] === "string"
|
|
170
|
+
? [[entry[0], entry[1]]]
|
|
171
|
+
: [])
|
|
172
|
+
: [];
|
|
173
|
+
}
|
|
174
|
+
async function evaluateString(page, expression) {
|
|
175
|
+
const result = await page.send("Runtime.evaluate", { expression, returnByValue: true });
|
|
176
|
+
return String((result["result"] ?? {})["value"] ?? "");
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* A read cookie, rewritten as one `Network.setCookies` will store identically.
|
|
180
|
+
*
|
|
181
|
+
* A host-only cookie is reported with a bare domain, and passing that back as
|
|
182
|
+
* `domain` would widen it to every subdomain (and make a `__Host-` cookie
|
|
183
|
+
* invalid). So host-only cookies are set by `url`, domain cookies by `domain`.
|
|
184
|
+
* Partitioning and source-port fields are left out: their shapes differ across
|
|
185
|
+
* Chromium versions, and the embedded one rejects what it does not know.
|
|
186
|
+
*/
|
|
187
|
+
export function cookieParam(cookie) {
|
|
188
|
+
const { name, value, domain } = cookie;
|
|
189
|
+
if (typeof name !== "string" || typeof value !== "string" || typeof domain !== "string" || !domain)
|
|
190
|
+
return undefined;
|
|
191
|
+
const cookiePath = typeof cookie["path"] === "string" && cookie["path"] ? cookie["path"] : "/";
|
|
192
|
+
const secure = cookie["secure"] === true;
|
|
193
|
+
const param = { name, value, path: cookiePath, secure };
|
|
194
|
+
if (domain.startsWith("."))
|
|
195
|
+
param["domain"] = domain;
|
|
196
|
+
else
|
|
197
|
+
param["url"] = `${secure ? "https" : "http"}://${domain}${cookiePath}`;
|
|
198
|
+
if (cookie["httpOnly"] === true)
|
|
199
|
+
param["httpOnly"] = true;
|
|
200
|
+
if (cookie["sameSite"] === "Strict" || cookie["sameSite"] === "Lax" || cookie["sameSite"] === "None") {
|
|
201
|
+
param["sameSite"] = cookie["sameSite"];
|
|
202
|
+
}
|
|
203
|
+
// A session cookie reads as -1; leaving `expires` out keeps it one.
|
|
204
|
+
if (cookie["session"] !== true && typeof cookie["expires"] === "number" && cookie["expires"] > 0) {
|
|
205
|
+
param["expires"] = cookie["expires"];
|
|
206
|
+
}
|
|
207
|
+
if (cookie["priority"] === "Low" || cookie["priority"] === "Medium" || cookie["priority"] === "High") {
|
|
208
|
+
param["priority"] = cookie["priority"];
|
|
209
|
+
}
|
|
210
|
+
return param;
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Sites the person already answered for — imported, or told the suggestion to
|
|
214
|
+
* go away. The built-in profile is shared and persistent, so the answer is too:
|
|
215
|
+
* asking again in the next conversation would be asking the same question.
|
|
216
|
+
*/
|
|
217
|
+
export class LoginImportMemory {
|
|
218
|
+
file;
|
|
219
|
+
loaded;
|
|
220
|
+
constructor(file = userPath("browser-import.json")) {
|
|
221
|
+
this.file = file;
|
|
222
|
+
}
|
|
223
|
+
sites() {
|
|
224
|
+
this.loaded ??= readFile(this.file, "utf8")
|
|
225
|
+
.then((text) => {
|
|
226
|
+
const parsed = JSON.parse(text);
|
|
227
|
+
return new Set(Array.isArray(parsed.quiet) ? parsed.quiet.filter((site) => typeof site === "string") : []);
|
|
228
|
+
})
|
|
229
|
+
.catch(() => new Set());
|
|
230
|
+
return this.loaded;
|
|
231
|
+
}
|
|
232
|
+
async has(site) {
|
|
233
|
+
return (await this.sites()).has(site);
|
|
234
|
+
}
|
|
235
|
+
async add(site) {
|
|
236
|
+
const sites = await this.sites();
|
|
237
|
+
if (sites.has(site))
|
|
238
|
+
return;
|
|
239
|
+
sites.add(site);
|
|
240
|
+
await mkdir(path.dirname(this.file), { recursive: true });
|
|
241
|
+
await writeFile(this.file, `${JSON.stringify({ quiet: [...sites].sort() }, null, 2)}\n`, "utf8").catch(() => { });
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
//# sourceMappingURL=login-import.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"login-import.js","sourceRoot":"","sources":["../../src/browser/login-import.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import type { Logger } from "../util/logger.ts";
|
|
2
|
+
import type { Tool } from "../tools/types.ts";
|
|
3
|
+
import type { BrowserSessionOptions } from "./session.ts";
|
|
4
|
+
import type { BrowserProviderPlugin } from "./provider.ts";
|
|
5
|
+
import type { ExternalSite, ImportedSite } from "./login-import.ts";
|
|
6
|
+
import type { BrowserInputEvent, FrameOptions } from "./target.ts";
|
|
7
|
+
/** Host-facing surface; custom feature plugins need not subclass BrowserSession. */
|
|
8
|
+
export interface BrowserUseSession {
|
|
9
|
+
readonly running: boolean;
|
|
10
|
+
/** Called once when the eager tool façade is assigned to its first conversation. */
|
|
11
|
+
bind(sessionId: string): void;
|
|
12
|
+
state(): {
|
|
13
|
+
running: boolean;
|
|
14
|
+
origin?: string;
|
|
15
|
+
presentation?: "stream" | "native";
|
|
16
|
+
/** The browser in use now. */
|
|
17
|
+
mode?: "builtin" | "extension";
|
|
18
|
+
/** Which browsers the session may use. */
|
|
19
|
+
policy?: "auto" | "builtin" | "extension";
|
|
20
|
+
};
|
|
21
|
+
viewport(): Promise<{
|
|
22
|
+
width: number;
|
|
23
|
+
height: number;
|
|
24
|
+
} | undefined>;
|
|
25
|
+
url(): Promise<string>;
|
|
26
|
+
frames(opts?: FrameOptions): AsyncIterable<Buffer>;
|
|
27
|
+
forward(event: BrowserInputEvent): Promise<void>;
|
|
28
|
+
navigate(url: string): Promise<{
|
|
29
|
+
url: string;
|
|
30
|
+
status?: number;
|
|
31
|
+
}>;
|
|
32
|
+
reload(): Promise<{
|
|
33
|
+
url: string;
|
|
34
|
+
}>;
|
|
35
|
+
/** Tabs visible to the Browser pane. Optional for third-party feature plugins. */
|
|
36
|
+
paneTabs?(): Promise<Array<{
|
|
37
|
+
id: string;
|
|
38
|
+
url: string;
|
|
39
|
+
title?: string;
|
|
40
|
+
active: boolean;
|
|
41
|
+
mine?: false;
|
|
42
|
+
}>>;
|
|
43
|
+
/** Hand one of the person's own tabs to this conversation, at their request. */
|
|
44
|
+
adoptYourTab?(id: string): Promise<{
|
|
45
|
+
id: string;
|
|
46
|
+
url: string;
|
|
47
|
+
}>;
|
|
48
|
+
/** Make a tab the one used by both the tools and the Browser pane. */
|
|
49
|
+
selectPaneTab?(id: string): Promise<{
|
|
50
|
+
id: string;
|
|
51
|
+
url: string;
|
|
52
|
+
}>;
|
|
53
|
+
/** Close one tab, releasing the browser target when it was the last one. */
|
|
54
|
+
closePaneTab?(id: string): Promise<{
|
|
55
|
+
closed: string;
|
|
56
|
+
active?: string;
|
|
57
|
+
running: boolean;
|
|
58
|
+
}>;
|
|
59
|
+
close(): Promise<void>;
|
|
60
|
+
/** Name the conversation this browser works for; see BrowserTargetContext.label. */
|
|
61
|
+
setLabel?(label: () => string | undefined): void;
|
|
62
|
+
/** Move to the other browser; the next call opens it. Optional for third-party feature plugins. */
|
|
63
|
+
setMode?(mode: "auto" | "builtin" | "extension"): Promise<void>;
|
|
64
|
+
/** The external browser and the sites it has open. Lists tabs; attaches to none. */
|
|
65
|
+
loginImportSources?(): Promise<{
|
|
66
|
+
browser: string;
|
|
67
|
+
sites: ExternalSite[];
|
|
68
|
+
current?: string;
|
|
69
|
+
}>;
|
|
70
|
+
/** Person-confirmed only: copy these sites' sign-in into the built-in profile. */
|
|
71
|
+
importLogin?(sites: readonly string[]): Promise<{
|
|
72
|
+
browser: string;
|
|
73
|
+
imported: ImportedSite[];
|
|
74
|
+
migrated: boolean;
|
|
75
|
+
url?: string;
|
|
76
|
+
}>;
|
|
77
|
+
/** A site the built-in browser is on that the external browser has open too. Never blocks. */
|
|
78
|
+
loginImportSuggestion?(): {
|
|
79
|
+
site: string;
|
|
80
|
+
browser: string;
|
|
81
|
+
} | undefined;
|
|
82
|
+
dismissLoginImport?(site: string): Promise<void>;
|
|
83
|
+
}
|
|
84
|
+
export interface BrowserUseSessionContext {
|
|
85
|
+
sessionId?: string;
|
|
86
|
+
mode: "auto" | "builtin" | "extension";
|
|
87
|
+
logger?: Logger;
|
|
88
|
+
label?: () => string | undefined;
|
|
89
|
+
cwd?: string;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Complete project-level Browser Use feature plugin.
|
|
93
|
+
*
|
|
94
|
+
* This is not an agent-installed plugin. The Harness host loads one feature
|
|
95
|
+
* implementation, then asks it for a session and its tools. Keeping these two
|
|
96
|
+
* operations together means another implementation can replace the entire
|
|
97
|
+
* Browser Use stack rather than only swapping the browser executable.
|
|
98
|
+
*/
|
|
99
|
+
export interface BrowserUsePlugin {
|
|
100
|
+
readonly id: string;
|
|
101
|
+
createSession(context: BrowserUseSessionContext): BrowserUseSession;
|
|
102
|
+
tools(session: BrowserUseSession): Tool[];
|
|
103
|
+
}
|
|
104
|
+
export interface DefaultBrowserUsePluginOptions {
|
|
105
|
+
providers?: readonly BrowserProviderPlugin[];
|
|
106
|
+
/** Compatibility seam for older embedders and focused tests. */
|
|
107
|
+
target?: BrowserSessionOptions["target"];
|
|
108
|
+
}
|
|
109
|
+
export declare function createBrowserUsePlugin(opts?: DefaultBrowserUsePluginOptions): BrowserUsePlugin;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { BrowserSession } from "./session.js";
|
|
2
|
+
import { createBrowserTools } from "./tools.js";
|
|
3
|
+
export function createBrowserUsePlugin(opts = {}) {
|
|
4
|
+
return {
|
|
5
|
+
id: "browser-use",
|
|
6
|
+
createSession(context) {
|
|
7
|
+
const session = new BrowserSession({
|
|
8
|
+
...(opts.target ? { target: opts.target } : {}),
|
|
9
|
+
...(opts.providers ? { providers: opts.providers } : {}),
|
|
10
|
+
mode: context.mode,
|
|
11
|
+
...(context.logger ? { logger: context.logger } : {}),
|
|
12
|
+
...(context.label ? { label: context.label } : {}),
|
|
13
|
+
...(context.cwd ? { cwd: context.cwd } : {}),
|
|
14
|
+
});
|
|
15
|
+
if (context.sessionId)
|
|
16
|
+
session.bind(context.sessionId);
|
|
17
|
+
return session;
|
|
18
|
+
},
|
|
19
|
+
tools: (session) => createBrowserTools(session),
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=plugin.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin.js","sourceRoot":"","sources":["../../src/browser/plugin.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAE9C,OAAO,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AA2EhD,MAAM,UAAU,sBAAsB,CAAC,OAAuC,EAAE;IAC9E,OAAO;QACL,EAAE,EAAE,aAAa;QACjB,aAAa,CAAC,OAAO;YACnB,MAAM,OAAO,GAAG,IAAI,cAAc,CAAC;gBACjC,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;gBAC/C,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,IAAI,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;gBACxD,IAAI,EAAE,OAAO,CAAC,IAAI;gBAClB,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;gBACrD,GAAG,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;gBAClD,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;aAC7C,CAAC,CAAC;YACH,IAAI,OAAO,CAAC,SAAS;gBAAE,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;YACvD,OAAO,OAAO,CAAC;QACjB,CAAC;QACD,KAAK,EAAE,CAAC,OAAO,EAAE,EAAE,CAAC,kBAAkB,CAAC,OAAyB,CAAC;KAClE,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Telling the browser extension where this OHarness is.
|
|
3
|
+
*
|
|
4
|
+
* The extension's side panel shows this process's own page, which lives at a
|
|
5
|
+
* port chosen at startup and opens only with a token made for this run. The
|
|
6
|
+
* extension cannot find either on its own, and the token is never written to
|
|
7
|
+
* disk — so a server says where it is, over the same local socket the
|
|
8
|
+
* extension's native host already listens on, and the host tells the
|
|
9
|
+
* extension. Nothing here leaves the machine.
|
|
10
|
+
*
|
|
11
|
+
* The host exists only while the browser is open, and a new one starts each
|
|
12
|
+
* time the extension's worker restarts. So this keeps trying, on a backoff,
|
|
13
|
+
* and registers again with every host it reaches. None of it keeps the
|
|
14
|
+
* process alive: a server that is otherwise done exits whether or not a
|
|
15
|
+
* browser was ever there to hear about it.
|
|
16
|
+
*/
|
|
17
|
+
export interface PresenceInfo {
|
|
18
|
+
/** Where the page is, without the token. */
|
|
19
|
+
url: string;
|
|
20
|
+
token: string;
|
|
21
|
+
/** Which kind of OHarness, so the extension can prefer the desktop app. */
|
|
22
|
+
kind: "desktop" | "web";
|
|
23
|
+
}
|
|
24
|
+
export interface Presence {
|
|
25
|
+
close(): void;
|
|
26
|
+
}
|
|
27
|
+
export declare function announcePresence(info: PresenceInfo, opts?: {
|
|
28
|
+
socketPath?: string;
|
|
29
|
+
}): Presence;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { connect } from "node:net";
|
|
2
|
+
import { extensionSocketPath } from "./socket.js";
|
|
3
|
+
/** Seconds between attempts to reach a host: quick at first, patient after. */
|
|
4
|
+
const BACKOFF = [1, 2, 5, 10, 30];
|
|
5
|
+
export function announcePresence(info, opts = {}) {
|
|
6
|
+
const socketPath = opts.socketPath ?? extensionSocketPath();
|
|
7
|
+
let socket;
|
|
8
|
+
let timer;
|
|
9
|
+
let attempt = 0;
|
|
10
|
+
let closed = false;
|
|
11
|
+
const retry = () => {
|
|
12
|
+
if (closed || timer)
|
|
13
|
+
return;
|
|
14
|
+
const seconds = BACKOFF[Math.min(attempt, BACKOFF.length - 1)];
|
|
15
|
+
attempt += 1;
|
|
16
|
+
timer = setTimeout(() => {
|
|
17
|
+
timer = undefined;
|
|
18
|
+
open();
|
|
19
|
+
}, seconds * 1000);
|
|
20
|
+
timer.unref?.();
|
|
21
|
+
};
|
|
22
|
+
const open = () => {
|
|
23
|
+
if (closed)
|
|
24
|
+
return;
|
|
25
|
+
const current = connect(socketPath);
|
|
26
|
+
socket = current;
|
|
27
|
+
current.unref();
|
|
28
|
+
current.on("connect", () => {
|
|
29
|
+
attempt = 0;
|
|
30
|
+
current.write(`${JSON.stringify({ t: "register", pid: process.pid, ...info })}\n`);
|
|
31
|
+
});
|
|
32
|
+
// Replies and the other harnesses' traffic are none of this connection's
|
|
33
|
+
// business; reading them keeps the socket from backing up.
|
|
34
|
+
current.on("data", () => { });
|
|
35
|
+
current.on("error", () => { });
|
|
36
|
+
current.on("close", () => {
|
|
37
|
+
if (socket === current)
|
|
38
|
+
socket = undefined;
|
|
39
|
+
retry();
|
|
40
|
+
});
|
|
41
|
+
};
|
|
42
|
+
open();
|
|
43
|
+
return {
|
|
44
|
+
close() {
|
|
45
|
+
closed = true;
|
|
46
|
+
if (timer)
|
|
47
|
+
clearTimeout(timer);
|
|
48
|
+
timer = undefined;
|
|
49
|
+
socket?.destroy();
|
|
50
|
+
socket = undefined;
|
|
51
|
+
},
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=presence.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"presence.js","sourceRoot":"","sources":["../../src/browser/presence.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AAEnC,OAAO,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AA+BlD,+EAA+E;AAC/E,MAAM,OAAO,GAAG,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC;AAElC,MAAM,UAAU,gBAAgB,CAAC,IAAkB,EAAE,OAAgC,EAAE;IACrF,MAAM,UAAU,GAAG,IAAI,CAAC,UAAU,IAAI,mBAAmB,EAAE,CAAC;IAC5D,IAAI,MAA0B,CAAC;IAC/B,IAAI,KAAiC,CAAC;IACtC,IAAI,OAAO,GAAG,CAAC,CAAC;IAChB,IAAI,MAAM,GAAG,KAAK,CAAC;IAEnB,MAAM,KAAK,GAAG,GAAS,EAAE;QACvB,IAAI,MAAM,IAAI,KAAK;YAAE,OAAO;QAC5B,MAAM,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAE,CAAC;QAChE,OAAO,IAAI,CAAC,CAAC;QACb,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE;YACtB,KAAK,GAAG,SAAS,CAAC;YAClB,IAAI,EAAE,CAAC;QACT,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC,CAAC;QACnB,KAAK,CAAC,KAAK,EAAE,EAAE,CAAC;IAClB,CAAC,CAAC;IAEF,MAAM,IAAI,GAAG,GAAS,EAAE;QACtB,IAAI,MAAM;YAAE,OAAO;QACnB,MAAM,OAAO,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;QACpC,MAAM,GAAG,OAAO,CAAC;QACjB,OAAO,CAAC,KAAK,EAAE,CAAC;QAChB,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,GAAG,EAAE;YACzB,OAAO,GAAG,CAAC,CAAC;YACZ,OAAO,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC,EAAE,UAAU,EAAE,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE,GAAG,IAAI,EAAE,CAAC,IAAI,CAAC,CAAC;QACrF,CAAC,CAAC,CAAC;QACH,yEAAyE;QACzE,2DAA2D;QAC3D,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;QAC7B,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;QAC9B,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE;YACvB,IAAI,MAAM,KAAK,OAAO;gBAAE,MAAM,GAAG,SAAS,CAAC;YAC3C,KAAK,EAAE,CAAC;QACV,CAAC,CAAC,CAAC;IACL,CAAC,CAAC;IAEF,IAAI,EAAE,CAAC;IACP,OAAO;QACL,KAAK;YACH,MAAM,GAAG,IAAI,CAAC;YACd,IAAI,KAAK;gBAAE,YAAY,CAAC,KAAK,CAAC,CAAC;YAC/B,KAAK,GAAG,SAAS,CAAC;YAClB,MAAM,EAAE,OAAO,EAAE,CAAC;YAClB,MAAM,GAAG,SAAS,CAAC;QACrB,CAAC;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { BrowserTargetFactory } from "./target.ts";
|
|
2
|
+
import type { Logger } from "../util/logger.ts";
|
|
3
|
+
/** A replaceable browser runtime used by the Browser Use feature plugin. */
|
|
4
|
+
export interface BrowserProviderPlugin {
|
|
5
|
+
readonly id: string;
|
|
6
|
+
readonly mode: "builtin" | "extension";
|
|
7
|
+
readonly createTarget: BrowserTargetFactory;
|
|
8
|
+
}
|
|
9
|
+
export declare function builtinBrowserProvider(opts?: {
|
|
10
|
+
headless?: boolean;
|
|
11
|
+
logger?: Logger;
|
|
12
|
+
}): BrowserProviderPlugin;
|
|
13
|
+
export declare function extensionBrowserProvider(): BrowserProviderPlugin;
|
|
14
|
+
export declare function browserProviderFor(mode: "builtin" | "extension", providers?: readonly BrowserProviderPlugin[], opts?: {
|
|
15
|
+
headless?: boolean;
|
|
16
|
+
logger?: Logger;
|
|
17
|
+
}): BrowserProviderPlugin;
|