chromiumfish 0.2.2 → 0.2.3
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 +8 -0
- package/dist/agent.d.ts +16 -1
- package/dist/agent.js +1 -1
- package/dist/cli.js +40 -0
- package/dist/mcp.d.ts +33 -0
- package/dist/mcp.js +209 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +5 -1
package/README.md
CHANGED
|
@@ -95,6 +95,12 @@ npx chromiumfish serve --persona-seed alice # -> http://127.0.0.1:9222
|
|
|
95
95
|
# e.g. Hermes ~/.hermes/config.yaml: browser: { cdp_url: "http://127.0.0.1:9222" }
|
|
96
96
|
```
|
|
97
97
|
|
|
98
|
+
Or run it as an **MCP server** for Claude Code/Desktop, Cursor, etc.:
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
npx chromiumfish mcp --persona-seed alice # exposes browser tools over MCP (stdio)
|
|
102
|
+
```
|
|
103
|
+
|
|
98
104
|
Full guide: [chromiumfish.com/agents](https://chromiumfish.com/agents).
|
|
99
105
|
|
|
100
106
|
## CLI
|
|
@@ -105,6 +111,8 @@ npx chromiumfish path # print binary path
|
|
|
105
111
|
npx chromiumfish serve [--port 9222] [--persona-seed S] # CDP endpoint for external agents
|
|
106
112
|
[--proxy URL] [--window-size WxH] [--timezone Z] [--headless]
|
|
107
113
|
[--browser-version X] [--extra-args ARGS] [--timeout S]
|
|
114
|
+
npx chromiumfish mcp [--persona-seed S] [--headed] # MCP server (Claude, Cursor, ...)
|
|
115
|
+
[--proxy URL] [--window-size WxH] [--port N] [--typing T] [--llm-key K]
|
|
108
116
|
npx chromiumfish clear # wipe the cache
|
|
109
117
|
npx chromiumfish --version
|
|
110
118
|
```
|
package/dist/agent.d.ts
CHANGED
|
@@ -24,6 +24,18 @@ export declare class AgentResult {
|
|
|
24
24
|
get recorded(): number;
|
|
25
25
|
summary(): string;
|
|
26
26
|
}
|
|
27
|
+
/** Minimal CDP-over-WebSocket client. */
|
|
28
|
+
export declare class CDP {
|
|
29
|
+
private ws;
|
|
30
|
+
private id;
|
|
31
|
+
private pending;
|
|
32
|
+
private waiters;
|
|
33
|
+
private constructor();
|
|
34
|
+
static connect(url: string): Promise<CDP>;
|
|
35
|
+
send(method: string, params?: Record<string, unknown>, timeoutMs?: number): Promise<any>;
|
|
36
|
+
waitEvent(method: string, timeoutMs: number): Promise<void>;
|
|
37
|
+
close(): void;
|
|
38
|
+
}
|
|
27
39
|
export interface RunTaskOptions {
|
|
28
40
|
/** Navigate here before the agent loop (the agent can also navigate itself). */
|
|
29
41
|
url?: string;
|
|
@@ -44,7 +56,10 @@ export declare class AgentClient {
|
|
|
44
56
|
constructor(port?: number, host?: string, timeoutMs?: number);
|
|
45
57
|
private httpGet;
|
|
46
58
|
/** Return {targetId, wsUrl}, reusing a real page or opening one. */
|
|
47
|
-
|
|
59
|
+
pickPage(): Promise<{
|
|
60
|
+
targetId: string;
|
|
61
|
+
wsUrl: string;
|
|
62
|
+
}>;
|
|
48
63
|
runTask(goal: string, opts?: RunTaskOptions): Promise<AgentResult>;
|
|
49
64
|
}
|
|
50
65
|
export interface LaunchAgentOptions {
|
package/dist/agent.js
CHANGED
package/dist/cli.js
CHANGED
|
@@ -144,6 +144,42 @@ async function serve(argv) {
|
|
|
144
144
|
cleanup();
|
|
145
145
|
}
|
|
146
146
|
}
|
|
147
|
+
/**
|
|
148
|
+
* Run the MCP server exposing the browser to MCP clients (Claude, Cursor, …).
|
|
149
|
+
* Stdio is the JSON-RPC channel, so this path must never write to stdout.
|
|
150
|
+
*/
|
|
151
|
+
async function runMcp(argv) {
|
|
152
|
+
const ws = (flag(argv, "--window-size") ?? "1920x1080").toLowerCase().split("x").map(Number);
|
|
153
|
+
if (!ws[0] || !ws[1]) {
|
|
154
|
+
console.error(`invalid --window-size; expected WIDTHxHEIGHT, e.g. 1920x1080`);
|
|
155
|
+
return 1;
|
|
156
|
+
}
|
|
157
|
+
const port = Number(flag(argv, "--port") ?? 9222);
|
|
158
|
+
if (!Number.isInteger(port) || port < 1 || port > 65535) {
|
|
159
|
+
console.error(`invalid --port; expected an integer 1-65535`);
|
|
160
|
+
return 1;
|
|
161
|
+
}
|
|
162
|
+
let mod;
|
|
163
|
+
try {
|
|
164
|
+
mod = await import("./mcp.js");
|
|
165
|
+
}
|
|
166
|
+
catch {
|
|
167
|
+
console.error("the MCP server needs @modelcontextprotocol/sdk + zod; run `npm i @modelcontextprotocol/sdk zod`");
|
|
168
|
+
return 1;
|
|
169
|
+
}
|
|
170
|
+
await mod.runServer({
|
|
171
|
+
personaSeed: flag(argv, "--persona-seed"),
|
|
172
|
+
headless: !argv.includes("--headed"),
|
|
173
|
+
windowSize: [ws[0], ws[1]],
|
|
174
|
+
proxy: flag(argv, "--proxy"),
|
|
175
|
+
port,
|
|
176
|
+
typing: flag(argv, "--typing") ?? "human",
|
|
177
|
+
apiKey: flag(argv, "--llm-key") ?? "",
|
|
178
|
+
apiBase: flag(argv, "--llm-base") ?? "",
|
|
179
|
+
model: flag(argv, "--llm-model") ?? "",
|
|
180
|
+
});
|
|
181
|
+
return 0;
|
|
182
|
+
}
|
|
147
183
|
async function main(argv) {
|
|
148
184
|
const cmd = argv[2];
|
|
149
185
|
switch (cmd) {
|
|
@@ -170,6 +206,8 @@ async function main(argv) {
|
|
|
170
206
|
}
|
|
171
207
|
case "serve":
|
|
172
208
|
return await serve(argv);
|
|
209
|
+
case "mcp":
|
|
210
|
+
return await runMcp(argv);
|
|
173
211
|
case "--version":
|
|
174
212
|
case "-V":
|
|
175
213
|
console.log(`chromiumfish ${SDK_VERSION} (browser ${browserVersion()})`);
|
|
@@ -184,6 +222,8 @@ async function main(argv) {
|
|
|
184
222
|
" chromiumfish serve [--port 9222] [--persona-seed S] CDP endpoint for agents",
|
|
185
223
|
" [--proxy URL] [--window-size WxH] [--timezone Z] [--headless]",
|
|
186
224
|
" [--browser-version X] [--extra-args ARGS] [--timeout S]",
|
|
225
|
+
" chromiumfish mcp [--persona-seed S] [--headed] MCP server (Claude, Cursor, ...)",
|
|
226
|
+
" [--proxy URL] [--window-size WxH] [--port N] [--typing T] [--llm-key K]",
|
|
187
227
|
" chromiumfish clear wipe the cache",
|
|
188
228
|
" chromiumfish --version",
|
|
189
229
|
].join("\n"));
|
package/dist/mcp.d.ts
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ChromiumFish MCP server — drive the stealth browser from any MCP client.
|
|
3
|
+
*
|
|
4
|
+
* Exposes ChromiumFish as a Model Context Protocol (https://modelcontextprotocol.io)
|
|
5
|
+
* server so MCP-speaking agents (Claude Code/Desktop, Cursor, Windsurf, …) can
|
|
6
|
+
* perceive and operate the hardened browser directly. Tools are driven over the
|
|
7
|
+
* Chrome DevTools Protocol against a ChromiumFish instance the server launches and
|
|
8
|
+
* holds for its lifetime — the persona/proxy/timezone you start it with stay
|
|
9
|
+
* active, so the agent operates a browser that already looks like a real person.
|
|
10
|
+
*
|
|
11
|
+
* npx chromiumfish mcp --persona-seed alice
|
|
12
|
+
*
|
|
13
|
+
* The granular tools (navigate/snapshot/get_text/screenshot/click/type_text/eval_js)
|
|
14
|
+
* need no LLM on the server side — the MCP client is the brain. `run_task` delegates
|
|
15
|
+
* a whole plain-language goal to the fork's native in-browser agent, which needs an
|
|
16
|
+
* OpenAI-compatible LLM (OPENAI_API_* / --llm-*). TypeScript port of `chromiumfish.mcp`.
|
|
17
|
+
*/
|
|
18
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
19
|
+
import { type TypingSpeed } from "./agent.js";
|
|
20
|
+
export interface McpConfig {
|
|
21
|
+
personaSeed?: string;
|
|
22
|
+
headless?: boolean;
|
|
23
|
+
windowSize?: [number, number];
|
|
24
|
+
proxy?: string;
|
|
25
|
+
port?: number;
|
|
26
|
+
typing?: TypingSpeed;
|
|
27
|
+
apiKey?: string;
|
|
28
|
+
apiBase?: string;
|
|
29
|
+
model?: string;
|
|
30
|
+
}
|
|
31
|
+
export declare function buildServer(): McpServer;
|
|
32
|
+
/** Configure and run the MCP server over stdio (blocks until the client disconnects). */
|
|
33
|
+
export declare function runServer(config?: McpConfig): Promise<void>;
|
package/dist/mcp.js
ADDED
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ChromiumFish MCP server — drive the stealth browser from any MCP client.
|
|
3
|
+
*
|
|
4
|
+
* Exposes ChromiumFish as a Model Context Protocol (https://modelcontextprotocol.io)
|
|
5
|
+
* server so MCP-speaking agents (Claude Code/Desktop, Cursor, Windsurf, …) can
|
|
6
|
+
* perceive and operate the hardened browser directly. Tools are driven over the
|
|
7
|
+
* Chrome DevTools Protocol against a ChromiumFish instance the server launches and
|
|
8
|
+
* holds for its lifetime — the persona/proxy/timezone you start it with stay
|
|
9
|
+
* active, so the agent operates a browser that already looks like a real person.
|
|
10
|
+
*
|
|
11
|
+
* npx chromiumfish mcp --persona-seed alice
|
|
12
|
+
*
|
|
13
|
+
* The granular tools (navigate/snapshot/get_text/screenshot/click/type_text/eval_js)
|
|
14
|
+
* need no LLM on the server side — the MCP client is the brain. `run_task` delegates
|
|
15
|
+
* a whole plain-language goal to the fork's native in-browser agent, which needs an
|
|
16
|
+
* OpenAI-compatible LLM (OPENAI_API_* / --llm-*). TypeScript port of `chromiumfish.mcp`.
|
|
17
|
+
*/
|
|
18
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
19
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
20
|
+
import { z } from "zod";
|
|
21
|
+
import { launchAgent, CDP } from "./agent.js";
|
|
22
|
+
import { SDK_VERSION } from "./version.js";
|
|
23
|
+
// Builds a readable, selector-addressable list of interactive elements. The raw
|
|
24
|
+
// `Page.getAnnotatedPageContent` CDP command returns the index-keyed proto the
|
|
25
|
+
// native agent consumes — not usable directly and not selector-addressable — so
|
|
26
|
+
// we perceive via the DOM. The selectors it emits are what click/type_text take.
|
|
27
|
+
const SNAPSHOT_JS = String.raw `
|
|
28
|
+
(function(){
|
|
29
|
+
function sel(el){
|
|
30
|
+
if (el.id) return '#'+CSS.escape(el.id);
|
|
31
|
+
var nm = el.getAttribute('name');
|
|
32
|
+
if (nm) return el.tagName.toLowerCase()+'[name="'+nm+'"]';
|
|
33
|
+
var path=[], e=el;
|
|
34
|
+
while(e && e.nodeType===1 && path.length<4){
|
|
35
|
+
var part=e.tagName.toLowerCase();
|
|
36
|
+
if(e.parentElement){
|
|
37
|
+
var sib=Array.prototype.filter.call(e.parentElement.children,function(c){return c.tagName===e.tagName;});
|
|
38
|
+
if(sib.length>1) part+=':nth-of-type('+(sib.indexOf(e)+1)+')';
|
|
39
|
+
}
|
|
40
|
+
path.unshift(part); e=e.parentElement;
|
|
41
|
+
}
|
|
42
|
+
return path.join(' > ');
|
|
43
|
+
}
|
|
44
|
+
function label(el){
|
|
45
|
+
return (el.getAttribute('aria-label')||el.value||el.placeholder||el.innerText||el.getAttribute('title')||'')
|
|
46
|
+
.trim().replace(/\s+/g,' ').slice(0,80);
|
|
47
|
+
}
|
|
48
|
+
var els=document.querySelectorAll('a,button,input,textarea,select,[role=button],[role=link],[onclick],[contenteditable=true]');
|
|
49
|
+
var out=[], n=0;
|
|
50
|
+
for(var i=0;i<els.length && n<200;i++){
|
|
51
|
+
var el=els[i];
|
|
52
|
+
if(!el.getClientRects().length) continue;
|
|
53
|
+
var role=el.tagName.toLowerCase()+(el.type?'['+el.type+']':'');
|
|
54
|
+
var line='['+n+'] '+role+' "'+label(el)+'" '+sel(el);
|
|
55
|
+
if(el.href) line+=' -> '+el.href;
|
|
56
|
+
out.push(line); n++;
|
|
57
|
+
}
|
|
58
|
+
return out.length ? out.join('\n') : '(no visible interactive elements)';
|
|
59
|
+
})()
|
|
60
|
+
`;
|
|
61
|
+
let _config = {};
|
|
62
|
+
let _session = null;
|
|
63
|
+
async function client() {
|
|
64
|
+
if (!_session) {
|
|
65
|
+
const extra = [];
|
|
66
|
+
if (_config.personaSeed)
|
|
67
|
+
extra.push(`--persona-seed=${_config.personaSeed}`);
|
|
68
|
+
if (_config.headless ?? true)
|
|
69
|
+
extra.push("--headless=new");
|
|
70
|
+
const [w, h] = _config.windowSize ?? [1920, 1080];
|
|
71
|
+
extra.push(`--window-size=${w},${h}`);
|
|
72
|
+
if (_config.proxy)
|
|
73
|
+
extra.push(`--proxy-server=${_config.proxy}`);
|
|
74
|
+
_session = await launchAgent({
|
|
75
|
+
port: _config.port ?? 9222,
|
|
76
|
+
apiKey: _config.apiKey ?? "",
|
|
77
|
+
apiBase: _config.apiBase ?? "",
|
|
78
|
+
model: _config.model ?? "",
|
|
79
|
+
typing: _config.typing ?? "human",
|
|
80
|
+
extraArgs: extra,
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
return _session.agent;
|
|
84
|
+
}
|
|
85
|
+
async function shutdown() {
|
|
86
|
+
if (_session) {
|
|
87
|
+
try {
|
|
88
|
+
await _session.close();
|
|
89
|
+
}
|
|
90
|
+
catch {
|
|
91
|
+
/* ignore */
|
|
92
|
+
}
|
|
93
|
+
_session = null;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
/** Run `fn` against a short-lived CDP connection to the active page target. */
|
|
97
|
+
async function withPage(fn) {
|
|
98
|
+
const agent = await client();
|
|
99
|
+
const { targetId, wsUrl } = await agent.pickPage();
|
|
100
|
+
const cdp = await CDP.connect(wsUrl);
|
|
101
|
+
try {
|
|
102
|
+
return await fn(cdp, targetId);
|
|
103
|
+
}
|
|
104
|
+
finally {
|
|
105
|
+
cdp.close();
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
async function evalJs(cdp, expression) {
|
|
109
|
+
const res = await cdp.send("Runtime.evaluate", { expression, returnByValue: true, awaitPromise: true });
|
|
110
|
+
if (res.exceptionDetails) {
|
|
111
|
+
throw new Error(res.exceptionDetails.exception?.description || res.exceptionDetails.text || "eval error");
|
|
112
|
+
}
|
|
113
|
+
return res.result?.value;
|
|
114
|
+
}
|
|
115
|
+
const textResult = (text) => ({ content: [{ type: "text", text }] });
|
|
116
|
+
export function buildServer() {
|
|
117
|
+
const mcp = new McpServer({ name: "chromiumfish", version: SDK_VERSION });
|
|
118
|
+
mcp.registerTool("navigate", {
|
|
119
|
+
description: "Open a URL in the ChromiumFish browser and wait for it to load. Returns the resolved URL and title; call `snapshot` next to see what's on the page.",
|
|
120
|
+
inputSchema: { url: z.string().describe("The URL to open") },
|
|
121
|
+
}, async ({ url }) => textResult(await withPage(async (cdp) => {
|
|
122
|
+
await cdp.send("Page.enable");
|
|
123
|
+
await cdp.send("Page.navigate", { url });
|
|
124
|
+
await cdp.waitEvent("Page.loadEventFired", 30_000).catch(() => { });
|
|
125
|
+
const title = await evalJs(cdp, "document.title");
|
|
126
|
+
const href = await evalJs(cdp, "document.location.href");
|
|
127
|
+
return `Loaded ${href}\nTitle: ${title}`;
|
|
128
|
+
})));
|
|
129
|
+
mcp.registerTool("snapshot", {
|
|
130
|
+
description: 'List the page\'s visible interactive elements, one per line, as `[i] <role> "label" <css-selector>` (links also show `-> url`). Use the CSS selector with click/type_text. For prose use get_text; for anything else, eval_js.',
|
|
131
|
+
inputSchema: {},
|
|
132
|
+
}, async () => textResult((await withPage((cdp) => evalJs(cdp, SNAPSHOT_JS))) || "(no visible interactive elements)"));
|
|
133
|
+
mcp.registerTool("get_text", {
|
|
134
|
+
description: "Return the visible text of the current page (`document.body.innerText`). Best for reading articles/long content.",
|
|
135
|
+
inputSchema: {},
|
|
136
|
+
}, async () => textResult((await withPage((cdp) => evalJs(cdp, "document.body && document.body.innerText || ''"))) || "(empty)"));
|
|
137
|
+
mcp.registerTool("screenshot", { description: "Capture a PNG screenshot of the current viewport.", inputSchema: {} }, async () => {
|
|
138
|
+
const data = (await withPage(async (cdp) => (await cdp.send("Page.captureScreenshot", { format: "png" })).data));
|
|
139
|
+
return { content: [{ type: "image", data, mimeType: "image/png" }] };
|
|
140
|
+
});
|
|
141
|
+
mcp.registerTool("click", {
|
|
142
|
+
description: "Humanized, trusted click on the first element matching a CSS selector (cursor moves along a bezier path; `navigator.webdriver` stays false). Fails if nothing matches.",
|
|
143
|
+
inputSchema: { selector: z.string().describe("CSS selector of the element to click") },
|
|
144
|
+
}, async ({ selector }) => textResult(await withPage(async (cdp, targetId) => {
|
|
145
|
+
const r = (await cdp.send("Browser.humanizedClickSelector", { targetId, selector })) ?? {};
|
|
146
|
+
return `Clicked ${JSON.stringify(selector)} at (${r.x}, ${r.y})`;
|
|
147
|
+
})));
|
|
148
|
+
mcp.registerTool("type_text", {
|
|
149
|
+
description: "Click the element matching `selector` to focus it, type `text`, and optionally press Enter (`submit=true`) to submit a search/form.",
|
|
150
|
+
inputSchema: {
|
|
151
|
+
selector: z.string().describe("CSS selector of the field"),
|
|
152
|
+
text: z.string().describe("Text to type"),
|
|
153
|
+
submit: z.boolean().optional().describe("Press Enter after typing"),
|
|
154
|
+
},
|
|
155
|
+
}, async ({ selector, text, submit }) => textResult(await withPage(async (cdp, targetId) => {
|
|
156
|
+
await cdp.send("Browser.humanizedClickSelector", { targetId, selector });
|
|
157
|
+
await cdp.send("Input.insertText", { text });
|
|
158
|
+
if (submit) {
|
|
159
|
+
for (const type of ["keyDown", "keyUp"]) {
|
|
160
|
+
await cdp.send("Input.dispatchKeyEvent", { type, key: "Enter", windowsVirtualKeyCode: 13, nativeVirtualKeyCode: 13 });
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
return `Typed into ${JSON.stringify(selector)}${submit ? " and pressed Enter" : ""}`;
|
|
164
|
+
})));
|
|
165
|
+
mcp.registerTool("eval_js", {
|
|
166
|
+
description: "Evaluate a JavaScript expression in the page and return its (JSON) result. Powerful escape hatch — read anything or act on the page.",
|
|
167
|
+
inputSchema: { expression: z.string().describe("JavaScript expression to evaluate") },
|
|
168
|
+
}, async ({ expression }) => {
|
|
169
|
+
const value = await withPage((cdp) => evalJs(cdp, expression));
|
|
170
|
+
let text;
|
|
171
|
+
try {
|
|
172
|
+
text = JSON.stringify(value);
|
|
173
|
+
}
|
|
174
|
+
catch {
|
|
175
|
+
text = String(value);
|
|
176
|
+
}
|
|
177
|
+
return textResult(text ?? "undefined");
|
|
178
|
+
});
|
|
179
|
+
mcp.registerTool("run_task", {
|
|
180
|
+
description: "Delegate a whole plain-language goal to ChromiumFish's native in-browser agent (perceive → think → act loop, humanized input). Best for multi-step flows. Requires an OpenAI-compatible LLM on the server (OPENAI_API_* / --llm-*); otherwise use the granular tools.",
|
|
181
|
+
inputSchema: { task: z.string().describe("Plain-language goal"), url: z.string().optional().describe("Page to start on") },
|
|
182
|
+
}, async ({ task, url }) => {
|
|
183
|
+
const agent = await client();
|
|
184
|
+
const r = await agent.runTask(task, { url: url || undefined });
|
|
185
|
+
const text = r.finalText || (r.success ? "(done)" : "Task did not complete. (If this needs the native agent, ensure an LLM is configured on the MCP server.)");
|
|
186
|
+
return textResult(text);
|
|
187
|
+
});
|
|
188
|
+
return mcp;
|
|
189
|
+
}
|
|
190
|
+
/** Configure and run the MCP server over stdio (blocks until the client disconnects). */
|
|
191
|
+
export async function runServer(config = {}) {
|
|
192
|
+
_config = config;
|
|
193
|
+
const server = buildServer();
|
|
194
|
+
const transport = new StdioServerTransport();
|
|
195
|
+
const stop = () => {
|
|
196
|
+
shutdown().finally(() => process.exit(0));
|
|
197
|
+
};
|
|
198
|
+
process.on("SIGINT", stop);
|
|
199
|
+
process.on("SIGTERM", stop);
|
|
200
|
+
try {
|
|
201
|
+
await server.connect(transport);
|
|
202
|
+
await new Promise((resolve) => {
|
|
203
|
+
transport.onclose = () => resolve();
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
finally {
|
|
207
|
+
await shutdown();
|
|
208
|
+
}
|
|
209
|
+
}
|
package/dist/version.d.ts
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* SDK downloads by default; override it with `CHROMIUMFISH_VERSION`.
|
|
7
7
|
*/
|
|
8
8
|
/** SDK package version (kept in sync with package.json). */
|
|
9
|
-
export declare const SDK_VERSION = "0.2.
|
|
9
|
+
export declare const SDK_VERSION = "0.2.3";
|
|
10
10
|
/** Default ChromiumFish browser build to fetch. Matches src/chrome/VERSION. */
|
|
11
11
|
export declare const DEFAULT_BROWSER_VERSION = "149.0.7827.115";
|
|
12
12
|
/** Public repo hosting the release assets. */
|
package/dist/version.js
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* SDK downloads by default; override it with `CHROMIUMFISH_VERSION`.
|
|
7
7
|
*/
|
|
8
8
|
/** SDK package version (kept in sync with package.json). */
|
|
9
|
-
export const SDK_VERSION = "0.2.
|
|
9
|
+
export const SDK_VERSION = "0.2.3";
|
|
10
10
|
/** Default ChromiumFish browser build to fetch. Matches src/chrome/VERSION. */
|
|
11
11
|
export const DEFAULT_BROWSER_VERSION = "149.0.7827.115";
|
|
12
12
|
/** Public repo hosting the release assets. */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "chromiumfish",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.3",
|
|
4
4
|
"description": "Stealth Chromium build with a drop-in Playwright harness — fetches and launches the ChromiumFish browser.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -56,5 +56,9 @@
|
|
|
56
56
|
"devDependencies": {
|
|
57
57
|
"@types/node": "^20.0.0",
|
|
58
58
|
"typescript": "^5.4.0"
|
|
59
|
+
},
|
|
60
|
+
"dependencies": {
|
|
61
|
+
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
62
|
+
"zod": "^4.4.3"
|
|
59
63
|
}
|
|
60
64
|
}
|