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.
Files changed (88) hide show
  1. package/README.md +197 -0
  2. package/SKILL.md +126 -0
  3. package/bin/cjig.js +3 -0
  4. package/dist/chrome/connection.d.ts +95 -0
  5. package/dist/chrome/connection.d.ts.map +1 -0
  6. package/dist/chrome/connection.js +220 -0
  7. package/dist/chrome/connection.js.map +1 -0
  8. package/dist/chrome/launcher.d.ts +53 -0
  9. package/dist/chrome/launcher.d.ts.map +1 -0
  10. package/dist/chrome/launcher.js +203 -0
  11. package/dist/chrome/launcher.js.map +1 -0
  12. package/dist/cli.d.ts +6 -0
  13. package/dist/cli.d.ts.map +1 -0
  14. package/dist/cli.js +352 -0
  15. package/dist/cli.js.map +1 -0
  16. package/dist/cljs/compiler.d.ts +10 -0
  17. package/dist/cljs/compiler.d.ts.map +1 -0
  18. package/dist/cljs/compiler.js +20 -0
  19. package/dist/cljs/compiler.js.map +1 -0
  20. package/dist/cljs/runtime.d.ts +32 -0
  21. package/dist/cljs/runtime.d.ts.map +1 -0
  22. package/dist/cljs/runtime.js +63 -0
  23. package/dist/cljs/runtime.js.map +1 -0
  24. package/dist/commands/cljs-eval.d.ts +7 -0
  25. package/dist/commands/cljs-eval.d.ts.map +1 -0
  26. package/dist/commands/cljs-eval.js +23 -0
  27. package/dist/commands/cljs-eval.js.map +1 -0
  28. package/dist/commands/eval.d.ts +13 -0
  29. package/dist/commands/eval.d.ts.map +1 -0
  30. package/dist/commands/eval.js +37 -0
  31. package/dist/commands/eval.js.map +1 -0
  32. package/dist/commands/init.d.ts +23 -0
  33. package/dist/commands/init.d.ts.map +1 -0
  34. package/dist/commands/init.js +149 -0
  35. package/dist/commands/init.js.map +1 -0
  36. package/dist/commands/inject.d.ts +14 -0
  37. package/dist/commands/inject.d.ts.map +1 -0
  38. package/dist/commands/inject.js +31 -0
  39. package/dist/commands/inject.js.map +1 -0
  40. package/dist/commands/install-skill.d.ts +11 -0
  41. package/dist/commands/install-skill.d.ts.map +1 -0
  42. package/dist/commands/install-skill.js +91 -0
  43. package/dist/commands/install-skill.js.map +1 -0
  44. package/dist/commands/launch.d.ts +11 -0
  45. package/dist/commands/launch.d.ts.map +1 -0
  46. package/dist/commands/launch.js +15 -0
  47. package/dist/commands/launch.js.map +1 -0
  48. package/dist/commands/status.d.ts +15 -0
  49. package/dist/commands/status.d.ts.map +1 -0
  50. package/dist/commands/status.js +25 -0
  51. package/dist/commands/status.js.map +1 -0
  52. package/dist/commands/tabs.d.ts +13 -0
  53. package/dist/commands/tabs.d.ts.map +1 -0
  54. package/dist/commands/tabs.js +40 -0
  55. package/dist/commands/tabs.js.map +1 -0
  56. package/dist/config/loader.d.ts +26 -0
  57. package/dist/config/loader.d.ts.map +1 -0
  58. package/dist/config/loader.js +119 -0
  59. package/dist/config/loader.js.map +1 -0
  60. package/dist/config/schema.d.ts +56 -0
  61. package/dist/config/schema.d.ts.map +1 -0
  62. package/dist/config/schema.js +9 -0
  63. package/dist/config/schema.js.map +1 -0
  64. package/dist/config/xdg.d.ts +16 -0
  65. package/dist/config/xdg.d.ts.map +1 -0
  66. package/dist/config/xdg.js +31 -0
  67. package/dist/config/xdg.js.map +1 -0
  68. package/dist/index.d.ts +25 -0
  69. package/dist/index.d.ts.map +1 -0
  70. package/dist/index.js +25 -0
  71. package/dist/index.js.map +1 -0
  72. package/dist/repl/commands.d.ts +32 -0
  73. package/dist/repl/commands.d.ts.map +1 -0
  74. package/dist/repl/commands.js +257 -0
  75. package/dist/repl/commands.js.map +1 -0
  76. package/dist/repl/completer.d.ts +6 -0
  77. package/dist/repl/completer.d.ts.map +1 -0
  78. package/dist/repl/completer.js +30 -0
  79. package/dist/repl/completer.js.map +1 -0
  80. package/dist/repl/repl.d.ts +31 -0
  81. package/dist/repl/repl.d.ts.map +1 -0
  82. package/dist/repl/repl.js +224 -0
  83. package/dist/repl/repl.js.map +1 -0
  84. package/dist/utils/env.d.ts +19 -0
  85. package/dist/utils/env.d.ts.map +1 -0
  86. package/dist/utils/env.js +37 -0
  87. package/dist/utils/env.js.map +1 -0
  88. 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,3 @@
1
+ #!/usr/bin/env node
2
+
3
+ import '../dist/cli.js';
@@ -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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"}