chromeflow 0.1.0 → 0.1.2

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.
@@ -0,0 +1,104 @@
1
+ import { execSync } from "child_process";
2
+ import { WebSocketServer, WebSocket } from "ws";
3
+ const WS_PORT = 7878;
4
+ const REQUEST_TIMEOUT_MS = 3e4;
5
+ class WsBridge {
6
+ wss;
7
+ client = null;
8
+ pending = /* @__PURE__ */ new Map();
9
+ constructor() {
10
+ this.bind();
11
+ }
12
+ bind() {
13
+ const wss = new WebSocketServer({ port: WS_PORT });
14
+ wss.on("error", (err) => {
15
+ if (err.code === "EADDRINUSE") {
16
+ console.error(
17
+ `[chromeflow] Port ${WS_PORT} in use \u2014 killing stale process and retrying...`
18
+ );
19
+ try {
20
+ execSync(`lsof -ti:${WS_PORT} | xargs kill -9`, { stdio: "ignore" });
21
+ } catch {
22
+ }
23
+ setTimeout(() => this.bind(), 800);
24
+ } else {
25
+ console.error("[chromeflow] WS server error:", err);
26
+ }
27
+ });
28
+ wss.on("listening", () => {
29
+ this.wss = wss;
30
+ console.error(`[chromeflow] WS bridge listening on ws://localhost:${WS_PORT}`);
31
+ });
32
+ wss.on("connection", (ws) => {
33
+ if (this.client) {
34
+ this.client.terminate();
35
+ }
36
+ this.client = ws;
37
+ console.error("[chromeflow] Extension connected");
38
+ ws.on("message", (data) => {
39
+ let msg;
40
+ try {
41
+ msg = JSON.parse(data.toString());
42
+ } catch {
43
+ return;
44
+ }
45
+ if (msg.type === "ready") {
46
+ console.error("[chromeflow] Extension ready");
47
+ return;
48
+ }
49
+ const pending = this.pending.get(msg.requestId);
50
+ if (pending) {
51
+ clearTimeout(pending.timer);
52
+ this.pending.delete(msg.requestId);
53
+ if (msg.type === "error") {
54
+ pending.reject(new Error(msg.message));
55
+ } else {
56
+ pending.resolve(msg);
57
+ }
58
+ }
59
+ });
60
+ ws.on("close", () => {
61
+ console.error("[chromeflow] Extension disconnected");
62
+ this.client = null;
63
+ for (const [id, pending] of this.pending) {
64
+ clearTimeout(pending.timer);
65
+ pending.reject(new Error("Extension disconnected"));
66
+ this.pending.delete(id);
67
+ }
68
+ });
69
+ });
70
+ }
71
+ isConnected() {
72
+ return this.client !== null && this.client.readyState === WebSocket.OPEN;
73
+ }
74
+ /** Send a message and wait for a response from the extension. */
75
+ request(message, timeoutMs = REQUEST_TIMEOUT_MS) {
76
+ if (!this.isConnected()) {
77
+ return Promise.reject(
78
+ new Error(
79
+ "Chromeflow extension is not connected. Open Chrome and ensure the extension is installed."
80
+ )
81
+ );
82
+ }
83
+ const requestId = crypto.randomUUID();
84
+ return new Promise((resolve, reject) => {
85
+ const timer = setTimeout(() => {
86
+ this.pending.delete(requestId);
87
+ reject(new Error(`Request timed out after ${timeoutMs}ms`));
88
+ }, timeoutMs);
89
+ this.pending.set(requestId, { resolve, reject, timer });
90
+ this.client.send(JSON.stringify({ ...message, requestId }));
91
+ });
92
+ }
93
+ /** Send a fire-and-forget message (no response expected). */
94
+ send(message) {
95
+ if (!this.isConnected()) {
96
+ throw new Error("Chromeflow extension is not connected.");
97
+ }
98
+ const requestId = crypto.randomUUID();
99
+ this.client.send(JSON.stringify({ ...message, requestId }));
100
+ }
101
+ }
102
+ export {
103
+ WsBridge
104
+ };
package/package.json CHANGED
@@ -1,13 +1,14 @@
1
1
  {
2
2
  "name": "chromeflow",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "Browser guidance MCP server for Claude Code — highlights, clicks, fills, and captures from the web so you don't have to.",
5
5
  "type": "module",
6
6
  "bin": {
7
7
  "chromeflow": "dist/index.js"
8
8
  },
9
9
  "files": [
10
- "dist"
10
+ "dist",
11
+ "CLAUDE.md"
11
12
  ],
12
13
  "repository": {
13
14
  "type": "git",
@@ -24,8 +25,10 @@
24
25
  "chrome"
25
26
  ],
26
27
  "scripts": {
27
- "build": "tsup",
28
- "dev": "tsup --watch"
28
+ "prebuild": "cp ../../CLAUDE.md ./CLAUDE.md",
29
+ "build": "tsc",
30
+ "postbuild": "node --input-type=commonjs -e \"const fs=require('fs');const f='dist/index.js';fs.writeFileSync(f,'#!/usr/bin/env node\\n'+fs.readFileSync(f,'utf8'));\"",
31
+ "dev": "tsc --watch"
29
32
  },
30
33
  "dependencies": {
31
34
  "@modelcontextprotocol/sdk": "^1.0.0",
@@ -35,7 +38,6 @@
35
38
  "devDependencies": {
36
39
  "@types/node": "^22.0.0",
37
40
  "@types/ws": "^8.5.0",
38
- "tsup": "^8.0.0",
39
41
  "typescript": "^5.5.0"
40
42
  }
41
43
  }