@cjwddz/browser-server 0.1.0-alpha
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 +148 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +161 -0
- package/dist/cli.js.map +1 -0
- package/dist/daemon.d.ts +2 -0
- package/dist/daemon.d.ts.map +1 -0
- package/dist/daemon.js +57 -0
- package/dist/daemon.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -0
- package/dist/mcp/connection-pool.d.ts +14 -0
- package/dist/mcp/connection-pool.d.ts.map +1 -0
- package/dist/mcp/connection-pool.js +65 -0
- package/dist/mcp/connection-pool.js.map +1 -0
- package/dist/mcp/tools.d.ts +230 -0
- package/dist/mcp/tools.d.ts.map +1 -0
- package/dist/mcp/tools.js +219 -0
- package/dist/mcp/tools.js.map +1 -0
- package/dist/server/mcp-server.d.ts +15 -0
- package/dist/server/mcp-server.d.ts.map +1 -0
- package/dist/server/mcp-server.js +96 -0
- package/dist/server/mcp-server.js.map +1 -0
- package/dist/server/vnc-proxy.d.ts +7 -0
- package/dist/server/vnc-proxy.d.ts.map +1 -0
- package/dist/server/vnc-proxy.js +38 -0
- package/dist/server/vnc-proxy.js.map +1 -0
- package/dist/server/web-pages.d.ts +4 -0
- package/dist/server/web-pages.d.ts.map +1 -0
- package/dist/server/web-pages.js +157 -0
- package/dist/server/web-pages.js.map +1 -0
- package/dist/server/web-server.d.ts +18 -0
- package/dist/server/web-server.d.ts.map +1 -0
- package/dist/server/web-server.js +161 -0
- package/dist/server/web-server.js.map +1 -0
- package/dist/session/process-group.d.ts +33 -0
- package/dist/session/process-group.d.ts.map +1 -0
- package/dist/session/process-group.js +185 -0
- package/dist/session/process-group.js.map +1 -0
- package/dist/session/session-manager.d.ts +26 -0
- package/dist/session/session-manager.d.ts.map +1 -0
- package/dist/session/session-manager.js +133 -0
- package/dist/session/session-manager.js.map +1 -0
- package/dist/utils/config.d.ts +14 -0
- package/dist/utils/config.d.ts.map +1 -0
- package/dist/utils/config.js +72 -0
- package/dist/utils/config.js.map +1 -0
- package/dist/utils/logger.d.ts +9 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +33 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/types.d.ts +26 -0
- package/dist/utils/types.d.ts.map +1 -0
- package/dist/utils/types.js +2 -0
- package/dist/utils/types.js.map +1 -0
- package/package.json +42 -0
package/README.md
ADDED
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
# @cjwddz/browser-server
|
|
2
|
+
|
|
3
|
+
面向 AI Agent 的自托管常驻浏览器服务。
|
|
4
|
+
|
|
5
|
+
## 解决的问题
|
|
6
|
+
|
|
7
|
+
AI Agent 在无 GUI 的远程服务器上操作浏览器时面临的核心难题:
|
|
8
|
+
|
|
9
|
+
- 浏览器生命周期绑定 Agent 对话,对话结束状态全丢
|
|
10
|
+
- 跨天登录态无法保持,每次都要重新登录
|
|
11
|
+
- 验证码、密码等场景 AI 无法处理,卡住无解
|
|
12
|
+
- 多 Agent 共用浏览器互相干扰
|
|
13
|
+
|
|
14
|
+
## 核心特性
|
|
15
|
+
|
|
16
|
+
- **常驻浏览器** — 浏览器 7×24 运行,不因 Agent 会话结束而关闭
|
|
17
|
+
- **状态完整保持** — Cookie、localStorage、页面 JS 上下文持续存活,跨天登录态不丢
|
|
18
|
+
- **Web 端实时操控** — 用户随时从浏览器打开管理页面,直接查看和操作浏览器(验证码、密码等)
|
|
19
|
+
- **Agent 隔离** — 每个会话独立 BrowserContext,Cookie/存储完全隔离
|
|
20
|
+
- **MCP 集成** — 15 个浏览器操控工具,支持 Streamable HTTP 协议
|
|
21
|
+
- **进程守护** — Chromium 崩溃自动重启
|
|
22
|
+
|
|
23
|
+
## 系统依赖
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
# Ubuntu/Debian
|
|
27
|
+
sudo apt-get install -y xvfb x11vnc chromium-browser
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## 安装
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
npm install -g @cjwddz/browser-server
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## 使用
|
|
37
|
+
|
|
38
|
+
### 启动服务
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
browser-server start -u admin -p mypassword
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### 完整参数
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
browser-server start \
|
|
48
|
+
-u admin \ # Web 管理登录用户名(必填)
|
|
49
|
+
-p mypassword \ # Web 管理登录密码(必填)
|
|
50
|
+
-w 33000 \ # Web 管理端口(默认 33000)
|
|
51
|
+
-m 33001 \ # MCP 服务端口(默认 33001)
|
|
52
|
+
-d ~/.browser-server \ # 服务根目录(默认 ~/.browser-server)
|
|
53
|
+
--host 0.0.0.0 # 监听地址(默认 0.0.0.0)
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### 其他命令
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
browser-server stop # 停止服务
|
|
60
|
+
browser-server restart -u admin -p mypassword # 重启
|
|
61
|
+
browser-server status # 查看状态
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## 使用流程
|
|
65
|
+
|
|
66
|
+
1. 启动服务
|
|
67
|
+
2. 浏览器打开 `http://localhost:33000`,输入账号密码
|
|
68
|
+
3. 点「新建会话」,输入名称
|
|
69
|
+
4. 点「MCP 配置」,复制 JSON 配置
|
|
70
|
+
5. 粘贴到 Cursor / Claude Desktop 的 MCP 配置中
|
|
71
|
+
6. Agent 开始操作浏览器
|
|
72
|
+
7. 需要人工介入时(验证码等),在管理页面点「查看浏览器」直接操作
|
|
73
|
+
|
|
74
|
+
## 架构
|
|
75
|
+
|
|
76
|
+
```
|
|
77
|
+
browser-server
|
|
78
|
+
├── Web 管理 (:33000) ← 用户浏览器访问
|
|
79
|
+
│ ├── 会话列表(创建/删除)
|
|
80
|
+
│ ├── 查看浏览器(noVNC 实时画面 + 操作)
|
|
81
|
+
│ └── MCP 配置(一键复制)
|
|
82
|
+
│
|
|
83
|
+
├── MCP 服务 (:33001) ← Agent 连接
|
|
84
|
+
│ └── 15 个浏览器操控工具
|
|
85
|
+
│
|
|
86
|
+
└── 每个会话 = 独立进程组
|
|
87
|
+
├── Xvfb (虚拟显示器)
|
|
88
|
+
├── Chromium (--user-data-dir 持久化)
|
|
89
|
+
└── x11vnc (VNC 服务)
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## MCP 工具列表
|
|
93
|
+
|
|
94
|
+
| 工具 | 说明 |
|
|
95
|
+
|------|------|
|
|
96
|
+
| navigate_page | 导航到指定 URL |
|
|
97
|
+
| click | 点击页面元素 |
|
|
98
|
+
| fill | 填写输入框 |
|
|
99
|
+
| hover | 悬停在元素上 |
|
|
100
|
+
| press_key | 按下键盘按键 |
|
|
101
|
+
| take_screenshot | 截取页面截图 |
|
|
102
|
+
| take_snapshot | 获取页面 DOM 快照 |
|
|
103
|
+
| evaluate_script | 执行 JavaScript |
|
|
104
|
+
| list_pages | 列出所有标签页 |
|
|
105
|
+
| new_page | 打开新标签页 |
|
|
106
|
+
| close_page | 关闭当前标签页 |
|
|
107
|
+
| select_page | 切换标签页 |
|
|
108
|
+
| wait_for | 等待元素出现或指定时间 |
|
|
109
|
+
| emulate | 模拟移动设备 |
|
|
110
|
+
| resize_page | 调整视口大小 |
|
|
111
|
+
|
|
112
|
+
## 数据目录结构
|
|
113
|
+
|
|
114
|
+
```
|
|
115
|
+
~/.browser-server/
|
|
116
|
+
├── server.pid # 守护进程 PID
|
|
117
|
+
├── server.json # 运行配置
|
|
118
|
+
├── sessions.json # 会话列表
|
|
119
|
+
├── server.log # 服务日志
|
|
120
|
+
├── <session-id>/ # 会话目录
|
|
121
|
+
│ ├── chrome-data/ # Chromium 用户数据(cookie/storage 等)
|
|
122
|
+
│ └── chrome.log # Chromium 日志
|
|
123
|
+
└── ...
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
## Docker 部署
|
|
127
|
+
|
|
128
|
+
适用于 macOS / Windows / 任何不想手动安装系统依赖的场景:
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
# 构建镜像
|
|
132
|
+
cd packages/browser-server
|
|
133
|
+
npm run build
|
|
134
|
+
docker build -t browser-server .
|
|
135
|
+
|
|
136
|
+
# 运行
|
|
137
|
+
docker run -d \
|
|
138
|
+
-p 33000:33000 \
|
|
139
|
+
-p 33001:33001 \
|
|
140
|
+
-v ~/.browser-server:/data \
|
|
141
|
+
browser-server start -u admin -p mypassword -d /data --host 0.0.0.0
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
镜像内已包含 Chromium、Xvfb、x11vnc 和中文字体,无需额外配置。
|
|
145
|
+
|
|
146
|
+
## License
|
|
147
|
+
|
|
148
|
+
MIT
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { Command } from 'commander';
|
|
3
|
+
import { fork } from 'node:child_process';
|
|
4
|
+
import * as path from 'node:path';
|
|
5
|
+
import { fileURLToPath } from 'node:url';
|
|
6
|
+
import { resolveDataDir, ensureDataDir, loadPid, removePid, isProcessRunning, loadServerConfig, saveServerConfig, } from './utils/config.js';
|
|
7
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
8
|
+
const __dirname = path.dirname(__filename);
|
|
9
|
+
const program = new Command();
|
|
10
|
+
program.name('browser-server').description('Self-hosted headless browser service for AI agents').version('0.1.0');
|
|
11
|
+
program
|
|
12
|
+
.command('start')
|
|
13
|
+
.description('启动浏览器服务(守护进程模式)')
|
|
14
|
+
.requiredOption('-u, --user <username>', 'Web 管理登录用户名')
|
|
15
|
+
.requiredOption('-p, --pass <password>', 'Web 管理登录密码')
|
|
16
|
+
.option('-w, --web-port <port>', 'Web 管理端口', '33000')
|
|
17
|
+
.option('-m, --mcp-port <port>', 'MCP 服务端口', '33001')
|
|
18
|
+
.option('-d, --data-dir <path>', '服务根目录', resolveDataDir())
|
|
19
|
+
.option('--host <host>', '监听地址', '0.0.0.0')
|
|
20
|
+
.action(async (opts) => {
|
|
21
|
+
const dataDir = resolveDataDir(opts.dataDir);
|
|
22
|
+
const existingPid = loadPid(dataDir);
|
|
23
|
+
if (existingPid && isProcessRunning(existingPid)) {
|
|
24
|
+
console.error(`服务已在运行 (PID: ${existingPid})。如需重启请使用 restart 命令。`);
|
|
25
|
+
process.exit(1);
|
|
26
|
+
}
|
|
27
|
+
const config = {
|
|
28
|
+
webPort: parseInt(opts.webPort, 10),
|
|
29
|
+
mcpPort: parseInt(opts.mcpPort, 10),
|
|
30
|
+
host: opts.host,
|
|
31
|
+
user: opts.user,
|
|
32
|
+
pass: opts.pass,
|
|
33
|
+
dataDir,
|
|
34
|
+
};
|
|
35
|
+
ensureDataDir(dataDir);
|
|
36
|
+
saveServerConfig(config);
|
|
37
|
+
const daemonScript = path.join(__dirname, 'daemon.js');
|
|
38
|
+
const child = fork(daemonScript, [], {
|
|
39
|
+
detached: true,
|
|
40
|
+
stdio: 'ignore',
|
|
41
|
+
env: { ...process.env, BROWSER_SERVER_DATA_DIR: dataDir },
|
|
42
|
+
});
|
|
43
|
+
child.unref();
|
|
44
|
+
console.log(`browser-server 已启动 (PID: ${child.pid})`);
|
|
45
|
+
console.log(` Web 管理: http://${config.host}:${config.webPort}`);
|
|
46
|
+
console.log(` MCP 服务: http://${config.host}:${config.mcpPort}`);
|
|
47
|
+
console.log(` 数据目录: ${dataDir}`);
|
|
48
|
+
process.exit(0);
|
|
49
|
+
});
|
|
50
|
+
program
|
|
51
|
+
.command('stop')
|
|
52
|
+
.description('停止浏览器服务')
|
|
53
|
+
.option('-d, --data-dir <path>', '服务根目录', resolveDataDir())
|
|
54
|
+
.action(async (opts) => {
|
|
55
|
+
const dataDir = resolveDataDir(opts.dataDir);
|
|
56
|
+
const pid = loadPid(dataDir);
|
|
57
|
+
if (!pid || !isProcessRunning(pid)) {
|
|
58
|
+
console.log('browser-server 未运行');
|
|
59
|
+
removePid(dataDir);
|
|
60
|
+
process.exit(0);
|
|
61
|
+
}
|
|
62
|
+
console.log(`正在停止 browser-server (PID: ${pid})...`);
|
|
63
|
+
process.kill(pid, 'SIGTERM');
|
|
64
|
+
const timeout = 10_000;
|
|
65
|
+
const start = Date.now();
|
|
66
|
+
while (Date.now() - start < timeout) {
|
|
67
|
+
if (!isProcessRunning(pid)) {
|
|
68
|
+
removePid(dataDir);
|
|
69
|
+
console.log('browser-server 已停止');
|
|
70
|
+
process.exit(0);
|
|
71
|
+
}
|
|
72
|
+
await sleep(500);
|
|
73
|
+
}
|
|
74
|
+
console.warn('优雅退出超时,强制终止...');
|
|
75
|
+
try {
|
|
76
|
+
process.kill(pid, 'SIGKILL');
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
// already dead
|
|
80
|
+
}
|
|
81
|
+
removePid(dataDir);
|
|
82
|
+
console.log('browser-server 已强制停止');
|
|
83
|
+
process.exit(0);
|
|
84
|
+
});
|
|
85
|
+
program
|
|
86
|
+
.command('restart')
|
|
87
|
+
.description('重启浏览器服务(等同于 stop + start)')
|
|
88
|
+
.requiredOption('-u, --user <username>', 'Web 管理登录用户名')
|
|
89
|
+
.requiredOption('-p, --pass <password>', 'Web 管理登录密码')
|
|
90
|
+
.option('-w, --web-port <port>', 'Web 管理端口', '33000')
|
|
91
|
+
.option('-m, --mcp-port <port>', 'MCP 服务端口', '33001')
|
|
92
|
+
.option('-d, --data-dir <path>', '服务根目录', resolveDataDir())
|
|
93
|
+
.option('--host <host>', '监听地址', '0.0.0.0')
|
|
94
|
+
.action(async (opts) => {
|
|
95
|
+
const dataDir = resolveDataDir(opts.dataDir);
|
|
96
|
+
const pid = loadPid(dataDir);
|
|
97
|
+
if (pid && isProcessRunning(pid)) {
|
|
98
|
+
console.log(`正在停止 browser-server (PID: ${pid})...`);
|
|
99
|
+
process.kill(pid, 'SIGTERM');
|
|
100
|
+
const start = Date.now();
|
|
101
|
+
while (Date.now() - start < 10_000) {
|
|
102
|
+
if (!isProcessRunning(pid))
|
|
103
|
+
break;
|
|
104
|
+
await sleep(500);
|
|
105
|
+
}
|
|
106
|
+
if (isProcessRunning(pid)) {
|
|
107
|
+
try {
|
|
108
|
+
process.kill(pid, 'SIGKILL');
|
|
109
|
+
}
|
|
110
|
+
catch { /* */ }
|
|
111
|
+
}
|
|
112
|
+
removePid(dataDir);
|
|
113
|
+
console.log('已停止');
|
|
114
|
+
}
|
|
115
|
+
// re-trigger start
|
|
116
|
+
const config = {
|
|
117
|
+
webPort: parseInt(opts.webPort, 10),
|
|
118
|
+
mcpPort: parseInt(opts.mcpPort, 10),
|
|
119
|
+
host: opts.host,
|
|
120
|
+
user: opts.user,
|
|
121
|
+
pass: opts.pass,
|
|
122
|
+
dataDir,
|
|
123
|
+
};
|
|
124
|
+
ensureDataDir(dataDir);
|
|
125
|
+
saveServerConfig(config);
|
|
126
|
+
const daemonScript = path.join(__dirname, 'daemon.js');
|
|
127
|
+
const child = fork(daemonScript, [], {
|
|
128
|
+
detached: true,
|
|
129
|
+
stdio: 'ignore',
|
|
130
|
+
env: { ...process.env, BROWSER_SERVER_DATA_DIR: dataDir },
|
|
131
|
+
});
|
|
132
|
+
child.unref();
|
|
133
|
+
console.log(`browser-server 已重启 (PID: ${child.pid})`);
|
|
134
|
+
console.log(` Web 管理: http://${config.host}:${config.webPort}`);
|
|
135
|
+
console.log(` MCP 服务: http://${config.host}:${config.mcpPort}`);
|
|
136
|
+
process.exit(0);
|
|
137
|
+
});
|
|
138
|
+
program
|
|
139
|
+
.command('status')
|
|
140
|
+
.description('查看服务状态')
|
|
141
|
+
.option('-d, --data-dir <path>', '服务根目录', resolveDataDir())
|
|
142
|
+
.action((opts) => {
|
|
143
|
+
const dataDir = resolveDataDir(opts.dataDir);
|
|
144
|
+
const pid = loadPid(dataDir);
|
|
145
|
+
if (!pid || !isProcessRunning(pid)) {
|
|
146
|
+
console.log('browser-server 未运行');
|
|
147
|
+
process.exit(0);
|
|
148
|
+
}
|
|
149
|
+
const config = loadServerConfig(dataDir);
|
|
150
|
+
console.log(`browser-server 运行中 (PID: ${pid})`);
|
|
151
|
+
if (config) {
|
|
152
|
+
console.log(` Web 管理: http://${config.host}:${config.webPort}`);
|
|
153
|
+
console.log(` MCP 服务: http://${config.host}:${config.mcpPort}`);
|
|
154
|
+
console.log(` 数据目录: ${config.dataDir}`);
|
|
155
|
+
}
|
|
156
|
+
});
|
|
157
|
+
program.parse();
|
|
158
|
+
function sleep(ms) {
|
|
159
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
160
|
+
}
|
|
161
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
package/dist/daemon.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"daemon.d.ts","sourceRoot":"","sources":["../src/daemon.ts"],"names":[],"mappings":""}
|
package/dist/daemon.js
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { loadServerConfig, savePid, removePid, } from './utils/config.js';
|
|
2
|
+
import { initLogger, log, closeLogger } from './utils/logger.js';
|
|
3
|
+
import { SessionManager } from './session/session-manager.js';
|
|
4
|
+
import { WebServer } from './server/web-server.js';
|
|
5
|
+
import { McpServer } from './server/mcp-server.js';
|
|
6
|
+
const dataDir = process.env.BROWSER_SERVER_DATA_DIR;
|
|
7
|
+
if (!dataDir) {
|
|
8
|
+
console.error('BROWSER_SERVER_DATA_DIR 环境变量未设置');
|
|
9
|
+
process.exit(1);
|
|
10
|
+
}
|
|
11
|
+
const config = loadServerConfig(dataDir);
|
|
12
|
+
if (!config) {
|
|
13
|
+
console.error('无法读取服务配置');
|
|
14
|
+
process.exit(1);
|
|
15
|
+
}
|
|
16
|
+
initLogger(dataDir);
|
|
17
|
+
savePid(dataDir, process.pid);
|
|
18
|
+
const sessionManager = new SessionManager(dataDir);
|
|
19
|
+
const webServer = new WebServer(config, sessionManager);
|
|
20
|
+
const mcpServer = new McpServer(config, sessionManager);
|
|
21
|
+
async function startup() {
|
|
22
|
+
log.info('browser-server 启动中...');
|
|
23
|
+
log.info(`PID: ${process.pid}`);
|
|
24
|
+
log.info(`Web 端口: ${config.webPort}, MCP 端口: ${config.mcpPort}`);
|
|
25
|
+
await sessionManager.restoreSessions();
|
|
26
|
+
await webServer.start();
|
|
27
|
+
await mcpServer.start();
|
|
28
|
+
log.info('browser-server 启动完成');
|
|
29
|
+
}
|
|
30
|
+
async function shutdown() {
|
|
31
|
+
log.info('browser-server 正在关闭...');
|
|
32
|
+
await mcpServer.stop();
|
|
33
|
+
await webServer.stop();
|
|
34
|
+
await sessionManager.stopAllSessions();
|
|
35
|
+
removePid(dataDir);
|
|
36
|
+
closeLogger();
|
|
37
|
+
}
|
|
38
|
+
process.on('SIGTERM', async () => {
|
|
39
|
+
await shutdown();
|
|
40
|
+
process.exit(0);
|
|
41
|
+
});
|
|
42
|
+
process.on('SIGINT', async () => {
|
|
43
|
+
await shutdown();
|
|
44
|
+
process.exit(0);
|
|
45
|
+
});
|
|
46
|
+
process.on('uncaughtException', (err) => {
|
|
47
|
+
log.error('未捕获的异常:', err.message);
|
|
48
|
+
});
|
|
49
|
+
process.on('unhandledRejection', (reason) => {
|
|
50
|
+
log.error('未处理的 Promise rejection:', reason);
|
|
51
|
+
});
|
|
52
|
+
startup().catch((err) => {
|
|
53
|
+
log.error('启动失败:', err.message);
|
|
54
|
+
removePid(dataDir);
|
|
55
|
+
process.exit(1);
|
|
56
|
+
});
|
|
57
|
+
//# sourceMappingURL=daemon.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"daemon.js","sourceRoot":"","sources":["../src/daemon.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export { SessionManager } from './session/session-manager.js';
|
|
2
|
+
export { ProcessGroup } from './session/process-group.js';
|
|
3
|
+
export { WebServer } from './server/web-server.js';
|
|
4
|
+
export { McpServer } from './server/mcp-server.js';
|
|
5
|
+
export { ConnectionPool } from './mcp/connection-pool.js';
|
|
6
|
+
export { defineTools } from './mcp/tools.js';
|
|
7
|
+
export type { ServerConfig, SessionInfo, SessionsData } from './utils/types.js';
|
|
8
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAC9D,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAC1D,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACnD,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACnD,OAAO,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAC1D,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAC7C,YAAY,EAAE,YAAY,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { SessionManager } from './session/session-manager.js';
|
|
2
|
+
export { ProcessGroup } from './session/process-group.js';
|
|
3
|
+
export { WebServer } from './server/web-server.js';
|
|
4
|
+
export { McpServer } from './server/mcp-server.js';
|
|
5
|
+
export { ConnectionPool } from './mcp/connection-pool.js';
|
|
6
|
+
export { defineTools } from './mcp/tools.js';
|
|
7
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAC9D,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAC1D,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACnD,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACnD,OAAO,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAC1D,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { BrowserConnection } from './tools.js';
|
|
2
|
+
/**
|
|
3
|
+
* 管理到各 session 的 Playwright CDP 连接。
|
|
4
|
+
* 懒加载:首次访问某 session 时建立连接,之后缓存复用。
|
|
5
|
+
*/
|
|
6
|
+
export declare class ConnectionPool {
|
|
7
|
+
private connections;
|
|
8
|
+
private connecting;
|
|
9
|
+
getConnection(sessionId: string, cdpPort: number): Promise<BrowserConnection>;
|
|
10
|
+
private doConnect;
|
|
11
|
+
disconnect(sessionId: string): Promise<void>;
|
|
12
|
+
disconnectAll(): Promise<void>;
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=connection-pool.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"connection-pool.d.ts","sourceRoot":"","sources":["../../src/mcp/connection-pool.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAEpD;;;GAGG;AACH,qBAAa,cAAc;IACzB,OAAO,CAAC,WAAW,CAA6C;IAChE,OAAO,CAAC,UAAU,CAAsD;IAElE,aAAa,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,CAAC;YA0BrE,SAAS;IAcjB,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAY5C,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC;CAKrC"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { chromium } from 'playwright-core';
|
|
2
|
+
import { log } from '../utils/logger.js';
|
|
3
|
+
/**
|
|
4
|
+
* 管理到各 session 的 Playwright CDP 连接。
|
|
5
|
+
* 懒加载:首次访问某 session 时建立连接,之后缓存复用。
|
|
6
|
+
*/
|
|
7
|
+
export class ConnectionPool {
|
|
8
|
+
connections = new Map();
|
|
9
|
+
connecting = new Map();
|
|
10
|
+
async getConnection(sessionId, cdpPort) {
|
|
11
|
+
const existing = this.connections.get(sessionId);
|
|
12
|
+
if (existing) {
|
|
13
|
+
try {
|
|
14
|
+
await existing.activePage.evaluate('1');
|
|
15
|
+
return existing;
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
log.warn(`[${sessionId}] CDP 连接失效,重新连接...`);
|
|
19
|
+
this.connections.delete(sessionId);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
const inflight = this.connecting.get(sessionId);
|
|
23
|
+
if (inflight)
|
|
24
|
+
return inflight;
|
|
25
|
+
const promise = this.doConnect(sessionId, cdpPort);
|
|
26
|
+
this.connecting.set(sessionId, promise);
|
|
27
|
+
try {
|
|
28
|
+
const conn = await promise;
|
|
29
|
+
this.connections.set(sessionId, conn);
|
|
30
|
+
return conn;
|
|
31
|
+
}
|
|
32
|
+
finally {
|
|
33
|
+
this.connecting.delete(sessionId);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
async doConnect(sessionId, cdpPort) {
|
|
37
|
+
const cdpUrl = `http://127.0.0.1:${cdpPort}`;
|
|
38
|
+
log.info(`[${sessionId}] 连接 CDP: ${cdpUrl}`);
|
|
39
|
+
const browser = await chromium.connectOverCDP(cdpUrl, { timeout: 10_000 });
|
|
40
|
+
const contexts = browser.contexts();
|
|
41
|
+
const context = contexts[0] || await browser.newContext();
|
|
42
|
+
const pages = context.pages();
|
|
43
|
+
const activePage = pages[0] || await context.newPage();
|
|
44
|
+
log.info(`[${sessionId}] CDP 连接成功,${pages.length} 个标签页`);
|
|
45
|
+
return { browser, context, activePage, pages };
|
|
46
|
+
}
|
|
47
|
+
async disconnect(sessionId) {
|
|
48
|
+
const conn = this.connections.get(sessionId);
|
|
49
|
+
if (conn) {
|
|
50
|
+
try {
|
|
51
|
+
await conn.browser.close();
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
// ignore
|
|
55
|
+
}
|
|
56
|
+
this.connections.delete(sessionId);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
async disconnectAll() {
|
|
60
|
+
for (const [id] of this.connections) {
|
|
61
|
+
await this.disconnect(id);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=connection-pool.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"connection-pool.js","sourceRoot":"","sources":["../../src/mcp/connection-pool.ts"],"names":[],"mappings":"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"}
|