@anvia/browser 1.0.0-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/LICENSE +21 -0
- package/README.md +106 -0
- package/dist/index.d.ts +131 -0
- package/dist/index.js +1026 -0
- package/dist/index.js.map +1 -0
- package/image/Dockerfile +35 -0
- package/image/anvia-browser-configure +3 -0
- package/image/anvia-browser-start +3 -0
- package/image/anvia-browser-version +3 -0
- package/image/configure.mjs +64 -0
- package/image/start.mjs +147 -0
- package/package.json +54 -0
- package/security/seccomp_profile.json +701 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Indra Zulfi
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
# `@anvia/browser`
|
|
2
|
+
|
|
3
|
+
Visible Chromium ownership and semantic browser tools for Anvia agents. Docker infrastructure remains
|
|
4
|
+
owned by `@anvia/sandbox`; this package owns the browser workload running inside that sandbox.
|
|
5
|
+
|
|
6
|
+
```ts
|
|
7
|
+
import { DockerBrowserClient, createBrowserTools } from "@anvia/browser";
|
|
8
|
+
import { Agent } from "@anvia/core/agent";
|
|
9
|
+
import { DockerSandboxClient } from "@anvia/sandbox";
|
|
10
|
+
|
|
11
|
+
const browserClient = new DockerBrowserClient({
|
|
12
|
+
sandboxClient: new DockerSandboxClient(),
|
|
13
|
+
image: "ghcr.io/anvia-hq/browser@sha256:...",
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
await browserClient.pullImage();
|
|
17
|
+
await using browser = await browserClient.createBrowser({
|
|
18
|
+
workspace: { type: "ephemeral" },
|
|
19
|
+
network: { mode: "bridge" },
|
|
20
|
+
desktop: {
|
|
21
|
+
protocol: "novnc",
|
|
22
|
+
password: "passw0rd",
|
|
23
|
+
viewport: { width: 1440, height: 900 },
|
|
24
|
+
},
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
await browser.waitUntilReady({ timeoutMs: 30_000 });
|
|
28
|
+
await using connection = await browser.connect();
|
|
29
|
+
|
|
30
|
+
const tools = createBrowserTools({
|
|
31
|
+
connection,
|
|
32
|
+
tools: [
|
|
33
|
+
"browser_list_tabs",
|
|
34
|
+
"browser_open_tab",
|
|
35
|
+
"browser_select_tab",
|
|
36
|
+
"browser_close_tab",
|
|
37
|
+
"browser_navigate",
|
|
38
|
+
"browser_snapshot",
|
|
39
|
+
"browser_click",
|
|
40
|
+
"browser_type",
|
|
41
|
+
"browser_press_key",
|
|
42
|
+
"browser_screenshot",
|
|
43
|
+
],
|
|
44
|
+
navigation: { mode: "allow-all-http" },
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
const agent = new Agent({ id: "browser-agent", model, tools });
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
The client constructor performs no I/O. `pullImage()`, `createBrowser()`, readiness, and CDP
|
|
51
|
+
connection are separate operations. `DockerBrowser` owns the underlying sandbox. A
|
|
52
|
+
`PlaywrightBrowserConnection` owns only its CDP connection and never destroys the browser.
|
|
53
|
+
|
|
54
|
+
`stop()` preserves the container. `resumeBrowser({ id })` starts a fresh browser service and requires
|
|
55
|
+
a new readiness check and CDP connection. A named Docker volume preserves Chromium profile state even
|
|
56
|
+
when the browser container is destroyed and later recreated with that volume.
|
|
57
|
+
|
|
58
|
+
The browser tools use ARIA state and strict Playwright locators. They do not expose JavaScript
|
|
59
|
+
evaluation, raw CDP, coordinate input, shell access, hidden retries, or automatic reconnection.
|
|
60
|
+
Aborting an action that Playwright cannot cancel closes the CDP connection and leaves the browser
|
|
61
|
+
running. The selected navigation policy is installed across the connection, so top-level navigation
|
|
62
|
+
from links, forms, redirects, popups, and direct navigation is checked consistently. It does not block
|
|
63
|
+
third-party subresources; Docker bridge networking remains outside that policy.
|
|
64
|
+
|
|
65
|
+
The image runs Chromium as a non-root user with Chromium sandboxing, the pinned Playwright seccomp
|
|
66
|
+
profile, every Linux capability dropped except the explicit `SYS_CHROOT` capability required by the
|
|
67
|
+
namespace sandbox, no-new-privileges, and private shared memory. Startup fails rather than silently
|
|
68
|
+
switching Chromium to `--no-sandbox`. Docker bridge networking is not an SSRF boundary; use
|
|
69
|
+
infrastructure network policy where browsing untrusted destinations requires isolation.
|
|
70
|
+
|
|
71
|
+
The VNC protocol uses exactly eight printable ASCII password characters. noVNC is published only on a
|
|
72
|
+
host-loopback Docker port, raw VNC is not published, and the password is not placed in image metadata,
|
|
73
|
+
URLs, environment variables, or logs.
|
|
74
|
+
|
|
75
|
+
## Studio desktop and takeover
|
|
76
|
+
|
|
77
|
+
`browser.desktop` is structurally compatible with Studio without creating a package dependency:
|
|
78
|
+
|
|
79
|
+
```ts
|
|
80
|
+
const studio = new Studio([agent], {
|
|
81
|
+
sandboxes: [
|
|
82
|
+
{
|
|
83
|
+
inspector: browser.inspector({ files: true, ports: true, processes: true }),
|
|
84
|
+
agentIds: [agent.id],
|
|
85
|
+
toolNames: tools.map((tool) => tool.name),
|
|
86
|
+
views: [
|
|
87
|
+
{
|
|
88
|
+
id: "desktop",
|
|
89
|
+
label: "Browser",
|
|
90
|
+
source: browser.desktop,
|
|
91
|
+
access: { mode: "local" },
|
|
92
|
+
authentication: { type: "password", password },
|
|
93
|
+
},
|
|
94
|
+
],
|
|
95
|
+
},
|
|
96
|
+
],
|
|
97
|
+
});
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
Use `{ mode: "authorize", authorize }` when Studio is reachable remotely. The callback is invoked for
|
|
101
|
+
the viewer connection, WebSocket upgrade, and every control operation. When the registered agent uses a
|
|
102
|
+
matching browser tool, Studio opens its clean programmatic noVNC viewer in a resizable Playground panel;
|
|
103
|
+
there is no stock noVNC toolbar, splash, or password prompt. Closing the panel restores Sessions and an
|
|
104
|
+
**Open browser** action restores the current desktop. Studio human takeover waits for an active agent
|
|
105
|
+
action, blocks new browser tool actions, and expires unless the Studio viewer renews its lease. Takeover
|
|
106
|
+
coordinates trusted viewers; application authorization remains the security boundary.
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import { DockerSandboxWorkspace, DockerSandboxResources, DockerSandboxRuntimeLimits, DockerSandboxState, DockerSandbox, DockerSandboxInspectionOptions, DockerSandboxInspector, DockerSandboxClient } from '@anvia/sandbox';
|
|
2
|
+
import { AnyTool } from '@anvia/core/tool';
|
|
3
|
+
|
|
4
|
+
type DockerBrowserClientOptions = Readonly<{
|
|
5
|
+
sandboxClient: DockerSandboxClient;
|
|
6
|
+
image: string;
|
|
7
|
+
}>;
|
|
8
|
+
type PullDockerBrowserImageOptions = Readonly<{
|
|
9
|
+
abortSignal?: AbortSignal;
|
|
10
|
+
}>;
|
|
11
|
+
type BrowserViewport = Readonly<{
|
|
12
|
+
width: number;
|
|
13
|
+
height: number;
|
|
14
|
+
}>;
|
|
15
|
+
type BrowserDesktopOptions = Readonly<{
|
|
16
|
+
protocol: "novnc";
|
|
17
|
+
password: string;
|
|
18
|
+
viewport: BrowserViewport;
|
|
19
|
+
}>;
|
|
20
|
+
type CreateDockerBrowserOptions = Readonly<{
|
|
21
|
+
id?: string;
|
|
22
|
+
workspace: DockerSandboxWorkspace;
|
|
23
|
+
network: Readonly<{
|
|
24
|
+
mode: "bridge";
|
|
25
|
+
}>;
|
|
26
|
+
desktop: BrowserDesktopOptions;
|
|
27
|
+
resources?: DockerSandboxResources;
|
|
28
|
+
runtime?: DockerSandboxRuntimeLimits;
|
|
29
|
+
abortSignal?: AbortSignal;
|
|
30
|
+
}>;
|
|
31
|
+
type ResumeDockerBrowserOptions = Readonly<{
|
|
32
|
+
id: string;
|
|
33
|
+
abortSignal?: AbortSignal;
|
|
34
|
+
}>;
|
|
35
|
+
type BrowserWaitUntilReadyOptions = Readonly<{
|
|
36
|
+
timeoutMs: number;
|
|
37
|
+
abortSignal?: AbortSignal;
|
|
38
|
+
}>;
|
|
39
|
+
type BrowserConnectOptions = Readonly<{
|
|
40
|
+
abortSignal?: AbortSignal;
|
|
41
|
+
}>;
|
|
42
|
+
type BrowserControlSnapshot = Readonly<{
|
|
43
|
+
mode: "agent" | "human";
|
|
44
|
+
ownerId?: string;
|
|
45
|
+
expiresAt?: string;
|
|
46
|
+
}>;
|
|
47
|
+
type AcquireBrowserHumanControlOptions = Readonly<{
|
|
48
|
+
ownerId: string;
|
|
49
|
+
leaseTimeoutMs: number;
|
|
50
|
+
abortSignal?: AbortSignal;
|
|
51
|
+
}>;
|
|
52
|
+
type RenewBrowserHumanControlOptions = Readonly<{
|
|
53
|
+
leaseTimeoutMs: number;
|
|
54
|
+
}>;
|
|
55
|
+
interface BrowserHumanControlLease extends AsyncDisposable {
|
|
56
|
+
readonly id: string;
|
|
57
|
+
readonly ownerId: string;
|
|
58
|
+
readonly expiresAt: string;
|
|
59
|
+
renew(options: RenewBrowserHumanControlOptions): BrowserControlSnapshot;
|
|
60
|
+
release(): void;
|
|
61
|
+
}
|
|
62
|
+
interface BrowserControl {
|
|
63
|
+
snapshot(): BrowserControlSnapshot;
|
|
64
|
+
acquireHumanControl(options: AcquireBrowserHumanControlOptions): Promise<BrowserHumanControlLease>;
|
|
65
|
+
}
|
|
66
|
+
type BrowserDesktopEndpoint = Readonly<{
|
|
67
|
+
protocol: "novnc";
|
|
68
|
+
containerPort: 6080;
|
|
69
|
+
control: BrowserControl;
|
|
70
|
+
}>;
|
|
71
|
+
interface DockerBrowser extends AsyncDisposable {
|
|
72
|
+
readonly id: string;
|
|
73
|
+
readonly state: DockerSandboxState;
|
|
74
|
+
readonly desktop: BrowserDesktopEndpoint;
|
|
75
|
+
readonly sandbox: DockerSandbox;
|
|
76
|
+
inspector(options: DockerSandboxInspectionOptions): DockerSandboxInspector;
|
|
77
|
+
waitUntilReady(options: BrowserWaitUntilReadyOptions): Promise<void>;
|
|
78
|
+
connect(options?: BrowserConnectOptions): Promise<PlaywrightBrowserConnection>;
|
|
79
|
+
stop(options?: Readonly<{
|
|
80
|
+
abortSignal?: AbortSignal;
|
|
81
|
+
}>): Promise<void>;
|
|
82
|
+
destroy(): Promise<void>;
|
|
83
|
+
}
|
|
84
|
+
type BrowserTab = Readonly<{
|
|
85
|
+
id: string;
|
|
86
|
+
title: string;
|
|
87
|
+
url: string;
|
|
88
|
+
selected: boolean;
|
|
89
|
+
}>;
|
|
90
|
+
interface PlaywrightBrowserConnection extends AsyncDisposable {
|
|
91
|
+
readonly closed: boolean;
|
|
92
|
+
listTabs(): Promise<readonly BrowserTab[]>;
|
|
93
|
+
disconnect(): Promise<void>;
|
|
94
|
+
}
|
|
95
|
+
type BrowserToolName = "browser_list_tabs" | "browser_open_tab" | "browser_select_tab" | "browser_close_tab" | "browser_navigate" | "browser_snapshot" | "browser_click" | "browser_type" | "browser_press_key" | "browser_screenshot";
|
|
96
|
+
type BrowserNavigationPolicy = Readonly<{
|
|
97
|
+
mode: "allow-all-http";
|
|
98
|
+
}> | Readonly<{
|
|
99
|
+
mode: "origins";
|
|
100
|
+
origins: readonly string[];
|
|
101
|
+
}>;
|
|
102
|
+
type BrowserToolLimits = Readonly<{
|
|
103
|
+
actionTimeoutMs?: number;
|
|
104
|
+
navigationTimeoutMs?: number;
|
|
105
|
+
snapshotMaxChars?: number;
|
|
106
|
+
}>;
|
|
107
|
+
type CreateBrowserToolsOptions = Readonly<{
|
|
108
|
+
connection: PlaywrightBrowserConnection;
|
|
109
|
+
tools: readonly [BrowserToolName, ...BrowserToolName[]];
|
|
110
|
+
navigation: BrowserNavigationPolicy;
|
|
111
|
+
limits?: BrowserToolLimits;
|
|
112
|
+
}>;
|
|
113
|
+
|
|
114
|
+
declare class DockerBrowserClient {
|
|
115
|
+
private readonly sandboxClient;
|
|
116
|
+
private readonly image;
|
|
117
|
+
constructor(options: DockerBrowserClientOptions);
|
|
118
|
+
pullImage(options?: PullDockerBrowserImageOptions): Promise<void>;
|
|
119
|
+
createBrowser(options: CreateDockerBrowserOptions): Promise<DockerBrowser>;
|
|
120
|
+
resumeBrowser(options: ResumeDockerBrowserOptions): Promise<DockerBrowser>;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
type BrowserErrorCode = "connection_closed" | "human_controlled" | "invalid_state" | "navigation_blocked" | "not_ready" | "startup_failed";
|
|
124
|
+
declare class BrowserError extends Error {
|
|
125
|
+
readonly code: BrowserErrorCode;
|
|
126
|
+
constructor(message: string, code: BrowserErrorCode, options?: ErrorOptions);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
declare function createBrowserTools(options: CreateBrowserToolsOptions): readonly AnyTool[];
|
|
130
|
+
|
|
131
|
+
export { type AcquireBrowserHumanControlOptions, type BrowserConnectOptions, type BrowserControl, type BrowserControlSnapshot, type BrowserDesktopEndpoint, type BrowserDesktopOptions, BrowserError, type BrowserErrorCode, type BrowserHumanControlLease, type BrowserNavigationPolicy, type BrowserTab, type BrowserToolLimits, type BrowserToolName, type BrowserViewport, type BrowserWaitUntilReadyOptions, type CreateBrowserToolsOptions, type CreateDockerBrowserOptions, type DockerBrowser, DockerBrowserClient, type DockerBrowserClientOptions, type PlaywrightBrowserConnection, type PullDockerBrowserImageOptions, type RenewBrowserHumanControlOptions, type ResumeDockerBrowserOptions, createBrowserTools };
|