botbrowser-mcp 0.1.4 → 0.1.6
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/bin/botbrowser-mcp.js +2 -1
- package/dist/cli.js +4 -6
- package/dist/cli.js.map +1 -1
- package/dist/db/database.d.ts +3 -0
- package/dist/db/database.js +65 -0
- package/dist/db/database.js.map +1 -0
- package/dist/db/repositories/account.d.ts +16 -0
- package/dist/db/repositories/account.js +44 -0
- package/dist/db/repositories/account.js.map +1 -0
- package/dist/db/repositories/instance.d.ts +18 -0
- package/dist/db/repositories/instance.js +45 -0
- package/dist/db/repositories/instance.js.map +1 -0
- package/dist/db/repositories/profile.d.ts +21 -0
- package/dist/db/repositories/profile.js +42 -0
- package/dist/db/repositories/profile.js.map +1 -0
- package/dist/index.d.ts +1 -14
- package/dist/index.js +76 -40
- package/dist/index.js.map +1 -1
- package/dist/playwright/manager.d.ts +43 -0
- package/dist/playwright/manager.js +195 -0
- package/dist/playwright/manager.js.map +1 -0
- package/dist/tools/account.d.ts +116 -0
- package/dist/tools/account.js +110 -0
- package/dist/tools/account.js.map +1 -0
- package/dist/tools/browser.d.ts +178 -0
- package/dist/tools/browser.js +211 -0
- package/dist/tools/browser.js.map +1 -0
- package/dist/tools/instance.d.ts +99 -0
- package/dist/tools/instance.js +102 -0
- package/dist/tools/instance.js.map +1 -0
- package/dist/tools/profile.d.ts +128 -0
- package/dist/tools/profile.js +98 -0
- package/dist/tools/profile.js.map +1 -0
- package/package.json +8 -7
package/bin/botbrowser-mcp.js
CHANGED
package/dist/cli.js
CHANGED
|
@@ -1,15 +1,13 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
"use strict";
|
|
3
2
|
/**
|
|
4
3
|
* botbrowser-mcp CLI
|
|
5
4
|
*/
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
const index_1 = require("./index");
|
|
5
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
6
|
+
import { createConnection } from './index';
|
|
9
7
|
(async () => {
|
|
10
8
|
// 直接使用 @playwright/mcp 的 createConnection
|
|
11
|
-
const server = await
|
|
12
|
-
const transport = new
|
|
9
|
+
const server = await createConnection();
|
|
10
|
+
const transport = new StdioServerTransport();
|
|
13
11
|
await server.connect(transport);
|
|
14
12
|
})();
|
|
15
13
|
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA;;GAEG;AAEH,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAE3C,CAAC,KAAK,IAAI,EAAE;IACV,0CAA0C;IAC1C,MAAM,MAAM,GAAG,MAAM,gBAAgB,EAAE,CAAC;IACxC,MAAM,SAAS,GAAG,IAAI,oBAAoB,EAAE,CAAC;IAC7C,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;AAClC,CAAC,CAAC,EAAE,CAAC"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SQLite 数据库连接管理
|
|
3
|
+
*/
|
|
4
|
+
import Database from 'better-sqlite3';
|
|
5
|
+
import * as path from 'path';
|
|
6
|
+
import * as os from 'os';
|
|
7
|
+
import * as fs from 'fs';
|
|
8
|
+
const DB_DIR = path.join(os.homedir(), '.botbrowser-mcp');
|
|
9
|
+
const DB_PATH = path.join(DB_DIR, 'botbrowser.db');
|
|
10
|
+
// 确保目录存在
|
|
11
|
+
if (!fs.existsSync(DB_DIR)) {
|
|
12
|
+
fs.mkdirSync(DB_DIR, { recursive: true });
|
|
13
|
+
}
|
|
14
|
+
export const db = new Database(DB_PATH);
|
|
15
|
+
// 启用外键约束
|
|
16
|
+
db.pragma('foreign_keys = ON');
|
|
17
|
+
export function initDatabase() {
|
|
18
|
+
// 浏览器配置表
|
|
19
|
+
db.exec(`
|
|
20
|
+
CREATE TABLE IF NOT EXISTS browser_profiles (
|
|
21
|
+
alias TEXT PRIMARY KEY,
|
|
22
|
+
executable_path TEXT NOT NULL,
|
|
23
|
+
fingerprint_path TEXT,
|
|
24
|
+
storage_state_path TEXT,
|
|
25
|
+
description TEXT,
|
|
26
|
+
proxy_server TEXT,
|
|
27
|
+
proxy_username TEXT,
|
|
28
|
+
proxy_password TEXT,
|
|
29
|
+
proxy_bypass TEXT,
|
|
30
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
31
|
+
last_used TEXT
|
|
32
|
+
)
|
|
33
|
+
`);
|
|
34
|
+
// 账号表
|
|
35
|
+
db.exec(`
|
|
36
|
+
CREATE TABLE IF NOT EXISTS accounts (
|
|
37
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
38
|
+
profile_alias TEXT NOT NULL,
|
|
39
|
+
username TEXT NOT NULL,
|
|
40
|
+
metadata TEXT,
|
|
41
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
42
|
+
FOREIGN KEY (profile_alias) REFERENCES browser_profiles(alias) ON DELETE CASCADE
|
|
43
|
+
)
|
|
44
|
+
`);
|
|
45
|
+
// 浏览器实例表
|
|
46
|
+
db.exec(`
|
|
47
|
+
CREATE TABLE IF NOT EXISTS browser_instances (
|
|
48
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
49
|
+
profile_alias TEXT NOT NULL,
|
|
50
|
+
account_id INTEGER,
|
|
51
|
+
is_active INTEGER DEFAULT 0,
|
|
52
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
53
|
+
last_active TEXT,
|
|
54
|
+
FOREIGN KEY (profile_alias) REFERENCES browser_profiles(alias) ON DELETE CASCADE,
|
|
55
|
+
FOREIGN KEY (account_id) REFERENCES accounts(id) ON DELETE SET NULL
|
|
56
|
+
)
|
|
57
|
+
`);
|
|
58
|
+
// 创建索引
|
|
59
|
+
db.exec(`
|
|
60
|
+
CREATE INDEX IF NOT EXISTS idx_accounts_profile ON accounts(profile_alias);
|
|
61
|
+
CREATE INDEX IF NOT EXISTS idx_instances_profile ON browser_instances(profile_alias);
|
|
62
|
+
CREATE INDEX IF NOT EXISTS idx_instances_active ON browser_instances(is_active);
|
|
63
|
+
`);
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=database.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"database.js","sourceRoot":"","sources":["../../src/db/database.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,QAAQ,MAAM,gBAAgB,CAAC;AAEtC,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAC7B,OAAO,KAAK,EAAE,MAAM,IAAI,CAAC;AACzB,OAAO,KAAK,EAAE,MAAM,IAAI,CAAC;AAEzB,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,iBAAiB,CAAC,CAAC;AAC1D,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;AAEnD,SAAS;AACT,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;IAC3B,EAAE,CAAC,SAAS,CAAC,MAAM,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;AAC5C,CAAC;AAED,MAAM,CAAC,MAAM,EAAE,GAA2B,IAAI,QAAQ,CAAC,OAAO,CAAC,CAAC;AAGhE,SAAS;AACT,EAAE,CAAC,MAAM,CAAC,mBAAmB,CAAC,CAAC;AAE/B,MAAM,UAAU,YAAY;IAC1B,SAAS;IACT,EAAE,CAAC,IAAI,CAAC;;;;;;;;;;;;;;GAcP,CAAC,CAAC;IAEH,MAAM;IACN,EAAE,CAAC,IAAI,CAAC;;;;;;;;;GASP,CAAC,CAAC;IAEH,SAAS;IACT,EAAE,CAAC,IAAI,CAAC;;;;;;;;;;;GAWP,CAAC,CAAC;IAEH,OAAO;IACP,EAAE,CAAC,IAAI,CAAC;;;;GAIP,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export interface Account {
|
|
2
|
+
id: number;
|
|
3
|
+
profile_alias: string;
|
|
4
|
+
username: string;
|
|
5
|
+
metadata?: string;
|
|
6
|
+
created_at: string;
|
|
7
|
+
}
|
|
8
|
+
export declare class AccountRepository {
|
|
9
|
+
getAll(): Account[];
|
|
10
|
+
getByProfile(profileAlias: string): Account[];
|
|
11
|
+
findByUsername(profileAlias: string, username: string): Account | undefined;
|
|
12
|
+
getById(id: number): Account | undefined;
|
|
13
|
+
create(account: Omit<Account, 'id' | 'created_at'>): number;
|
|
14
|
+
update(id: number, updates: Partial<Omit<Account, 'id' | 'created_at'>>): void;
|
|
15
|
+
delete(id: number): void;
|
|
16
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 账号数据访问层
|
|
3
|
+
*/
|
|
4
|
+
import { db } from '../database.js';
|
|
5
|
+
export class AccountRepository {
|
|
6
|
+
getAll() {
|
|
7
|
+
return db.prepare('SELECT * FROM accounts ORDER BY created_at DESC').all();
|
|
8
|
+
}
|
|
9
|
+
getByProfile(profileAlias) {
|
|
10
|
+
return db.prepare('SELECT * FROM accounts WHERE profile_alias = ? ORDER BY created_at DESC')
|
|
11
|
+
.all(profileAlias);
|
|
12
|
+
}
|
|
13
|
+
findByUsername(profileAlias, username) {
|
|
14
|
+
return db.prepare('SELECT * FROM accounts WHERE profile_alias = ? AND username = ?')
|
|
15
|
+
.get(profileAlias, username);
|
|
16
|
+
}
|
|
17
|
+
getById(id) {
|
|
18
|
+
return db.prepare('SELECT * FROM accounts WHERE id = ?').get(id);
|
|
19
|
+
}
|
|
20
|
+
create(account) {
|
|
21
|
+
const stmt = db.prepare(`
|
|
22
|
+
INSERT INTO accounts (profile_alias, username, metadata)
|
|
23
|
+
VALUES (?, ?, ?)
|
|
24
|
+
`);
|
|
25
|
+
const result = stmt.run(account.profile_alias, account.username, account.metadata || null);
|
|
26
|
+
return result.lastInsertRowid;
|
|
27
|
+
}
|
|
28
|
+
update(id, updates) {
|
|
29
|
+
const fields = [];
|
|
30
|
+
const values = [];
|
|
31
|
+
for (const [key, value] of Object.entries(updates)) {
|
|
32
|
+
fields.push(`${key} = ?`);
|
|
33
|
+
values.push(value);
|
|
34
|
+
}
|
|
35
|
+
if (fields.length === 0)
|
|
36
|
+
return;
|
|
37
|
+
values.push(id);
|
|
38
|
+
db.prepare(`UPDATE accounts SET ${fields.join(', ')} WHERE id = ?`).run(...values);
|
|
39
|
+
}
|
|
40
|
+
delete(id) {
|
|
41
|
+
db.prepare('DELETE FROM accounts WHERE id = ?').run(id);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=account.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"account.js","sourceRoot":"","sources":["../../../src/db/repositories/account.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,EAAE,EAAE,EAAE,MAAM,gBAAgB,CAAC;AAUpC,MAAM,OAAO,iBAAiB;IAC5B,MAAM;QACJ,OAAO,EAAE,CAAC,OAAO,CAAC,iDAAiD,CAAC,CAAC,GAAG,EAAe,CAAC;IAC1F,CAAC;IAED,YAAY,CAAC,YAAoB;QAC/B,OAAO,EAAE,CAAC,OAAO,CAAC,yEAAyE,CAAC;aACzF,GAAG,CAAC,YAAY,CAAc,CAAC;IACpC,CAAC;IAED,cAAc,CAAC,YAAoB,EAAE,QAAgB;QACnD,OAAO,EAAE,CAAC,OAAO,CAAC,iEAAiE,CAAC;aACjF,GAAG,CAAC,YAAY,EAAE,QAAQ,CAAwB,CAAC;IACxD,CAAC;IAED,OAAO,CAAC,EAAU;QAChB,OAAO,EAAE,CAAC,OAAO,CAAC,qCAAqC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAwB,CAAC;IAC1F,CAAC;IAED,MAAM,CAAC,OAA2C;QAChD,MAAM,IAAI,GAAG,EAAE,CAAC,OAAO,CAAC;;;KAGvB,CAAC,CAAC;QAEH,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CACrB,OAAO,CAAC,aAAa,EACrB,OAAO,CAAC,QAAQ,EAChB,OAAO,CAAC,QAAQ,IAAI,IAAI,CACzB,CAAC;QAEF,OAAO,MAAM,CAAC,eAAyB,CAAC;IAC1C,CAAC;IAED,MAAM,CAAC,EAAU,EAAE,OAAoD;QACrE,MAAM,MAAM,GAAG,EAAE,CAAC;QAClB,MAAM,MAAM,GAAG,EAAE,CAAC;QAElB,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;YACnD,MAAM,CAAC,IAAI,CAAC,GAAG,GAAG,MAAM,CAAC,CAAC;YAC1B,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACrB,CAAC;QAED,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO;QAEhC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAChB,EAAE,CAAC,OAAO,CAAC,uBAAuB,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC,CAAC;IACrF,CAAC;IAED,MAAM,CAAC,EAAU;QACf,EAAE,CAAC,OAAO,CAAC,mCAAmC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAC1D,CAAC;CACF"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export interface BrowserInstance {
|
|
2
|
+
id: number;
|
|
3
|
+
profile_alias: string;
|
|
4
|
+
account_id?: number;
|
|
5
|
+
is_active: number;
|
|
6
|
+
created_at: string;
|
|
7
|
+
last_active?: string;
|
|
8
|
+
}
|
|
9
|
+
export declare class InstanceRepository {
|
|
10
|
+
getAll(): BrowserInstance[];
|
|
11
|
+
getActive(): BrowserInstance | undefined;
|
|
12
|
+
getById(id: number): BrowserInstance | undefined;
|
|
13
|
+
create(instance: Omit<BrowserInstance, 'id' | 'created_at' | 'last_active'>): number;
|
|
14
|
+
setActive(id: number): void;
|
|
15
|
+
updateLastActive(id: number): void;
|
|
16
|
+
delete(id: number): void;
|
|
17
|
+
deleteAll(): void;
|
|
18
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 浏览器实例数据访问层
|
|
3
|
+
*/
|
|
4
|
+
import { db } from '../database.js';
|
|
5
|
+
export class InstanceRepository {
|
|
6
|
+
getAll() {
|
|
7
|
+
return db.prepare('SELECT * FROM browser_instances ORDER BY last_active DESC').all();
|
|
8
|
+
}
|
|
9
|
+
getActive() {
|
|
10
|
+
return db.prepare('SELECT * FROM browser_instances WHERE is_active = 1').get();
|
|
11
|
+
}
|
|
12
|
+
getById(id) {
|
|
13
|
+
return db.prepare('SELECT * FROM browser_instances WHERE id = ?').get(id);
|
|
14
|
+
}
|
|
15
|
+
create(instance) {
|
|
16
|
+
const stmt = db.prepare(`
|
|
17
|
+
INSERT INTO browser_instances (profile_alias, account_id, is_active)
|
|
18
|
+
VALUES (?, ?, ?)
|
|
19
|
+
`);
|
|
20
|
+
const result = stmt.run(instance.profile_alias, instance.account_id || null, instance.is_active);
|
|
21
|
+
return result.lastInsertRowid;
|
|
22
|
+
}
|
|
23
|
+
setActive(id) {
|
|
24
|
+
const setInactive = db.prepare('UPDATE browser_instances SET is_active = 0');
|
|
25
|
+
const setActiveStmt = db.prepare(`
|
|
26
|
+
UPDATE browser_instances
|
|
27
|
+
SET is_active = 1, last_active = datetime('now')
|
|
28
|
+
WHERE id = ?
|
|
29
|
+
`);
|
|
30
|
+
db.transaction(() => {
|
|
31
|
+
setInactive.run();
|
|
32
|
+
setActiveStmt.run(id);
|
|
33
|
+
})();
|
|
34
|
+
}
|
|
35
|
+
updateLastActive(id) {
|
|
36
|
+
db.prepare("UPDATE browser_instances SET last_active = datetime('now') WHERE id = ?").run(id);
|
|
37
|
+
}
|
|
38
|
+
delete(id) {
|
|
39
|
+
db.prepare('DELETE FROM browser_instances WHERE id = ?').run(id);
|
|
40
|
+
}
|
|
41
|
+
deleteAll() {
|
|
42
|
+
db.prepare('DELETE FROM browser_instances').run();
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=instance.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"instance.js","sourceRoot":"","sources":["../../../src/db/repositories/instance.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,EAAE,EAAE,EAAE,MAAM,gBAAgB,CAAC;AAWpC,MAAM,OAAO,kBAAkB;IAC7B,MAAM;QACJ,OAAO,EAAE,CAAC,OAAO,CAAC,2DAA2D,CAAC,CAAC,GAAG,EAAuB,CAAC;IAC5G,CAAC;IAED,SAAS;QACP,OAAO,EAAE,CAAC,OAAO,CAAC,qDAAqD,CAAC,CAAC,GAAG,EAAiC,CAAC;IAChH,CAAC;IAED,OAAO,CAAC,EAAU;QAChB,OAAO,EAAE,CAAC,OAAO,CAAC,8CAA8C,CAAC,CAAC,GAAG,CAAC,EAAE,CAAgC,CAAC;IAC3G,CAAC;IAED,MAAM,CAAC,QAAoE;QACzE,MAAM,IAAI,GAAG,EAAE,CAAC,OAAO,CAAC;;;KAGvB,CAAC,CAAC;QAEH,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CACrB,QAAQ,CAAC,aAAa,EACtB,QAAQ,CAAC,UAAU,IAAI,IAAI,EAC3B,QAAQ,CAAC,SAAS,CACnB,CAAC;QAEF,OAAO,MAAM,CAAC,eAAyB,CAAC;IAC1C,CAAC;IAED,SAAS,CAAC,EAAU;QAClB,MAAM,WAAW,GAAG,EAAE,CAAC,OAAO,CAAC,4CAA4C,CAAC,CAAC;QAC7E,MAAM,aAAa,GAAG,EAAE,CAAC,OAAO,CAAC;;;;KAIhC,CAAC,CAAC;QAEH,EAAE,CAAC,WAAW,CAAC,GAAG,EAAE;YAClB,WAAW,CAAC,GAAG,EAAE,CAAC;YAClB,aAAa,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACxB,CAAC,CAAC,EAAE,CAAC;IACP,CAAC;IAED,gBAAgB,CAAC,EAAU;QACzB,EAAE,CAAC,OAAO,CAAC,yEAAyE,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAChG,CAAC;IAED,MAAM,CAAC,EAAU;QACf,EAAE,CAAC,OAAO,CAAC,4CAA4C,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IACnE,CAAC;IAED,SAAS;QACP,EAAE,CAAC,OAAO,CAAC,+BAA+B,CAAC,CAAC,GAAG,EAAE,CAAC;IACpD,CAAC;CACF"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export interface BrowserProfile {
|
|
2
|
+
alias: string;
|
|
3
|
+
executable_path: string;
|
|
4
|
+
fingerprint_path?: string;
|
|
5
|
+
storage_state_path?: string;
|
|
6
|
+
description?: string;
|
|
7
|
+
proxy_server?: string;
|
|
8
|
+
proxy_username?: string;
|
|
9
|
+
proxy_password?: string;
|
|
10
|
+
proxy_bypass?: string;
|
|
11
|
+
created_at: string;
|
|
12
|
+
last_used?: string;
|
|
13
|
+
}
|
|
14
|
+
export declare class ProfileRepository {
|
|
15
|
+
getAll(): BrowserProfile[];
|
|
16
|
+
getByAlias(alias: string): BrowserProfile | undefined;
|
|
17
|
+
create(profile: Omit<BrowserProfile, 'created_at' | 'last_used'>): void;
|
|
18
|
+
update(alias: string, updates: Partial<BrowserProfile>): void;
|
|
19
|
+
delete(alias: string): void;
|
|
20
|
+
updateLastUsed(alias: string): void;
|
|
21
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 浏览器配置数据访问层
|
|
3
|
+
*/
|
|
4
|
+
import { db } from '../database.js';
|
|
5
|
+
export class ProfileRepository {
|
|
6
|
+
getAll() {
|
|
7
|
+
return db.prepare('SELECT * FROM browser_profiles').all();
|
|
8
|
+
}
|
|
9
|
+
getByAlias(alias) {
|
|
10
|
+
return db.prepare('SELECT * FROM browser_profiles WHERE alias = ?').get(alias);
|
|
11
|
+
}
|
|
12
|
+
create(profile) {
|
|
13
|
+
const stmt = db.prepare(`
|
|
14
|
+
INSERT INTO browser_profiles (
|
|
15
|
+
alias, executable_path, fingerprint_path, storage_state_path,
|
|
16
|
+
description, proxy_server, proxy_username, proxy_password, proxy_bypass
|
|
17
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
18
|
+
`);
|
|
19
|
+
stmt.run(profile.alias, profile.executable_path, profile.fingerprint_path || null, profile.storage_state_path || null, profile.description || null, profile.proxy_server || null, profile.proxy_username || null, profile.proxy_password || null, profile.proxy_bypass || null);
|
|
20
|
+
}
|
|
21
|
+
update(alias, updates) {
|
|
22
|
+
const fields = [];
|
|
23
|
+
const values = [];
|
|
24
|
+
for (const [key, value] of Object.entries(updates)) {
|
|
25
|
+
if (key !== 'alias' && key !== 'created_at') {
|
|
26
|
+
fields.push(`${key} = ?`);
|
|
27
|
+
values.push(value);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
if (fields.length === 0)
|
|
31
|
+
return;
|
|
32
|
+
values.push(alias);
|
|
33
|
+
db.prepare(`UPDATE browser_profiles SET ${fields.join(', ')} WHERE alias = ?`).run(...values);
|
|
34
|
+
}
|
|
35
|
+
delete(alias) {
|
|
36
|
+
db.prepare('DELETE FROM browser_profiles WHERE alias = ?').run(alias);
|
|
37
|
+
}
|
|
38
|
+
updateLastUsed(alias) {
|
|
39
|
+
db.prepare("UPDATE browser_profiles SET last_used = datetime('now') WHERE alias = ?").run(alias);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=profile.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"profile.js","sourceRoot":"","sources":["../../../src/db/repositories/profile.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,EAAE,EAAE,EAAE,MAAM,gBAAgB,CAAC;AAgBpC,MAAM,OAAO,iBAAiB;IAC5B,MAAM;QACJ,OAAO,EAAE,CAAC,OAAO,CAAC,gCAAgC,CAAC,CAAC,GAAG,EAAsB,CAAC;IAChF,CAAC;IAED,UAAU,CAAC,KAAa;QACtB,OAAO,EAAE,CAAC,OAAO,CAAC,gDAAgD,CAAC,CAAC,GAAG,CAAC,KAAK,CAA+B,CAAC;IAC/G,CAAC;IAED,MAAM,CAAC,OAAyD;QAC9D,MAAM,IAAI,GAAG,EAAE,CAAC,OAAO,CAAC;;;;;KAKvB,CAAC,CAAC;QAEH,IAAI,CAAC,GAAG,CACN,OAAO,CAAC,KAAK,EACb,OAAO,CAAC,eAAe,EACvB,OAAO,CAAC,gBAAgB,IAAI,IAAI,EAChC,OAAO,CAAC,kBAAkB,IAAI,IAAI,EAClC,OAAO,CAAC,WAAW,IAAI,IAAI,EAC3B,OAAO,CAAC,YAAY,IAAI,IAAI,EAC5B,OAAO,CAAC,cAAc,IAAI,IAAI,EAC9B,OAAO,CAAC,cAAc,IAAI,IAAI,EAC9B,OAAO,CAAC,YAAY,IAAI,IAAI,CAC7B,CAAC;IACJ,CAAC;IAED,MAAM,CAAC,KAAa,EAAE,OAAgC;QACpD,MAAM,MAAM,GAAG,EAAE,CAAC;QAClB,MAAM,MAAM,GAAG,EAAE,CAAC;QAElB,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;YACnD,IAAI,GAAG,KAAK,OAAO,IAAI,GAAG,KAAK,YAAY,EAAE,CAAC;gBAC5C,MAAM,CAAC,IAAI,CAAC,GAAG,GAAG,MAAM,CAAC,CAAC;gBAC1B,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACrB,CAAC;QACH,CAAC;QAED,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO;QAEhC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACnB,EAAE,CAAC,OAAO,CAAC,+BAA+B,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,kBAAkB,CAAC,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC,CAAC;IAChG,CAAC;IAED,MAAM,CAAC,KAAa;QAClB,EAAE,CAAC,OAAO,CAAC,8CAA8C,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IACxE,CAAC;IAED,cAAc,CAAC,KAAa;QAC1B,EAAE,CAAC,OAAO,CAAC,yEAAyE,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IACnG,CAAC;CACF"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,15 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
* botbrowser-mcp 主入口
|
|
3
|
-
*/
|
|
4
|
-
import type { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
5
|
-
interface BotBrowserConfig {
|
|
6
|
-
botMode?: boolean;
|
|
7
|
-
stealth?: boolean;
|
|
8
|
-
customHeaders?: Record<string, string>;
|
|
9
|
-
[key: string]: any;
|
|
10
|
-
}
|
|
11
|
-
/**
|
|
12
|
-
* 创建增强的 MCP 连接
|
|
13
|
-
*/
|
|
14
|
-
export declare function createConnection(userConfig?: BotBrowserConfig, contextGetter?: any): Promise<Server>;
|
|
1
|
+
#!/usr/bin/env node
|
|
15
2
|
export {};
|
package/dist/index.js
CHANGED
|
@@ -1,48 +1,84 @@
|
|
|
1
|
-
|
|
1
|
+
#!/usr/bin/env node
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
3
|
+
* BotBrowser-MCP Server 主入口
|
|
4
4
|
*/
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
5
|
+
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
6
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
7
|
+
import { CallToolRequestSchema, ListToolsRequestSchema, } from '@modelcontextprotocol/sdk/types.js';
|
|
8
|
+
import { initDatabase } from './db/database.js';
|
|
9
|
+
import { PlaywrightManager } from './playwright/manager.js';
|
|
10
|
+
import { profileTools } from './tools/profile.js';
|
|
11
|
+
import { accountTools } from './tools/account.js';
|
|
12
|
+
import { instanceTools, setManager as setInstanceManager } from './tools/instance.js';
|
|
13
|
+
import { browserTools, setManager as setBrowserManager } from './tools/browser.js';
|
|
14
|
+
// 初始化数据库
|
|
15
|
+
initDatabase();
|
|
16
|
+
// 创建 Playwright 管理器
|
|
17
|
+
const manager = new PlaywrightManager();
|
|
18
|
+
setInstanceManager(manager);
|
|
19
|
+
setBrowserManager(manager);
|
|
20
|
+
// 合并所有工具
|
|
21
|
+
const allTools = {
|
|
22
|
+
...profileTools,
|
|
23
|
+
...accountTools,
|
|
24
|
+
...instanceTools,
|
|
25
|
+
...browserTools,
|
|
26
|
+
};
|
|
27
|
+
// 创建 MCP Server
|
|
28
|
+
const server = new Server({
|
|
29
|
+
name: 'botbrowser-mcp',
|
|
30
|
+
version: '1.0.0',
|
|
31
|
+
}, {
|
|
32
|
+
capabilities: {
|
|
33
|
+
tools: {},
|
|
34
|
+
},
|
|
35
|
+
});
|
|
36
|
+
// 注册工具列表处理
|
|
37
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
38
|
+
return {
|
|
39
|
+
tools: Object.entries(allTools).map(([name, tool]) => ({
|
|
40
|
+
name,
|
|
41
|
+
description: tool.description,
|
|
42
|
+
inputSchema: tool.inputSchema,
|
|
43
|
+
})),
|
|
44
|
+
};
|
|
45
|
+
});
|
|
46
|
+
// 注册工具调用处理
|
|
47
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
48
|
+
const toolName = request.params.name;
|
|
49
|
+
const tool = allTools[toolName];
|
|
50
|
+
if (!tool) {
|
|
51
|
+
throw new Error(`未知工具: ${toolName}`);
|
|
21
52
|
}
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
config.browser = config.browser || {};
|
|
25
|
-
config.browser.launchOptions = config.browser.launchOptions || {};
|
|
26
|
-
config.browser.launchOptions.args = [
|
|
27
|
-
...(config.browser.launchOptions.args || []),
|
|
28
|
-
'--disable-blink-features=AutomationControlled',
|
|
29
|
-
'--disable-dev-shm-usage',
|
|
30
|
-
];
|
|
31
|
-
config.browser.contextOptions = config.browser.contextOptions || {};
|
|
32
|
-
if (!config.browser.contextOptions.userAgent) {
|
|
33
|
-
config.browser.contextOptions.userAgent =
|
|
34
|
-
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36';
|
|
35
|
-
}
|
|
53
|
+
try {
|
|
54
|
+
return await tool.handler(request.params.arguments || {});
|
|
36
55
|
}
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
56
|
+
catch (error) {
|
|
57
|
+
return {
|
|
58
|
+
content: [{
|
|
59
|
+
type: 'text',
|
|
60
|
+
text: `错误: ${error instanceof Error ? error.message : String(error)}`,
|
|
61
|
+
}],
|
|
62
|
+
isError: true,
|
|
44
63
|
};
|
|
45
64
|
}
|
|
46
|
-
|
|
65
|
+
});
|
|
66
|
+
// 启动服务器
|
|
67
|
+
async function main() {
|
|
68
|
+
const transport = new StdioServerTransport();
|
|
69
|
+
await server.connect(transport);
|
|
70
|
+
// 优雅退出
|
|
71
|
+
process.on('SIGINT', async () => {
|
|
72
|
+
await manager.stopAll();
|
|
73
|
+
process.exit(0);
|
|
74
|
+
});
|
|
75
|
+
process.on('SIGTERM', async () => {
|
|
76
|
+
await manager.stopAll();
|
|
77
|
+
process.exit(0);
|
|
78
|
+
});
|
|
47
79
|
}
|
|
80
|
+
main().catch((error) => {
|
|
81
|
+
console.error('Server error:', error);
|
|
82
|
+
process.exit(1);
|
|
83
|
+
});
|
|
48
84
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA;;GAEG;AACH,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AACnE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EACL,qBAAqB,EACrB,sBAAsB,GACvB,MAAM,oCAAoC,CAAC;AAC5C,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAChD,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAC5D,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,aAAa,EAAE,UAAU,IAAI,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AACtF,OAAO,EAAE,YAAY,EAAE,UAAU,IAAI,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAEnF,SAAS;AACT,YAAY,EAAE,CAAC;AAEf,oBAAoB;AACpB,MAAM,OAAO,GAAG,IAAI,iBAAiB,EAAE,CAAC;AACxC,kBAAkB,CAAC,OAAO,CAAC,CAAC;AAC5B,iBAAiB,CAAC,OAAO,CAAC,CAAC;AAE3B,SAAS;AACT,MAAM,QAAQ,GAAG;IACf,GAAG,YAAY;IACf,GAAG,YAAY;IACf,GAAG,aAAa;IAChB,GAAG,YAAY;CAChB,CAAC;AAEF,gBAAgB;AAChB,MAAM,MAAM,GAAG,IAAI,MAAM,CACvB;IACE,IAAI,EAAE,gBAAgB;IACtB,OAAO,EAAE,OAAO;CACjB,EACD;IACE,YAAY,EAAE;QACZ,KAAK,EAAE,EAAE;KACV;CACF,CACF,CAAC;AAEF,WAAW;AACX,MAAM,CAAC,iBAAiB,CAAC,sBAAsB,EAAE,KAAK,IAAI,EAAE;IAC1D,OAAO;QACL,KAAK,EAAE,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC;YACrD,IAAI;YACJ,WAAW,EAAE,IAAI,CAAC,WAAW;YAC7B,WAAW,EAAE,IAAI,CAAC,WAAW;SAC9B,CAAC,CAAC;KACJ,CAAC;AACJ,CAAC,CAAC,CAAC;AAEH,WAAW;AACX,MAAM,CAAC,iBAAiB,CAAC,qBAAqB,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE;IAChE,MAAM,QAAQ,GAAG,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC;IACrC,MAAM,IAAI,GAAG,QAAQ,CAAC,QAAiC,CAAC,CAAC;IAEzD,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,MAAM,IAAI,KAAK,CAAC,SAAS,QAAQ,EAAE,CAAC,CAAC;IACvC,CAAC;IAED,IAAI,CAAC;QACH,OAAO,MAAM,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,SAAS,IAAI,EAAE,CAAC,CAAC;IAC5D,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO;YACL,OAAO,EAAE,CAAC;oBACR,IAAI,EAAE,MAAM;oBACZ,IAAI,EAAE,OAAO,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE;iBACtE,CAAC;YACF,OAAO,EAAE,IAAI;SACd,CAAC;IACJ,CAAC;AACH,CAAC,CAAC,CAAC;AAEH,QAAQ;AACR,KAAK,UAAU,IAAI;IACjB,MAAM,SAAS,GAAG,IAAI,oBAAoB,EAAE,CAAC;IAC7C,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IAEhC,OAAO;IACP,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,KAAK,IAAI,EAAE;QAC9B,MAAM,OAAO,CAAC,OAAO,EAAE,CAAC;QACxB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC,CAAC,CAAC;IAEH,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,KAAK,IAAI,EAAE;QAC/B,MAAM,OAAO,CAAC,OAAO,EAAE,CAAC;QACxB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC,CAAC,CAAC;AACL,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;IACrB,OAAO,CAAC,KAAK,CAAC,eAAe,EAAE,KAAK,CAAC,CAAC;IACtC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Playwright 浏览器实例管理器
|
|
3
|
+
*/
|
|
4
|
+
import { BrowserContext } from 'playwright';
|
|
5
|
+
export declare class PlaywrightManager {
|
|
6
|
+
private instances;
|
|
7
|
+
private profileRepo;
|
|
8
|
+
private instanceRepo;
|
|
9
|
+
private accountRepo;
|
|
10
|
+
/**
|
|
11
|
+
* 启动浏览器实例
|
|
12
|
+
*/
|
|
13
|
+
launchInstance(profileAlias: string, accountId?: number, launchOptions?: any): Promise<number>;
|
|
14
|
+
/**
|
|
15
|
+
* 切换活跃实例
|
|
16
|
+
*/
|
|
17
|
+
switchActive(instanceId: number): Promise<void>;
|
|
18
|
+
/**
|
|
19
|
+
* 停止实例
|
|
20
|
+
*/
|
|
21
|
+
stopInstance(instanceId: number): Promise<void>;
|
|
22
|
+
/**
|
|
23
|
+
* 停止所有实例
|
|
24
|
+
*/
|
|
25
|
+
stopAll(): Promise<void>;
|
|
26
|
+
/**
|
|
27
|
+
* 获取活跃实例的上下文
|
|
28
|
+
*/
|
|
29
|
+
getActiveContext(): BrowserContext | null;
|
|
30
|
+
/**
|
|
31
|
+
* 获取所有实例列表
|
|
32
|
+
*/
|
|
33
|
+
listInstances(): {
|
|
34
|
+
id: number;
|
|
35
|
+
profile_alias: string;
|
|
36
|
+
account_id: number | undefined;
|
|
37
|
+
is_active: boolean;
|
|
38
|
+
}[];
|
|
39
|
+
/**
|
|
40
|
+
* 清理已停止的实例记录(实例在内存中不存在但数据库有记录)
|
|
41
|
+
*/
|
|
42
|
+
cleanupOrphaned(): Promise<number>;
|
|
43
|
+
}
|