chrome-jig 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.
- package/README.md +197 -0
- package/SKILL.md +126 -0
- package/bin/cjig.js +3 -0
- package/dist/chrome/connection.d.ts +95 -0
- package/dist/chrome/connection.d.ts.map +1 -0
- package/dist/chrome/connection.js +220 -0
- package/dist/chrome/connection.js.map +1 -0
- package/dist/chrome/launcher.d.ts +53 -0
- package/dist/chrome/launcher.d.ts.map +1 -0
- package/dist/chrome/launcher.js +203 -0
- package/dist/chrome/launcher.js.map +1 -0
- package/dist/cli.d.ts +6 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +352 -0
- package/dist/cli.js.map +1 -0
- package/dist/cljs/compiler.d.ts +10 -0
- package/dist/cljs/compiler.d.ts.map +1 -0
- package/dist/cljs/compiler.js +20 -0
- package/dist/cljs/compiler.js.map +1 -0
- package/dist/cljs/runtime.d.ts +32 -0
- package/dist/cljs/runtime.d.ts.map +1 -0
- package/dist/cljs/runtime.js +63 -0
- package/dist/cljs/runtime.js.map +1 -0
- package/dist/commands/cljs-eval.d.ts +7 -0
- package/dist/commands/cljs-eval.d.ts.map +1 -0
- package/dist/commands/cljs-eval.js +23 -0
- package/dist/commands/cljs-eval.js.map +1 -0
- package/dist/commands/eval.d.ts +13 -0
- package/dist/commands/eval.d.ts.map +1 -0
- package/dist/commands/eval.js +37 -0
- package/dist/commands/eval.js.map +1 -0
- package/dist/commands/init.d.ts +23 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +149 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/inject.d.ts +14 -0
- package/dist/commands/inject.d.ts.map +1 -0
- package/dist/commands/inject.js +31 -0
- package/dist/commands/inject.js.map +1 -0
- package/dist/commands/install-skill.d.ts +11 -0
- package/dist/commands/install-skill.d.ts.map +1 -0
- package/dist/commands/install-skill.js +91 -0
- package/dist/commands/install-skill.js.map +1 -0
- package/dist/commands/launch.d.ts +11 -0
- package/dist/commands/launch.d.ts.map +1 -0
- package/dist/commands/launch.js +15 -0
- package/dist/commands/launch.js.map +1 -0
- package/dist/commands/status.d.ts +15 -0
- package/dist/commands/status.d.ts.map +1 -0
- package/dist/commands/status.js +25 -0
- package/dist/commands/status.js.map +1 -0
- package/dist/commands/tabs.d.ts +13 -0
- package/dist/commands/tabs.d.ts.map +1 -0
- package/dist/commands/tabs.js +40 -0
- package/dist/commands/tabs.js.map +1 -0
- package/dist/config/loader.d.ts +26 -0
- package/dist/config/loader.d.ts.map +1 -0
- package/dist/config/loader.js +119 -0
- package/dist/config/loader.js.map +1 -0
- package/dist/config/schema.d.ts +56 -0
- package/dist/config/schema.d.ts.map +1 -0
- package/dist/config/schema.js +9 -0
- package/dist/config/schema.js.map +1 -0
- package/dist/config/xdg.d.ts +16 -0
- package/dist/config/xdg.d.ts.map +1 -0
- package/dist/config/xdg.js +31 -0
- package/dist/config/xdg.js.map +1 -0
- package/dist/index.d.ts +25 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +25 -0
- package/dist/index.js.map +1 -0
- package/dist/repl/commands.d.ts +32 -0
- package/dist/repl/commands.d.ts.map +1 -0
- package/dist/repl/commands.js +257 -0
- package/dist/repl/commands.js.map +1 -0
- package/dist/repl/completer.d.ts +6 -0
- package/dist/repl/completer.d.ts.map +1 -0
- package/dist/repl/completer.js +30 -0
- package/dist/repl/completer.js.map +1 -0
- package/dist/repl/repl.d.ts +31 -0
- package/dist/repl/repl.d.ts.map +1 -0
- package/dist/repl/repl.js +224 -0
- package/dist/repl/repl.js.map +1 -0
- package/dist/utils/env.d.ts +19 -0
- package/dist/utils/env.d.ts.map +1 -0
- package/dist/utils/env.js +37 -0
- package/dist/utils/env.js.map +1 -0
- package/package.json +50 -0
package/README.md
ADDED
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
# Chrome Jig
|
|
2
|
+
|
|
3
|
+
A CLI tool for Chrome debugging with script injection, file watching, and Claude skill support.
|
|
4
|
+
|
|
5
|
+
## Why This Tool
|
|
6
|
+
|
|
7
|
+
chrome-jig is a CLI for Chrome debugging workflows: launch, inject scripts, evaluate JavaScript, watch files, and re-inject on change. It uses CDP (Chrome DevTools Protocol) underneath — the same protocol that Chrome MCP, Puppeteer, and other tools use.
|
|
8
|
+
|
|
9
|
+
**Where it helps over generic CDP tools**: Compound operations that would otherwise require multiple manual steps. A named script registry resolves short names to URLs via project config (`.cjig.json`). File watching detects changes and re-injects automatically. Idempotent launch reuses an existing Chrome instance instead of failing on port conflicts. These are workflow-level conveniences — they compose CDP primitives, they don't extend them.
|
|
10
|
+
|
|
11
|
+
**Where it doesn't help**: Single evaluations, screenshots, DOM inspection, network traces. Any CDP client can do these equally well. Chrome MCP's `evaluate_script` and this tool's `eval` both call `Runtime.evaluate` in the end.
|
|
12
|
+
|
|
13
|
+
**Independent developer workflow**: The CLI is usable without any LLM. `cjig launch && cjig inject my-script && cjig repl` is a complete development loop with no AI in the path.
|
|
14
|
+
|
|
15
|
+
## Installation
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
# From npm registry
|
|
19
|
+
pnpm add -g chrome-jig
|
|
20
|
+
|
|
21
|
+
# Or for development
|
|
22
|
+
git clone https://github.com/yourname/chrome-jig.git
|
|
23
|
+
cd chrome-jig
|
|
24
|
+
pnpm install
|
|
25
|
+
npm link # uses nvm's bin directory
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Quick Start
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
# Launch Chrome with debugging enabled
|
|
32
|
+
cjig launch
|
|
33
|
+
|
|
34
|
+
# Start interactive REPL
|
|
35
|
+
cjig repl
|
|
36
|
+
|
|
37
|
+
# In REPL:
|
|
38
|
+
> document.title
|
|
39
|
+
"My Page"
|
|
40
|
+
|
|
41
|
+
> .tabs
|
|
42
|
+
→ [0] My Page
|
|
43
|
+
https://example.com
|
|
44
|
+
|
|
45
|
+
> .inject my-harness
|
|
46
|
+
Injected: http://localhost:5173/harnesses/my-harness.js
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## CLI Commands
|
|
50
|
+
|
|
51
|
+
### Chrome Management
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
cjig launch # Launch with default profile
|
|
55
|
+
cjig launch --profile=testing # Named profile
|
|
56
|
+
cjig status # Check if Chrome is running
|
|
57
|
+
cjig status --host=192.168.1.5 # Check remote Chrome
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### Tab Operations
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
cjig tabs # List open tabs
|
|
64
|
+
cjig tab example # Select tab by URL pattern
|
|
65
|
+
cjig tab 0 # Select tab by index
|
|
66
|
+
cjig open https://... # Open new tab
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### Script Injection
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
cjig inject my-script # Inject by name (from config)
|
|
73
|
+
cjig inject https://... # Inject by URL
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### Evaluation
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
cjig eval "document.title" # One-shot eval
|
|
80
|
+
cjig eval "window.myApi.status()" # Call injected API
|
|
81
|
+
cjig repl # Interactive REPL
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## REPL Commands
|
|
85
|
+
|
|
86
|
+
```
|
|
87
|
+
> expression Evaluate JavaScript in browser
|
|
88
|
+
|
|
89
|
+
.help Show available commands
|
|
90
|
+
.tabs List open tabs
|
|
91
|
+
.tab <pattern|index> Switch to tab
|
|
92
|
+
.open <url> Open new tab
|
|
93
|
+
.inject <name|url> Inject script
|
|
94
|
+
.reload Reload current tab
|
|
95
|
+
.watch [on|off] Toggle file watching
|
|
96
|
+
.build Run preBuild hook
|
|
97
|
+
.config Show current config
|
|
98
|
+
.clear Clear console
|
|
99
|
+
.exit Exit REPL
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## Configuration
|
|
103
|
+
|
|
104
|
+
### Global Config (`~/.config/cjig/config.json`)
|
|
105
|
+
|
|
106
|
+
```json
|
|
107
|
+
{
|
|
108
|
+
"defaults": {
|
|
109
|
+
"port": 9222,
|
|
110
|
+
"profile": "default"
|
|
111
|
+
},
|
|
112
|
+
"chrome": {
|
|
113
|
+
"path": "/path/to/chrome",
|
|
114
|
+
"flags": ["--disable-background-timer-throttling"]
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### Project Config (`.cjig.json`)
|
|
120
|
+
|
|
121
|
+
```json
|
|
122
|
+
{
|
|
123
|
+
"scripts": {
|
|
124
|
+
"baseUrl": "http://localhost:5173/harnesses/",
|
|
125
|
+
"registry": {
|
|
126
|
+
"bs": {
|
|
127
|
+
"path": "block-segmenter-harness.js",
|
|
128
|
+
"label": "Block Segmenter",
|
|
129
|
+
"windowApi": "BlockSegmenter",
|
|
130
|
+
"alias": "BS",
|
|
131
|
+
"quickStart": "BS.overlayOn()"
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
},
|
|
135
|
+
"watch": {
|
|
136
|
+
"paths": ["dist/harnesses/*.js"],
|
|
137
|
+
"debounce": 300
|
|
138
|
+
},
|
|
139
|
+
"hooks": {
|
|
140
|
+
"preBuild": "pnpm build:harnesses"
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
## Environment Variables
|
|
146
|
+
|
|
147
|
+
| Variable | Default | Description |
|
|
148
|
+
| -------------- | ------------- | ----------------- |
|
|
149
|
+
| `CJIG_PORT` | `9222` | CDP port |
|
|
150
|
+
| `CJIG_PROFILE` | `default` | Profile name |
|
|
151
|
+
| `CJIG_HOST` | `localhost` | Chrome host |
|
|
152
|
+
| `CHROME_PATH` | (auto-detect) | Chrome executable |
|
|
153
|
+
|
|
154
|
+
## Shell Setup
|
|
155
|
+
|
|
156
|
+
```bash
|
|
157
|
+
cjig env >> ~/.zshrc
|
|
158
|
+
source ~/.zshrc
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
This adds:
|
|
162
|
+
|
|
163
|
+
- `cjr` - alias for `cjig repl`
|
|
164
|
+
- `cjl` - alias for `cjig launch`
|
|
165
|
+
- `cjt` - alias for `cjig tabs`
|
|
166
|
+
|
|
167
|
+
## Use as Claude Skill
|
|
168
|
+
|
|
169
|
+
```bash
|
|
170
|
+
cjig install-skill # Symlinks this package to ~/.claude/skills/chrome-jig
|
|
171
|
+
cjig uninstall-skill # Removes the symlink
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
Then Claude can use it via the SKILL.md instructions.
|
|
175
|
+
|
|
176
|
+
## Directory Layout (XDG)
|
|
177
|
+
|
|
178
|
+
```
|
|
179
|
+
~/.config/cjig/
|
|
180
|
+
├── config.json # Global config
|
|
181
|
+
└── profiles/ # Named config profiles
|
|
182
|
+
|
|
183
|
+
~/.local/share/cjig/
|
|
184
|
+
└── chrome-profiles/ # Chrome user-data dirs
|
|
185
|
+
└── default/
|
|
186
|
+
|
|
187
|
+
~/.local/state/cjig/
|
|
188
|
+
└── last-session.json # Session state
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
## Development
|
|
192
|
+
|
|
193
|
+
See [ARCHITECTURE.md](ARCHITECTURE.md) for technical internals — module structure, data flow diagrams, CDP execution model, and design decisions.
|
|
194
|
+
|
|
195
|
+
## License
|
|
196
|
+
|
|
197
|
+
MIT
|
package/SKILL.md
ADDED
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
# Chrome Jig — Claude Skill
|
|
2
|
+
|
|
3
|
+
## Purpose
|
|
4
|
+
|
|
5
|
+
Drive harness development workflows in Chrome from the command line. This skill lets you launch isolated Chrome sessions, inject named scripts, evaluate JavaScript, and manage the full inject → exercise → modify → re-inject development loop.
|
|
6
|
+
|
|
7
|
+
## Mental Model
|
|
8
|
+
|
|
9
|
+
- **Session** = Chrome instance + profile + persisted state (cookies, localStorage, etc.)
|
|
10
|
+
- **Harness** = named script from the project's script registry, injected into a page
|
|
11
|
+
- **Lifecycle**: launch → navigate → inject → exercise → [modify → auto-reinject] → exercise
|
|
12
|
+
|
|
13
|
+
The project's `.cjig.json` is the source of truth. Read it first to understand what scripts are available, what build hooks exist, and what file paths are watched.
|
|
14
|
+
|
|
15
|
+
## Speed Principle
|
|
16
|
+
|
|
17
|
+
Prefer `cjig` CLI commands over Chrome DevTools MCP tools whenever possible:
|
|
18
|
+
|
|
19
|
+
- `cjig eval "expr"` = direct, instant, no LLM round-trip
|
|
20
|
+
- MCP browser tools = each action round-trips through the LLM
|
|
21
|
+
|
|
22
|
+
**Rule**: Use `cjig` for eval, inject, tabs, launch, and status.
|
|
23
|
+
|
|
24
|
+
**Fall back to Chrome DevTools MCP** only for capabilities the CLI lacks: screenshots, performance traces, network inspection, DOM snapshots, and visual page interaction.
|
|
25
|
+
|
|
26
|
+
## Session Management
|
|
27
|
+
|
|
28
|
+
- `cjig status` — check if Chrome is running
|
|
29
|
+
- `cjig launch` — start Chrome with default profile
|
|
30
|
+
- `cjig launch --profile=testing` — start with a named profile
|
|
31
|
+
- Profiles are isolated from the user's normal browser
|
|
32
|
+
- State persists across runs in `~/.local/share/cjig/chrome-profiles/`
|
|
33
|
+
- Session metadata stored at `~/.local/state/cjig/`
|
|
34
|
+
|
|
35
|
+
## Harness Workflow
|
|
36
|
+
|
|
37
|
+
1. **Read project config**: check `.cjig.json` in the project root for `scripts.registry`
|
|
38
|
+
2. **Launch Chrome** if not running: `cjig launch`
|
|
39
|
+
3. **Navigate** to the target page: `cjig eval "location.href = 'http://...'"`
|
|
40
|
+
4. **Inject harness** by name: `cjig inject <name>`
|
|
41
|
+
5. **Exercise** via eval using `windowApi` or `alias` from the registry: `cjig eval "BS.overlayOn()"`
|
|
42
|
+
6. **For iterative development**, suggest `.watch on` in the REPL for live reload on file save
|
|
43
|
+
|
|
44
|
+
## Command Reference
|
|
45
|
+
|
|
46
|
+
### Session
|
|
47
|
+
|
|
48
|
+
| Command | Description |
|
|
49
|
+
|---|---|
|
|
50
|
+
| `cjig launch [--profile=NAME]` | Launch Chrome with debugging enabled |
|
|
51
|
+
| `cjig status` | Check if Chrome is running |
|
|
52
|
+
|
|
53
|
+
### Navigation
|
|
54
|
+
|
|
55
|
+
| Command | Description |
|
|
56
|
+
|---|---|
|
|
57
|
+
| `cjig tabs` | List open tabs |
|
|
58
|
+
| `cjig tab <pattern\|index>` | Switch to a tab |
|
|
59
|
+
| `cjig open <url>` | Open a new tab |
|
|
60
|
+
|
|
61
|
+
### Harness
|
|
62
|
+
|
|
63
|
+
| Command | Description |
|
|
64
|
+
|---|---|
|
|
65
|
+
| `cjig inject <name\|url>` | Inject a script by registry name or URL |
|
|
66
|
+
| `cjig eval <expression>` | Evaluate JavaScript in the current tab |
|
|
67
|
+
| `cjig repl` | Start interactive REPL |
|
|
68
|
+
|
|
69
|
+
### Configuration
|
|
70
|
+
|
|
71
|
+
| Command | Description |
|
|
72
|
+
|---|---|
|
|
73
|
+
| `cjig config` | Show resolved configuration |
|
|
74
|
+
| `cjig init` | Generate project `.cjig.json` |
|
|
75
|
+
| `cjig env` | Print shell aliases and exports |
|
|
76
|
+
| `cjig install-skill` | Symlink skill to `~/.claude/skills/` |
|
|
77
|
+
| `cjig uninstall-skill` | Remove skill symlink |
|
|
78
|
+
|
|
79
|
+
## Reading Project Config
|
|
80
|
+
|
|
81
|
+
The `.cjig.json` file defines the development environment:
|
|
82
|
+
|
|
83
|
+
```json
|
|
84
|
+
{
|
|
85
|
+
"scripts": {
|
|
86
|
+
"baseUrl": "http://localhost:5173/harnesses/",
|
|
87
|
+
"registry": {
|
|
88
|
+
"bs": {
|
|
89
|
+
"path": "block-segmenter-harness.js",
|
|
90
|
+
"windowApi": "BlockSegmenter",
|
|
91
|
+
"alias": "BS",
|
|
92
|
+
"quickStart": "BS.overlayOn()"
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
},
|
|
96
|
+
"watch": {
|
|
97
|
+
"paths": ["dist/harnesses/*.js"],
|
|
98
|
+
"debounce": 300
|
|
99
|
+
},
|
|
100
|
+
"hooks": {
|
|
101
|
+
"preBuild": "pnpm build:harnesses"
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
Key fields:
|
|
107
|
+
|
|
108
|
+
- `scripts.registry[name].path` — script filename, resolved against `scripts.baseUrl`
|
|
109
|
+
- `scripts.registry[name].windowApi` — the global object the script exposes (e.g., `window.BlockSegmenter`)
|
|
110
|
+
- `scripts.registry[name].alias` — short name for the API (e.g., `BS`)
|
|
111
|
+
- `scripts.registry[name].quickStart` — example expression to try after injection
|
|
112
|
+
- `watch.paths` — glob patterns for auto-reinject on file change
|
|
113
|
+
- `hooks.preBuild` — build command run before injection
|
|
114
|
+
|
|
115
|
+
## When to Suggest REPL vs eval
|
|
116
|
+
|
|
117
|
+
- **REPL** (`cjig repl`): when the user needs interactive exploration, or wants `.watch on` for live reload during development
|
|
118
|
+
- **eval** (`cjig eval "..."`): for automated actions — fast, scriptable, one-shot
|
|
119
|
+
- REPL dot-commands (`.inject`, `.watch`, `.tabs`, `.build`, `.reload`) are for human interactive use
|
|
120
|
+
|
|
121
|
+
## Anti-Patterns
|
|
122
|
+
|
|
123
|
+
- Do not use Chrome DevTools MCP `evaluate_script` when `cjig eval` works — it adds unnecessary LLM round-trips
|
|
124
|
+
- Do not suggest manual URL injection when a script is registered by name in the project config
|
|
125
|
+
- Do not forget to check `cjig status` before attempting to connect
|
|
126
|
+
- Do not skip reading `.cjig.json` — it tells you what harnesses exist and how to use them
|
package/bin/cjig.js
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Chrome DevTools Protocol connection via Playwright
|
|
3
|
+
*/
|
|
4
|
+
import { Page, CDPSession } from 'playwright-core';
|
|
5
|
+
export interface ChromeTarget {
|
|
6
|
+
id: string;
|
|
7
|
+
type: string;
|
|
8
|
+
title: string;
|
|
9
|
+
url: string;
|
|
10
|
+
webSocketDebuggerUrl?: string;
|
|
11
|
+
}
|
|
12
|
+
export interface ConnectionOptions {
|
|
13
|
+
host: string;
|
|
14
|
+
port: number;
|
|
15
|
+
}
|
|
16
|
+
export declare class ChromeConnection {
|
|
17
|
+
private options;
|
|
18
|
+
private browser;
|
|
19
|
+
private context;
|
|
20
|
+
private currentPage;
|
|
21
|
+
private cdpSession;
|
|
22
|
+
constructor(options: ConnectionOptions);
|
|
23
|
+
get endpoint(): string;
|
|
24
|
+
get wsEndpoint(): string;
|
|
25
|
+
/**
|
|
26
|
+
* Check if Chrome is running on the configured port
|
|
27
|
+
*/
|
|
28
|
+
isRunning(): Promise<boolean>;
|
|
29
|
+
/**
|
|
30
|
+
* Get Chrome version info
|
|
31
|
+
*/
|
|
32
|
+
getVersion(): Promise<Record<string, string> | null>;
|
|
33
|
+
/**
|
|
34
|
+
* List all available targets (tabs)
|
|
35
|
+
*/
|
|
36
|
+
listTargets(): Promise<ChromeTarget[]>;
|
|
37
|
+
/**
|
|
38
|
+
* Connect to Chrome using Playwright CDP
|
|
39
|
+
*/
|
|
40
|
+
connect(): Promise<void>;
|
|
41
|
+
/**
|
|
42
|
+
* Disconnect from Chrome (does not close the browser)
|
|
43
|
+
*/
|
|
44
|
+
disconnect(): Promise<void>;
|
|
45
|
+
/**
|
|
46
|
+
* Get list of pages (tabs)
|
|
47
|
+
*/
|
|
48
|
+
getPages(): Promise<Page[]>;
|
|
49
|
+
/**
|
|
50
|
+
* Get current page
|
|
51
|
+
*/
|
|
52
|
+
getCurrentPage(): Page | null;
|
|
53
|
+
/**
|
|
54
|
+
* Select a page by URL pattern
|
|
55
|
+
*/
|
|
56
|
+
selectPage(urlPattern: string): Promise<Page | null>;
|
|
57
|
+
/**
|
|
58
|
+
* Select a page by index
|
|
59
|
+
*/
|
|
60
|
+
selectPageByIndex(index: number): Promise<Page | null>;
|
|
61
|
+
/**
|
|
62
|
+
* Open a new page with the given URL
|
|
63
|
+
*/
|
|
64
|
+
openPage(url: string): Promise<Page>;
|
|
65
|
+
/**
|
|
66
|
+
* Evaluate JavaScript in the current page via CDP Runtime.evaluate.
|
|
67
|
+
* Runs in the page's main world (same as the browser console),
|
|
68
|
+
* so globals persist and background navigations don't break it.
|
|
69
|
+
*/
|
|
70
|
+
evaluate<T>(expression: string): Promise<T>;
|
|
71
|
+
/**
|
|
72
|
+
* Inject a script into the current page by URL.
|
|
73
|
+
* Fetches inside the browser via CDP evaluation, then executes
|
|
74
|
+
* with indirect eval to place globals in the page's main world.
|
|
75
|
+
*/
|
|
76
|
+
injectScript(url: string): Promise<void>;
|
|
77
|
+
/**
|
|
78
|
+
* Inject script content directly via CDP evaluation,
|
|
79
|
+
* bypassing CSP restrictions.
|
|
80
|
+
*/
|
|
81
|
+
injectScriptContent(content: string): Promise<void>;
|
|
82
|
+
/**
|
|
83
|
+
* Reload the current page
|
|
84
|
+
*/
|
|
85
|
+
reload(): Promise<void>;
|
|
86
|
+
/**
|
|
87
|
+
* Get CDP session for advanced operations
|
|
88
|
+
*/
|
|
89
|
+
getCDPSession(): Promise<CDPSession>;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Create a connection with default or provided options
|
|
93
|
+
*/
|
|
94
|
+
export declare function createConnection(options?: Partial<ConnectionOptions>): ChromeConnection;
|
|
95
|
+
//# sourceMappingURL=connection.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"connection.d.ts","sourceRoot":"","sources":["../../src/chrome/connection.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAqC,IAAI,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAEtF,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,oBAAoB,CAAC,EAAE,MAAM,CAAC;CAC/B;AAED,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd;AAED,qBAAa,gBAAgB;IAMf,OAAO,CAAC,OAAO;IAL3B,OAAO,CAAC,OAAO,CAAwB;IACvC,OAAO,CAAC,OAAO,CAA+B;IAC9C,OAAO,CAAC,WAAW,CAAqB;IACxC,OAAO,CAAC,UAAU,CAA2B;gBAEzB,OAAO,EAAE,iBAAiB;IAE9C,IAAI,QAAQ,IAAI,MAAM,CAErB;IAED,IAAI,UAAU,IAAI,MAAM,CAEvB;IAED;;OAEG;IACG,SAAS,IAAI,OAAO,CAAC,OAAO,CAAC;IASnC;;OAEG;IACG,UAAU,IAAI,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,CAAC;IAU1D;;OAEG;IACG,WAAW,IAAI,OAAO,CAAC,YAAY,EAAE,CAAC;IAU5C;;OAEG;IACG,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAe9B;;OAEG;IACG,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAajC;;OAEG;IACG,QAAQ,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC;IAKjC;;OAEG;IACH,cAAc,IAAI,IAAI,GAAG,IAAI;IAI7B;;OAEG;IACG,UAAU,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC;IAc1D;;OAEG;IACG,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC;IAW5D;;OAEG;IACG,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAW1C;;;;OAIG;IACG,QAAQ,CAAC,CAAC,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC;IAsBjD;;;;OAIG;IACG,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAc9C;;;OAGG;IACG,mBAAmB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIzD;;OAEG;IACG,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;IAQ7B;;OAEG;IACG,aAAa,IAAI,OAAO,CAAC,UAAU,CAAC;CAW3C;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,GAAE,OAAO,CAAC,iBAAiB,CAAM,GAAG,gBAAgB,CAK3F"}
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Chrome DevTools Protocol connection via Playwright
|
|
3
|
+
*/
|
|
4
|
+
import { chromium } from 'playwright-core';
|
|
5
|
+
export class ChromeConnection {
|
|
6
|
+
options;
|
|
7
|
+
browser = null;
|
|
8
|
+
context = null;
|
|
9
|
+
currentPage = null;
|
|
10
|
+
cdpSession = null;
|
|
11
|
+
constructor(options) {
|
|
12
|
+
this.options = options;
|
|
13
|
+
}
|
|
14
|
+
get endpoint() {
|
|
15
|
+
return `http://${this.options.host}:${this.options.port}`;
|
|
16
|
+
}
|
|
17
|
+
get wsEndpoint() {
|
|
18
|
+
return `ws://${this.options.host}:${this.options.port}`;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Check if Chrome is running on the configured port
|
|
22
|
+
*/
|
|
23
|
+
async isRunning() {
|
|
24
|
+
try {
|
|
25
|
+
const response = await fetch(`${this.endpoint}/json/version`);
|
|
26
|
+
return response.ok;
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Get Chrome version info
|
|
34
|
+
*/
|
|
35
|
+
async getVersion() {
|
|
36
|
+
try {
|
|
37
|
+
const response = await fetch(`${this.endpoint}/json/version`);
|
|
38
|
+
if (!response.ok)
|
|
39
|
+
return null;
|
|
40
|
+
return (await response.json());
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* List all available targets (tabs)
|
|
48
|
+
*/
|
|
49
|
+
async listTargets() {
|
|
50
|
+
try {
|
|
51
|
+
const response = await fetch(`${this.endpoint}/json/list`);
|
|
52
|
+
if (!response.ok)
|
|
53
|
+
return [];
|
|
54
|
+
return (await response.json());
|
|
55
|
+
}
|
|
56
|
+
catch {
|
|
57
|
+
return [];
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Connect to Chrome using Playwright CDP
|
|
62
|
+
*/
|
|
63
|
+
async connect() {
|
|
64
|
+
if (this.browser)
|
|
65
|
+
return;
|
|
66
|
+
this.browser = await chromium.connectOverCDP(this.endpoint);
|
|
67
|
+
const contexts = this.browser.contexts();
|
|
68
|
+
if (contexts.length > 0) {
|
|
69
|
+
this.context = contexts[0];
|
|
70
|
+
const pages = this.context.pages();
|
|
71
|
+
if (pages.length > 0) {
|
|
72
|
+
this.currentPage = pages[0];
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Disconnect from Chrome (does not close the browser)
|
|
78
|
+
*/
|
|
79
|
+
async disconnect() {
|
|
80
|
+
if (this.cdpSession) {
|
|
81
|
+
await this.cdpSession.detach();
|
|
82
|
+
this.cdpSession = null;
|
|
83
|
+
}
|
|
84
|
+
if (this.browser) {
|
|
85
|
+
await this.browser.close();
|
|
86
|
+
this.browser = null;
|
|
87
|
+
this.context = null;
|
|
88
|
+
this.currentPage = null;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Get list of pages (tabs)
|
|
93
|
+
*/
|
|
94
|
+
async getPages() {
|
|
95
|
+
if (!this.context)
|
|
96
|
+
return [];
|
|
97
|
+
return this.context.pages();
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Get current page
|
|
101
|
+
*/
|
|
102
|
+
getCurrentPage() {
|
|
103
|
+
return this.currentPage;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Select a page by URL pattern
|
|
107
|
+
*/
|
|
108
|
+
async selectPage(urlPattern) {
|
|
109
|
+
const pages = await this.getPages();
|
|
110
|
+
const pattern = urlPattern.toLowerCase();
|
|
111
|
+
const page = pages.find((p) => p.url().toLowerCase().includes(pattern));
|
|
112
|
+
if (page) {
|
|
113
|
+
this.currentPage = page;
|
|
114
|
+
return page;
|
|
115
|
+
}
|
|
116
|
+
return null;
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Select a page by index
|
|
120
|
+
*/
|
|
121
|
+
async selectPageByIndex(index) {
|
|
122
|
+
const pages = await this.getPages();
|
|
123
|
+
if (index >= 0 && index < pages.length) {
|
|
124
|
+
this.currentPage = pages[index];
|
|
125
|
+
return this.currentPage;
|
|
126
|
+
}
|
|
127
|
+
return null;
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Open a new page with the given URL
|
|
131
|
+
*/
|
|
132
|
+
async openPage(url) {
|
|
133
|
+
if (!this.context) {
|
|
134
|
+
throw new Error('Not connected to Chrome');
|
|
135
|
+
}
|
|
136
|
+
const page = await this.context.newPage();
|
|
137
|
+
await page.goto(url);
|
|
138
|
+
this.currentPage = page;
|
|
139
|
+
return page;
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Evaluate JavaScript in the current page via CDP Runtime.evaluate.
|
|
143
|
+
* Runs in the page's main world (same as the browser console),
|
|
144
|
+
* so globals persist and background navigations don't break it.
|
|
145
|
+
*/
|
|
146
|
+
async evaluate(expression) {
|
|
147
|
+
if (!this.currentPage) {
|
|
148
|
+
throw new Error('No page selected');
|
|
149
|
+
}
|
|
150
|
+
const cdp = await this.getCDPSession();
|
|
151
|
+
const result = await cdp.send('Runtime.evaluate', {
|
|
152
|
+
expression,
|
|
153
|
+
returnByValue: true,
|
|
154
|
+
awaitPromise: true,
|
|
155
|
+
});
|
|
156
|
+
if (result.exceptionDetails) {
|
|
157
|
+
const text = result.exceptionDetails.text
|
|
158
|
+
?? result.exceptionDetails.exception?.description
|
|
159
|
+
?? 'Evaluation failed';
|
|
160
|
+
throw new Error(text);
|
|
161
|
+
}
|
|
162
|
+
return result.result.value;
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Inject a script into the current page by URL.
|
|
166
|
+
* Fetches inside the browser via CDP evaluation, then executes
|
|
167
|
+
* with indirect eval to place globals in the page's main world.
|
|
168
|
+
*/
|
|
169
|
+
async injectScript(url) {
|
|
170
|
+
const expression = `
|
|
171
|
+
fetch(${JSON.stringify(url)})
|
|
172
|
+
.then(r => {
|
|
173
|
+
if (!r.ok) throw new Error('Failed to fetch ' + ${JSON.stringify(url)} + ': ' + r.status);
|
|
174
|
+
return r.text();
|
|
175
|
+
})
|
|
176
|
+
.then(t => {
|
|
177
|
+
(0, eval)(t);
|
|
178
|
+
})
|
|
179
|
+
`;
|
|
180
|
+
await this.evaluate(expression);
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Inject script content directly via CDP evaluation,
|
|
184
|
+
* bypassing CSP restrictions.
|
|
185
|
+
*/
|
|
186
|
+
async injectScriptContent(content) {
|
|
187
|
+
await this.evaluate(content);
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Reload the current page
|
|
191
|
+
*/
|
|
192
|
+
async reload() {
|
|
193
|
+
if (!this.currentPage) {
|
|
194
|
+
throw new Error('No page selected');
|
|
195
|
+
}
|
|
196
|
+
await this.currentPage.reload();
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Get CDP session for advanced operations
|
|
200
|
+
*/
|
|
201
|
+
async getCDPSession() {
|
|
202
|
+
if (!this.currentPage) {
|
|
203
|
+
throw new Error('No page selected');
|
|
204
|
+
}
|
|
205
|
+
if (!this.cdpSession) {
|
|
206
|
+
this.cdpSession = await this.currentPage.context().newCDPSession(this.currentPage);
|
|
207
|
+
}
|
|
208
|
+
return this.cdpSession;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* Create a connection with default or provided options
|
|
213
|
+
*/
|
|
214
|
+
export function createConnection(options = {}) {
|
|
215
|
+
return new ChromeConnection({
|
|
216
|
+
host: options.host ?? 'localhost',
|
|
217
|
+
port: options.port ?? 9222,
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
//# sourceMappingURL=connection.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"connection.js","sourceRoot":"","sources":["../../src/chrome/connection.ts"],"names":[],"mappings":"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"}
|