crawlio-browser 1.4.4 → 1.4.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.
@@ -1,7 +1,7 @@
1
1
  // src/shared/constants.ts
2
2
  import { homedir } from "os";
3
3
  import { join } from "path";
4
- var PKG_VERSION = "1.4.4";
4
+ var PKG_VERSION = "1.4.6";
5
5
  var WS_PORT = 9333;
6
6
  var WS_HOST = "127.0.0.1";
7
7
  var CRAWLIO_PORT_FILE = join(
@@ -7,7 +7,7 @@ import {
7
7
  WS_PORT,
8
8
  WS_RECONNECT_GRACE,
9
9
  WS_STALE_THRESHOLD
10
- } from "./chunk-QZGTL4WX.js";
10
+ } from "./chunk-WJU4IGLN.js";
11
11
 
12
12
  // src/mcp-server/index.ts
13
13
  import { randomBytes as randomBytes2 } from "crypto";
@@ -1266,7 +1266,7 @@ var TOOL_TIMEOUTS = {
1266
1266
  get_frame_tree: 1e4,
1267
1267
  switch_to_frame: 5e3,
1268
1268
  switch_to_main_frame: 5e3,
1269
- create_tab: 1e4,
1269
+ create_tab: 2e4,
1270
1270
  close_tab: 5e3,
1271
1271
  switch_tab: 5e3,
1272
1272
  set_cookie: 5e3,
@@ -2156,25 +2156,28 @@ function createTools(bridge2, crawlio2) {
2156
2156
  // --- Tab management tools (AC-3) ---
2157
2157
  {
2158
2158
  name: "create_tab",
2159
- description: "Create a new browser tab with the given URL. Returns the new tab's ID, URL, and title. The tab is created but not automatically connected \u2014 use connect_tab to attach CDP.",
2159
+ description: "Create a new browser tab with the given URL. Pass connect:true to auto-attach CDP and start capturing \u2014 the tab is immediately ready for interaction (screenshot, evaluate, navigate, etc).",
2160
2160
  inputSchema: {
2161
2161
  type: "object",
2162
2162
  properties: {
2163
2163
  url: { type: "string", description: "URL to open in the new tab" },
2164
- active: { type: "boolean", description: "Whether to make the new tab active/focused (default: true)" }
2164
+ active: { type: "boolean", description: "Whether to make the new tab active/focused (default: true)" },
2165
+ connect: { type: "boolean", description: "Auto-connect CDP debugger after creation \u2014 tab is ready for interaction immediately (default: false)" }
2165
2166
  },
2166
2167
  required: ["url"]
2167
2168
  },
2168
2169
  handler: async (args) => {
2169
2170
  const schema = z.object({
2170
2171
  url: urlSchema,
2171
- active: z.boolean().default(true)
2172
+ active: z.boolean().default(true),
2173
+ connect: z.boolean().default(false)
2172
2174
  });
2173
2175
  const parsed = schema.parse(args);
2174
2176
  const data = await bridge2.send({
2175
2177
  type: "create_tab",
2176
2178
  url: parsed.url,
2177
- active: parsed.active
2179
+ active: parsed.active,
2180
+ connect: parsed.connect
2178
2181
  }, TOOL_TIMEOUTS.create_tab);
2179
2182
  return toolSuccess(data);
2180
2183
  }
@@ -4027,7 +4030,7 @@ function createCodeModeTools(bridge2, crawlio2) {
4027
4030
  process.title = "Crawlio Agent";
4028
4031
  var initMode = process.argv.includes("init") || process.argv.includes("--setup") || process.argv.includes("setup");
4029
4032
  if (initMode) {
4030
- const { runInit } = await import("./init-I76TEVJC.js");
4033
+ const { runInit } = await import("./init-E233DCMN.js");
4031
4034
  await runInit(process.argv.slice(2));
4032
4035
  process.exit(0);
4033
4036
  }
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  PKG_VERSION
3
- } from "./chunk-QZGTL4WX.js";
3
+ } from "./chunk-WJU4IGLN.js";
4
4
 
5
5
  // src/mcp-server/init.ts
6
6
  import { execFileSync, spawn } from "child_process";
@@ -58,21 +58,220 @@ function parseFlags(argv) {
58
58
  }
59
59
  return opts;
60
60
  }
61
- function buildAddMcpArgs(options) {
62
- const args = ["-y", "add-mcp"];
63
- const pkg = `crawlio-browser@${PKG_VERSION}`;
64
- if (options.portal) {
65
- args.push(MCP_URL);
66
- } else if (options.full) {
67
- args.push(`${pkg} --full`);
68
- } else {
69
- args.push(pkg);
61
+ var CLIENT_REGISTRY = [
62
+ {
63
+ name: "Claude Code",
64
+ configPath: join(HOME, ".claude.json"),
65
+ serverKey: "mcpServers",
66
+ format: "json",
67
+ detect: () => existsSync(join(HOME, ".claude"))
68
+ },
69
+ {
70
+ name: "Claude Desktop",
71
+ configPath: join(HOME, "Library", "Application Support", "Claude", "claude_desktop_config.json"),
72
+ serverKey: "mcpServers",
73
+ format: "json",
74
+ detect: () => existsSync(join(HOME, "Library", "Application Support", "Claude"))
75
+ },
76
+ {
77
+ name: "VS Code",
78
+ configPath: join(HOME, "Library", "Application Support", "Code", "User", "mcp.json"),
79
+ serverKey: "servers",
80
+ format: "json",
81
+ detect: () => existsSync(join(HOME, ".vscode"))
82
+ },
83
+ {
84
+ name: "Cursor",
85
+ configPath: join(HOME, ".cursor", "mcp.json"),
86
+ serverKey: "mcpServers",
87
+ format: "json",
88
+ detect: () => existsSync(join(HOME, ".cursor"))
89
+ },
90
+ {
91
+ name: "Windsurf",
92
+ configPath: join(HOME, ".codeium", "windsurf", "mcp_config.json"),
93
+ serverKey: "mcpServers",
94
+ format: "json",
95
+ detect: () => existsSync(join(HOME, ".codeium", "windsurf"))
96
+ },
97
+ {
98
+ name: "Cline (VS Code)",
99
+ configPath: join(HOME, "Library", "Application Support", "Code", "User", "globalStorage", "saoudrizwan.claude-dev", "settings", "cline_mcp_settings.json"),
100
+ serverKey: "mcpServers",
101
+ format: "json",
102
+ detect: () => existsSync(join(HOME, "Library", "Application Support", "Code", "User", "globalStorage", "saoudrizwan.claude-dev", "settings")),
103
+ transform: (entry) => ({ ...entry, disabled: false })
104
+ },
105
+ {
106
+ name: "Cline CLI",
107
+ configPath: join(HOME, ".cline", "data", "settings", "cline_mcp_settings.json"),
108
+ serverKey: "mcpServers",
109
+ format: "json",
110
+ detect: () => existsSync(join(HOME, ".cline")),
111
+ transform: (entry) => ({ ...entry, disabled: false })
112
+ },
113
+ {
114
+ name: "Copilot CLI",
115
+ configPath: join(HOME, ".copilot", "mcp-config.json"),
116
+ serverKey: "mcpServers",
117
+ format: "json",
118
+ detect: () => existsSync(join(HOME, ".copilot"))
119
+ },
120
+ {
121
+ name: "Gemini CLI",
122
+ configPath: join(HOME, ".gemini", "settings.json"),
123
+ serverKey: "mcpServers",
124
+ format: "json",
125
+ detect: () => existsSync(join(HOME, ".gemini"))
126
+ },
127
+ {
128
+ name: "Codex CLI",
129
+ configPath: join(HOME, ".codex", "config.toml"),
130
+ serverKey: "mcp_servers",
131
+ format: "toml",
132
+ detect: () => existsSync(join(HOME, ".codex"))
133
+ },
134
+ {
135
+ name: "Goose",
136
+ configPath: join(HOME, ".config", "goose", "config.yaml"),
137
+ serverKey: "extensions",
138
+ format: "yaml",
139
+ detect: () => existsSync(join(HOME, ".config", "goose"))
140
+ },
141
+ {
142
+ name: "OpenCode",
143
+ configPath: join(HOME, ".config", "opencode", "opencode.json"),
144
+ serverKey: "mcp",
145
+ format: "json",
146
+ detect: () => existsSync(join(HOME, ".config", "opencode")),
147
+ transform: (entry) => {
148
+ const e = entry;
149
+ return { command: [e.command, ...e.args || []], env: entry.env };
150
+ }
151
+ },
152
+ {
153
+ name: "Zed",
154
+ configPath: join(HOME, "Library", "Application Support", "Zed", "settings.json"),
155
+ serverKey: "context_servers",
156
+ format: "json",
157
+ detect: () => existsSync(join(HOME, "Library", "Application Support", "Zed")),
158
+ transform: (entry) => ({
159
+ settings: { source: "custom", command: entry }
160
+ })
161
+ },
162
+ {
163
+ name: "Antigravity",
164
+ configPath: join(HOME, ".gemini", "antigravity", "mcp_config.json"),
165
+ serverKey: "mcpServers",
166
+ format: "json",
167
+ detect: () => existsSync(join(HOME, ".gemini", "antigravity"))
168
+ }
169
+ ];
170
+ function configureClient(client, entry, dryRun) {
171
+ const finalEntry = client.transform ? client.transform(entry) : entry;
172
+ if (client.format === "json") {
173
+ let config = {};
174
+ if (existsSync(client.configPath)) {
175
+ try {
176
+ config = JSON.parse(readFileSync(client.configPath, "utf-8"));
177
+ } catch {
178
+ config = {};
179
+ }
180
+ }
181
+ const section = config[client.serverKey] || {};
182
+ if ("crawlio-browser" in section) return "skipped";
183
+ if (dryRun) return "configured";
184
+ section["crawlio-browser"] = finalEntry;
185
+ config[client.serverKey] = section;
186
+ mkdirSync(dirname(client.configPath), { recursive: true });
187
+ writeFileSync(client.configPath, JSON.stringify(config, null, 2) + "\n");
188
+ return "configured";
189
+ }
190
+ if (client.format === "toml") {
191
+ let content = "";
192
+ if (existsSync(client.configPath)) {
193
+ content = readFileSync(client.configPath, "utf-8");
194
+ }
195
+ if (content.includes("[mcp_servers.crawlio-browser]") || content.includes('[mcp_servers."crawlio-browser"]')) {
196
+ return "skipped";
197
+ }
198
+ if (dryRun) return "configured";
199
+ const e = entry;
200
+ const argsStr = (e.args || []).map((a) => `"${a}"`).join(", ");
201
+ const block = `
202
+ [mcp_servers.crawlio-browser]
203
+ command = "${e.command}"
204
+ args = [${argsStr}]
205
+ `;
206
+ mkdirSync(dirname(client.configPath), { recursive: true });
207
+ writeFileSync(client.configPath, content + block);
208
+ return "configured";
70
209
  }
71
- args.push("--name", "crawlio-browser", "--global", "--yes");
72
- for (const agent of options.agents) {
73
- args.push("-a", agent);
210
+ if (client.format === "yaml") {
211
+ let content = "";
212
+ if (existsSync(client.configPath)) {
213
+ content = readFileSync(client.configPath, "utf-8");
214
+ }
215
+ if (content.includes("crawlio-browser:")) {
216
+ return "skipped";
217
+ }
218
+ if (dryRun) return "configured";
219
+ const e = entry;
220
+ const argsYaml = (e.args || []).map((a) => ` - ${a}`).join("\n");
221
+ const block = `
222
+ crawlio-browser:
223
+ name: crawlio-browser
224
+ type: stdio
225
+ cmd: ${e.command}
226
+ args:
227
+ ${argsYaml}
228
+ `;
229
+ if (!content.includes("extensions:")) {
230
+ content += "\nextensions:\n";
231
+ }
232
+ mkdirSync(dirname(client.configPath), { recursive: true });
233
+ writeFileSync(client.configPath, content + block);
234
+ return "configured";
74
235
  }
75
- return args;
236
+ return "error";
237
+ }
238
+ function configureAllClients(options) {
239
+ const entry = options.portal ? buildPortalEntry() : buildStdioEntry({ full: options.full });
240
+ const candidates = options.agents.length > 0 ? CLIENT_REGISTRY.filter((c) => options.agents.some((a) => c.name.toLowerCase().includes(a.toLowerCase()))) : CLIENT_REGISTRY.filter((c) => c.detect());
241
+ if (candidates.length === 0) {
242
+ console.log(` ${dim(" No MCP clients detected on this machine")}`);
243
+ printManualInstructions(entry);
244
+ return;
245
+ }
246
+ let configured = 0;
247
+ let skipped = 0;
248
+ for (const client of candidates) {
249
+ const result = configureClient(client, entry, options.dryRun);
250
+ if (result === "configured") {
251
+ const prefix = options.dryRun ? dim("~") : green("+");
252
+ console.log(` ${prefix} ${client.name} ${dim("\u2192 " + client.configPath)}`);
253
+ configured++;
254
+ } else if (result === "skipped") {
255
+ console.log(` ${dim("=")} ${client.name} ${dim("(already configured)")}`);
256
+ skipped++;
257
+ } else {
258
+ console.log(` ${yellow("!")} ${client.name} ${dim("\u2014 failed to write config")}`);
259
+ }
260
+ }
261
+ if (configured === 0 && skipped > 0) {
262
+ console.log(` ${dim(" All detected clients already configured")}`);
263
+ }
264
+ }
265
+ function printManualInstructions(entry) {
266
+ console.log("");
267
+ console.log(` ${dim("Add this to your MCP client config:")}`);
268
+ console.log("");
269
+ const snippet = { "crawlio-browser": entry };
270
+ const lines = JSON.stringify(snippet, null, 2).split("\n");
271
+ for (const line of lines) {
272
+ console.log(` ${dim(line)}`);
273
+ }
274
+ console.log("");
76
275
  }
77
276
  function buildStdioEntry(options) {
78
277
  if (platform() === "darwin") {
@@ -91,7 +290,7 @@ function buildStdioEntry(options) {
91
290
  if (options?.full) args2.push("--full");
92
291
  return { command: nodePath, args: args2 };
93
292
  }
94
- const args = ["-y", `crawlio-browser@${PKG_VERSION}`];
293
+ const args = ["-y", "crawlio-browser"];
95
294
  if (options?.full) args.push("--full");
96
295
  return { command: "npx", args };
97
296
  }
@@ -620,7 +819,7 @@ async function portalFlow(options) {
620
819
  await ensurePortalRunning(options.dryRun);
621
820
  console.log("");
622
821
  console.log(` ${cyan("\u25C6")} ${bold("MCP Configuration")} ${dim("(portal mode)")}`);
623
- runAddMcp(options);
822
+ configureAllClients(options);
624
823
  }
625
824
  async function configureMetaMcp(found, options) {
626
825
  console.log("");
@@ -667,47 +866,7 @@ async function configureMetaMcp(found, options) {
667
866
  function configureStdioClients(options) {
668
867
  console.log("");
669
868
  console.log(` ${cyan("\u25C6")} ${bold("MCP Configuration")} ${dim("(stdio mode)")}`);
670
- runAddMcp(options);
671
- }
672
- function runAddMcp(options) {
673
- const npxBin = platform() === "win32" ? "npx.cmd" : "npx";
674
- const args = buildAddMcpArgs(options);
675
- if (options.dryRun) {
676
- console.log(` ${dim("~")} Would run: ${npxBin} ${args.join(" ")}`);
677
- return;
678
- }
679
- try {
680
- const output = execFileSync(npxBin, args, {
681
- encoding: "utf-8",
682
- timeout: 6e4,
683
- env: { ...process.env, npm_config_yes: "true" }
684
- });
685
- const lines = output.split("\n").filter((l) => l.trim());
686
- let configuredCount = 0;
687
- for (const line of lines) {
688
- const trimmed = line.trim();
689
- if (trimmed) {
690
- console.log(` ${green("+")} ${trimmed}`);
691
- configuredCount++;
692
- }
693
- }
694
- if (configuredCount === 0) {
695
- console.log(` ${dim(" add-mcp ran but no clients detected")}`);
696
- }
697
- } catch (error) {
698
- const errObj = error;
699
- const code = errObj?.code;
700
- const msg = error instanceof Error ? error.message : String(error);
701
- if (code === "ENOENT") {
702
- console.log(` ${yellow("!")} ${npxBin} not found in PATH \u2014 install Node.js 18+ and retry`);
703
- } else if (code === "ETIMEDOUT" || msg.includes("ETIMEDOUT") || msg.includes("timed out")) {
704
- console.log(` ${yellow("!")} add-mcp timed out \u2014 check network and retry`);
705
- } else {
706
- console.log(` ${yellow("!")} add-mcp failed: ${msg.slice(0, 200)}`);
707
- }
708
- const target = options.portal ? MCP_URL : "crawlio-browser";
709
- console.log(` ${dim(` Manual: npx add-mcp ${target} --name crawlio-browser --global`)}`);
710
- }
869
+ configureAllClients(options);
711
870
  }
712
871
  var LOGO_LINES = [
713
872
  " \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2557 \u2588\u2588\u2557 \u2588\u2588\u2557 \u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557 ",
@@ -832,10 +991,12 @@ async function runInit(argv) {
832
991
  await printSummary(options);
833
992
  }
834
993
  export {
835
- buildAddMcpArgs,
994
+ CLIENT_REGISTRY,
836
995
  buildCloudflareEntry,
837
996
  buildPortalEntry,
838
997
  buildStdioEntry,
998
+ configureAllClients,
999
+ configureClient,
839
1000
  createAppWrapper,
840
1001
  extractSkillName,
841
1002
  findConflictingConfigs,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "crawlio-browser",
3
- "version": "1.4.4",
3
+ "version": "1.4.6",
4
4
  "description": "MCP server with 96 CDP-backed tools for browser automation — screenshots, DOM, network capture, framework detection, cookies, storage, session recording, performance metrics via Chrome",
5
5
  "type": "module",
6
6
  "main": "dist/mcp-server/index.js",