cbrowser 5.0.2 → 5.2.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/README.md +299 -263
- package/dist/browser.d.ts.map +1 -1
- package/dist/browser.js +22 -0
- package/dist/browser.js.map +1 -1
- package/dist/cli.js +14 -1
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts +1 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +1 -1
- package/dist/config.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/mcp-server.d.ts +10 -0
- package/dist/mcp-server.d.ts.map +1 -0
- package/dist/mcp-server.js +286 -0
- package/dist/mcp-server.js.map +1 -0
- package/docs/INSTALL.md +1 -1
- package/examples/basic-usage.ts +3 -1
- package/examples/smart-automation.ts +184 -0
- package/package.json +19 -11
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
/**
|
|
4
|
+
* CBrowser MCP Server
|
|
5
|
+
*
|
|
6
|
+
* Exposes CBrowser browser automation tools via Model Context Protocol.
|
|
7
|
+
* Run with: cbrowser mcp-server
|
|
8
|
+
* Or: npx cbrowser mcp-server
|
|
9
|
+
*/
|
|
10
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
|
+
exports.startMcpServer = startMcpServer;
|
|
12
|
+
const mcp_js_1 = require("@modelcontextprotocol/sdk/server/mcp.js");
|
|
13
|
+
const stdio_js_1 = require("@modelcontextprotocol/sdk/server/stdio.js");
|
|
14
|
+
const zod_1 = require("zod");
|
|
15
|
+
const browser_js_1 = require("./browser.js");
|
|
16
|
+
// Shared browser instance
|
|
17
|
+
let browser = null;
|
|
18
|
+
async function getBrowser() {
|
|
19
|
+
if (!browser) {
|
|
20
|
+
browser = new browser_js_1.CBrowser({
|
|
21
|
+
headless: true,
|
|
22
|
+
persistent: true,
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
return browser;
|
|
26
|
+
}
|
|
27
|
+
async function startMcpServer() {
|
|
28
|
+
const server = new mcp_js_1.McpServer({
|
|
29
|
+
name: "cbrowser",
|
|
30
|
+
version: "5.0.0",
|
|
31
|
+
});
|
|
32
|
+
// =========================================================================
|
|
33
|
+
// Navigation Tools
|
|
34
|
+
// =========================================================================
|
|
35
|
+
server.tool("navigate", "Navigate to a URL and take a screenshot", {
|
|
36
|
+
url: zod_1.z.string().url().describe("The URL to navigate to"),
|
|
37
|
+
}, async ({ url }) => {
|
|
38
|
+
const b = await getBrowser();
|
|
39
|
+
const result = await b.navigate(url);
|
|
40
|
+
return {
|
|
41
|
+
content: [
|
|
42
|
+
{
|
|
43
|
+
type: "text",
|
|
44
|
+
text: JSON.stringify({
|
|
45
|
+
success: true,
|
|
46
|
+
url: result.url,
|
|
47
|
+
title: result.title,
|
|
48
|
+
loadTime: result.loadTime,
|
|
49
|
+
screenshot: result.screenshot,
|
|
50
|
+
}, null, 2),
|
|
51
|
+
},
|
|
52
|
+
],
|
|
53
|
+
};
|
|
54
|
+
});
|
|
55
|
+
// =========================================================================
|
|
56
|
+
// Interaction Tools
|
|
57
|
+
// =========================================================================
|
|
58
|
+
server.tool("click", "Click an element on the page using text, selector, or description", {
|
|
59
|
+
selector: zod_1.z.string().describe("Element to click (text content, CSS selector, or description)"),
|
|
60
|
+
force: zod_1.z.boolean().optional().describe("Bypass safety checks for destructive actions"),
|
|
61
|
+
}, async ({ selector, force }) => {
|
|
62
|
+
const b = await getBrowser();
|
|
63
|
+
const result = await b.click(selector, { force });
|
|
64
|
+
return {
|
|
65
|
+
content: [
|
|
66
|
+
{
|
|
67
|
+
type: "text",
|
|
68
|
+
text: JSON.stringify({
|
|
69
|
+
success: result.success,
|
|
70
|
+
message: result.message,
|
|
71
|
+
screenshot: result.screenshot,
|
|
72
|
+
}, null, 2),
|
|
73
|
+
},
|
|
74
|
+
],
|
|
75
|
+
};
|
|
76
|
+
});
|
|
77
|
+
server.tool("smart_click", "Click with auto-retry and self-healing selectors", {
|
|
78
|
+
selector: zod_1.z.string().describe("Element to click"),
|
|
79
|
+
maxRetries: zod_1.z.number().optional().default(3).describe("Maximum retry attempts"),
|
|
80
|
+
}, async ({ selector, maxRetries }) => {
|
|
81
|
+
const b = await getBrowser();
|
|
82
|
+
const result = await b.smartClick(selector, { maxRetries });
|
|
83
|
+
return {
|
|
84
|
+
content: [
|
|
85
|
+
{
|
|
86
|
+
type: "text",
|
|
87
|
+
text: JSON.stringify({
|
|
88
|
+
success: result.success,
|
|
89
|
+
attempts: result.attempts.length,
|
|
90
|
+
finalSelector: result.finalSelector,
|
|
91
|
+
message: result.message,
|
|
92
|
+
aiSuggestion: result.aiSuggestion,
|
|
93
|
+
}, null, 2),
|
|
94
|
+
},
|
|
95
|
+
],
|
|
96
|
+
};
|
|
97
|
+
});
|
|
98
|
+
server.tool("fill", "Fill a form field with text", {
|
|
99
|
+
selector: zod_1.z.string().describe("Input field to fill (name, placeholder, label, or selector)"),
|
|
100
|
+
value: zod_1.z.string().describe("Value to enter"),
|
|
101
|
+
}, async ({ selector, value }) => {
|
|
102
|
+
const b = await getBrowser();
|
|
103
|
+
const result = await b.fill(selector, value);
|
|
104
|
+
return {
|
|
105
|
+
content: [
|
|
106
|
+
{
|
|
107
|
+
type: "text",
|
|
108
|
+
text: JSON.stringify({
|
|
109
|
+
success: result.success,
|
|
110
|
+
message: result.message,
|
|
111
|
+
}, null, 2),
|
|
112
|
+
},
|
|
113
|
+
],
|
|
114
|
+
};
|
|
115
|
+
});
|
|
116
|
+
// =========================================================================
|
|
117
|
+
// Extraction Tools
|
|
118
|
+
// =========================================================================
|
|
119
|
+
server.tool("screenshot", "Take a screenshot of the current page", {
|
|
120
|
+
path: zod_1.z.string().optional().describe("Optional path to save the screenshot"),
|
|
121
|
+
}, async ({ path }) => {
|
|
122
|
+
const b = await getBrowser();
|
|
123
|
+
const file = await b.screenshot(path);
|
|
124
|
+
return {
|
|
125
|
+
content: [
|
|
126
|
+
{
|
|
127
|
+
type: "text",
|
|
128
|
+
text: JSON.stringify({ screenshot: file }, null, 2),
|
|
129
|
+
},
|
|
130
|
+
],
|
|
131
|
+
};
|
|
132
|
+
});
|
|
133
|
+
server.tool("extract", "Extract data from the page", {
|
|
134
|
+
what: zod_1.z.enum(["links", "headings", "forms", "images", "text"]).describe("What to extract"),
|
|
135
|
+
}, async ({ what }) => {
|
|
136
|
+
const b = await getBrowser();
|
|
137
|
+
const result = await b.extract(what);
|
|
138
|
+
return {
|
|
139
|
+
content: [
|
|
140
|
+
{
|
|
141
|
+
type: "text",
|
|
142
|
+
text: JSON.stringify(result.data, null, 2),
|
|
143
|
+
},
|
|
144
|
+
],
|
|
145
|
+
};
|
|
146
|
+
});
|
|
147
|
+
// =========================================================================
|
|
148
|
+
// Assertion Tools
|
|
149
|
+
// =========================================================================
|
|
150
|
+
server.tool("assert", "Assert a condition using natural language", {
|
|
151
|
+
assertion: zod_1.z.string().describe("Natural language assertion like \"page contains 'Welcome'\" or \"title is 'Home'\""),
|
|
152
|
+
}, async ({ assertion }) => {
|
|
153
|
+
const b = await getBrowser();
|
|
154
|
+
const result = await b.assert(assertion);
|
|
155
|
+
return {
|
|
156
|
+
content: [
|
|
157
|
+
{
|
|
158
|
+
type: "text",
|
|
159
|
+
text: JSON.stringify({
|
|
160
|
+
passed: result.passed,
|
|
161
|
+
message: result.message,
|
|
162
|
+
actual: result.actual,
|
|
163
|
+
expected: result.expected,
|
|
164
|
+
}, null, 2),
|
|
165
|
+
},
|
|
166
|
+
],
|
|
167
|
+
};
|
|
168
|
+
});
|
|
169
|
+
// =========================================================================
|
|
170
|
+
// Analysis Tools
|
|
171
|
+
// =========================================================================
|
|
172
|
+
server.tool("analyze_page", "Analyze page structure for forms, buttons, links", {}, async () => {
|
|
173
|
+
const b = await getBrowser();
|
|
174
|
+
const analysis = await b.analyzePage();
|
|
175
|
+
return {
|
|
176
|
+
content: [
|
|
177
|
+
{
|
|
178
|
+
type: "text",
|
|
179
|
+
text: JSON.stringify({
|
|
180
|
+
title: analysis.title,
|
|
181
|
+
forms: analysis.forms.length,
|
|
182
|
+
buttons: analysis.buttons.length,
|
|
183
|
+
links: analysis.links.length,
|
|
184
|
+
hasLogin: analysis.hasLogin,
|
|
185
|
+
hasSearch: analysis.hasSearch,
|
|
186
|
+
hasNavigation: analysis.hasNavigation,
|
|
187
|
+
}, null, 2),
|
|
188
|
+
},
|
|
189
|
+
],
|
|
190
|
+
};
|
|
191
|
+
});
|
|
192
|
+
server.tool("generate_tests", "Generate test scenarios for a page", {
|
|
193
|
+
url: zod_1.z.string().url().optional().describe("URL to analyze (uses current page if not provided)"),
|
|
194
|
+
}, async ({ url }) => {
|
|
195
|
+
const b = await getBrowser();
|
|
196
|
+
const result = await b.generateTests(url);
|
|
197
|
+
return {
|
|
198
|
+
content: [
|
|
199
|
+
{
|
|
200
|
+
type: "text",
|
|
201
|
+
text: JSON.stringify({
|
|
202
|
+
testsGenerated: result.tests.length,
|
|
203
|
+
tests: result.tests.map(t => ({
|
|
204
|
+
name: t.name,
|
|
205
|
+
description: t.description,
|
|
206
|
+
steps: t.steps.length,
|
|
207
|
+
})),
|
|
208
|
+
}, null, 2),
|
|
209
|
+
},
|
|
210
|
+
],
|
|
211
|
+
};
|
|
212
|
+
});
|
|
213
|
+
// =========================================================================
|
|
214
|
+
// Session Tools
|
|
215
|
+
// =========================================================================
|
|
216
|
+
server.tool("save_session", "Save browser session (cookies, storage) for later use", {
|
|
217
|
+
name: zod_1.z.string().describe("Name for the saved session"),
|
|
218
|
+
}, async ({ name }) => {
|
|
219
|
+
const b = await getBrowser();
|
|
220
|
+
await b.saveSession(name);
|
|
221
|
+
return {
|
|
222
|
+
content: [
|
|
223
|
+
{
|
|
224
|
+
type: "text",
|
|
225
|
+
text: JSON.stringify({ success: true, sessionName: name }, null, 2),
|
|
226
|
+
},
|
|
227
|
+
],
|
|
228
|
+
};
|
|
229
|
+
});
|
|
230
|
+
server.tool("load_session", "Load a previously saved session", {
|
|
231
|
+
name: zod_1.z.string().describe("Name of the session to load"),
|
|
232
|
+
}, async ({ name }) => {
|
|
233
|
+
const b = await getBrowser();
|
|
234
|
+
const loaded = await b.loadSession(name);
|
|
235
|
+
return {
|
|
236
|
+
content: [
|
|
237
|
+
{
|
|
238
|
+
type: "text",
|
|
239
|
+
text: JSON.stringify({ success: loaded, sessionName: name }, null, 2),
|
|
240
|
+
},
|
|
241
|
+
],
|
|
242
|
+
};
|
|
243
|
+
});
|
|
244
|
+
server.tool("list_sessions", "List all saved sessions", {}, async () => {
|
|
245
|
+
const b = await getBrowser();
|
|
246
|
+
const sessions = await b.listSessions();
|
|
247
|
+
return {
|
|
248
|
+
content: [
|
|
249
|
+
{
|
|
250
|
+
type: "text",
|
|
251
|
+
text: JSON.stringify(sessions, null, 2),
|
|
252
|
+
},
|
|
253
|
+
],
|
|
254
|
+
};
|
|
255
|
+
});
|
|
256
|
+
// =========================================================================
|
|
257
|
+
// Self-Healing Tools
|
|
258
|
+
// =========================================================================
|
|
259
|
+
server.tool("heal_stats", "Get self-healing selector cache statistics", {}, async () => {
|
|
260
|
+
const b = await getBrowser();
|
|
261
|
+
const stats = b.getSelectorCacheStats();
|
|
262
|
+
return {
|
|
263
|
+
content: [
|
|
264
|
+
{
|
|
265
|
+
type: "text",
|
|
266
|
+
text: JSON.stringify(stats, null, 2),
|
|
267
|
+
},
|
|
268
|
+
],
|
|
269
|
+
};
|
|
270
|
+
});
|
|
271
|
+
// Connect via stdio transport
|
|
272
|
+
const transport = new stdio_js_1.StdioServerTransport();
|
|
273
|
+
await server.connect(transport);
|
|
274
|
+
// Handle shutdown
|
|
275
|
+
process.on("SIGINT", async () => {
|
|
276
|
+
if (browser) {
|
|
277
|
+
await browser.close();
|
|
278
|
+
}
|
|
279
|
+
process.exit(0);
|
|
280
|
+
});
|
|
281
|
+
}
|
|
282
|
+
// Run if called directly
|
|
283
|
+
if (process.argv[1]?.endsWith("mcp-server.js") || process.argv[1]?.endsWith("mcp-server.ts")) {
|
|
284
|
+
startMcpServer().catch(console.error);
|
|
285
|
+
}
|
|
286
|
+
//# sourceMappingURL=mcp-server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcp-server.js","sourceRoot":"","sources":["../src/mcp-server.ts"],"names":[],"mappings":"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"}
|
package/docs/INSTALL.md
CHANGED
|
@@ -57,7 +57,7 @@ CBROWSER_DATA_DIR="./data" npx cbrowser navigate "https://example.com"
|
|
|
57
57
|
| Variable | Default | Description |
|
|
58
58
|
|----------|---------|-------------|
|
|
59
59
|
| `CBROWSER_DATA_DIR` | `~/.cbrowser` | Data storage directory |
|
|
60
|
-
| `CBROWSER_HEADLESS` | `
|
|
60
|
+
| `CBROWSER_HEADLESS` | `true` | Run headless by default (set to `false` for GUI) |
|
|
61
61
|
| `CBROWSER_TIMEOUT` | `30000` | Default timeout (ms) |
|
|
62
62
|
| `CBROWSER_VIEWPORT_WIDTH` | `1280` | Default viewport width |
|
|
63
63
|
| `CBROWSER_VIEWPORT_HEIGHT` | `800` | Default viewport height |
|
package/examples/basic-usage.ts
CHANGED
|
@@ -2,9 +2,11 @@
|
|
|
2
2
|
* CBrowser Basic Usage Examples
|
|
3
3
|
*
|
|
4
4
|
* Run with: npx ts-node examples/basic-usage.ts
|
|
5
|
+
* Or: bun run examples/basic-usage.ts
|
|
5
6
|
*/
|
|
6
7
|
|
|
7
|
-
import { CBrowser } from "
|
|
8
|
+
import { CBrowser } from "cbrowser";
|
|
9
|
+
// For local development: import { CBrowser } from "../src/index.js";
|
|
8
10
|
|
|
9
11
|
async function basicNavigation() {
|
|
10
12
|
console.log("=== Basic Navigation ===\n");
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CBrowser v5.0.0 Smart Automation Examples
|
|
3
|
+
*
|
|
4
|
+
* Demonstrates:
|
|
5
|
+
* - Smart click with auto-retry
|
|
6
|
+
* - Natural language assertions
|
|
7
|
+
* - Self-healing selector cache
|
|
8
|
+
* - AI test generation
|
|
9
|
+
*
|
|
10
|
+
* Run with: npx ts-node examples/smart-automation.ts
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { CBrowser } from "../src/index.js";
|
|
14
|
+
|
|
15
|
+
async function smartClickExample() {
|
|
16
|
+
console.log("=== Smart Click with Auto-Retry ===\n");
|
|
17
|
+
|
|
18
|
+
const browser = new CBrowser({ headless: true });
|
|
19
|
+
|
|
20
|
+
try {
|
|
21
|
+
await browser.navigate("https://example.com");
|
|
22
|
+
|
|
23
|
+
// Smart click automatically:
|
|
24
|
+
// 1. Checks self-healing cache for known working selectors
|
|
25
|
+
// 2. Tries the original selector
|
|
26
|
+
// 3. Generates alternatives (text variants, ARIA roles, attributes)
|
|
27
|
+
// 4. Retries with each alternative
|
|
28
|
+
// 5. Caches the working selector for future use
|
|
29
|
+
const result = await browser.smartClick("More information...", {
|
|
30
|
+
maxRetries: 3,
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
console.log(`Success: ${result.success}`);
|
|
34
|
+
console.log(`Attempts: ${result.attempts.length}`);
|
|
35
|
+
console.log(`Final selector: ${result.finalSelector}`);
|
|
36
|
+
|
|
37
|
+
if (result.aiSuggestion) {
|
|
38
|
+
console.log(`AI suggestion: ${result.aiSuggestion}`);
|
|
39
|
+
}
|
|
40
|
+
} finally {
|
|
41
|
+
await browser.close();
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
async function assertionsExample() {
|
|
46
|
+
console.log("\n=== Natural Language Assertions ===\n");
|
|
47
|
+
|
|
48
|
+
const browser = new CBrowser({ headless: true });
|
|
49
|
+
|
|
50
|
+
try {
|
|
51
|
+
await browser.navigate("https://example.com");
|
|
52
|
+
|
|
53
|
+
// Title assertions
|
|
54
|
+
const titleResult = await browser.assert("title contains 'Example'");
|
|
55
|
+
console.log(`Title assertion: ${titleResult.passed ? "PASS" : "FAIL"}`);
|
|
56
|
+
console.log(` Message: ${titleResult.message}`);
|
|
57
|
+
|
|
58
|
+
// URL assertions
|
|
59
|
+
const urlResult = await browser.assert("url contains 'example.com'");
|
|
60
|
+
console.log(`URL assertion: ${urlResult.passed ? "PASS" : "FAIL"}`);
|
|
61
|
+
console.log(` Message: ${urlResult.message}`);
|
|
62
|
+
|
|
63
|
+
// Content assertions
|
|
64
|
+
const contentResult = await browser.assert(
|
|
65
|
+
"page contains 'illustrative examples'"
|
|
66
|
+
);
|
|
67
|
+
console.log(`Content assertion: ${contentResult.passed ? "PASS" : "FAIL"}`);
|
|
68
|
+
console.log(` Message: ${contentResult.message}`);
|
|
69
|
+
|
|
70
|
+
// Element count assertions
|
|
71
|
+
const linkResult = await browser.assert("1 links");
|
|
72
|
+
console.log(`Link count assertion: ${linkResult.passed ? "PASS" : "FAIL"}`);
|
|
73
|
+
console.log(` Actual: ${linkResult.actual}, Expected: ${linkResult.expected}`);
|
|
74
|
+
} finally {
|
|
75
|
+
await browser.close();
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
async function selfHealingExample() {
|
|
80
|
+
console.log("\n=== Self-Healing Selector Cache ===\n");
|
|
81
|
+
|
|
82
|
+
const browser = new CBrowser({ headless: true });
|
|
83
|
+
|
|
84
|
+
try {
|
|
85
|
+
// Get cache statistics
|
|
86
|
+
const stats = browser.getSelectorCacheStats();
|
|
87
|
+
console.log(`Cache entries: ${stats.totalEntries}`);
|
|
88
|
+
console.log(`Total successes: ${stats.totalSuccesses}`);
|
|
89
|
+
console.log(`Total failures: ${stats.totalFailures}`);
|
|
90
|
+
|
|
91
|
+
if (stats.topDomains.length > 0) {
|
|
92
|
+
console.log("\nTop domains:");
|
|
93
|
+
for (const { domain, count } of stats.topDomains) {
|
|
94
|
+
console.log(` ${domain}: ${count} cached selectors`);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// The cache is stored in ~/.cbrowser/selector-cache.json
|
|
99
|
+
// Each entry maps: domain + originalSelector -> workingSelector
|
|
100
|
+
// When a selector fails, CBrowser checks the cache first
|
|
101
|
+
// If found, it uses the cached working selector
|
|
102
|
+
// If not found, it tries alternatives and caches the one that works
|
|
103
|
+
} finally {
|
|
104
|
+
await browser.close();
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
async function testGenerationExample() {
|
|
109
|
+
console.log("\n=== AI Test Generation ===\n");
|
|
110
|
+
|
|
111
|
+
const browser = new CBrowser({ headless: true });
|
|
112
|
+
|
|
113
|
+
try {
|
|
114
|
+
// Generate tests for a page
|
|
115
|
+
const result = await browser.generateTests("https://example.com");
|
|
116
|
+
|
|
117
|
+
console.log(`Page analyzed: ${result.url}`);
|
|
118
|
+
console.log(`Tests generated: ${result.tests.length}\n`);
|
|
119
|
+
|
|
120
|
+
// Show generated test scenarios
|
|
121
|
+
for (const test of result.tests) {
|
|
122
|
+
console.log(`Test: ${test.name}`);
|
|
123
|
+
console.log(` Description: ${test.description}`);
|
|
124
|
+
console.log(` Steps: ${test.steps.length}`);
|
|
125
|
+
console.log(` Assertions: ${test.assertions.join(", ")}`);
|
|
126
|
+
console.log();
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Generated Playwright code
|
|
130
|
+
console.log("=== Generated Playwright Code ===");
|
|
131
|
+
console.log(result.playwrightCode.slice(0, 500) + "...\n");
|
|
132
|
+
|
|
133
|
+
// Generated CBrowser script
|
|
134
|
+
console.log("=== Generated CBrowser Script ===");
|
|
135
|
+
console.log(result.cbrowserScript.slice(0, 500) + "...\n");
|
|
136
|
+
} finally {
|
|
137
|
+
await browser.close();
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
async function pageAnalysisExample() {
|
|
142
|
+
console.log("\n=== Page Analysis ===\n");
|
|
143
|
+
|
|
144
|
+
const browser = new CBrowser({ headless: true });
|
|
145
|
+
|
|
146
|
+
try {
|
|
147
|
+
await browser.navigate("https://example.com");
|
|
148
|
+
|
|
149
|
+
const analysis = await browser.analyzePage();
|
|
150
|
+
|
|
151
|
+
console.log(`Title: ${analysis.title}`);
|
|
152
|
+
console.log(`URL: ${analysis.url}`);
|
|
153
|
+
console.log(`Forms: ${analysis.forms.length}`);
|
|
154
|
+
console.log(`Buttons: ${analysis.buttons.length}`);
|
|
155
|
+
console.log(`Links: ${analysis.links.length}`);
|
|
156
|
+
console.log(`Inputs: ${analysis.inputs.length}`);
|
|
157
|
+
console.log(`Has login form: ${analysis.hasLogin}`);
|
|
158
|
+
console.log(`Has search: ${analysis.hasSearch}`);
|
|
159
|
+
console.log(`Has navigation: ${analysis.hasNavigation}`);
|
|
160
|
+
|
|
161
|
+
// Detailed form analysis
|
|
162
|
+
if (analysis.forms.length > 0) {
|
|
163
|
+
console.log("\nForm details:");
|
|
164
|
+
for (const form of analysis.forms) {
|
|
165
|
+
console.log(` Selector: ${form.selector}`);
|
|
166
|
+
console.log(` Fields: ${form.fields.length}`);
|
|
167
|
+
console.log(` Is login: ${form.isLoginForm}`);
|
|
168
|
+
console.log(` Is search: ${form.isSearchForm}`);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
} finally {
|
|
172
|
+
await browser.close();
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
async function main() {
|
|
177
|
+
await smartClickExample();
|
|
178
|
+
await assertionsExample();
|
|
179
|
+
await selfHealingExample();
|
|
180
|
+
await testGenerationExample();
|
|
181
|
+
await pageAnalysisExample();
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
main().catch(console.error);
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cbrowser",
|
|
3
|
-
"version": "5.0
|
|
4
|
-
"description": "AI-powered browser automation with
|
|
3
|
+
"version": "5.2.0",
|
|
4
|
+
"description": "AI-powered browser automation with self-healing selectors, natural language assertions, test generation, MCP server, constitutional safety, and autonomous journeys",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
7
7
|
"bin": {
|
|
@@ -32,30 +32,38 @@
|
|
|
32
32
|
"core-web-vitals",
|
|
33
33
|
"har-export",
|
|
34
34
|
"network-mocking",
|
|
35
|
-
"video-recording"
|
|
35
|
+
"video-recording",
|
|
36
|
+
"self-healing-selectors",
|
|
37
|
+
"smart-retry",
|
|
38
|
+
"natural-language-assertions",
|
|
39
|
+
"test-generation",
|
|
40
|
+
"mcp-server",
|
|
41
|
+
"claude-integration"
|
|
36
42
|
],
|
|
37
|
-
"author": "
|
|
43
|
+
"author": "Wyld Digital",
|
|
38
44
|
"license": "MIT",
|
|
39
45
|
"repository": {
|
|
40
46
|
"type": "git",
|
|
41
|
-
"url": "git+https://github.com/
|
|
47
|
+
"url": "git+https://github.com/wyld-digital/cbrowser.git"
|
|
42
48
|
},
|
|
43
49
|
"bugs": {
|
|
44
|
-
"url": "https://github.com/
|
|
50
|
+
"url": "https://github.com/wyld-digital/cbrowser/issues"
|
|
45
51
|
},
|
|
46
|
-
"homepage": "https://github.com/
|
|
52
|
+
"homepage": "https://github.com/wyld-digital/cbrowser#readme",
|
|
47
53
|
"engines": {
|
|
48
54
|
"node": ">=18.0.0"
|
|
49
55
|
},
|
|
50
56
|
"dependencies": {
|
|
51
|
-
"
|
|
57
|
+
"@modelcontextprotocol/sdk": "^1.25.3",
|
|
58
|
+
"playwright": "^1.40.0",
|
|
59
|
+
"zod": "^3.25.76"
|
|
52
60
|
},
|
|
53
61
|
"devDependencies": {
|
|
54
62
|
"@types/node": "^20.10.0",
|
|
55
|
-
"typescript": "^5.3.0",
|
|
56
|
-
"eslint": "^8.55.0",
|
|
57
63
|
"@typescript-eslint/eslint-plugin": "^6.13.0",
|
|
58
|
-
"@typescript-eslint/parser": "^6.13.0"
|
|
64
|
+
"@typescript-eslint/parser": "^6.13.0",
|
|
65
|
+
"eslint": "^8.55.0",
|
|
66
|
+
"typescript": "^5.3.0"
|
|
59
67
|
},
|
|
60
68
|
"files": [
|
|
61
69
|
"dist/",
|