@mcpware/chrome-pilot 0.1.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.
@@ -0,0 +1,176 @@
1
+ import { chromium } from "playwright-core";
2
+ import { existsSync, mkdirSync, readdirSync, readFileSync, rmSync } from "fs";
3
+ import { join } from "path";
4
+ import { homedir } from "os";
5
+ const PROFILES_DIR = join(homedir(), ".chrome-pilot", "profiles");
6
+ const CONFIG_PATH = join(homedir(), ".chrome-pilot", "config.json");
7
+ // Track active sessions per profile
8
+ const activeSessions = new Map();
9
+ // Next available CDP port
10
+ let nextPort = 9300;
11
+ /**
12
+ * Find Chrome executable on the system
13
+ */
14
+ export function findChrome() {
15
+ const candidates = [
16
+ // Linux
17
+ "/opt/google/chrome/chrome",
18
+ "/usr/bin/google-chrome",
19
+ "/usr/bin/google-chrome-stable",
20
+ "/usr/bin/chromium-browser",
21
+ "/usr/bin/chromium",
22
+ // macOS
23
+ "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome",
24
+ // WSL / Windows
25
+ "/mnt/c/Program Files/Google/Chrome/Application/chrome.exe",
26
+ "/mnt/c/Program Files (x86)/Google/Chrome/Application/chrome.exe",
27
+ ];
28
+ for (const path of candidates) {
29
+ if (existsSync(path))
30
+ return path;
31
+ }
32
+ throw new Error("Chrome not found. Install Google Chrome or set CHROME_PATH environment variable.");
33
+ }
34
+ /**
35
+ * Ensure profiles directory exists
36
+ */
37
+ function ensureProfilesDir() {
38
+ if (!existsSync(PROFILES_DIR)) {
39
+ mkdirSync(PROFILES_DIR, { recursive: true });
40
+ }
41
+ }
42
+ /**
43
+ * List all available profiles
44
+ */
45
+ export function listProfiles() {
46
+ ensureProfilesDir();
47
+ const dirs = readdirSync(PROFILES_DIR, { withFileTypes: true })
48
+ .filter((d) => d.isDirectory())
49
+ .map((d) => {
50
+ const profilePath = join(PROFILES_DIR, d.name);
51
+ const prefsPath = join(profilePath, "Default", "Preferences");
52
+ let lastUsed;
53
+ try {
54
+ const prefs = JSON.parse(readFileSync(prefsPath, "utf-8"));
55
+ const accountInfo = prefs?.account_info?.[0];
56
+ if (accountInfo?.email) {
57
+ lastUsed = accountInfo.email;
58
+ }
59
+ }
60
+ catch {
61
+ // No prefs yet — fresh profile
62
+ }
63
+ return {
64
+ name: d.name,
65
+ path: profilePath,
66
+ lastUsed,
67
+ };
68
+ });
69
+ return dirs;
70
+ }
71
+ /**
72
+ * Create a new profile
73
+ */
74
+ export function createProfile(name) {
75
+ ensureProfilesDir();
76
+ const profilePath = join(PROFILES_DIR, name);
77
+ if (existsSync(profilePath)) {
78
+ throw new Error(`Profile "${name}" already exists`);
79
+ }
80
+ mkdirSync(profilePath, { recursive: true });
81
+ return { name, path: profilePath };
82
+ }
83
+ /**
84
+ * Delete a profile
85
+ */
86
+ export function deleteProfile(name) {
87
+ const profilePath = join(PROFILES_DIR, name);
88
+ if (!existsSync(profilePath)) {
89
+ throw new Error(`Profile "${name}" does not exist`);
90
+ }
91
+ // Close session if active
92
+ const session = activeSessions.get(name);
93
+ if (session) {
94
+ session.context.close().catch(() => { });
95
+ activeSessions.delete(name);
96
+ }
97
+ rmSync(profilePath, { recursive: true, force: true });
98
+ }
99
+ /**
100
+ * Launch Chrome with a profile — the core function
101
+ *
102
+ * Uses Playwright's launch_persistent_context with the real Chrome binary.
103
+ * First launch: user sees Chrome sign-in page → signs into Google → Chrome Sync pulls data.
104
+ * Subsequent launches: all data persists (cookies, passwords, bookmarks, extensions).
105
+ */
106
+ export async function launchProfile(name, options) {
107
+ // Check if already active
108
+ const existing = activeSessions.get(name);
109
+ if (existing) {
110
+ return {
111
+ context: existing.context,
112
+ page: existing.pages[0] || (await existing.context.newPage()),
113
+ port: existing.cdpPort,
114
+ };
115
+ }
116
+ ensureProfilesDir();
117
+ const profilePath = join(PROFILES_DIR, name);
118
+ // Auto-create profile if it doesn't exist
119
+ if (!existsSync(profilePath)) {
120
+ mkdirSync(profilePath, { recursive: true });
121
+ }
122
+ const chromePath = process.env.CHROME_PATH || findChrome();
123
+ const port = nextPort++;
124
+ const context = await chromium.launchPersistentContext(profilePath, {
125
+ executablePath: chromePath,
126
+ headless: options?.headless ?? false,
127
+ args: [
128
+ "--disable-blink-features=AutomationControlled",
129
+ `--remote-debugging-port=${port}`,
130
+ ],
131
+ viewport: null, // Use default window size
132
+ ignoreDefaultArgs: ["--enable-automation"],
133
+ });
134
+ const page = context.pages()[0] || (await context.newPage());
135
+ // Navigate to requested URL or Chrome welcome page
136
+ if (options?.url) {
137
+ await page.goto(options.url, { waitUntil: "domcontentloaded" });
138
+ }
139
+ // Track session
140
+ activeSessions.set(name, {
141
+ context,
142
+ pages: context.pages(),
143
+ cdpPort: port,
144
+ });
145
+ return { context, page, port };
146
+ }
147
+ /**
148
+ * Get active sessions
149
+ */
150
+ export function getActiveSessions() {
151
+ return Array.from(activeSessions.entries()).map(([name, session]) => ({
152
+ name,
153
+ port: session.cdpPort,
154
+ pageCount: session.context.pages().length,
155
+ }));
156
+ }
157
+ /**
158
+ * Get a page from an active session
159
+ */
160
+ export function getSessionPage(name) {
161
+ const session = activeSessions.get(name);
162
+ if (!session)
163
+ return null;
164
+ return session.context.pages()[0] || null;
165
+ }
166
+ /**
167
+ * Close a profile session
168
+ */
169
+ export async function closeProfile(name) {
170
+ const session = activeSessions.get(name);
171
+ if (!session) {
172
+ throw new Error(`No active session for profile "${name}"`);
173
+ }
174
+ await session.context.close();
175
+ activeSessions.delete(name);
176
+ }
package/package.json ADDED
@@ -0,0 +1,41 @@
1
+ {
2
+ "name": "@mcpware/chrome-pilot",
3
+ "version": "0.1.0",
4
+ "description": "AI-controlled Chrome with real profile sync. Like Playwright, but with your bookmarks, passwords, and extensions.",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "bin": {
8
+ "chrome-pilot": "dist/cli.js"
9
+ },
10
+ "scripts": {
11
+ "build": "tsc",
12
+ "dev": "tsc --watch",
13
+ "start": "node dist/index.js"
14
+ },
15
+ "keywords": [
16
+ "mcp",
17
+ "chrome",
18
+ "playwright",
19
+ "browser-automation",
20
+ "chrome-profile",
21
+ "ai-agent"
22
+ ],
23
+ "author": "mcpware",
24
+ "license": "MIT",
25
+ "repository": {
26
+ "type": "git",
27
+ "url": "https://github.com/mcpware/chrome-pilot"
28
+ },
29
+ "dependencies": {
30
+ "@anthropic-ai/sdk": "^0.39.0",
31
+ "@modelcontextprotocol/sdk": "^1.0.0",
32
+ "playwright-core": "^1.50.0"
33
+ },
34
+ "devDependencies": {
35
+ "typescript": "^5.7.0",
36
+ "@types/node": "^22.0.0"
37
+ },
38
+ "engines": {
39
+ "node": ">=20.0.0"
40
+ }
41
+ }