bb-browser 0.8.3 → 0.10.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/dist/cdp-monitor.js +535 -0
- package/dist/cdp-monitor.js.map +1 -0
- package/dist/chunk-AHGAQEFO.js +0 -0
- package/dist/chunk-D4HDZEJT.js +0 -0
- package/dist/chunk-DBJBHYC7.js +0 -0
- package/dist/chunk-FSL4RNI6.js +53 -0
- package/dist/chunk-FSL4RNI6.js.map +1 -0
- package/dist/cli.js +423 -87
- package/dist/cli.js.map +1 -1
- package/dist/daemon.js +1 -1
- package/dist/daemon.js.map +1 -1
- package/dist/jq-HHMLHEPA.js +0 -0
- package/dist/mcp.js +194 -5
- package/dist/mcp.js.map +1 -1
- package/dist/{openclaw-bridge-P3G4KGYM.js → openclaw-bridge-HBJH6UFO.js} +7 -4
- package/dist/openclaw-bridge-HBJH6UFO.js.map +1 -0
- package/extension/background.js +2 -1
- package/extension/background.js.map +1 -1
- package/extension/manifest.json +26 -7
- package/package.json +21 -16
- package/dist/openclaw-bridge-P3G4KGYM.js.map +0 -1
- package/extension/dist/background.js +0 -3257
- package/extension/dist/background.js.map +0 -1
- package/extension/dist/buildDomTree.js +0 -1505
- package/extension/dist/content/trace.js +0 -339
- package/extension/dist/content/trace.js.map +0 -1
- package/extension/dist/manifest.json +0 -26
- package/extension/dist/options.html +0 -26
- package/extension/dist/options.js +0 -19
- package/extension/dist/options.js.map +0 -1
package/dist/daemon.js
CHANGED
|
@@ -155,7 +155,7 @@ var RequestManager = class {
|
|
|
155
155
|
* 清理所有 pending 请求
|
|
156
156
|
*/
|
|
157
157
|
clear() {
|
|
158
|
-
for (const
|
|
158
|
+
for (const request of this.pending.values()) {
|
|
159
159
|
clearTimeout(request.timeout);
|
|
160
160
|
request.reject(new Error("Daemon shutting down"));
|
|
161
161
|
}
|
package/dist/daemon.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../packages/daemon/src/index.ts","../packages/daemon/src/http-server.ts","../packages/daemon/src/sse-manager.ts","../packages/daemon/src/request-manager.ts"],"sourcesContent":["/**\n * bb-browser Daemon 主入口\n *\n * HTTP Server + SSE 推送架构\n *\n * 职责:\n * 1. 启动 HTTP 服务器监听 localhost:19824\n * 2. 处理 CLI 命令请求 (POST /command)\n * 3. 管理扩展 SSE 连接 (GET /sse)\n * 4. 接收扩展结果回传 (POST /result)\n */\n\nimport { parseArgs } from \"node:util\";\nimport { writeFileSync, unlinkSync, existsSync } from \"node:fs\";\nimport { DAEMON_PORT, DAEMON_HOST } from \"@bb-browser/shared\";\nimport { HttpServer } from \"./http-server.js\";\n\nconst PID_FILE_PATH = \"/tmp/bb-browser.pid\";\n\ninterface DaemonOptions {\n host: string;\n port: number;\n}\n\n/**\n * 解析命令行参数\n */\nfunction parseOptions(): DaemonOptions {\n const { values } = parseArgs({\n allowPositionals: true,\n options: {\n host: {\n type: \"string\",\n short: \"H\",\n default: DAEMON_HOST,\n },\n port: {\n type: \"string\",\n short: \"p\",\n default: String(DAEMON_PORT),\n },\n help: {\n type: \"boolean\",\n short: \"h\",\n default: false,\n },\n },\n });\n\n if (values.help) {\n console.error(`\nbb-browser-daemon - HTTP Server Daemon for bb-browser\n\nUsage:\n bb-browser-daemon [options]\n\nOptions:\n -H, --host <host> HTTP server host (default: ${DAEMON_HOST})\n -p, --port <port> HTTP server port (default: ${DAEMON_PORT})\n -h, --help Show this help message\n\nEndpoints:\n POST /command Send command and wait for result (CLI)\n GET /sse Subscribe to command stream (Extension)\n POST /result Report command result (Extension)\n GET /status Query daemon status\n`);\n process.exit(0);\n }\n\n return {\n host: values.host ?? DAEMON_HOST,\n port: parseInt(values.port ?? String(DAEMON_PORT), 10),\n };\n}\n\n/**\n * 写入 PID 文件\n */\nfunction writePidFile(): void {\n writeFileSync(PID_FILE_PATH, String(process.pid), \"utf-8\");\n}\n\n/**\n * 清理 PID 文件\n */\nfunction cleanupPidFile(): void {\n if (existsSync(PID_FILE_PATH)) {\n try {\n unlinkSync(PID_FILE_PATH);\n } catch {\n // 忽略清理失败\n }\n }\n}\n\n/**\n * 主函数\n */\nasync function main(): Promise<void> {\n const options = parseOptions();\n\n // 优雅关闭\n const shutdown = async () => {\n console.error(\"[Daemon] Shutting down...\");\n await httpServer.stop();\n cleanupPidFile();\n process.exit(0);\n };\n\n // 创建 HTTP 服务器\n const httpServer = new HttpServer({\n host: options.host,\n port: options.port,\n onShutdown: shutdown,\n });\n\n process.on(\"SIGINT\", shutdown);\n process.on(\"SIGTERM\", shutdown);\n\n // 启动服务器\n await httpServer.start();\n\n // 写入 PID 文件\n writePidFile();\n\n console.error(`[Daemon] HTTP server listening on http://${options.host}:${options.port}`);\n console.error(\"[Daemon] Waiting for extension connection...\");\n}\n\n// 启动 Daemon\nmain().catch((error) => {\n console.error(\"[Daemon] Fatal error:\", error);\n cleanupPidFile();\n process.exit(1);\n});\n","/**\n * HTTP 服务器\n *\n * 提供 REST API 端点:\n * - POST /command: CLI 发送命令\n * - GET /sse: 扩展订阅命令流\n * - POST /result: 扩展回传结果\n * - GET /status: 查询状态\n */\n\nimport { createServer, type Server, type IncomingMessage, type ServerResponse } from \"node:http\";\nimport type { Request, Response } from \"@bb-browser/shared\";\nimport { DAEMON_PORT } from \"@bb-browser/shared\";\nimport { SSEManager } from \"./sse-manager.js\";\nimport { RequestManager } from \"./request-manager.js\";\n\nexport interface HttpServerOptions {\n host?: string;\n port?: number;\n onShutdown?: () => void;\n}\n\n/**\n * HTTP 服务器\n */\nexport class HttpServer {\n private server: Server | null = null;\n private host: string;\n private port: number;\n private startTime: number = 0;\n private onShutdown?: () => void;\n\n readonly sseManager = new SSEManager();\n readonly requestManager = new RequestManager();\n\n constructor(options: HttpServerOptions = {}) {\n this.host = options.host ?? \"127.0.0.1\";\n this.port = options.port ?? DAEMON_PORT;\n this.onShutdown = options.onShutdown;\n }\n\n /**\n * 启动服务器\n */\n async start(): Promise<void> {\n return new Promise((resolve, reject) => {\n this.server = createServer((req, res) => {\n this.handleRequest(req, res);\n });\n\n this.server.on(\"error\", (error) => {\n reject(error);\n });\n\n this.server.listen(this.port, this.host, () => {\n this.startTime = Date.now();\n resolve();\n });\n });\n }\n\n /**\n * 停止服务器\n */\n async stop(): Promise<void> {\n // 清理 pending 请求\n this.requestManager.clear();\n\n // 断开 SSE 连接\n this.sseManager.disconnect();\n\n // 关闭服务器\n if (this.server) {\n return new Promise((resolve) => {\n this.server!.close(() => {\n resolve();\n });\n });\n }\n }\n\n /**\n * 获取运行时间(秒)\n */\n get uptime(): number {\n if (this.startTime === 0) {\n return 0;\n }\n return Math.floor((Date.now() - this.startTime) / 1000);\n }\n\n /**\n * 路由请求\n */\n private handleRequest(req: IncomingMessage, res: ServerResponse): void {\n // CORS 支持\n res.setHeader(\"Access-Control-Allow-Origin\", \"*\");\n res.setHeader(\"Access-Control-Allow-Methods\", \"GET, POST, OPTIONS\");\n res.setHeader(\"Access-Control-Allow-Headers\", \"Content-Type\");\n\n if (req.method === \"OPTIONS\") {\n res.writeHead(204);\n res.end();\n return;\n }\n\n const url = req.url ?? \"/\";\n\n if (req.method === \"POST\" && url === \"/command\") {\n this.handleCommand(req, res);\n } else if (req.method === \"GET\" && url === \"/sse\") {\n this.handleSSE(req, res);\n } else if (req.method === \"POST\" && url === \"/result\") {\n this.handleResult(req, res);\n } else if (req.method === \"GET\" && url === \"/status\") {\n this.handleStatus(req, res);\n } else if (req.method === \"POST\" && url === \"/shutdown\") {\n this.handleShutdown(req, res);\n } else {\n this.sendJson(res, 404, { error: \"Not found\" });\n }\n }\n\n /**\n * POST /command - CLI 发送命令\n */\n private async handleCommand(req: IncomingMessage, res: ServerResponse): Promise<void> {\n try {\n const body = await this.readBody(req);\n const request = JSON.parse(body) as Request;\n\n // 检查扩展是否连接\n if (!this.sseManager.isConnected) {\n this.sendJson(res, 503, {\n id: request.id,\n success: false,\n error: \"Extension not connected\",\n });\n return;\n }\n\n // 创建 Promise 等待响应\n const responsePromise = new Promise<Response>((resolve, reject) => {\n this.requestManager.add(request.id, resolve, reject);\n });\n\n // 推送命令给扩展\n const sent = this.sseManager.sendCommand(request);\n if (!sent) {\n // 移除 pending 请求\n this.requestManager.resolve(request.id, {\n id: request.id,\n success: false,\n error: \"Failed to send command to extension\",\n });\n this.sendJson(res, 503, {\n id: request.id,\n success: false,\n error: \"Failed to send command to extension\",\n });\n return;\n }\n\n // 等待响应\n try {\n const response = await responsePromise;\n this.sendJson(res, 200, response);\n } catch (error) {\n // 超时或其他错误\n this.sendJson(res, 408, {\n id: request.id,\n success: false,\n error: error instanceof Error ? error.message : \"Unknown error\",\n });\n }\n } catch (error) {\n this.sendJson(res, 400, {\n success: false,\n error: error instanceof Error ? error.message : \"Invalid request\",\n });\n }\n }\n\n /**\n * GET /sse - 扩展订阅命令流\n */\n private handleSSE(_req: IncomingMessage, res: ServerResponse): void {\n this.sseManager.connect(res);\n }\n\n /**\n * POST /result - 扩展回传结果\n */\n private async handleResult(req: IncomingMessage, res: ServerResponse): Promise<void> {\n try {\n const body = await this.readBody(req);\n const result = JSON.parse(body) as Response;\n\n // 匹配 pending 请求\n const resolved = this.requestManager.resolve(result.id, result);\n\n if (resolved) {\n this.sendJson(res, 200, { code: 0, message: \"ok\" });\n } else {\n // 找不到对应请求(可能已超时)\n this.sendJson(res, 200, { code: 1, message: \"Request not found or already expired\" });\n }\n } catch (error) {\n this.sendJson(res, 400, {\n code: -1,\n message: error instanceof Error ? error.message : \"Invalid request\",\n });\n }\n }\n\n /**\n * GET /status - 查询状态\n */\n private handleStatus(_req: IncomingMessage, res: ServerResponse): void {\n this.sendJson(res, 200, {\n running: true,\n extensionConnected: this.sseManager.isConnected,\n pendingRequests: this.requestManager.pendingCount,\n uptime: this.uptime,\n });\n }\n\n /**\n * POST /shutdown - 关闭服务器\n */\n private handleShutdown(_req: IncomingMessage, res: ServerResponse): void {\n this.sendJson(res, 200, { code: 0, message: \"Shutting down\" });\n \n // 延迟关闭,确保响应发送完成\n setTimeout(() => {\n if (this.onShutdown) {\n this.onShutdown();\n }\n }, 100);\n }\n\n /**\n * 读取请求体\n */\n private readBody(req: IncomingMessage): Promise<string> {\n return new Promise((resolve, reject) => {\n const chunks: Buffer[] = [];\n\n req.on(\"data\", (chunk: Buffer) => {\n chunks.push(chunk);\n });\n\n req.on(\"end\", () => {\n resolve(Buffer.concat(chunks).toString(\"utf-8\"));\n });\n\n req.on(\"error\", (error) => {\n reject(error);\n });\n });\n }\n\n /**\n * 发送 JSON 响应\n */\n private sendJson(res: ServerResponse, status: number, data: unknown): void {\n res.writeHead(status, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify(data));\n }\n}\n","/**\n * SSE 连接管理\n *\n * 职责:\n * - 管理与扩展的 SSE 长连接\n * - 发送心跳保活\n * - 推送命令事件\n */\n\nimport type { ServerResponse } from \"node:http\";\nimport type { Request } from \"@bb-browser/shared\";\nimport { SSE_HEARTBEAT_INTERVAL } from \"@bb-browser/shared\";\n\n/**\n * SSE 连接管理器\n */\nexport class SSEManager {\n private connection: ServerResponse | null = null;\n private heartbeatTimer: NodeJS.Timeout | null = null;\n\n /**\n * 检查是否有活跃连接\n */\n get isConnected(): boolean {\n return this.connection !== null && !this.connection.writableEnded;\n }\n\n /**\n * 建立 SSE 连接\n */\n connect(res: ServerResponse): void {\n // 关闭旧连接\n if (this.connection) {\n this.disconnect();\n }\n\n // 设置 SSE 响应头\n res.writeHead(200, {\n \"Content-Type\": \"text/event-stream\",\n \"Cache-Control\": \"no-cache\",\n Connection: \"keep-alive\",\n \"Access-Control-Allow-Origin\": \"*\",\n });\n\n this.connection = res;\n\n // 发送 connected 事件\n this.sendEvent(\"connected\", { time: Date.now() });\n\n // 启动心跳\n this.startHeartbeat();\n\n // 监听连接关闭\n res.on(\"close\", () => {\n this.cleanupConnection();\n });\n }\n\n /**\n * 断开连接\n */\n disconnect(): void {\n this.stopHeartbeat();\n\n if (this.connection && !this.connection.writableEnded) {\n this.connection.end();\n }\n\n this.connection = null;\n }\n\n /**\n * 发送命令给扩展\n */\n sendCommand(request: Request): boolean {\n return this.sendEvent(\"command\", request);\n }\n\n /**\n * 发送 SSE 事件\n */\n private sendEvent(eventType: string, data: unknown): boolean {\n if (!this.connection || this.connection.writableEnded) {\n return false;\n }\n\n try {\n this.connection.write(`event: ${eventType}\\n`);\n this.connection.write(`data: ${JSON.stringify(data)}\\n\\n`);\n return true;\n } catch {\n return false;\n }\n }\n\n /**\n * 启动心跳定时器\n */\n private startHeartbeat(): void {\n this.stopHeartbeat();\n\n this.heartbeatTimer = setInterval(() => {\n const sent = this.sendEvent(\"heartbeat\", { time: Date.now() });\n if (!sent) {\n this.cleanupConnection();\n }\n }, SSE_HEARTBEAT_INTERVAL);\n }\n\n /**\n * 停止心跳定时器\n */\n private stopHeartbeat(): void {\n if (this.heartbeatTimer) {\n clearInterval(this.heartbeatTimer);\n this.heartbeatTimer = null;\n }\n }\n\n /**\n * 清理连接\n */\n private cleanupConnection(): void {\n this.stopHeartbeat();\n this.connection = null;\n }\n}\n","/**\n * Pending 请求管理\n *\n * 职责:\n * - 管理等待响应的请求\n * - 超时处理\n * - 匹配请求和响应\n */\n\nimport type { Response } from \"@bb-browser/shared\";\nimport { COMMAND_TIMEOUT } from \"@bb-browser/shared\";\n\ninterface PendingRequest {\n resolve: (response: Response) => void;\n reject: (error: Error) => void;\n timeout: NodeJS.Timeout;\n}\n\n/**\n * 请求管理器\n */\nexport class RequestManager {\n private pending = new Map<string, PendingRequest>();\n\n /**\n * 获取等待中的请求数量\n */\n get pendingCount(): number {\n return this.pending.size;\n }\n\n /**\n * 添加一个 pending 请求\n */\n add(\n requestId: string,\n resolve: (response: Response) => void,\n reject: (error: Error) => void\n ): void {\n // 设置超时\n const timeout = setTimeout(() => {\n this.timeout(requestId);\n }, COMMAND_TIMEOUT);\n\n this.pending.set(requestId, { resolve, reject, timeout });\n }\n\n /**\n * 解决一个 pending 请求\n * @returns 是否找到并解决了请求\n */\n resolve(requestId: string, response: Response): boolean {\n const pendingRequest = this.pending.get(requestId);\n\n if (!pendingRequest) {\n return false;\n }\n\n // 清理\n clearTimeout(pendingRequest.timeout);\n this.pending.delete(requestId);\n\n // 解决 Promise\n pendingRequest.resolve(response);\n return true;\n }\n\n /**\n * 请求超时处理\n */\n private timeout(requestId: string): void {\n const pendingRequest = this.pending.get(requestId);\n\n if (!pendingRequest) {\n return;\n }\n\n this.pending.delete(requestId);\n pendingRequest.reject(new Error(\"Command timeout\"));\n }\n\n /**\n * 清理所有 pending 请求\n */\n clear(): void {\n for (const [id, request] of this.pending) {\n clearTimeout(request.timeout);\n request.reject(new Error(\"Daemon shutting down\"));\n }\n this.pending.clear();\n }\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../packages/daemon/src/index.ts","../packages/daemon/src/http-server.ts","../packages/daemon/src/sse-manager.ts","../packages/daemon/src/request-manager.ts"],"sourcesContent":["/**\n * bb-browser Daemon 主入口\n *\n * HTTP Server + SSE 推送架构\n *\n * 职责:\n * 1. 启动 HTTP 服务器监听 localhost:19824\n * 2. 处理 CLI 命令请求 (POST /command)\n * 3. 管理扩展 SSE 连接 (GET /sse)\n * 4. 接收扩展结果回传 (POST /result)\n */\n\nimport { parseArgs } from \"node:util\";\nimport { writeFileSync, unlinkSync, existsSync } from \"node:fs\";\nimport { DAEMON_PORT, DAEMON_HOST } from \"@bb-browser/shared\";\nimport { HttpServer } from \"./http-server.js\";\n\nconst PID_FILE_PATH = \"/tmp/bb-browser.pid\";\n\ninterface DaemonOptions {\n host: string;\n port: number;\n}\n\n/**\n * 解析命令行参数\n */\nfunction parseOptions(): DaemonOptions {\n const { values } = parseArgs({\n allowPositionals: true,\n options: {\n host: {\n type: \"string\",\n short: \"H\",\n default: DAEMON_HOST,\n },\n port: {\n type: \"string\",\n short: \"p\",\n default: String(DAEMON_PORT),\n },\n help: {\n type: \"boolean\",\n short: \"h\",\n default: false,\n },\n },\n });\n\n if (values.help) {\n console.error(`\nbb-browser-daemon - HTTP Server Daemon for bb-browser\n\nUsage:\n bb-browser-daemon [options]\n\nOptions:\n -H, --host <host> HTTP server host (default: ${DAEMON_HOST})\n -p, --port <port> HTTP server port (default: ${DAEMON_PORT})\n -h, --help Show this help message\n\nEndpoints:\n POST /command Send command and wait for result (CLI)\n GET /sse Subscribe to command stream (Extension)\n POST /result Report command result (Extension)\n GET /status Query daemon status\n`);\n process.exit(0);\n }\n\n return {\n host: values.host ?? DAEMON_HOST,\n port: parseInt(values.port ?? String(DAEMON_PORT), 10),\n };\n}\n\n/**\n * 写入 PID 文件\n */\nfunction writePidFile(): void {\n writeFileSync(PID_FILE_PATH, String(process.pid), \"utf-8\");\n}\n\n/**\n * 清理 PID 文件\n */\nfunction cleanupPidFile(): void {\n if (existsSync(PID_FILE_PATH)) {\n try {\n unlinkSync(PID_FILE_PATH);\n } catch {\n // 忽略清理失败\n }\n }\n}\n\n/**\n * 主函数\n */\nasync function main(): Promise<void> {\n const options = parseOptions();\n\n // 优雅关闭\n const shutdown = async () => {\n console.error(\"[Daemon] Shutting down...\");\n await httpServer.stop();\n cleanupPidFile();\n process.exit(0);\n };\n\n // 创建 HTTP 服务器\n const httpServer = new HttpServer({\n host: options.host,\n port: options.port,\n onShutdown: shutdown,\n });\n\n process.on(\"SIGINT\", shutdown);\n process.on(\"SIGTERM\", shutdown);\n\n // 启动服务器\n await httpServer.start();\n\n // 写入 PID 文件\n writePidFile();\n\n console.error(`[Daemon] HTTP server listening on http://${options.host}:${options.port}`);\n console.error(\"[Daemon] Waiting for extension connection...\");\n}\n\n// 启动 Daemon\nmain().catch((error) => {\n console.error(\"[Daemon] Fatal error:\", error);\n cleanupPidFile();\n process.exit(1);\n});\n","/**\n * HTTP 服务器\n *\n * 提供 REST API 端点:\n * - POST /command: CLI 发送命令\n * - GET /sse: 扩展订阅命令流\n * - POST /result: 扩展回传结果\n * - GET /status: 查询状态\n */\n\nimport { createServer, type Server, type IncomingMessage, type ServerResponse } from \"node:http\";\nimport type { Request, Response } from \"@bb-browser/shared\";\nimport { DAEMON_PORT } from \"@bb-browser/shared\";\nimport { SSEManager } from \"./sse-manager.js\";\nimport { RequestManager } from \"./request-manager.js\";\n\nexport interface HttpServerOptions {\n host?: string;\n port?: number;\n onShutdown?: () => void;\n}\n\n/**\n * HTTP 服务器\n */\nexport class HttpServer {\n private server: Server | null = null;\n private host: string;\n private port: number;\n private startTime: number = 0;\n private onShutdown?: () => void;\n\n readonly sseManager = new SSEManager();\n readonly requestManager = new RequestManager();\n\n constructor(options: HttpServerOptions = {}) {\n this.host = options.host ?? \"127.0.0.1\";\n this.port = options.port ?? DAEMON_PORT;\n this.onShutdown = options.onShutdown;\n }\n\n /**\n * 启动服务器\n */\n async start(): Promise<void> {\n return new Promise((resolve, reject) => {\n this.server = createServer((req, res) => {\n this.handleRequest(req, res);\n });\n\n this.server.on(\"error\", (error) => {\n reject(error);\n });\n\n this.server.listen(this.port, this.host, () => {\n this.startTime = Date.now();\n resolve();\n });\n });\n }\n\n /**\n * 停止服务器\n */\n async stop(): Promise<void> {\n // 清理 pending 请求\n this.requestManager.clear();\n\n // 断开 SSE 连接\n this.sseManager.disconnect();\n\n // 关闭服务器\n if (this.server) {\n return new Promise((resolve) => {\n this.server!.close(() => {\n resolve();\n });\n });\n }\n }\n\n /**\n * 获取运行时间(秒)\n */\n get uptime(): number {\n if (this.startTime === 0) {\n return 0;\n }\n return Math.floor((Date.now() - this.startTime) / 1000);\n }\n\n /**\n * 路由请求\n */\n private handleRequest(req: IncomingMessage, res: ServerResponse): void {\n // CORS 支持\n res.setHeader(\"Access-Control-Allow-Origin\", \"*\");\n res.setHeader(\"Access-Control-Allow-Methods\", \"GET, POST, OPTIONS\");\n res.setHeader(\"Access-Control-Allow-Headers\", \"Content-Type\");\n\n if (req.method === \"OPTIONS\") {\n res.writeHead(204);\n res.end();\n return;\n }\n\n const url = req.url ?? \"/\";\n\n if (req.method === \"POST\" && url === \"/command\") {\n this.handleCommand(req, res);\n } else if (req.method === \"GET\" && url === \"/sse\") {\n this.handleSSE(req, res);\n } else if (req.method === \"POST\" && url === \"/result\") {\n this.handleResult(req, res);\n } else if (req.method === \"GET\" && url === \"/status\") {\n this.handleStatus(req, res);\n } else if (req.method === \"POST\" && url === \"/shutdown\") {\n this.handleShutdown(req, res);\n } else {\n this.sendJson(res, 404, { error: \"Not found\" });\n }\n }\n\n /**\n * POST /command - CLI 发送命令\n */\n private async handleCommand(req: IncomingMessage, res: ServerResponse): Promise<void> {\n try {\n const body = await this.readBody(req);\n const request = JSON.parse(body) as Request;\n\n // 检查扩展是否连接\n if (!this.sseManager.isConnected) {\n this.sendJson(res, 503, {\n id: request.id,\n success: false,\n error: \"Extension not connected\",\n });\n return;\n }\n\n // 创建 Promise 等待响应\n const responsePromise = new Promise<Response>((resolve, reject) => {\n this.requestManager.add(request.id, resolve, reject);\n });\n\n // 推送命令给扩展\n const sent = this.sseManager.sendCommand(request);\n if (!sent) {\n // 移除 pending 请求\n this.requestManager.resolve(request.id, {\n id: request.id,\n success: false,\n error: \"Failed to send command to extension\",\n });\n this.sendJson(res, 503, {\n id: request.id,\n success: false,\n error: \"Failed to send command to extension\",\n });\n return;\n }\n\n // 等待响应\n try {\n const response = await responsePromise;\n this.sendJson(res, 200, response);\n } catch (error) {\n // 超时或其他错误\n this.sendJson(res, 408, {\n id: request.id,\n success: false,\n error: error instanceof Error ? error.message : \"Unknown error\",\n });\n }\n } catch (error) {\n this.sendJson(res, 400, {\n success: false,\n error: error instanceof Error ? error.message : \"Invalid request\",\n });\n }\n }\n\n /**\n * GET /sse - 扩展订阅命令流\n */\n private handleSSE(_req: IncomingMessage, res: ServerResponse): void {\n this.sseManager.connect(res);\n }\n\n /**\n * POST /result - 扩展回传结果\n */\n private async handleResult(req: IncomingMessage, res: ServerResponse): Promise<void> {\n try {\n const body = await this.readBody(req);\n const result = JSON.parse(body) as Response;\n\n // 匹配 pending 请求\n const resolved = this.requestManager.resolve(result.id, result);\n\n if (resolved) {\n this.sendJson(res, 200, { code: 0, message: \"ok\" });\n } else {\n // 找不到对应请求(可能已超时)\n this.sendJson(res, 200, { code: 1, message: \"Request not found or already expired\" });\n }\n } catch (error) {\n this.sendJson(res, 400, {\n code: -1,\n message: error instanceof Error ? error.message : \"Invalid request\",\n });\n }\n }\n\n /**\n * GET /status - 查询状态\n */\n private handleStatus(_req: IncomingMessage, res: ServerResponse): void {\n this.sendJson(res, 200, {\n running: true,\n extensionConnected: this.sseManager.isConnected,\n pendingRequests: this.requestManager.pendingCount,\n uptime: this.uptime,\n });\n }\n\n /**\n * POST /shutdown - 关闭服务器\n */\n private handleShutdown(_req: IncomingMessage, res: ServerResponse): void {\n this.sendJson(res, 200, { code: 0, message: \"Shutting down\" });\n \n // 延迟关闭,确保响应发送完成\n setTimeout(() => {\n if (this.onShutdown) {\n this.onShutdown();\n }\n }, 100);\n }\n\n /**\n * 读取请求体\n */\n private readBody(req: IncomingMessage): Promise<string> {\n return new Promise((resolve, reject) => {\n const chunks: Buffer[] = [];\n\n req.on(\"data\", (chunk: Buffer) => {\n chunks.push(chunk);\n });\n\n req.on(\"end\", () => {\n resolve(Buffer.concat(chunks).toString(\"utf-8\"));\n });\n\n req.on(\"error\", (error) => {\n reject(error);\n });\n });\n }\n\n /**\n * 发送 JSON 响应\n */\n private sendJson(res: ServerResponse, status: number, data: unknown): void {\n res.writeHead(status, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify(data));\n }\n}\n","/**\n * SSE 连接管理\n *\n * 职责:\n * - 管理与扩展的 SSE 长连接\n * - 发送心跳保活\n * - 推送命令事件\n */\n\nimport type { ServerResponse } from \"node:http\";\nimport type { Request } from \"@bb-browser/shared\";\nimport { SSE_HEARTBEAT_INTERVAL } from \"@bb-browser/shared\";\n\n/**\n * SSE 连接管理器\n */\nexport class SSEManager {\n private connection: ServerResponse | null = null;\n private heartbeatTimer: NodeJS.Timeout | null = null;\n\n /**\n * 检查是否有活跃连接\n */\n get isConnected(): boolean {\n return this.connection !== null && !this.connection.writableEnded;\n }\n\n /**\n * 建立 SSE 连接\n */\n connect(res: ServerResponse): void {\n // 关闭旧连接\n if (this.connection) {\n this.disconnect();\n }\n\n // 设置 SSE 响应头\n res.writeHead(200, {\n \"Content-Type\": \"text/event-stream\",\n \"Cache-Control\": \"no-cache\",\n Connection: \"keep-alive\",\n \"Access-Control-Allow-Origin\": \"*\",\n });\n\n this.connection = res;\n\n // 发送 connected 事件\n this.sendEvent(\"connected\", { time: Date.now() });\n\n // 启动心跳\n this.startHeartbeat();\n\n // 监听连接关闭\n res.on(\"close\", () => {\n this.cleanupConnection();\n });\n }\n\n /**\n * 断开连接\n */\n disconnect(): void {\n this.stopHeartbeat();\n\n if (this.connection && !this.connection.writableEnded) {\n this.connection.end();\n }\n\n this.connection = null;\n }\n\n /**\n * 发送命令给扩展\n */\n sendCommand(request: Request): boolean {\n return this.sendEvent(\"command\", request);\n }\n\n /**\n * 发送 SSE 事件\n */\n private sendEvent(eventType: string, data: unknown): boolean {\n if (!this.connection || this.connection.writableEnded) {\n return false;\n }\n\n try {\n this.connection.write(`event: ${eventType}\\n`);\n this.connection.write(`data: ${JSON.stringify(data)}\\n\\n`);\n return true;\n } catch {\n return false;\n }\n }\n\n /**\n * 启动心跳定时器\n */\n private startHeartbeat(): void {\n this.stopHeartbeat();\n\n this.heartbeatTimer = setInterval(() => {\n const sent = this.sendEvent(\"heartbeat\", { time: Date.now() });\n if (!sent) {\n this.cleanupConnection();\n }\n }, SSE_HEARTBEAT_INTERVAL);\n }\n\n /**\n * 停止心跳定时器\n */\n private stopHeartbeat(): void {\n if (this.heartbeatTimer) {\n clearInterval(this.heartbeatTimer);\n this.heartbeatTimer = null;\n }\n }\n\n /**\n * 清理连接\n */\n private cleanupConnection(): void {\n this.stopHeartbeat();\n this.connection = null;\n }\n}\n","/**\n * Pending 请求管理\n *\n * 职责:\n * - 管理等待响应的请求\n * - 超时处理\n * - 匹配请求和响应\n */\n\nimport type { Response } from \"@bb-browser/shared\";\nimport { COMMAND_TIMEOUT } from \"@bb-browser/shared\";\n\ninterface PendingRequest {\n resolve: (response: Response) => void;\n reject: (error: Error) => void;\n timeout: NodeJS.Timeout;\n}\n\n/**\n * 请求管理器\n */\nexport class RequestManager {\n private pending = new Map<string, PendingRequest>();\n\n /**\n * 获取等待中的请求数量\n */\n get pendingCount(): number {\n return this.pending.size;\n }\n\n /**\n * 添加一个 pending 请求\n */\n add(\n requestId: string,\n resolve: (response: Response) => void,\n reject: (error: Error) => void\n ): void {\n // 设置超时\n const timeout = setTimeout(() => {\n this.timeout(requestId);\n }, COMMAND_TIMEOUT);\n\n this.pending.set(requestId, { resolve, reject, timeout });\n }\n\n /**\n * 解决一个 pending 请求\n * @returns 是否找到并解决了请求\n */\n resolve(requestId: string, response: Response): boolean {\n const pendingRequest = this.pending.get(requestId);\n\n if (!pendingRequest) {\n return false;\n }\n\n // 清理\n clearTimeout(pendingRequest.timeout);\n this.pending.delete(requestId);\n\n // 解决 Promise\n pendingRequest.resolve(response);\n return true;\n }\n\n /**\n * 请求超时处理\n */\n private timeout(requestId: string): void {\n const pendingRequest = this.pending.get(requestId);\n\n if (!pendingRequest) {\n return;\n }\n\n this.pending.delete(requestId);\n pendingRequest.reject(new Error(\"Command timeout\"));\n }\n\n /**\n * 清理所有 pending 请求\n */\n clear(): void {\n for (const request of this.pending.values()) {\n clearTimeout(request.timeout);\n request.reject(new Error(\"Daemon shutting down\"));\n }\n this.pending.clear();\n }\n}\n"],"mappings":"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","names":[]}
|
package/dist/jq-HHMLHEPA.js
CHANGED
|
File without changes
|
package/dist/mcp.js
CHANGED
|
@@ -20967,7 +20967,7 @@ var StdioServerTransport = class {
|
|
|
20967
20967
|
};
|
|
20968
20968
|
|
|
20969
20969
|
// packages/mcp/src/index.ts
|
|
20970
|
-
import { spawn } from "child_process";
|
|
20970
|
+
import { execFile, spawn } from "child_process";
|
|
20971
20971
|
import { existsSync } from "fs";
|
|
20972
20972
|
import { fileURLToPath } from "url";
|
|
20973
20973
|
import { dirname, resolve } from "path";
|
|
@@ -20985,6 +20985,12 @@ function getDaemonPath() {
|
|
|
20985
20985
|
if (existsSync(sameDirPath)) return sameDirPath;
|
|
20986
20986
|
return resolve(currentDir, "../../daemon/dist/index.js");
|
|
20987
20987
|
}
|
|
20988
|
+
function getCliPath() {
|
|
20989
|
+
const currentDir = dirname(fileURLToPath(import.meta.url));
|
|
20990
|
+
const sameDirPath = resolve(currentDir, "cli.js");
|
|
20991
|
+
if (existsSync(sameDirPath)) return sameDirPath;
|
|
20992
|
+
return resolve(currentDir, "../../cli/dist/index.js");
|
|
20993
|
+
}
|
|
20988
20994
|
async function isDaemonRunning() {
|
|
20989
20995
|
try {
|
|
20990
20996
|
const controller = new AbortController();
|
|
@@ -21046,8 +21052,81 @@ function textResult(value) {
|
|
|
21046
21052
|
async function runCommand(request) {
|
|
21047
21053
|
return sendCommand({ id: generateId(), ...request });
|
|
21048
21054
|
}
|
|
21055
|
+
function tryParseJson(raw) {
|
|
21056
|
+
const trimmed = raw.trim();
|
|
21057
|
+
if (!trimmed) {
|
|
21058
|
+
return null;
|
|
21059
|
+
}
|
|
21060
|
+
try {
|
|
21061
|
+
return JSON.parse(trimmed);
|
|
21062
|
+
} catch {
|
|
21063
|
+
}
|
|
21064
|
+
const lines = trimmed.split(/\r?\n/);
|
|
21065
|
+
for (let end = lines.length; end > 0; end -= 1) {
|
|
21066
|
+
for (let start = end - 1; start >= 0; start -= 1) {
|
|
21067
|
+
const candidate = lines.slice(start, end).join("\n").trim();
|
|
21068
|
+
if (!candidate) {
|
|
21069
|
+
continue;
|
|
21070
|
+
}
|
|
21071
|
+
try {
|
|
21072
|
+
return JSON.parse(candidate);
|
|
21073
|
+
} catch {
|
|
21074
|
+
}
|
|
21075
|
+
}
|
|
21076
|
+
}
|
|
21077
|
+
return null;
|
|
21078
|
+
}
|
|
21079
|
+
function formatSiteCliError(value, stderr, stdout) {
|
|
21080
|
+
if (value && typeof value === "object" && "error" in value && typeof value.error === "string") {
|
|
21081
|
+
const lines = [value.error];
|
|
21082
|
+
if ("hint" in value && typeof value.hint === "string" && value.hint) {
|
|
21083
|
+
lines.push(`Hint: ${value.hint}`);
|
|
21084
|
+
}
|
|
21085
|
+
if ("action" in value && typeof value.action === "string" && value.action) {
|
|
21086
|
+
lines.push(`Action: ${value.action}`);
|
|
21087
|
+
}
|
|
21088
|
+
if ("reportHint" in value && typeof value.reportHint === "string" && value.reportHint) {
|
|
21089
|
+
lines.push(`Report: ${value.reportHint}`);
|
|
21090
|
+
}
|
|
21091
|
+
if ("suggestions" in value && Array.isArray(value.suggestions) && value.suggestions.length > 0) {
|
|
21092
|
+
lines.push(`Suggestions: ${value.suggestions.join(", ")}`);
|
|
21093
|
+
}
|
|
21094
|
+
return lines.join("\n");
|
|
21095
|
+
}
|
|
21096
|
+
const fallback = [stderr.trim(), stdout.trim()].find(Boolean);
|
|
21097
|
+
return fallback || "bb-browser site command failed";
|
|
21098
|
+
}
|
|
21099
|
+
async function runSiteCli(args) {
|
|
21100
|
+
const cliPath = getCliPath();
|
|
21101
|
+
const result = await new Promise((resolvePromise) => {
|
|
21102
|
+
execFile(
|
|
21103
|
+
process.execPath,
|
|
21104
|
+
[cliPath, "site", ...args],
|
|
21105
|
+
{
|
|
21106
|
+
encoding: "utf8",
|
|
21107
|
+
timeout: COMMAND_TIMEOUT,
|
|
21108
|
+
maxBuffer: 10 * 1024 * 1024
|
|
21109
|
+
},
|
|
21110
|
+
(error2, stdout, stderr) => {
|
|
21111
|
+
resolvePromise({
|
|
21112
|
+
ok: !error2,
|
|
21113
|
+
stdout,
|
|
21114
|
+
stderr
|
|
21115
|
+
});
|
|
21116
|
+
}
|
|
21117
|
+
);
|
|
21118
|
+
});
|
|
21119
|
+
const parsed = tryParseJson(result.stdout);
|
|
21120
|
+
if (parsed && typeof parsed === "object" && parsed !== null && "success" in parsed && parsed.success === false) {
|
|
21121
|
+
throw new Error(formatSiteCliError(parsed, result.stderr, result.stdout));
|
|
21122
|
+
}
|
|
21123
|
+
if (!result.ok) {
|
|
21124
|
+
throw new Error(formatSiteCliError(parsed, result.stderr, result.stdout));
|
|
21125
|
+
}
|
|
21126
|
+
return parsed ?? result.stdout.trim();
|
|
21127
|
+
}
|
|
21049
21128
|
var server = new McpServer(
|
|
21050
|
-
{ name: "bb-browser", version: "0.
|
|
21129
|
+
{ name: "bb-browser", version: "0.10.0" },
|
|
21051
21130
|
{ instructions: `bb-browser lets you control the user's real Chrome browser \u2014 with their login state, cookies, and sessions.
|
|
21052
21131
|
|
|
21053
21132
|
Your browser is the API. No headless browser, no cookie extraction, no anti-bot bypass.
|
|
@@ -21061,10 +21140,11 @@ Key capabilities:
|
|
|
21061
21140
|
- browser_tab_list/tab_new: Multi-tab support \u2014 use tab parameter for concurrent operations
|
|
21062
21141
|
|
|
21063
21142
|
Site adapters (pre-built commands for popular sites):
|
|
21064
|
-
-
|
|
21143
|
+
- site_list/site_search/site_info: Discover available adapters and their signatures
|
|
21144
|
+
- site_recommend: Suggest adapters based on browsing history
|
|
21145
|
+
- site_run: Execute an adapter directly from MCP
|
|
21146
|
+
- site_update: Pull the community adapter repository
|
|
21065
21147
|
- Available: reddit, twitter, github, hackernews, xiaohongshu, zhihu, bilibili, weibo, douban, youtube
|
|
21066
|
-
- Update: bb-browser site update
|
|
21067
|
-
- List all: bb-browser site list
|
|
21068
21148
|
|
|
21069
21149
|
To create a new site adapter, run: bb-browser guide` }
|
|
21070
21150
|
);
|
|
@@ -21295,6 +21375,115 @@ server.tool(
|
|
|
21295
21375
|
return textResult(resp.data || `Waited ${time3}ms`);
|
|
21296
21376
|
}
|
|
21297
21377
|
);
|
|
21378
|
+
server.tool(
|
|
21379
|
+
"site_list",
|
|
21380
|
+
"List installed site adapters",
|
|
21381
|
+
{},
|
|
21382
|
+
async () => {
|
|
21383
|
+
try {
|
|
21384
|
+
const result = await runSiteCli(["list", "--json"]);
|
|
21385
|
+
return textResult(result);
|
|
21386
|
+
} catch (error2) {
|
|
21387
|
+
return errorResult(error2 instanceof Error ? error2.message : String(error2));
|
|
21388
|
+
}
|
|
21389
|
+
}
|
|
21390
|
+
);
|
|
21391
|
+
server.tool(
|
|
21392
|
+
"site_search",
|
|
21393
|
+
"Search installed site adapters by name, description, or domain",
|
|
21394
|
+
{
|
|
21395
|
+
query: external_exports.string().describe("Search query")
|
|
21396
|
+
},
|
|
21397
|
+
async ({ query }) => {
|
|
21398
|
+
try {
|
|
21399
|
+
const result = await runSiteCli(["search", query, "--json"]);
|
|
21400
|
+
return textResult(result);
|
|
21401
|
+
} catch (error2) {
|
|
21402
|
+
return errorResult(error2 instanceof Error ? error2.message : String(error2));
|
|
21403
|
+
}
|
|
21404
|
+
}
|
|
21405
|
+
);
|
|
21406
|
+
server.tool(
|
|
21407
|
+
"site_info",
|
|
21408
|
+
"Get adapter metadata including args, example, and domain",
|
|
21409
|
+
{
|
|
21410
|
+
name: external_exports.string().describe("Adapter name, e.g. twitter/search")
|
|
21411
|
+
},
|
|
21412
|
+
async ({ name }) => {
|
|
21413
|
+
try {
|
|
21414
|
+
const result = await runSiteCli(["info", name, "--json"]);
|
|
21415
|
+
return textResult(result);
|
|
21416
|
+
} catch (error2) {
|
|
21417
|
+
return errorResult(error2 instanceof Error ? error2.message : String(error2));
|
|
21418
|
+
}
|
|
21419
|
+
}
|
|
21420
|
+
);
|
|
21421
|
+
server.tool(
|
|
21422
|
+
"site_recommend",
|
|
21423
|
+
"Recommend adapters based on recent browsing history",
|
|
21424
|
+
{
|
|
21425
|
+
days: external_exports.number().int().positive().optional().describe("How many recent days of history to inspect")
|
|
21426
|
+
},
|
|
21427
|
+
async ({ days }) => {
|
|
21428
|
+
try {
|
|
21429
|
+
const args = ["recommend", "--json"];
|
|
21430
|
+
if (days !== void 0) {
|
|
21431
|
+
args.push("--days", String(days));
|
|
21432
|
+
}
|
|
21433
|
+
const result = await runSiteCli(args);
|
|
21434
|
+
return textResult(result);
|
|
21435
|
+
} catch (error2) {
|
|
21436
|
+
return errorResult(error2 instanceof Error ? error2.message : String(error2));
|
|
21437
|
+
}
|
|
21438
|
+
}
|
|
21439
|
+
);
|
|
21440
|
+
server.tool(
|
|
21441
|
+
"site_run",
|
|
21442
|
+
"Run a site adapter and return its structured data",
|
|
21443
|
+
{
|
|
21444
|
+
name: external_exports.string().describe("Adapter name, e.g. twitter/search"),
|
|
21445
|
+
args: external_exports.array(external_exports.string()).optional().describe("Positional arguments in adapter-defined order"),
|
|
21446
|
+
namedArgs: external_exports.record(external_exports.string()).optional().describe("Named adapter arguments passed as --key value"),
|
|
21447
|
+
tab: external_exports.number().optional().describe("Optional tab ID to target"),
|
|
21448
|
+
openclaw: external_exports.boolean().optional().describe("Prefer the OpenClaw browser instead of the extension flow")
|
|
21449
|
+
},
|
|
21450
|
+
async ({ name, args, namedArgs, tab, openclaw }) => {
|
|
21451
|
+
try {
|
|
21452
|
+
const cliArgs = ["run", name];
|
|
21453
|
+
for (const arg of args || []) {
|
|
21454
|
+
cliArgs.push(arg);
|
|
21455
|
+
}
|
|
21456
|
+
for (const [key, value] of Object.entries(namedArgs || {})) {
|
|
21457
|
+
cliArgs.push(`--${key}`, value);
|
|
21458
|
+
}
|
|
21459
|
+
if (tab !== void 0) {
|
|
21460
|
+
cliArgs.push("--tab", String(tab));
|
|
21461
|
+
}
|
|
21462
|
+
if (openclaw) {
|
|
21463
|
+
cliArgs.push("--openclaw");
|
|
21464
|
+
}
|
|
21465
|
+
cliArgs.push("--json");
|
|
21466
|
+
const result = await runSiteCli(cliArgs);
|
|
21467
|
+
const unwrapped = result && typeof result === "object" && "data" in result ? result.data : result;
|
|
21468
|
+
return textResult(unwrapped);
|
|
21469
|
+
} catch (error2) {
|
|
21470
|
+
return errorResult(error2 instanceof Error ? error2.message : String(error2));
|
|
21471
|
+
}
|
|
21472
|
+
}
|
|
21473
|
+
);
|
|
21474
|
+
server.tool(
|
|
21475
|
+
"site_update",
|
|
21476
|
+
"Pull or clone the community adapter repository",
|
|
21477
|
+
{},
|
|
21478
|
+
async () => {
|
|
21479
|
+
try {
|
|
21480
|
+
const result = await runSiteCli(["update", "--json"]);
|
|
21481
|
+
return textResult(result);
|
|
21482
|
+
} catch (error2) {
|
|
21483
|
+
return errorResult(error2 instanceof Error ? error2.message : String(error2));
|
|
21484
|
+
}
|
|
21485
|
+
}
|
|
21486
|
+
);
|
|
21298
21487
|
async function startMcpServer() {
|
|
21299
21488
|
const transport = new StdioServerTransport();
|
|
21300
21489
|
await server.connect(transport);
|