Skip to content

Commit c82d6e5

Browse files
authored
feat: add custom HTTP headers support (#17)
* feat(helpers): add env-based HTTP header injection * feat(run): expose header utility in wrapper * docs(skill): document custom headers feature * docs: add headers API reference * chore: bump version to 4.1.0
1 parent a91f718 commit c82d6e5

File tree

7 files changed

+132
-6
lines changed

7 files changed

+132
-6
lines changed

.claude-plugin/marketplace.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
"name": "playwright-skill",
1414
"source": "./",
1515
"description": "Claude Code Skill for general-purpose browser automation with Playwright. Claude autonomously writes and executes custom automation for testing pages, validating UX, and any browser task.",
16-
"version": "3.1.0",
16+
"version": "4.1.0",
1717
"author": {
1818
"name": "lackeyjb"
1919
},

.claude-plugin/plugin.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "playwright-skill",
3-
"version": "4.0.2",
3+
"version": "4.1.0",
44
"description": "Claude Code Skill for general-purpose browser automation with Playwright. Auto-detects dev servers, writes clean test scripts to /tmp, and autonomously handles any browser automation task.",
55
"author": {
66
"name": "lackeyjb"

API_REFERENCE.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -408,6 +408,29 @@ await page.route('**/api/**', route => {
408408
await page.route('**/*.{png,jpg,jpeg,gif}', route => route.abort());
409409
```
410410

411+
### Custom Headers via Environment Variables
412+
413+
The skill supports automatic header injection via environment variables:
414+
415+
```bash
416+
# Single header (simple)
417+
PW_HEADER_NAME=X-Automated-By PW_HEADER_VALUE=playwright-skill
418+
419+
# Multiple headers (JSON)
420+
PW_EXTRA_HEADERS='{"X-Automated-By":"playwright-skill","X-Request-ID":"123"}'
421+
```
422+
423+
These headers are automatically applied to all requests when using:
424+
- `helpers.createContext(browser)` - headers merged automatically
425+
- `getContextOptionsWithHeaders(options)` - utility injected by run.js wrapper
426+
427+
**Precedence (highest to lowest):**
428+
1. Headers passed directly in `options.extraHTTPHeaders`
429+
2. Environment variable headers
430+
3. Playwright defaults
431+
432+
**Use case:** Identify automated traffic so your backend can return LLM-optimized responses (e.g., plain text errors instead of styled HTML).
433+
411434
## Visual Testing
412435

413436
### Screenshots

skills/playwright-skill/SKILL.md

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -323,6 +323,45 @@ const data = await helpers.extractTableData(page, 'table.results');
323323

324324
See `lib/helpers.js` for full list.
325325

326+
## Custom HTTP Headers
327+
328+
Configure custom headers for all HTTP requests via environment variables. Useful for:
329+
- Identifying automated traffic to your backend
330+
- Getting LLM-optimized responses (e.g., plain text errors instead of styled HTML)
331+
- Adding authentication tokens globally
332+
333+
### Configuration
334+
335+
**Single header (common case):**
336+
```bash
337+
PW_HEADER_NAME=X-Automated-By PW_HEADER_VALUE=playwright-skill \
338+
cd $SKILL_DIR && node run.js /tmp/my-script.js
339+
```
340+
341+
**Multiple headers (JSON format):**
342+
```bash
343+
PW_EXTRA_HEADERS='{"X-Automated-By":"playwright-skill","X-Debug":"true"}' \
344+
cd $SKILL_DIR && node run.js /tmp/my-script.js
345+
```
346+
347+
### How It Works
348+
349+
Headers are automatically applied when using `helpers.createContext()`:
350+
351+
```javascript
352+
const context = await helpers.createContext(browser);
353+
const page = await context.newPage();
354+
// All requests from this page include your custom headers
355+
```
356+
357+
For scripts using raw Playwright API, use the injected `getContextOptionsWithHeaders()`:
358+
359+
```javascript
360+
const context = await browser.newContext(
361+
getContextOptionsWithHeaders({ viewport: { width: 1920, height: 1080 } })
362+
);
363+
```
364+
326365
## Advanced Usage
327366

328367
For comprehensive Playwright API documentation, see [API_REFERENCE.md](API_REFERENCE.md):
@@ -339,6 +378,7 @@ For comprehensive Playwright API documentation, see [API_REFERENCE.md](API_REFER
339378
## Tips
340379

341380
- **CRITICAL: Detect servers FIRST** - Always run `detectDevServers()` before writing test code for localhost testing
381+
- **Custom headers** - Use `PW_HEADER_NAME`/`PW_HEADER_VALUE` env vars to identify automated traffic to your backend
342382
- **Use /tmp for test files** - Write to `/tmp/playwright-test-*.js`, never to skill directory or user's project
343383
- **Parameterize URLs** - Put detected/provided URL in a `TARGET_URL` constant at the top of every script
344384
- **DEFAULT: Visible browser** - Always use `headless: false` unless user explicitly asks for headless mode

skills/playwright-skill/lib/helpers.js

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,38 @@
33

44
const { chromium, firefox, webkit } = require('playwright');
55

6+
/**
7+
* Parse extra HTTP headers from environment variables.
8+
* Supports two formats:
9+
* - PW_HEADER_NAME + PW_HEADER_VALUE: Single header (simple, common case)
10+
* - PW_EXTRA_HEADERS: JSON object for multiple headers (advanced)
11+
* Single header format takes precedence if both are set.
12+
* @returns {Object|null} Headers object or null if none configured
13+
*/
14+
function getExtraHeadersFromEnv() {
15+
const headerName = process.env.PW_HEADER_NAME;
16+
const headerValue = process.env.PW_HEADER_VALUE;
17+
18+
if (headerName && headerValue) {
19+
return { [headerName]: headerValue };
20+
}
21+
22+
const headersJson = process.env.PW_EXTRA_HEADERS;
23+
if (headersJson) {
24+
try {
25+
const parsed = JSON.parse(headersJson);
26+
if (typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed)) {
27+
return parsed;
28+
}
29+
console.warn('PW_EXTRA_HEADERS must be a JSON object, ignoring...');
30+
} catch (e) {
31+
console.warn('Failed to parse PW_EXTRA_HEADERS as JSON:', e.message);
32+
}
33+
}
34+
35+
return null;
36+
}
37+
638
/**
739
* Launch browser with standard configuration
840
* @param {string} browserType - 'chromium', 'firefox', or 'webkit'
@@ -313,6 +345,14 @@ async function retryWithBackoff(fn, maxRetries = 3, initialDelay = 1000) {
313345
* @param {Object} options - Context options
314346
*/
315347
async function createContext(browser, options = {}) {
348+
const envHeaders = getExtraHeadersFromEnv();
349+
350+
// Merge environment headers with any passed in options
351+
const mergedHeaders = {
352+
...envHeaders,
353+
...options.extraHTTPHeaders
354+
};
355+
316356
const defaultOptions = {
317357
viewport: { width: 1280, height: 720 },
318358
userAgent: options.mobile
@@ -321,7 +361,9 @@ async function createContext(browser, options = {}) {
321361
permissions: options.permissions || [],
322362
geolocation: options.geolocation,
323363
locale: options.locale || 'en-US',
324-
timezoneId: options.timezoneId || 'America/New_York'
364+
timezoneId: options.timezoneId || 'America/New_York',
365+
// Only include extraHTTPHeaders if we have any
366+
...(Object.keys(mergedHeaders).length > 0 && { extraHTTPHeaders: mergedHeaders })
325367
};
326368

327369
return await browser.newContext({ ...defaultOptions, ...options });
@@ -394,5 +436,6 @@ module.exports = {
394436
handleCookieBanner,
395437
retryWithBackoff,
396438
createContext,
397-
detectDevServers
439+
detectDevServers,
440+
getExtraHeadersFromEnv
398441
};

skills/playwright-skill/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "playwright-skill",
3-
"version": "4.0.2",
3+
"version": "4.1.0",
44
"description": "General-purpose browser automation with Playwright for Claude Code with auto-detection and smart test management",
55
"author": "lackeyjb",
66
"main": "run.js",
@@ -17,7 +17,7 @@
1717
"general-purpose"
1818
],
1919
"dependencies": {
20-
"playwright": "^1.48.0"
20+
"playwright": "^1.57.0"
2121
},
2222
"engines": {
2323
"node": ">=14.0.0"

skills/playwright-skill/run.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,26 @@ function wrapCodeIfNeeded(code) {
122122
const { chromium, firefox, webkit, devices } = require('playwright');
123123
const helpers = require('./lib/helpers');
124124
125+
// Extra headers from environment variables (if configured)
126+
const __extraHeaders = helpers.getExtraHeadersFromEnv();
127+
128+
/**
129+
* Utility to merge environment headers into context options.
130+
* Use when creating contexts with raw Playwright API instead of helpers.createContext().
131+
* @param {Object} options - Context options
132+
* @returns {Object} Options with extraHTTPHeaders merged in
133+
*/
134+
function getContextOptionsWithHeaders(options = {}) {
135+
if (!__extraHeaders) return options;
136+
return {
137+
...options,
138+
extraHTTPHeaders: {
139+
...__extraHeaders,
140+
...(options.extraHTTPHeaders || {})
141+
}
142+
};
143+
}
144+
125145
(async () => {
126146
try {
127147
${code}

0 commit comments

Comments
 (0)