playwriter 0.0.0 → 0.0.1
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 +60 -0
- package/bin.js +3 -0
- package/dist/browser-config.d.ts +2 -0
- package/dist/browser-config.d.ts.map +1 -0
- package/dist/browser-config.js +59 -0
- package/dist/browser-config.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/dist/index.test.d.ts +2 -0
- package/dist/index.test.d.ts.map +1 -0
- package/dist/index.test.js +8 -0
- package/dist/index.test.js.map +1 -0
- package/dist/mcp-client.d.ts +16 -0
- package/dist/mcp-client.d.ts.map +1 -0
- package/dist/mcp-client.js +52 -0
- package/dist/mcp-client.js.map +1 -0
- package/dist/mcp.d.ts +2 -0
- package/dist/mcp.d.ts.map +1 -0
- package/dist/mcp.js +596 -0
- package/dist/mcp.js.map +1 -0
- package/dist/mcp.test.d.ts +2 -0
- package/dist/mcp.test.d.ts.map +1 -0
- package/dist/mcp.test.js +205 -0
- package/dist/mcp.test.js.map +1 -0
- package/dist/playwriter.d.ts +5 -0
- package/dist/playwriter.d.ts.map +1 -0
- package/dist/playwriter.js +177 -0
- package/dist/playwriter.js.map +1 -0
- package/dist/profiles.d.ts +16 -0
- package/dist/profiles.d.ts.map +1 -0
- package/dist/profiles.js +76 -0
- package/dist/profiles.js.map +1 -0
- package/dist/profiles.test.d.ts +2 -0
- package/dist/profiles.test.d.ts.map +1 -0
- package/dist/profiles.test.js +169 -0
- package/dist/profiles.test.js.map +1 -0
- package/package.json +22 -6
- package/src/browser-config.ts +66 -0
- package/src/mcp-client.ts +73 -0
- package/src/mcp.test.ts +240 -0
- package/src/mcp.ts +751 -0
- package/src/prompt.md +539 -0
- package/src/snapshots/hacker-news-focused-accessibility.md +202 -0
- package/src/snapshots/hacker-news-initial-accessibility.md +202 -0
- package/src/snapshots/hacker-news-tabbed-accessibility.md +202 -0
- package/src/snapshots/shadcn-ui-accessibility.md +553 -0
- package/src/index.test.ts +0 -9
- package/src/index.ts +0 -0
package/dist/mcp.test.js
ADDED
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
import { createMCPClient } from './mcp-client.js';
|
|
2
|
+
import { describe, it, expect, beforeAll, afterAll } from 'vitest';
|
|
3
|
+
describe('MCP Server Tests', () => {
|
|
4
|
+
let client;
|
|
5
|
+
let cleanup = null;
|
|
6
|
+
beforeAll(async () => {
|
|
7
|
+
const result = await createMCPClient();
|
|
8
|
+
client = result.client;
|
|
9
|
+
cleanup = result.cleanup;
|
|
10
|
+
});
|
|
11
|
+
afterAll(async () => {
|
|
12
|
+
if (cleanup) {
|
|
13
|
+
await cleanup();
|
|
14
|
+
cleanup = null;
|
|
15
|
+
}
|
|
16
|
+
});
|
|
17
|
+
it('should capture console logs', async () => {
|
|
18
|
+
// Connect first (open a new page)
|
|
19
|
+
const connectResult = await client.callTool({
|
|
20
|
+
name: 'new_page',
|
|
21
|
+
arguments: {},
|
|
22
|
+
});
|
|
23
|
+
expect(connectResult.content).toBeDefined();
|
|
24
|
+
expect(connectResult.content).toMatchInlineSnapshot(`
|
|
25
|
+
[
|
|
26
|
+
{
|
|
27
|
+
"text": "Created new page. URL: about:blank. Total pages: 20",
|
|
28
|
+
"type": "text",
|
|
29
|
+
},
|
|
30
|
+
]
|
|
31
|
+
`);
|
|
32
|
+
// Navigate to a page and log something
|
|
33
|
+
const result = await client.callTool({
|
|
34
|
+
name: 'execute',
|
|
35
|
+
arguments: {
|
|
36
|
+
code: `
|
|
37
|
+
await page.goto('https://news.ycombinator.com');
|
|
38
|
+
await page.evaluate(() => {
|
|
39
|
+
console.log('Test log message');
|
|
40
|
+
console.error('Test error message');
|
|
41
|
+
});
|
|
42
|
+
`,
|
|
43
|
+
},
|
|
44
|
+
});
|
|
45
|
+
expect(result.content).toBeDefined();
|
|
46
|
+
expect(result.content).toMatchInlineSnapshot(`
|
|
47
|
+
[
|
|
48
|
+
{
|
|
49
|
+
"text": "Code executed successfully (no output)",
|
|
50
|
+
"type": "text",
|
|
51
|
+
},
|
|
52
|
+
]
|
|
53
|
+
`);
|
|
54
|
+
// Get console logs
|
|
55
|
+
const logsResult = (await client.callTool({
|
|
56
|
+
name: 'console_logs',
|
|
57
|
+
arguments: {
|
|
58
|
+
limit: 10,
|
|
59
|
+
},
|
|
60
|
+
}));
|
|
61
|
+
expect(logsResult.content).toBeDefined();
|
|
62
|
+
expect(logsResult.content).toMatchInlineSnapshot(`
|
|
63
|
+
[
|
|
64
|
+
{
|
|
65
|
+
"text": "[log]: Test log message :1:32
|
|
66
|
+
[error]: Test error message :2:32",
|
|
67
|
+
"type": "text",
|
|
68
|
+
},
|
|
69
|
+
]
|
|
70
|
+
`);
|
|
71
|
+
// Close the page opened
|
|
72
|
+
await client.callTool({
|
|
73
|
+
name: 'close_page',
|
|
74
|
+
arguments: {},
|
|
75
|
+
});
|
|
76
|
+
}, 30000);
|
|
77
|
+
it('should capture accessibility snapshot of hacker news', async () => {
|
|
78
|
+
// Create new page
|
|
79
|
+
await client.callTool({
|
|
80
|
+
name: 'new_page',
|
|
81
|
+
arguments: {},
|
|
82
|
+
});
|
|
83
|
+
// Navigate to a specific old Hacker News story that won't change
|
|
84
|
+
await client.callTool({
|
|
85
|
+
name: 'execute',
|
|
86
|
+
arguments: {
|
|
87
|
+
code: `await page.goto('https://news.ycombinator.com/item?id=1', { waitUntil: 'networkidle' })`,
|
|
88
|
+
},
|
|
89
|
+
});
|
|
90
|
+
// Get initial accessibility snapshot
|
|
91
|
+
const initialSnapshot = await client.callTool({
|
|
92
|
+
name: 'accessibility_snapshot',
|
|
93
|
+
arguments: {},
|
|
94
|
+
});
|
|
95
|
+
expect(initialSnapshot.content).toBeDefined();
|
|
96
|
+
// Save initial snapshot
|
|
97
|
+
const initialData = typeof initialSnapshot === 'object' &&
|
|
98
|
+
initialSnapshot.content?.[0]?.text
|
|
99
|
+
? tryJsonParse(initialSnapshot.content[0].text)
|
|
100
|
+
: initialSnapshot;
|
|
101
|
+
expect(initialData).toMatchFileSnapshot('snapshots/hacker-news-initial-accessibility.md');
|
|
102
|
+
expect(initialData).toContain('table');
|
|
103
|
+
expect(initialData).toContain('Hacker News');
|
|
104
|
+
// Focus on first link on the page
|
|
105
|
+
await client.callTool({
|
|
106
|
+
name: 'execute',
|
|
107
|
+
arguments: {
|
|
108
|
+
code: `
|
|
109
|
+
// Find and focus the first link
|
|
110
|
+
const firstLink = await page.$('a')
|
|
111
|
+
if (firstLink) {
|
|
112
|
+
await firstLink.focus()
|
|
113
|
+
const linkText = await firstLink.textContent()
|
|
114
|
+
console.log('Focused on first link:', linkText)
|
|
115
|
+
}
|
|
116
|
+
`,
|
|
117
|
+
},
|
|
118
|
+
});
|
|
119
|
+
// Get snapshot after focusing
|
|
120
|
+
const focusedSnapshot = await client.callTool({
|
|
121
|
+
name: 'accessibility_snapshot',
|
|
122
|
+
arguments: {},
|
|
123
|
+
});
|
|
124
|
+
expect(focusedSnapshot.content).toBeDefined();
|
|
125
|
+
// Save focused snapshot
|
|
126
|
+
const focusedData = typeof focusedSnapshot === 'object' &&
|
|
127
|
+
focusedSnapshot.content?.[0]?.text
|
|
128
|
+
? tryJsonParse(focusedSnapshot.content[0].text)
|
|
129
|
+
: focusedSnapshot;
|
|
130
|
+
expect(focusedData).toMatchFileSnapshot('snapshots/hacker-news-focused-accessibility.md');
|
|
131
|
+
// Verify the snapshot contains expected content
|
|
132
|
+
expect(focusedData).toBeDefined();
|
|
133
|
+
expect(focusedData).toContain('link');
|
|
134
|
+
// Press Tab to go to next item
|
|
135
|
+
await client.callTool({
|
|
136
|
+
name: 'execute',
|
|
137
|
+
arguments: {
|
|
138
|
+
code: `
|
|
139
|
+
await page.keyboard.press('Tab')
|
|
140
|
+
console.log('Pressed Tab key')
|
|
141
|
+
`,
|
|
142
|
+
},
|
|
143
|
+
});
|
|
144
|
+
// Get snapshot after tab navigation
|
|
145
|
+
const tabbedSnapshot = await client.callTool({
|
|
146
|
+
name: 'accessibility_snapshot',
|
|
147
|
+
arguments: {},
|
|
148
|
+
});
|
|
149
|
+
expect(tabbedSnapshot.content).toBeDefined();
|
|
150
|
+
// Save tabbed snapshot
|
|
151
|
+
const tabbedData = typeof tabbedSnapshot === 'object' &&
|
|
152
|
+
tabbedSnapshot.content?.[0]?.text
|
|
153
|
+
? tryJsonParse(tabbedSnapshot.content[0].text)
|
|
154
|
+
: tabbedSnapshot;
|
|
155
|
+
expect(tabbedData).toMatchFileSnapshot('snapshots/hacker-news-tabbed-accessibility.md');
|
|
156
|
+
// Verify the snapshot is different
|
|
157
|
+
expect(tabbedData).toBeDefined();
|
|
158
|
+
expect(tabbedData).toContain('Hacker News');
|
|
159
|
+
// Close the page opened
|
|
160
|
+
await client.callTool({
|
|
161
|
+
name: 'close_page',
|
|
162
|
+
arguments: {},
|
|
163
|
+
});
|
|
164
|
+
}, 30000);
|
|
165
|
+
it('should capture accessibility snapshot of shadcn UI', async () => {
|
|
166
|
+
// Create new page
|
|
167
|
+
await client.callTool({
|
|
168
|
+
name: 'new_page',
|
|
169
|
+
arguments: {},
|
|
170
|
+
});
|
|
171
|
+
// Navigate to shadcn UI
|
|
172
|
+
await client.callTool({
|
|
173
|
+
name: 'execute',
|
|
174
|
+
arguments: {
|
|
175
|
+
code: `await page.goto('https://ui.shadcn.com/', { waitUntil: 'networkidle' })`,
|
|
176
|
+
},
|
|
177
|
+
});
|
|
178
|
+
// Get accessibility snapshot
|
|
179
|
+
const snapshot = await client.callTool({
|
|
180
|
+
name: 'accessibility_snapshot',
|
|
181
|
+
arguments: {},
|
|
182
|
+
});
|
|
183
|
+
expect(snapshot.content).toBeDefined();
|
|
184
|
+
// Save snapshot
|
|
185
|
+
const data = typeof snapshot === 'object' && snapshot.content?.[0]?.text
|
|
186
|
+
? tryJsonParse(snapshot.content[0].text)
|
|
187
|
+
: snapshot;
|
|
188
|
+
expect(data).toMatchFileSnapshot('snapshots/shadcn-ui-accessibility.md');
|
|
189
|
+
expect(data).toContain('shadcn');
|
|
190
|
+
// Close the page opened
|
|
191
|
+
await client.callTool({
|
|
192
|
+
name: 'close_page',
|
|
193
|
+
arguments: {},
|
|
194
|
+
});
|
|
195
|
+
}, 30000);
|
|
196
|
+
});
|
|
197
|
+
function tryJsonParse(str) {
|
|
198
|
+
try {
|
|
199
|
+
return JSON.parse(str);
|
|
200
|
+
}
|
|
201
|
+
catch {
|
|
202
|
+
return str;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
//# sourceMappingURL=mcp.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcp.test.js","sourceRoot":"","sources":["../src/mcp.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"playwriter.d.ts","sourceRoot":"","sources":["../src/playwriter.ts"],"names":[],"mappings":"AAmDA,wBAAsB,eAAe,CAAC,YAAY,CAAC,EAAE,MAAM;;;GAqJ1D"}
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
// Removed Playwright import - launching Chrome directly
|
|
2
|
+
import { spawn } from 'child_process';
|
|
3
|
+
import os from 'node:os';
|
|
4
|
+
import fs from 'node:fs';
|
|
5
|
+
import path from 'node:path';
|
|
6
|
+
import { getAllProfiles } from './profiles.js';
|
|
7
|
+
// Find Chrome executable path based on OS
|
|
8
|
+
function findChromeExecutablePath() {
|
|
9
|
+
const osPlatform = os.platform();
|
|
10
|
+
const paths = (() => {
|
|
11
|
+
if (osPlatform === 'darwin') {
|
|
12
|
+
return [
|
|
13
|
+
'/Applications/Google Chrome.app/Contents/MacOS/Google Chrome',
|
|
14
|
+
'/Applications/Google Chrome Canary.app/Contents/MacOS/Google Chrome Canary',
|
|
15
|
+
'~/Applications/Google Chrome.app/Contents/MacOS/Google Chrome',
|
|
16
|
+
];
|
|
17
|
+
}
|
|
18
|
+
if (osPlatform === 'win32') {
|
|
19
|
+
return [
|
|
20
|
+
'C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe',
|
|
21
|
+
'C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe',
|
|
22
|
+
`${process.env.LOCALAPPDATA}\\Google\\Chrome\\Application\\chrome.exe`,
|
|
23
|
+
`${process.env.PROGRAMFILES}\\Google\\Chrome\\Application\\chrome.exe`,
|
|
24
|
+
`${process.env['PROGRAMFILES(X86)']}\\Google\\Chrome\\Application\\chrome.exe`,
|
|
25
|
+
].filter(Boolean);
|
|
26
|
+
}
|
|
27
|
+
// Linux
|
|
28
|
+
return [
|
|
29
|
+
'/usr/bin/google-chrome',
|
|
30
|
+
'/usr/bin/google-chrome-stable',
|
|
31
|
+
'/usr/bin/chromium',
|
|
32
|
+
'/usr/bin/chromium-browser',
|
|
33
|
+
'/snap/bin/chromium',
|
|
34
|
+
];
|
|
35
|
+
})();
|
|
36
|
+
for (const path of paths) {
|
|
37
|
+
const resolvedPath = path.startsWith('~')
|
|
38
|
+
? path.replace('~', process.env.HOME || '')
|
|
39
|
+
: path;
|
|
40
|
+
if (fs.existsSync(resolvedPath)) {
|
|
41
|
+
return resolvedPath;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
throw new Error('Could not find Chrome executable. Please install Google Chrome.');
|
|
45
|
+
}
|
|
46
|
+
export async function startPlaywriter(emailProfile) {
|
|
47
|
+
const cdpPort = 9922;
|
|
48
|
+
try {
|
|
49
|
+
// Find Chrome executable
|
|
50
|
+
const executablePath = findChromeExecutablePath();
|
|
51
|
+
console.log(`Found Chrome at: ${executablePath}`);
|
|
52
|
+
// Get available Chrome profiles
|
|
53
|
+
const profiles = getAllProfiles();
|
|
54
|
+
let selectedProfilePath;
|
|
55
|
+
// If no emailProfile provided, we can't proceed
|
|
56
|
+
if (!emailProfile) {
|
|
57
|
+
throw new Error('Email profile is required to start Chrome');
|
|
58
|
+
}
|
|
59
|
+
// Find the profile matching the email
|
|
60
|
+
const matchingProfile = profiles.find(p => p.email === emailProfile);
|
|
61
|
+
if (!matchingProfile) {
|
|
62
|
+
if (profiles.length === 0) {
|
|
63
|
+
// Create a temporary profile directory for automation
|
|
64
|
+
const tempDir = path.join(os.tmpdir(), 'playwriter-automation-profile');
|
|
65
|
+
if (!fs.existsSync(tempDir)) {
|
|
66
|
+
fs.mkdirSync(tempDir, { recursive: true });
|
|
67
|
+
}
|
|
68
|
+
selectedProfilePath = tempDir;
|
|
69
|
+
console.warn(`No Chrome profiles found. Using temporary profile at: ${tempDir}`);
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
throw new Error(`No Chrome profile found for email: ${emailProfile}. Available emails: ${profiles.map(p => p.email).join(', ')}`);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
selectedProfilePath = matchingProfile.path;
|
|
77
|
+
console.log(`Using profile for ${emailProfile}: ${selectedProfilePath}`);
|
|
78
|
+
}
|
|
79
|
+
// Start browser with CDP enabled
|
|
80
|
+
console.log(`Starting Chrome with CDP on port ${cdpPort}...`);
|
|
81
|
+
// Get the Chrome user data directory and profile folder
|
|
82
|
+
const chromeUserDataDir = path.dirname(selectedProfilePath);
|
|
83
|
+
const profileFolder = path.basename(selectedProfilePath);
|
|
84
|
+
// Build Chrome arguments
|
|
85
|
+
const chromeArgs = [
|
|
86
|
+
`--remote-debugging-port=${cdpPort}`,
|
|
87
|
+
'--window-position=-32000,-32000', // Position window off-screen
|
|
88
|
+
'--window-size=1280,720',
|
|
89
|
+
'--disable-backgrounding-occluded-windows', // Prevents Chrome from throttling/suspending hidden tabs
|
|
90
|
+
'--disable-gpu', // Disable GPU acceleration for better compatibility
|
|
91
|
+
`--user-data-dir=${chromeUserDataDir}`, // Chrome's main user data directory
|
|
92
|
+
'--no-first-run', // Skip first-run dialogs
|
|
93
|
+
'--disable-default-apps', // Disable default app installation
|
|
94
|
+
'--disable-translate', // Disable translate prompts
|
|
95
|
+
'--disable-features=TranslateUI', // Disable translate UI
|
|
96
|
+
'--no-default-browser-check', // Don't check if Chrome is default browser
|
|
97
|
+
'--disable-session-crashed-bubble', // Disable session restore bubble
|
|
98
|
+
'--disable-infobars', // Disable info bars
|
|
99
|
+
'--automation', // Enable automation mode
|
|
100
|
+
];
|
|
101
|
+
// Add profile-directory for non-default profiles
|
|
102
|
+
if (profileFolder !== 'Default') {
|
|
103
|
+
chromeArgs.push(`--profile-directory=${profileFolder}`);
|
|
104
|
+
}
|
|
105
|
+
// Launch Chrome directly as a subprocess
|
|
106
|
+
console.log('Launching Chrome with args:', chromeArgs);
|
|
107
|
+
const chromeProcess = spawn(executablePath, chromeArgs, {
|
|
108
|
+
detached: false,
|
|
109
|
+
stdio: 'ignore', // Ignore Chrome's output to avoid noise
|
|
110
|
+
});
|
|
111
|
+
chromeProcess.on('error', (error) => {
|
|
112
|
+
console.error('Failed to start Chrome:', error);
|
|
113
|
+
throw error;
|
|
114
|
+
});
|
|
115
|
+
// Give Chrome a moment to start up and open the debugging port
|
|
116
|
+
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
117
|
+
// On macOS, minimize only this Chrome window using its PID
|
|
118
|
+
if (os.platform() === 'darwin' && chromeProcess.pid) {
|
|
119
|
+
try {
|
|
120
|
+
// Minimize the specific Chrome window using its process ID
|
|
121
|
+
// This keeps it running but out of the way
|
|
122
|
+
const minimizeScript = spawn('osascript', [
|
|
123
|
+
'-e', `tell application "System Events"`,
|
|
124
|
+
'-e', `tell (first process whose unix id is ${chromeProcess.pid})`,
|
|
125
|
+
'-e', `try`,
|
|
126
|
+
'-e', `set value of attribute "AXMinimized" of window 1 to true`,
|
|
127
|
+
'-e', `end try`,
|
|
128
|
+
'-e', `end tell`,
|
|
129
|
+
'-e', `end tell`
|
|
130
|
+
]);
|
|
131
|
+
minimizeScript.on('error', () => {
|
|
132
|
+
// Silently ignore - window might already be hidden
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
catch (e) {
|
|
136
|
+
// Ignore errors, this is best-effort
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
console.log(`Chrome started with CDP on port ${cdpPort} (window is hidden off-screen)`);
|
|
140
|
+
return { cdpPort, chromeProcess };
|
|
141
|
+
// // Resolve @playwright/mcp package.json path
|
|
142
|
+
// const require = createRequire(import.meta.url)
|
|
143
|
+
// const mcpPackageJsonPath = require.resolve('@playwright/mcp/package.json')
|
|
144
|
+
// const mcpCliPath = path.resolve(mcpPackageJsonPath, '..', 'cli.js')
|
|
145
|
+
// console.log(`Found MCP CLI at: ${mcpCliPath}`)
|
|
146
|
+
// // Start MCP CLI process
|
|
147
|
+
// console.log('Starting MCP CLI...')
|
|
148
|
+
// const mcpProcess = spawn('node', [
|
|
149
|
+
// mcpCliPath,
|
|
150
|
+
// '--cdp-endpoint',
|
|
151
|
+
// `http://localhost:${cdpPort}`
|
|
152
|
+
// ], {
|
|
153
|
+
// stdio: 'inherit', // Forward all logs
|
|
154
|
+
// })
|
|
155
|
+
// mcpProcess.on('error', (error) => {
|
|
156
|
+
// console.error('Failed to start MCP CLI:', error)
|
|
157
|
+
// })
|
|
158
|
+
// mcpProcess.on('exit', (code, signal) => {
|
|
159
|
+
// console.log(`MCP CLI exited with code ${code} and signal ${signal}`)
|
|
160
|
+
// })
|
|
161
|
+
// // Handle cleanup
|
|
162
|
+
// const cleanup = async () => {
|
|
163
|
+
// console.log('Shutting down...')
|
|
164
|
+
// mcpProcess.kill()
|
|
165
|
+
// chromeProcess.kill()
|
|
166
|
+
// process.exit(0)
|
|
167
|
+
// }
|
|
168
|
+
// process.on('SIGINT', cleanup)
|
|
169
|
+
// process.on('SIGTERM', cleanup)
|
|
170
|
+
// return { chromeProcess, mcpProcess }
|
|
171
|
+
}
|
|
172
|
+
catch (error) {
|
|
173
|
+
console.error('Failed to start Playwriter:', error);
|
|
174
|
+
throw error;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
//# sourceMappingURL=playwriter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"playwriter.js","sourceRoot":"","sources":["../src/playwriter.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/** Resolve Chrome's "User Data" root on the current OS. */
|
|
2
|
+
export declare function getChromeUserDataDir(): string;
|
|
3
|
+
/** Read and parse the top-level "Local State" JSON once. */
|
|
4
|
+
export declare function readLocalState(): any;
|
|
5
|
+
/** Map <profile-folder> → <signed-in email> (empty string if none). */
|
|
6
|
+
export declare function getProfileEmailMap(): Map<string, string>;
|
|
7
|
+
/** Return the full path to a profile directory for the given email. */
|
|
8
|
+
export declare function getProfilePathByEmail(email: string): string | null;
|
|
9
|
+
/** Get all available Chrome profiles with their paths and emails */
|
|
10
|
+
export declare function getAllProfiles(): Array<{
|
|
11
|
+
folder: string;
|
|
12
|
+
email: string;
|
|
13
|
+
path: string;
|
|
14
|
+
displayName: string;
|
|
15
|
+
}>;
|
|
16
|
+
//# sourceMappingURL=profiles.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"profiles.d.ts","sourceRoot":"","sources":["../src/profiles.ts"],"names":[],"mappings":"AAIA,2DAA2D;AAC3D,wBAAgB,oBAAoB,IAAI,MAAM,CAqB7C;AAED,4DAA4D;AAC5D,wBAAgB,cAAc,IAAI,GAAG,CAgBpC;AAED,uEAAuE;AACvE,wBAAgB,kBAAkB,IAAI,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CASxD;AAED,uEAAuE;AACvE,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAQlE;AAED,oEAAoE;AACpE,wBAAgB,cAAc,IAAI,KAAK,CAAC;IACpC,MAAM,EAAE,MAAM,CAAA;IACd,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,EAAE,MAAM,CAAA;IACZ,WAAW,EAAE,MAAM,CAAA;CACtB,CAAC,CA8BD"}
|
package/dist/profiles.js
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import os from 'node:os';
|
|
4
|
+
/** Resolve Chrome's "User Data" root on the current OS. */
|
|
5
|
+
export function getChromeUserDataDir() {
|
|
6
|
+
const home = os.homedir();
|
|
7
|
+
switch (os.platform()) {
|
|
8
|
+
case 'win32':
|
|
9
|
+
return path.join(process.env.LOCALAPPDATA, 'Google', 'Chrome', 'User Data');
|
|
10
|
+
case 'darwin':
|
|
11
|
+
return path.join(home, 'Library', 'Application Support', 'Google', 'Chrome');
|
|
12
|
+
default: // linux, freebsd, …
|
|
13
|
+
return path.join(home, '.config', 'google-chrome');
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
/** Read and parse the top-level "Local State" JSON once. */
|
|
17
|
+
export function readLocalState() {
|
|
18
|
+
try {
|
|
19
|
+
const dir = getChromeUserDataDir();
|
|
20
|
+
const localStatePath = path.join(dir, 'Local State');
|
|
21
|
+
if (!fs.existsSync(localStatePath)) {
|
|
22
|
+
console.warn(`Chrome Local State file not found at: ${localStatePath}`);
|
|
23
|
+
return {};
|
|
24
|
+
}
|
|
25
|
+
const raw = fs.readFileSync(localStatePath, 'utf8');
|
|
26
|
+
return JSON.parse(raw);
|
|
27
|
+
}
|
|
28
|
+
catch (error) {
|
|
29
|
+
console.error('Failed to read Chrome Local State:', error);
|
|
30
|
+
return {};
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
/** Map <profile-folder> → <signed-in email> (empty string if none). */
|
|
34
|
+
export function getProfileEmailMap() {
|
|
35
|
+
const localState = readLocalState();
|
|
36
|
+
const infoCache = localState.profile?.info_cache ?? {};
|
|
37
|
+
return new Map(Object.entries(infoCache).map(([folder, obj]) => [
|
|
38
|
+
folder,
|
|
39
|
+
obj.user_name ?? '',
|
|
40
|
+
]));
|
|
41
|
+
}
|
|
42
|
+
/** Return the full path to a profile directory for the given email. */
|
|
43
|
+
export function getProfilePathByEmail(email) {
|
|
44
|
+
const root = getChromeUserDataDir();
|
|
45
|
+
for (const [folder, userEmail] of getProfileEmailMap()) {
|
|
46
|
+
if (userEmail.toLowerCase() === email.toLowerCase()) {
|
|
47
|
+
return path.join(root, folder); // e.g. "…/User Data/Profile 2"
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return null; // not found
|
|
51
|
+
}
|
|
52
|
+
/** Get all available Chrome profiles with their paths and emails */
|
|
53
|
+
export function getAllProfiles() {
|
|
54
|
+
const root = getChromeUserDataDir();
|
|
55
|
+
const profiles = [];
|
|
56
|
+
for (const [folder, email] of getProfileEmailMap()) {
|
|
57
|
+
profiles.push({
|
|
58
|
+
folder,
|
|
59
|
+
email,
|
|
60
|
+
path: path.join(root, folder),
|
|
61
|
+
displayName: email || `${folder} (no email)`,
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
// Also include Default profile if it exists
|
|
65
|
+
const defaultPath = path.join(root, 'Default');
|
|
66
|
+
if (fs.existsSync(defaultPath) && !profiles.some(p => p.folder === 'Default')) {
|
|
67
|
+
profiles.unshift({
|
|
68
|
+
folder: 'Default',
|
|
69
|
+
email: '',
|
|
70
|
+
path: defaultPath,
|
|
71
|
+
displayName: 'Default (no email)',
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
return profiles;
|
|
75
|
+
}
|
|
76
|
+
//# sourceMappingURL=profiles.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"profiles.js","sourceRoot":"","sources":["../src/profiles.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"profiles.test.d.ts","sourceRoot":"","sources":["../src/profiles.test.ts"],"names":[],"mappings":""}
|