drissionpage-cli 0.1.0__tar.gz → 0.1.2__tar.gz

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 (22) hide show
  1. {drissionpage_cli-0.1.0 → drissionpage_cli-0.1.2}/PKG-INFO +54 -26
  2. {drissionpage_cli-0.1.0 → drissionpage_cli-0.1.2}/README.md +53 -25
  3. {drissionpage_cli-0.1.0 → drissionpage_cli-0.1.2}/drissionpage_cli/__init__.py +60 -15
  4. {drissionpage_cli-0.1.0 → drissionpage_cli-0.1.2}/drissionpage_cli/skills/drissionpage-cli/SKILL.md +3 -1
  5. {drissionpage_cli-0.1.0 → drissionpage_cli-0.1.2}/drissionpage_cli/skills/drissionpage-cli/references/session-management.md +4 -1
  6. {drissionpage_cli-0.1.0 → drissionpage_cli-0.1.2}/drissionpage_cli.egg-info/PKG-INFO +54 -26
  7. {drissionpage_cli-0.1.0 → drissionpage_cli-0.1.2}/pyproject.toml +1 -1
  8. {drissionpage_cli-0.1.0 → drissionpage_cli-0.1.2}/tests/test_unit.py +63 -1
  9. {drissionpage_cli-0.1.0 → drissionpage_cli-0.1.2}/LICENSE +0 -0
  10. {drissionpage_cli-0.1.0 → drissionpage_cli-0.1.2}/drissionpage_cli/skills/drissionpage-cli/references/dual-mode.md +0 -0
  11. {drissionpage_cli-0.1.0 → drissionpage_cli-0.1.2}/drissionpage_cli/skills/drissionpage-cli/references/element-locators.md +0 -0
  12. {drissionpage_cli-0.1.0 → drissionpage_cli-0.1.2}/drissionpage_cli/skills/drissionpage-cli/references/network-listening.md +0 -0
  13. {drissionpage_cli-0.1.0 → drissionpage_cli-0.1.2}/drissionpage_cli/skills/drissionpage-cli/references/running-code.md +0 -0
  14. {drissionpage_cli-0.1.0 → drissionpage_cli-0.1.2}/drissionpage_cli/skills/drissionpage-cli/references/screenshots-pdf.md +0 -0
  15. {drissionpage_cli-0.1.0 → drissionpage_cli-0.1.2}/drissionpage_cli/skills/drissionpage-cli/references/storage-state.md +0 -0
  16. {drissionpage_cli-0.1.0 → drissionpage_cli-0.1.2}/drissionpage_cli.egg-info/SOURCES.txt +0 -0
  17. {drissionpage_cli-0.1.0 → drissionpage_cli-0.1.2}/drissionpage_cli.egg-info/dependency_links.txt +0 -0
  18. {drissionpage_cli-0.1.0 → drissionpage_cli-0.1.2}/drissionpage_cli.egg-info/entry_points.txt +0 -0
  19. {drissionpage_cli-0.1.0 → drissionpage_cli-0.1.2}/drissionpage_cli.egg-info/requires.txt +0 -0
  20. {drissionpage_cli-0.1.0 → drissionpage_cli-0.1.2}/drissionpage_cli.egg-info/top_level.txt +0 -0
  21. {drissionpage_cli-0.1.0 → drissionpage_cli-0.1.2}/setup.cfg +0 -0
  22. {drissionpage_cli-0.1.0 → drissionpage_cli-0.1.2}/tests/test_integration.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: drissionpage-cli
3
- Version: 0.1.0
3
+ Version: 0.1.2
4
4
  Summary: DrissionPage CLI - Token-efficient browser automation for coding agents
5
5
  Author: DrissionPage-MCP Contributors
6
6
  License: Apache-2.0
@@ -25,11 +25,15 @@ Requires-Dist: DrissionPage>=4.0.0
25
25
  Dynamic: license-file
26
26
 
27
27
  # DrissionPage-cli
28
-
29
28
  Token-efficient browser automation CLI for coding agents, powered by [DrissionPage](https://DrissionPage.cn).
30
29
 
31
30
  Mirrors the architecture of [playwright-cli](https://github.com/microsoft/playwright-cli) but uses DrissionPage as the backend — pure Python, no Node.js required.
32
31
 
32
+ ## Why use drissionpage-cli
33
+
34
+ If you know, you know. If you don't, go use playwright-cli.
35
+
36
+
33
37
  ## Why CLI + Skills over MCP
34
38
 
35
39
  Modern coding agents (Claude Code, GitHub Copilot, etc.) increasingly favour CLI-based workflows exposed as **SKILLs** over MCP because CLI invocations are more token-efficient: they avoid loading large tool schemas and verbose accessibility trees into the model context. This makes CLI + SKILLs better suited for high-throughput agents that must balance browser automation with large codebases within limited context windows.
@@ -41,18 +45,18 @@ Modern coding agents (Claude Code, GitHub Copilot, etc.) increasingly favour CLI
41
45
 
42
46
  ## Installation
43
47
 
44
- ### From source (development)
48
+ ### From PyPI (recommended)
45
49
 
46
50
  ```bash
47
- git clone https://github.com/nicekate/DrissionPage-MCP.git
48
- cd DrissionPage-MCP/DrissionPage-cli
49
- pip install -e .
51
+ pip install drissionpage-cli
50
52
  ```
51
53
 
52
- ### Dependencies only
54
+ ### From source (development)
53
55
 
54
56
  ```bash
55
- pip install DrissionPage>=4.0.0
57
+ git clone https://github.com/nicekate/DrissionPage-MCP.git
58
+ cd DrissionPage-MCP/DrissionPage-cli
59
+ pip install -e .
56
60
  ```
57
61
 
58
62
  After installation the `drissionpage-cli` command is available globally:
@@ -70,6 +74,29 @@ drissionpage-cli install --skills
70
74
 
71
75
  This copies the SKILL.md and reference guides into `.claude/skills/drissionpage-cli/` so that Claude Code (or any compatible agent) can discover them automatically.
72
76
 
77
+ **One-liner** — install the package and set up Claude Code skills in a single command:
78
+
79
+ ```bash
80
+ pip install drissionpage-cli && drissionpage-cli install --skills
81
+ ```
82
+
83
+ #### What gets installed
84
+
85
+ The `install --skills` command copies the following into `.claude/skills/drissionpage-cli/`:
86
+
87
+ | File | Purpose |
88
+ |------|---------|
89
+ | `SKILL.md` | Skill definition & quick-start guide for the agent |
90
+ | `references/element-locators.md` | CSS, XPath, text, and attribute locator syntax |
91
+ | `references/running-code.md` | Custom DrissionPage Python code execution |
92
+ | `references/session-management.md` | Multiple concurrent browser sessions |
93
+ | `references/storage-state.md` | Cookies, localStorage, sessionStorage management |
94
+ | `references/screenshots-pdf.md` | Visual capture and PDF generation |
95
+ | `references/network-listening.md` | Network request monitoring |
96
+ | `references/dual-mode.md` | Browser + HTTP request mode switching |
97
+
98
+ Once installed, Claude Code automatically discovers the skill and can use `drissionpage-cli` commands for browser automation tasks.
99
+
73
100
  ## Quick Start
74
101
 
75
102
  ```bash
@@ -276,28 +303,29 @@ python3 -m pytest tests/ -v
276
303
 
277
304
  ```
278
305
  DrissionPage-cli/
279
- drissionpage_cli.py # Main CLI (40+ commands)
280
- pyproject.toml # Python packaging
281
- requirements.txt # DrissionPage>=4.0.0
282
- pytest.ini # Test configuration
306
+ drissionpage_cli/ # Main package
307
+ __init__.py # CLI entry point (59 commands)
308
+ skills/drissionpage-cli/ # Bundled skill files
309
+ SKILL.md
310
+ references/
311
+ element-locators.md
312
+ running-code.md
313
+ session-management.md
314
+ storage-state.md
315
+ screenshots-pdf.md
316
+ network-listening.md
317
+ dual-mode.md
318
+ pyproject.toml # Python packaging
319
+ requirements.txt # DrissionPage>=4.0.0
320
+ pytest.ini # Test configuration
283
321
  README.md
284
322
  LICENSE
285
323
  scripts/
286
- update.py # Skill update script
287
- skills/drissionpage-cli/
288
- SKILL.md # Agent skill definition
289
- references/
290
- element-locators.md # CSS, XPath, text, attribute locators
291
- running-code.md # run-code for custom automation
292
- session-management.md # Multiple browser sessions
293
- storage-state.md # Cookies, localStorage, sessionStorage
294
- screenshots-pdf.md # Visual capture
295
- network-listening.md # Network monitoring
296
- dual-mode.md # Browser + HTTP requests mode
324
+ update.py # Skill update script
297
325
  tests/
298
- conftest.py # Shared fixtures
299
- test_unit.py # 92 unit tests (mocked, no browser)
300
- test_integration.py # 30+ integration tests (real browser)
326
+ conftest.py # Shared fixtures
327
+ test_unit.py # 92 unit tests (mocked, no browser)
328
+ test_integration.py # 30+ integration tests (real browser)
301
329
  ```
302
330
 
303
331
  ## Environment Variables
@@ -1,9 +1,13 @@
1
1
  # DrissionPage-cli
2
-
3
2
  Token-efficient browser automation CLI for coding agents, powered by [DrissionPage](https://DrissionPage.cn).
4
3
 
5
4
  Mirrors the architecture of [playwright-cli](https://github.com/microsoft/playwright-cli) but uses DrissionPage as the backend — pure Python, no Node.js required.
6
5
 
6
+ ## Why use drissionpage-cli
7
+
8
+ If you know, you know. If you don't, go use playwright-cli.
9
+
10
+
7
11
  ## Why CLI + Skills over MCP
8
12
 
9
13
  Modern coding agents (Claude Code, GitHub Copilot, etc.) increasingly favour CLI-based workflows exposed as **SKILLs** over MCP because CLI invocations are more token-efficient: they avoid loading large tool schemas and verbose accessibility trees into the model context. This makes CLI + SKILLs better suited for high-throughput agents that must balance browser automation with large codebases within limited context windows.
@@ -15,18 +19,18 @@ Modern coding agents (Claude Code, GitHub Copilot, etc.) increasingly favour CLI
15
19
 
16
20
  ## Installation
17
21
 
18
- ### From source (development)
22
+ ### From PyPI (recommended)
19
23
 
20
24
  ```bash
21
- git clone https://github.com/nicekate/DrissionPage-MCP.git
22
- cd DrissionPage-MCP/DrissionPage-cli
23
- pip install -e .
25
+ pip install drissionpage-cli
24
26
  ```
25
27
 
26
- ### Dependencies only
28
+ ### From source (development)
27
29
 
28
30
  ```bash
29
- pip install DrissionPage>=4.0.0
31
+ git clone https://github.com/nicekate/DrissionPage-MCP.git
32
+ cd DrissionPage-MCP/DrissionPage-cli
33
+ pip install -e .
30
34
  ```
31
35
 
32
36
  After installation the `drissionpage-cli` command is available globally:
@@ -44,6 +48,29 @@ drissionpage-cli install --skills
44
48
 
45
49
  This copies the SKILL.md and reference guides into `.claude/skills/drissionpage-cli/` so that Claude Code (or any compatible agent) can discover them automatically.
46
50
 
51
+ **One-liner** — install the package and set up Claude Code skills in a single command:
52
+
53
+ ```bash
54
+ pip install drissionpage-cli && drissionpage-cli install --skills
55
+ ```
56
+
57
+ #### What gets installed
58
+
59
+ The `install --skills` command copies the following into `.claude/skills/drissionpage-cli/`:
60
+
61
+ | File | Purpose |
62
+ |------|---------|
63
+ | `SKILL.md` | Skill definition & quick-start guide for the agent |
64
+ | `references/element-locators.md` | CSS, XPath, text, and attribute locator syntax |
65
+ | `references/running-code.md` | Custom DrissionPage Python code execution |
66
+ | `references/session-management.md` | Multiple concurrent browser sessions |
67
+ | `references/storage-state.md` | Cookies, localStorage, sessionStorage management |
68
+ | `references/screenshots-pdf.md` | Visual capture and PDF generation |
69
+ | `references/network-listening.md` | Network request monitoring |
70
+ | `references/dual-mode.md` | Browser + HTTP request mode switching |
71
+
72
+ Once installed, Claude Code automatically discovers the skill and can use `drissionpage-cli` commands for browser automation tasks.
73
+
47
74
  ## Quick Start
48
75
 
49
76
  ```bash
@@ -250,28 +277,29 @@ python3 -m pytest tests/ -v
250
277
 
251
278
  ```
252
279
  DrissionPage-cli/
253
- drissionpage_cli.py # Main CLI (40+ commands)
254
- pyproject.toml # Python packaging
255
- requirements.txt # DrissionPage>=4.0.0
256
- pytest.ini # Test configuration
280
+ drissionpage_cli/ # Main package
281
+ __init__.py # CLI entry point (59 commands)
282
+ skills/drissionpage-cli/ # Bundled skill files
283
+ SKILL.md
284
+ references/
285
+ element-locators.md
286
+ running-code.md
287
+ session-management.md
288
+ storage-state.md
289
+ screenshots-pdf.md
290
+ network-listening.md
291
+ dual-mode.md
292
+ pyproject.toml # Python packaging
293
+ requirements.txt # DrissionPage>=4.0.0
294
+ pytest.ini # Test configuration
257
295
  README.md
258
296
  LICENSE
259
297
  scripts/
260
- update.py # Skill update script
261
- skills/drissionpage-cli/
262
- SKILL.md # Agent skill definition
263
- references/
264
- element-locators.md # CSS, XPath, text, attribute locators
265
- running-code.md # run-code for custom automation
266
- session-management.md # Multiple browser sessions
267
- storage-state.md # Cookies, localStorage, sessionStorage
268
- screenshots-pdf.md # Visual capture
269
- network-listening.md # Network monitoring
270
- dual-mode.md # Browser + HTTP requests mode
298
+ update.py # Skill update script
271
299
  tests/
272
- conftest.py # Shared fixtures
273
- test_unit.py # 92 unit tests (mocked, no browser)
274
- test_integration.py # 30+ integration tests (real browser)
300
+ conftest.py # Shared fixtures
301
+ test_unit.py # 92 unit tests (mocked, no browser)
302
+ test_integration.py # 30+ integration tests (real browser)
275
303
  ```
276
304
 
277
305
  ## Environment Variables
@@ -15,7 +15,7 @@ import time
15
15
  import traceback
16
16
  from pathlib import Path
17
17
 
18
- __version__ = "0.1.0"
18
+ __version__ = "0.1.2"
19
19
 
20
20
  # Session storage directory
21
21
  CLI_DIR = Path(os.environ.get("DRISSIONPAGE_CLI_DIR", ".drissionpage-cli"))
@@ -108,7 +108,13 @@ def _get_page(session_name, create=False, options=None):
108
108
 
109
109
  # Create new session
110
110
  co = ChromiumOptions()
111
- co.auto_port() # pick a free port to avoid conflicts with stale browsers
111
+
112
+ use_system_user_path = options and options.get("system_user_path")
113
+ if use_system_user_path:
114
+ # use_system_user_path is incompatible with auto_port (auto_port creates a temp profile)
115
+ co.use_system_user_path(True)
116
+ else:
117
+ co.auto_port() # pick a free port to avoid conflicts with stale browsers
112
118
 
113
119
  if options:
114
120
  if options.get("headless") is not None:
@@ -182,6 +188,32 @@ def _find_element(page, ref):
182
188
  # Command handlers
183
189
  # ---------------------------------------------------------------------------
184
190
 
191
+ _CONSOLE_CAPTURE_JS = """
192
+ if (!window.__dp_console_capture_installed) {
193
+ window.__dp_console_logs = window.__dp_console_logs || [];
194
+ ['log', 'info', 'warn', 'error', 'debug'].forEach(function(t) {
195
+ var orig = console[t];
196
+ console[t] = function() {
197
+ var text = Array.prototype.slice.call(arguments).map(function(a) {
198
+ try { return (typeof a === 'object') ? JSON.stringify(a) : String(a); }
199
+ catch(e) { return String(a); }
200
+ }).join(' ');
201
+ window.__dp_console_logs.push({type: t === 'warn' ? 'warning' : t, text: text});
202
+ orig.apply(console, arguments);
203
+ };
204
+ });
205
+ window.__dp_console_capture_installed = true;
206
+ }
207
+ """
208
+
209
+
210
+ def _inject_console_capture(page):
211
+ """Inject console capture script into the page if not already installed."""
212
+ try:
213
+ page.run_js(_CONSOLE_CAPTURE_JS)
214
+ except Exception:
215
+ pass
216
+
185
217
 
186
218
  def cmd_open(args):
187
219
  """Open a browser, optionally navigate to a URL."""
@@ -190,6 +222,7 @@ def cmd_open(args):
190
222
  "headless": not getattr(args, "headed", False),
191
223
  "user_data_path": getattr(args, "profile", None),
192
224
  "port": getattr(args, "port", None),
225
+ "system_user_path": getattr(args, "system_user_path", False),
193
226
  }
194
227
  page = _get_page(session, create=True, options=options)
195
228
 
@@ -197,6 +230,7 @@ def cmd_open(args):
197
230
  if url:
198
231
  page.get(url)
199
232
 
233
+ _inject_console_capture(page)
200
234
  print(_format_snapshot(page))
201
235
 
202
236
 
@@ -205,6 +239,7 @@ def cmd_goto(args):
205
239
  session = _get_session_name(args)
206
240
  page = _get_page(session)
207
241
  page.get(args.url)
242
+ _inject_console_capture(page)
208
243
  print(_format_snapshot(page))
209
244
 
210
245
 
@@ -448,7 +483,13 @@ def cmd_pdf(args):
448
483
  ensure_cli_dir()
449
484
  filename = str(CLI_DIR / f"page-{timestamp}.pdf")
450
485
 
451
- page.save(path=filename, as_pdf=True)
486
+ # Use CDP directly to avoid DrissionPage bug: open(..., 'wb', newline='\n')
487
+ # is invalid since binary mode doesn't accept a newline argument.
488
+ from base64 import b64decode
489
+ r = page._run_cdp('Page.printToPDF', transferMode='ReturnAsBase64', printBackground=True)
490
+ pdf_bytes = b64decode(r['data'])
491
+ with open(filename, 'wb') as f:
492
+ f.write(pdf_bytes)
452
493
  print(f"PDF saved to {filename}")
453
494
 
454
495
 
@@ -529,7 +570,7 @@ def cmd_mousemove(args):
529
570
  """Move mouse to coordinates."""
530
571
  session = _get_session_name(args)
531
572
  page = _get_page(session)
532
- page.actions.move_to(args.x, args.y)
573
+ page.actions.move_to((args.x, args.y))
533
574
  print("Mouse moved to", args.x, args.y)
534
575
 
535
576
 
@@ -651,7 +692,7 @@ def cmd_cookie_list(args):
651
692
  """List cookies."""
652
693
  session = _get_session_name(args)
653
694
  page = _get_page(session)
654
- cookies = page.cookies(as_dict=False, all_info=True)
695
+ cookies = page.cookies(all_info=True)
655
696
  domain = getattr(args, "domain", None)
656
697
  if domain:
657
698
  cookies = [c for c in cookies if domain in c.get("domain", "")]
@@ -663,7 +704,7 @@ def cmd_cookie_get(args):
663
704
  """Get a cookie by name."""
664
705
  session = _get_session_name(args)
665
706
  page = _get_page(session)
666
- cookies = page.cookies(as_dict=False, all_info=True)
707
+ cookies = page.cookies(all_info=True)
667
708
  for c in cookies:
668
709
  if c.get("name") == args.name:
669
710
  print(json.dumps(c, indent=2, ensure_ascii=False))
@@ -813,6 +854,8 @@ def cmd_console(args):
813
854
  """List console messages."""
814
855
  session = _get_session_name(args)
815
856
  page = _get_page(session)
857
+ # Ensure the interceptor is installed (no-op if already installed)
858
+ _inject_console_capture(page)
816
859
  result = page.run_js("""
817
860
  return JSON.stringify(
818
861
  (window.__dp_console_logs || []).map(e => ({
@@ -820,17 +863,17 @@ def cmd_console(args):
820
863
  }))
821
864
  )
822
865
  """)
823
- if result:
824
- logs = json.loads(result)
825
- min_level = getattr(args, "level", None)
826
- levels = ["error", "warning", "info", "debug"]
827
- if min_level and min_level in levels:
828
- allowed = set(levels[: levels.index(min_level) + 1])
829
- logs = [l for l in logs if l.get("type", "info") in allowed]
866
+ logs = json.loads(result) if result else []
867
+ min_level = getattr(args, "level", None)
868
+ levels = ["error", "warning", "info", "debug"]
869
+ if min_level and min_level in levels:
870
+ allowed = set(levels[: levels.index(min_level) + 1])
871
+ logs = [l for l in logs if l.get("type", "info") in allowed]
872
+ if logs:
830
873
  for log in logs:
831
874
  print(f"[{log.get('type', 'info')}] {log.get('text', '')}")
832
875
  else:
833
- print("No console messages captured. Use run-code to inject console capture first.")
876
+ print("No console messages captured yet. Console interceptor is now active — any future console.log/warn/error calls will be recorded.")
834
877
 
835
878
 
836
879
  def cmd_network(args):
@@ -961,7 +1004,7 @@ def cmd_state_save(args):
961
1004
  ensure_cli_dir()
962
1005
  filename = str(CLI_DIR / f"state-{timestamp}.json")
963
1006
 
964
- cookies = page.cookies(as_dict=False, all_info=True)
1007
+ cookies = page.cookies(all_info=True)
965
1008
  local_storage = page.run_js(
966
1009
  "return JSON.stringify(Object.entries(localStorage))"
967
1010
  )
@@ -1069,6 +1112,8 @@ def build_parser():
1069
1112
  p.add_argument("url", nargs="?", help="URL to navigate to")
1070
1113
  p.add_argument("--headed", action="store_true", help="Run in headed mode")
1071
1114
  p.add_argument("--profile", help="User data directory path")
1115
+ p.add_argument("--system-user-path", action="store_true", dest="system_user_path",
1116
+ help="Use system default Chrome profile (inherits login sessions)")
1072
1117
  p.add_argument("--port", type=int, help="CDP debugging port")
1073
1118
  p.set_defaults(func=cmd_open)
1074
1119
 
@@ -146,7 +146,9 @@ drissionpage-cli run-code --filename=script.py
146
146
  ```bash
147
147
  # Run in headed mode (browser window visible)
148
148
  drissionpage-cli open --headed
149
- # Use persistent user profile
149
+ # Use system default Chrome profile (inherits all login sessions)
150
+ drissionpage-cli open --system-user-path
151
+ # Use a specific user profile directory
150
152
  drissionpage-cli open --profile=/path/to/profile
151
153
  # Use specific CDP port
152
154
  drissionpage-cli open --port=9222
@@ -90,7 +90,10 @@ drissionpage-cli -s=variant-b screenshot
90
90
  ### Persistent Profile
91
91
 
92
92
  ```bash
93
- # Use persistent profile
93
+ # Use system default Chrome profile (inherits all login sessions)
94
+ drissionpage-cli open --system-user-path
95
+
96
+ # Use a specific profile directory
94
97
  drissionpage-cli open https://example.com --profile=/path/to/profile
95
98
 
96
99
  # Specific CDP port
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: drissionpage-cli
3
- Version: 0.1.0
3
+ Version: 0.1.2
4
4
  Summary: DrissionPage CLI - Token-efficient browser automation for coding agents
5
5
  Author: DrissionPage-MCP Contributors
6
6
  License: Apache-2.0
@@ -25,11 +25,15 @@ Requires-Dist: DrissionPage>=4.0.0
25
25
  Dynamic: license-file
26
26
 
27
27
  # DrissionPage-cli
28
-
29
28
  Token-efficient browser automation CLI for coding agents, powered by [DrissionPage](https://DrissionPage.cn).
30
29
 
31
30
  Mirrors the architecture of [playwright-cli](https://github.com/microsoft/playwright-cli) but uses DrissionPage as the backend — pure Python, no Node.js required.
32
31
 
32
+ ## Why use drissionpage-cli
33
+
34
+ If you know, you know. If you don't, go use playwright-cli.
35
+
36
+
33
37
  ## Why CLI + Skills over MCP
34
38
 
35
39
  Modern coding agents (Claude Code, GitHub Copilot, etc.) increasingly favour CLI-based workflows exposed as **SKILLs** over MCP because CLI invocations are more token-efficient: they avoid loading large tool schemas and verbose accessibility trees into the model context. This makes CLI + SKILLs better suited for high-throughput agents that must balance browser automation with large codebases within limited context windows.
@@ -41,18 +45,18 @@ Modern coding agents (Claude Code, GitHub Copilot, etc.) increasingly favour CLI
41
45
 
42
46
  ## Installation
43
47
 
44
- ### From source (development)
48
+ ### From PyPI (recommended)
45
49
 
46
50
  ```bash
47
- git clone https://github.com/nicekate/DrissionPage-MCP.git
48
- cd DrissionPage-MCP/DrissionPage-cli
49
- pip install -e .
51
+ pip install drissionpage-cli
50
52
  ```
51
53
 
52
- ### Dependencies only
54
+ ### From source (development)
53
55
 
54
56
  ```bash
55
- pip install DrissionPage>=4.0.0
57
+ git clone https://github.com/nicekate/DrissionPage-MCP.git
58
+ cd DrissionPage-MCP/DrissionPage-cli
59
+ pip install -e .
56
60
  ```
57
61
 
58
62
  After installation the `drissionpage-cli` command is available globally:
@@ -70,6 +74,29 @@ drissionpage-cli install --skills
70
74
 
71
75
  This copies the SKILL.md and reference guides into `.claude/skills/drissionpage-cli/` so that Claude Code (or any compatible agent) can discover them automatically.
72
76
 
77
+ **One-liner** — install the package and set up Claude Code skills in a single command:
78
+
79
+ ```bash
80
+ pip install drissionpage-cli && drissionpage-cli install --skills
81
+ ```
82
+
83
+ #### What gets installed
84
+
85
+ The `install --skills` command copies the following into `.claude/skills/drissionpage-cli/`:
86
+
87
+ | File | Purpose |
88
+ |------|---------|
89
+ | `SKILL.md` | Skill definition & quick-start guide for the agent |
90
+ | `references/element-locators.md` | CSS, XPath, text, and attribute locator syntax |
91
+ | `references/running-code.md` | Custom DrissionPage Python code execution |
92
+ | `references/session-management.md` | Multiple concurrent browser sessions |
93
+ | `references/storage-state.md` | Cookies, localStorage, sessionStorage management |
94
+ | `references/screenshots-pdf.md` | Visual capture and PDF generation |
95
+ | `references/network-listening.md` | Network request monitoring |
96
+ | `references/dual-mode.md` | Browser + HTTP request mode switching |
97
+
98
+ Once installed, Claude Code automatically discovers the skill and can use `drissionpage-cli` commands for browser automation tasks.
99
+
73
100
  ## Quick Start
74
101
 
75
102
  ```bash
@@ -276,28 +303,29 @@ python3 -m pytest tests/ -v
276
303
 
277
304
  ```
278
305
  DrissionPage-cli/
279
- drissionpage_cli.py # Main CLI (40+ commands)
280
- pyproject.toml # Python packaging
281
- requirements.txt # DrissionPage>=4.0.0
282
- pytest.ini # Test configuration
306
+ drissionpage_cli/ # Main package
307
+ __init__.py # CLI entry point (59 commands)
308
+ skills/drissionpage-cli/ # Bundled skill files
309
+ SKILL.md
310
+ references/
311
+ element-locators.md
312
+ running-code.md
313
+ session-management.md
314
+ storage-state.md
315
+ screenshots-pdf.md
316
+ network-listening.md
317
+ dual-mode.md
318
+ pyproject.toml # Python packaging
319
+ requirements.txt # DrissionPage>=4.0.0
320
+ pytest.ini # Test configuration
283
321
  README.md
284
322
  LICENSE
285
323
  scripts/
286
- update.py # Skill update script
287
- skills/drissionpage-cli/
288
- SKILL.md # Agent skill definition
289
- references/
290
- element-locators.md # CSS, XPath, text, attribute locators
291
- running-code.md # run-code for custom automation
292
- session-management.md # Multiple browser sessions
293
- storage-state.md # Cookies, localStorage, sessionStorage
294
- screenshots-pdf.md # Visual capture
295
- network-listening.md # Network monitoring
296
- dual-mode.md # Browser + HTTP requests mode
324
+ update.py # Skill update script
297
325
  tests/
298
- conftest.py # Shared fixtures
299
- test_unit.py # 92 unit tests (mocked, no browser)
300
- test_integration.py # 30+ integration tests (real browser)
326
+ conftest.py # Shared fixtures
327
+ test_unit.py # 92 unit tests (mocked, no browser)
328
+ test_integration.py # 30+ integration tests (real browser)
301
329
  ```
302
330
 
303
331
  ## Environment Variables
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "drissionpage-cli"
7
- version = "0.1.0"
7
+ version = "0.1.2"
8
8
  description = "DrissionPage CLI - Token-efficient browser automation for coding agents"
9
9
  readme = "README.md"
10
10
  license = {text = "Apache-2.0"}
@@ -26,7 +26,7 @@ import drissionpage_cli as cli
26
26
 
27
27
  class TestVersionAndHelp:
28
28
  def test_version_string(self):
29
- assert cli.__version__ == "0.1.0"
29
+ assert cli.__version__ == "0.1.2"
30
30
 
31
31
  def test_build_parser(self):
32
32
  parser = cli.build_parser()
@@ -292,6 +292,17 @@ class TestVersionAndHelp:
292
292
  assert args.command == "open"
293
293
  assert args.profile == "/tmp/profile"
294
294
 
295
+ def test_parse_open_with_system_user_path(self):
296
+ parser = cli.build_parser()
297
+ args = parser.parse_args(["open", "--system-user-path"])
298
+ assert args.command == "open"
299
+ assert args.system_user_path is True
300
+
301
+ def test_parse_open_system_user_path_default_false(self):
302
+ parser = cli.build_parser()
303
+ args = parser.parse_args(["open"])
304
+ assert args.system_user_path is False
305
+
295
306
  def test_parse_console_command(self):
296
307
  parser = cli.build_parser()
297
308
  args = parser.parse_args(["console", "warning"])
@@ -578,6 +589,57 @@ class TestStateSaveLoad:
578
589
  assert any("sessionStorage.setItem" in str(call) for call in mock_page.run_js.call_args_list)
579
590
 
580
591
 
592
+ # ---------------------------------------------------------------------------
593
+ # _get_page options (mocked ChromiumOptions / ChromiumPage)
594
+ # ---------------------------------------------------------------------------
595
+
596
+
597
+ class TestGetPageOptions:
598
+ """Test that _get_page passes options correctly to ChromiumOptions."""
599
+
600
+ def _make_mock_co(self):
601
+ return MagicMock()
602
+
603
+ def _run_get_page(self, options, tmp_path, mock_co, mock_page):
604
+ sessions_file = tmp_path / "sessions.json"
605
+ cli_dir_path = tmp_path / ".drissionpage-cli"
606
+ cli_dir_path.mkdir()
607
+
608
+ with patch.object(cli, "SESSIONS_FILE", sessions_file), \
609
+ patch.object(cli, "CLI_DIR", cli_dir_path), \
610
+ patch("DrissionPage.ChromiumOptions", return_value=mock_co), \
611
+ patch("DrissionPage.ChromiumPage", return_value=mock_page):
612
+ mock_page.address = "127.0.0.1:9333"
613
+ mock_page.process_id = 999
614
+ cli._get_page("default", create=True, options=options)
615
+
616
+ def test_system_user_path_calls_use_system_user_path(self, tmp_path):
617
+ mock_co = self._make_mock_co()
618
+ mock_page = MagicMock()
619
+ self._run_get_page({"system_user_path": True}, tmp_path, mock_co, mock_page)
620
+ mock_co.use_system_user_path.assert_called_once_with(True)
621
+
622
+ def test_system_user_path_skips_auto_port(self, tmp_path):
623
+ mock_co = self._make_mock_co()
624
+ mock_page = MagicMock()
625
+ self._run_get_page({"system_user_path": True}, tmp_path, mock_co, mock_page)
626
+ mock_co.auto_port.assert_not_called()
627
+
628
+ def test_no_system_user_path_calls_auto_port(self, tmp_path):
629
+ mock_co = self._make_mock_co()
630
+ mock_page = MagicMock()
631
+ self._run_get_page({"system_user_path": False}, tmp_path, mock_co, mock_page)
632
+ mock_co.auto_port.assert_called_once()
633
+ mock_co.use_system_user_path.assert_not_called()
634
+
635
+ def test_system_user_path_with_headed(self, tmp_path):
636
+ mock_co = self._make_mock_co()
637
+ mock_page = MagicMock()
638
+ self._run_get_page({"system_user_path": True, "headless": False}, tmp_path, mock_co, mock_page)
639
+ mock_co.use_system_user_path.assert_called_once_with(True)
640
+ mock_co.headless.assert_called_once_with(False)
641
+
642
+
581
643
  # ---------------------------------------------------------------------------
582
644
  # Command handlers (mocked browser)
583
645
  # ---------------------------------------------------------------------------