@pencil-agent/nano-pencil 2.0.1 → 2.0.2

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 (186) hide show
  1. package/README.md +267 -267
  2. package/dist/build-meta.json +3 -3
  3. package/dist/core/export-html/AGENT.md +11 -11
  4. package/dist/core/export-html/template.css +971 -971
  5. package/dist/core/export-html/template.html +54 -54
  6. package/dist/core/model/custom-providers.js +1 -1
  7. package/dist/core/model-registry.js +5 -5
  8. package/dist/extensions/builtin/AGENT.md +115 -115
  9. package/dist/extensions/builtin/browser/AGENT.md +17 -17
  10. package/dist/extensions/builtin/browser/agent-workspace/agent_helpers.py +12 -12
  11. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/amazon/product-search.md +198 -198
  12. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/archive-org/scraping.md +341 -341
  13. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/arxiv/scraping.md +311 -311
  14. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/arxiv-bulk/scraping.md +333 -333
  15. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/atlas/overview.md +70 -70
  16. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/booking-com/scraping.md +578 -578
  17. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/capterra/scraping.md +440 -440
  18. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/centilebrain/generate-estimates.md +110 -110
  19. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/coingecko/scraping.md +325 -325
  20. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/coinmarketcap/scraping.md +463 -463
  21. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/coursera/scraping.md +360 -360
  22. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/craigslist/scraping.md +390 -390
  23. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/crossref/scraping.md +568 -568
  24. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/dev-to/scraping.md +323 -323
  25. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/duckduckgo/scraping.md +349 -349
  26. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/ebay/scraping.md +435 -435
  27. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/etsy/scraping.md +506 -506
  28. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/eventbrite/scraping.md +363 -363
  29. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/expedia/automation.md +168 -168
  30. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/facebook/groups.md +236 -236
  31. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/facebook/pages.md +295 -295
  32. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/framer/editor.md +108 -108
  33. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/fred/scraping.md +493 -493
  34. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/g2/scraping.md +580 -580
  35. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/genius/scraping.md +511 -511
  36. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/github/repo-actions.md +65 -65
  37. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/github/scraping.md +184 -184
  38. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/glassdoor/scraping.md +543 -543
  39. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/gmail/compose.md +122 -122
  40. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/goodreads/scraping.md +461 -461
  41. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/gutenberg/scraping.md +383 -383
  42. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/hackernews/scraping.md +243 -243
  43. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/howlongtobeat/scraping.md +473 -473
  44. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/imdb/scraping.md +271 -271
  45. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/itch-io/scraping.md +436 -436
  46. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/job-boards/indeed-glassdoor.md +1021 -1021
  47. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/letterboxd/scraping.md +349 -349
  48. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/linkedin/invitation-manager.md +109 -109
  49. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/loom/folder-enumeration.md +170 -170
  50. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/macrotrends/scraping.md +537 -537
  51. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/medium/article-hydration.md +120 -120
  52. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/medium/scraping.md +414 -414
  53. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/metacritic/scraping.md +477 -477
  54. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/musicbrainz/scraping.md +478 -478
  55. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/nasa/scraping.md +339 -339
  56. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/news-aggregation/multi-source.md +205 -205
  57. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/open-library/scraping.md +472 -472
  58. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/openalex/scraping.md +470 -470
  59. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/openstreetmap/scraping.md +490 -490
  60. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/package-registries/npm-pypi.md +478 -478
  61. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/polymarket/scraping.md +234 -234
  62. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/producthunt/scraping.md +307 -307
  63. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/pubmed/scraping.md +421 -421
  64. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/quora/scraping.md +364 -364
  65. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/rawg/scraping.md +352 -352
  66. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/reddit/scraping.md +124 -124
  67. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/rest-countries/scraping.md +233 -233
  68. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/sec-edgar/scraping.md +361 -361
  69. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/shopify-admin/README.md +36 -36
  70. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/shopify-admin/embedded-apps.md +72 -72
  71. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/shopify-admin/knowledge-base.md +109 -109
  72. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/shopify-admin/polaris-inputs.md +137 -137
  73. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/soundcloud/scraping.md +362 -362
  74. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/spotify/scraping.md +339 -339
  75. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/stackoverflow/scraping.md +435 -435
  76. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/steam/scraping.md +575 -575
  77. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/substack/scraping.md +338 -338
  78. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/thetechgeeks/pricing.md +52 -52
  79. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/tiktok/upload.md +107 -107
  80. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/tradingview/scraping.md +309 -309
  81. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/trello/boards-and-lists.md +88 -88
  82. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/trustpilot/scraping.md +375 -375
  83. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/walmart/scraping.md +444 -444
  84. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/wayback-machine/scraping.md +306 -306
  85. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/weather/scraping.md +398 -398
  86. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/wellfound/scraping.md +596 -596
  87. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/world-bank/scraping.md +356 -356
  88. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/xiaohongshu/scraping.md +84 -84
  89. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/youtube/scraping.md +418 -418
  90. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/zillow/scraping.md +433 -433
  91. package/dist/extensions/builtin/browser/browser.md +73 -73
  92. package/dist/extensions/builtin/browser/install.md +142 -142
  93. package/dist/extensions/builtin/browser/interaction-skills/connection.md +48 -48
  94. package/dist/extensions/builtin/browser/interaction-skills/cookies.md +3 -3
  95. package/dist/extensions/builtin/browser/interaction-skills/cross-origin-iframes.md +3 -3
  96. package/dist/extensions/builtin/browser/interaction-skills/dialogs.md +64 -64
  97. package/dist/extensions/builtin/browser/interaction-skills/downloads.md +3 -3
  98. package/dist/extensions/builtin/browser/interaction-skills/drag-and-drop.md +3 -3
  99. package/dist/extensions/builtin/browser/interaction-skills/dropdowns.md +3 -3
  100. package/dist/extensions/builtin/browser/interaction-skills/iframes.md +3 -3
  101. package/dist/extensions/builtin/browser/interaction-skills/network-requests.md +3 -3
  102. package/dist/extensions/builtin/browser/interaction-skills/print-as-pdf.md +3 -3
  103. package/dist/extensions/builtin/browser/interaction-skills/profile-sync.md +90 -90
  104. package/dist/extensions/builtin/browser/interaction-skills/screenshots.md +17 -17
  105. package/dist/extensions/builtin/browser/interaction-skills/scrolling.md +3 -3
  106. package/dist/extensions/builtin/browser/interaction-skills/shadow-dom.md +3 -3
  107. package/dist/extensions/builtin/browser/interaction-skills/tabs.md +69 -69
  108. package/dist/extensions/builtin/browser/interaction-skills/uploads.md +1 -1
  109. package/dist/extensions/builtin/browser/interaction-skills/viewport.md +3 -3
  110. package/dist/extensions/builtin/browser/src/browser_harness/AGENT.md +15 -15
  111. package/dist/extensions/builtin/browser/src/browser_harness/__init__.py +8 -8
  112. package/dist/extensions/builtin/browser/src/browser_harness/_ipc.py +90 -90
  113. package/dist/extensions/builtin/browser/src/browser_harness/admin.py +722 -722
  114. package/dist/extensions/builtin/browser/src/browser_harness/daemon.py +328 -328
  115. package/dist/extensions/builtin/browser/src/browser_harness/helpers.py +396 -396
  116. package/dist/extensions/builtin/browser/src/browser_harness/run.py +103 -103
  117. package/dist/extensions/builtin/discipline/skills/brainstorming/SKILL.md +33 -33
  118. package/dist/extensions/builtin/discipline/skills/executing-plans/SKILL.md +25 -25
  119. package/dist/extensions/builtin/discipline/skills/finishing-development-branch/SKILL.md +25 -25
  120. package/dist/extensions/builtin/discipline/skills/receiving-code-review/SKILL.md +22 -22
  121. package/dist/extensions/builtin/discipline/skills/requesting-code-review/SKILL.md +31 -31
  122. package/dist/extensions/builtin/discipline/skills/systematic-debugging/SKILL.md +28 -28
  123. package/dist/extensions/builtin/discipline/skills/test-driven-development/SKILL.md +32 -32
  124. package/dist/extensions/builtin/discipline/skills/using-git-worktrees/SKILL.md +25 -25
  125. package/dist/extensions/builtin/discipline/skills/verification-before-completion/SKILL.md +27 -27
  126. package/dist/extensions/builtin/discipline/skills/writing-plans/SKILL.md +26 -26
  127. package/dist/extensions/builtin/goal/README.md +67 -67
  128. package/dist/extensions/builtin/grub/README.md +112 -112
  129. package/dist/extensions/builtin/link-world/agent-workspace/README.md +16 -16
  130. package/dist/extensions/builtin/link-world/internet-search/internet-search.md +65 -65
  131. package/dist/extensions/builtin/link-world/link-world-agent.md +82 -82
  132. package/dist/extensions/builtin/link-world/linkworld.md +313 -313
  133. package/dist/extensions/builtin/link-world/network-routing/network-routing.md +67 -67
  134. package/dist/extensions/builtin/loop/README.md +92 -92
  135. package/dist/extensions/builtin/mcp/figma-design.md +68 -68
  136. package/dist/extensions/builtin/mcp/mcp-management.md +85 -85
  137. package/dist/extensions/builtin/recap/AGENT.md +15 -15
  138. package/dist/extensions/builtin/sal/README.md +72 -72
  139. package/dist/extensions/builtin/security-audit/README.md +289 -289
  140. package/dist/extensions/builtin/team/AGENT.md +112 -112
  141. package/dist/extensions/builtin/team/TESTING.md +299 -299
  142. package/dist/extensions/builtin/token-save/README.md +56 -56
  143. package/dist/extensions/optional/AGENT.md +10 -10
  144. package/dist/modes/interactive/controllers/input-submit-controller.js +2 -2
  145. package/dist/modes/interactive/controllers/stream-render-controller.js +2 -2
  146. package/dist/modes/interactive/interactive-mode.js +19 -19
  147. package/dist/modes/interactive/theme/dark.json +85 -85
  148. package/dist/modes/interactive/theme/light.json +84 -84
  149. package/dist/modes/interactive/theme/theme-schema.json +335 -335
  150. package/dist/modes/interactive/theme/warm.json +81 -81
  151. package/dist/node_modules/@pencil-agent/ai/dist/cli.js +0 -0
  152. package/dist/node_modules/@pencil-agent/ai/dist/models.generated.js +1 -1
  153. package/docs/ACP/345/215/217/350/256/256/351/233/206/346/210/220/345/274/200/345/217/221/346/226/207/346/241/243.md +851 -0
  154. package/docs/SDK-TESTING.md +364 -0
  155. package/docs/codex-goal-command-impl.md +1055 -1055
  156. package/docs/codex-goal-vs-grub.md +500 -500
  157. package/docs/custom-provider.md +27 -27
  158. package/docs/extensions.md +27 -27
  159. package/docs/keybindings.md +27 -27
  160. package/docs/loop /351/207/215/346/236/204/345/256/214/346/210/220/346/200/273/347/273/223.md" +250 -250
  161. package/docs/loop /351/207/215/346/236/204/345/256/214/346/210/220/346/212/245/345/221/212.md" +122 -122
  162. package/docs/loop /351/207/215/346/236/204/346/226/271/346/241/210.md" +1222 -1222
  163. package/docs/loop /351/207/215/346/236/204/346/226/271/346/241/210/345/256/236/347/216/260/346/212/245/345/221/212.md" +158 -158
  164. package/docs/loop /351/207/215/346/236/204/346/226/271/346/241/210/345/257/271/346/257/224/345/210/206/346/236/220.md" +128 -128
  165. package/docs/loop /351/207/215/346/236/204/350/256/241/345/210/222.md" +320 -320
  166. package/docs/loop-usage-examples.md +214 -214
  167. package/docs/mem-core/346/212/200/346/234/257/346/226/207/346/241/243.md +593 -0
  168. package/docs/models.md +27 -27
  169. package/docs/packages.md +27 -27
  170. package/docs/pi-design-philosophy.md +457 -457
  171. package/docs/planmode.md +1987 -1987
  172. package/docs/prompt-templates.md +27 -27
  173. package/docs/providers.md +27 -27
  174. package/docs/sdk.md +27 -27
  175. package/docs/skills.md +27 -27
  176. package/docs/startup-performance-optimization.md +301 -0
  177. package/docs/themes.md +27 -27
  178. package/docs/tui.md +27 -27
  179. package/docs//350/256/244/347/237/245/345/234/260/345/233/276.md +47 -0
  180. package/package.json +190 -190
  181. package/docs/cc-agent-design.md +0 -1297
  182. package/docs/cc-tui-design.md +0 -1333
  183. package/docs/nanoPencil-/345/255/246/344/271/240/350/256/241/345/210/222.md +0 -170
  184. package/docs/scan-report.md +0 -3820
  185. package/docs//345/257/271/346/240/207Claude-Code.md +0 -1775
  186. package/docs//351/230/277/351/207/214/345/267/264/345/267/264/350/264/242/346/212/245/345/210/206/346/236/220/344/271/246.md +0 -261
@@ -1,64 +1,64 @@
1
- # Dialogs
2
-
3
- Browser dialogs (`alert`, `confirm`, `prompt`, `beforeunload`) freeze the JS thread. Two approaches depending on timing.
4
-
5
- ## Detection
6
-
7
- `page_info()` auto-surfaces any open dialog: if one is pending it returns `{"dialog": {"type", "message", ...}}` instead of the usual viewport dict (because the page's JS is frozen anyway). So if you call `page_info()` after an action and see a `dialog` key, handle it before doing anything else.
8
-
9
- ## Reactive: dismiss via CDP (preferred)
10
-
11
- Works even when JS is frozen. Handles all dialog types including `beforeunload`.
12
-
13
- ```python
14
- # Dismiss and read the message
15
- cdp("Page.handleJavaScriptDialog", accept=True) # accept / click OK
16
- cdp("Page.handleJavaScriptDialog", accept=False) # cancel / click Cancel
17
-
18
- # Read what the dialog said (from buffered CDP events)
19
- events = drain_events()
20
- for e in events:
21
- if e["method"] == "Page.javascriptDialogOpening":
22
- print(e["params"]["type"]) # "alert", "confirm", "prompt", "beforeunload"
23
- print(e["params"]["message"]) # the dialog text
24
- ```
25
-
26
- Undetectable by antibot — no JS injected into the page.
27
-
28
- ## Proactive: stub via JS
29
-
30
- Prevents dialogs from ever appearing. Good when you expect multiple `alert()`/`confirm()` calls in sequence.
31
-
32
- ```python
33
- js("""
34
- window.__dialogs__=[];
35
- window.alert=m=>window.__dialogs__.push(String(m));
36
- window.confirm=m=>{window.__dialogs__.push(String(m));return true;};
37
- window.prompt=(m,d)=>{window.__dialogs__.push(String(m));return d||'';};
38
- """)
39
- # ... do actions that trigger dialogs ...
40
- msgs = js("window.__dialogs__||[]")
41
- ```
42
-
43
- Tradeoffs:
44
- - Stubs are lost on page navigation -- must re-run the snippet
45
- - `confirm()` always returns `true` (auto-approves)
46
- - Detectable by antibot (`window.alert.toString()` reveals non-native code)
47
- - Does NOT handle `beforeunload`
48
-
49
- ## beforeunload specifically
50
-
51
- Fires when navigating away from a page with unsaved changes (forms, editors, upload pages). The page freezes until the user clicks Leave/Stay.
52
-
53
- ```python
54
- # Option A: dismiss after navigating (CDP-level, safe)
55
- goto_url("https://new-url.com")
56
- try:
57
- cdp("Page.handleJavaScriptDialog", accept=True) # click "Leave"
58
- except:
59
- pass # no dialog — normal
60
-
61
- # Option B: prevent before navigating (JS injection, detectable)
62
- js("window.onbeforeunload=null")
63
- goto_url("https://new-url.com")
64
- ```
1
+ # Dialogs
2
+
3
+ Browser dialogs (`alert`, `confirm`, `prompt`, `beforeunload`) freeze the JS thread. Two approaches depending on timing.
4
+
5
+ ## Detection
6
+
7
+ `page_info()` auto-surfaces any open dialog: if one is pending it returns `{"dialog": {"type", "message", ...}}` instead of the usual viewport dict (because the page's JS is frozen anyway). So if you call `page_info()` after an action and see a `dialog` key, handle it before doing anything else.
8
+
9
+ ## Reactive: dismiss via CDP (preferred)
10
+
11
+ Works even when JS is frozen. Handles all dialog types including `beforeunload`.
12
+
13
+ ```python
14
+ # Dismiss and read the message
15
+ cdp("Page.handleJavaScriptDialog", accept=True) # accept / click OK
16
+ cdp("Page.handleJavaScriptDialog", accept=False) # cancel / click Cancel
17
+
18
+ # Read what the dialog said (from buffered CDP events)
19
+ events = drain_events()
20
+ for e in events:
21
+ if e["method"] == "Page.javascriptDialogOpening":
22
+ print(e["params"]["type"]) # "alert", "confirm", "prompt", "beforeunload"
23
+ print(e["params"]["message"]) # the dialog text
24
+ ```
25
+
26
+ Undetectable by antibot — no JS injected into the page.
27
+
28
+ ## Proactive: stub via JS
29
+
30
+ Prevents dialogs from ever appearing. Good when you expect multiple `alert()`/`confirm()` calls in sequence.
31
+
32
+ ```python
33
+ js("""
34
+ window.__dialogs__=[];
35
+ window.alert=m=>window.__dialogs__.push(String(m));
36
+ window.confirm=m=>{window.__dialogs__.push(String(m));return true;};
37
+ window.prompt=(m,d)=>{window.__dialogs__.push(String(m));return d||'';};
38
+ """)
39
+ # ... do actions that trigger dialogs ...
40
+ msgs = js("window.__dialogs__||[]")
41
+ ```
42
+
43
+ Tradeoffs:
44
+ - Stubs are lost on page navigation -- must re-run the snippet
45
+ - `confirm()` always returns `true` (auto-approves)
46
+ - Detectable by antibot (`window.alert.toString()` reveals non-native code)
47
+ - Does NOT handle `beforeunload`
48
+
49
+ ## beforeunload specifically
50
+
51
+ Fires when navigating away from a page with unsaved changes (forms, editors, upload pages). The page freezes until the user clicks Leave/Stay.
52
+
53
+ ```python
54
+ # Option A: dismiss after navigating (CDP-level, safe)
55
+ goto_url("https://new-url.com")
56
+ try:
57
+ cdp("Page.handleJavaScriptDialog", accept=True) # click "Leave"
58
+ except:
59
+ pass # no dialog — normal
60
+
61
+ # Option B: prevent before navigating (JS injection, detectable)
62
+ js("window.onbeforeunload=null")
63
+ goto_url("https://new-url.com")
64
+ ```
@@ -1,3 +1,3 @@
1
- # Downloads
2
-
3
- Separate browser-triggered downloads from direct `http_get(...)` fetches, and document the minimal signals that prove a download actually started.
1
+ # Downloads
2
+
3
+ Separate browser-triggered downloads from direct `http_get(...)` fetches, and document the minimal signals that prove a download actually started.
@@ -1,3 +1,3 @@
1
- # Drag And Drop
2
-
3
- Focus on when drag-and-drop can be driven with low-level input events versus when the site really expects a file upload or DOM-specific drag sequence.
1
+ # Drag And Drop
2
+
3
+ Focus on when drag-and-drop can be driven with low-level input events versus when the site really expects a file upload or DOM-specific drag sequence.
@@ -1,3 +1,3 @@
1
- # Dropdowns
2
-
3
- Split dropdowns into native selects, custom overlays, searchable comboboxes, and virtualized menus, and always re-measure after opening because option geometry often appears late.
1
+ # Dropdowns
2
+
3
+ Split dropdowns into native selects, custom overlays, searchable comboboxes, and virtualized menus, and always re-measure after opening because option geometry often appears late.
@@ -1,3 +1,3 @@
1
- # Iframes
2
-
3
- Cover same-origin iframe traversal through `contentDocument` / `contentWindow`, and keep the frame-local versus page-coordinate warning explicit for clicks.
1
+ # Iframes
2
+
3
+ Cover same-origin iframe traversal through `contentDocument` / `contentWindow`, and keep the frame-local versus page-coordinate warning explicit for clicks.
@@ -1,3 +1,3 @@
1
- # Network Requests
2
-
3
- Document how to watch or infer network activity when page state is ambiguous, especially for submit flows, downloads, and SPA actions that succeed without obvious DOM changes.
1
+ # Network Requests
2
+
3
+ Document how to watch or infer network activity when page state is ambiguous, especially for submit flows, downloads, and SPA actions that succeed without obvious DOM changes.
@@ -1,3 +1,3 @@
1
- # Print As PDF
2
-
3
- Cover both direct PDF generation via CDP and sites that only expose a visible "Print" button which must be clicked before handling the browser print flow.
1
+ # Print As PDF
2
+
3
+ Cover both direct PDF generation via CDP and sites that only expose a visible "Print" button which must be clicked before handling the browser print flow.
@@ -1,90 +1,90 @@
1
- # Profile sync
2
-
3
- Make a remote Browser Use browser start already logged in, by uploading cookies from a local Chrome profile.
4
-
5
- ## One-time install
6
-
7
- ```bash
8
- curl -fsSL https://browser-use.com/profile.sh | sh
9
- ```
10
-
11
- Downloads `profile-use` (macOS / Linux / Windows, x64 / arm64). The Python helpers shell out to it; you don't run `profile-use` directly.
12
-
13
- ## Python API (pre-imported in `browser-harness -c`)
14
-
15
- ```python
16
- list_cloud_profiles()
17
- # [{id, name, userId, cookieDomains, lastUsedAt}, ...] — every profile under this API key
18
-
19
- list_local_profiles()
20
- # [{BrowserName, ProfileName, DisplayName, ProfilePath, ...}, ...] — detected on this machine
21
-
22
- sync_local_profile(local_profile_name, browser=None,
23
- cloud_profile_id=None, # update an existing cloud profile instead of creating new
24
- include_domains=None, # only these domains (and subdomains); leading dot optional
25
- exclude_domains=None) # drop these domains; applied before include
26
- # Shells out to `profile-use sync`. Returns the cloud profile UUID
27
- # (the existing one if cloud_profile_id was passed, else the newly-created one).
28
-
29
- start_remote_daemon("work", profileName="my-work") # name→id resolved client-side
30
- start_remote_daemon("work", profileId="<uuid>") # or pass UUID directly
31
-
32
- stop_remote_daemon("work") # shut the daemon and PATCH the cloud browser to stop — billing ends
33
- ```
34
-
35
- `sync_local_profile` prints `♻️ Using existing cloud profile` when `cloud_profile_id` is accepted, or `📝 Creating remote profile...` → `✓ Profile created: <uuid>` when it creates a new one. Check that line if you want to confirm which path ran.
36
-
37
- ## Chat-driven flow (don't guess — ask the user)
38
-
39
- Cookies are real auth. Don't sync or pick a profile unilaterally.
40
-
41
- ```python
42
- # 1. Show what's already in the cloud.
43
- for p in list_cloud_profiles():
44
- print(f"{p['name']:25} {len(p['cookieDomains']):3} domains {p['id']}")
45
- ```
46
- → Agent: *"You have these cloud profiles (<N> domains each). Want to reuse one, sync a local profile, or start clean?"*
47
-
48
- ```python
49
- # 2a. Reuse cloud → one call.
50
- start_remote_daemon("work", profileName="browser-use.com")
51
-
52
- # 2b. Sync local first. Show the options:
53
- for lp in list_local_profiles():
54
- print(lp["DisplayName"])
55
- ```
56
- → Agent: *"Which local profile?"* → user picks → before syncing, inspect domain-level cookie counts with `profile-use inspect --profile <name>` (or `--verbose` for individual cookies) and report the summary; never dump 500 cookies into chat.
57
-
58
- ```python
59
- # 3. Sync + use. Returns the cloud UUID.
60
- uuid = sync_local_profile("browser-use.com")
61
- start_remote_daemon("work", profileId=uuid)
62
-
63
- # 3b. Refresh that same cloud profile later (idempotent — no duplicate profiles).
64
- sync_local_profile("browser-use.com", cloud_profile_id=uuid)
65
-
66
- # 3c. Scoped: push *only* Stripe cookies into a dedicated cloud profile.
67
- sync_local_profile("browser-use.com",
68
- cloud_profile_id=uuid,
69
- include_domains=["stripe.com"])
70
- ```
71
-
72
- ## What actually gets synced
73
-
74
- **Cookies only.** No localStorage, no IndexedDB, no extensions. Enough for session-cookie sites (Google, GitHub, Stripe, most SaaS); not for sites that store auth in localStorage.
75
-
76
- Cookies mutated during a remote session only persist on a clean `PATCH /browsers/{id} {"action":"stop"}` — the daemon does this on shutdown when `BU_BROWSER_ID` + `BROWSER_USE_API_KEY` are set (default for remote daemons). Sessions that hit the timeout lose in-session state.
77
-
78
- ## Cloud profile CRUD
79
-
80
- - UI: https://cloud.browser-use.com/settings?tab=profiles
81
- - API: `GET /profiles`, `GET/PATCH/DELETE /profiles/{id}` (paths are relative to `BU_API = "https://api.browser-use.com/api/v3"` in `admin.py`). Fields: `id`, `name`, `userId`, `lastUsedAt`, `cookieDomains[]`. `list_cloud_profiles()` wraps this.
82
- - Name → UUID: `profileName=` on `start_remote_daemon` resolves client-side; no API change needed.
83
- - Need the UUID for an existing profile? `matches = [p["id"] for p in list_cloud_profiles() if p["name"] == "<name>"]` — then verify `len(matches) == 1` before using it. Profile names are not unique; syncs create duplicates unless you pass `cloud_profile_id=`.
84
- - Lower-level raw calls: `from admin import _browser_use; _browser_use("/profiles/<id>", "DELETE")`. Pass the path *without* the `/api/v3` prefix — it's already on `BU_API`.
85
-
86
- ## Traps
87
-
88
- - **Default proxy (`proxyCountryCode="us"`) blocks some destinations** with `ERR_TUNNEL_CONNECTION_FAILED` (e.g. `cloud.browser-use.com` itself). `proxyCountryCode=None` disables the BU proxy; a different country code picks a different exit.
89
- - **Prefer a dedicated work profile over your personal one.** Especially while testing.
90
- - **Older than `profile-use` v1.0.5?** Pre-1.0.5 the sync needed the Chrome profile to be closed (exclusive SQLite lock on the `Cookies` DB). v1.0.5+ copies the profile dir to a temp and syncs from the copy — Chrome can stay open.
1
+ # Profile sync
2
+
3
+ Make a remote Browser Use browser start already logged in, by uploading cookies from a local Chrome profile.
4
+
5
+ ## One-time install
6
+
7
+ ```bash
8
+ curl -fsSL https://browser-use.com/profile.sh | sh
9
+ ```
10
+
11
+ Downloads `profile-use` (macOS / Linux / Windows, x64 / arm64). The Python helpers shell out to it; you don't run `profile-use` directly.
12
+
13
+ ## Python API (pre-imported in `browser-harness -c`)
14
+
15
+ ```python
16
+ list_cloud_profiles()
17
+ # [{id, name, userId, cookieDomains, lastUsedAt}, ...] — every profile under this API key
18
+
19
+ list_local_profiles()
20
+ # [{BrowserName, ProfileName, DisplayName, ProfilePath, ...}, ...] — detected on this machine
21
+
22
+ sync_local_profile(local_profile_name, browser=None,
23
+ cloud_profile_id=None, # update an existing cloud profile instead of creating new
24
+ include_domains=None, # only these domains (and subdomains); leading dot optional
25
+ exclude_domains=None) # drop these domains; applied before include
26
+ # Shells out to `profile-use sync`. Returns the cloud profile UUID
27
+ # (the existing one if cloud_profile_id was passed, else the newly-created one).
28
+
29
+ start_remote_daemon("work", profileName="my-work") # name→id resolved client-side
30
+ start_remote_daemon("work", profileId="<uuid>") # or pass UUID directly
31
+
32
+ stop_remote_daemon("work") # shut the daemon and PATCH the cloud browser to stop — billing ends
33
+ ```
34
+
35
+ `sync_local_profile` prints `♻️ Using existing cloud profile` when `cloud_profile_id` is accepted, or `📝 Creating remote profile...` → `✓ Profile created: <uuid>` when it creates a new one. Check that line if you want to confirm which path ran.
36
+
37
+ ## Chat-driven flow (don't guess — ask the user)
38
+
39
+ Cookies are real auth. Don't sync or pick a profile unilaterally.
40
+
41
+ ```python
42
+ # 1. Show what's already in the cloud.
43
+ for p in list_cloud_profiles():
44
+ print(f"{p['name']:25} {len(p['cookieDomains']):3} domains {p['id']}")
45
+ ```
46
+ → Agent: *"You have these cloud profiles (<N> domains each). Want to reuse one, sync a local profile, or start clean?"*
47
+
48
+ ```python
49
+ # 2a. Reuse cloud → one call.
50
+ start_remote_daemon("work", profileName="browser-use.com")
51
+
52
+ # 2b. Sync local first. Show the options:
53
+ for lp in list_local_profiles():
54
+ print(lp["DisplayName"])
55
+ ```
56
+ → Agent: *"Which local profile?"* → user picks → before syncing, inspect domain-level cookie counts with `profile-use inspect --profile <name>` (or `--verbose` for individual cookies) and report the summary; never dump 500 cookies into chat.
57
+
58
+ ```python
59
+ # 3. Sync + use. Returns the cloud UUID.
60
+ uuid = sync_local_profile("browser-use.com")
61
+ start_remote_daemon("work", profileId=uuid)
62
+
63
+ # 3b. Refresh that same cloud profile later (idempotent — no duplicate profiles).
64
+ sync_local_profile("browser-use.com", cloud_profile_id=uuid)
65
+
66
+ # 3c. Scoped: push *only* Stripe cookies into a dedicated cloud profile.
67
+ sync_local_profile("browser-use.com",
68
+ cloud_profile_id=uuid,
69
+ include_domains=["stripe.com"])
70
+ ```
71
+
72
+ ## What actually gets synced
73
+
74
+ **Cookies only.** No localStorage, no IndexedDB, no extensions. Enough for session-cookie sites (Google, GitHub, Stripe, most SaaS); not for sites that store auth in localStorage.
75
+
76
+ Cookies mutated during a remote session only persist on a clean `PATCH /browsers/{id} {"action":"stop"}` — the daemon does this on shutdown when `BU_BROWSER_ID` + `BROWSER_USE_API_KEY` are set (default for remote daemons). Sessions that hit the timeout lose in-session state.
77
+
78
+ ## Cloud profile CRUD
79
+
80
+ - UI: https://cloud.browser-use.com/settings?tab=profiles
81
+ - API: `GET /profiles`, `GET/PATCH/DELETE /profiles/{id}` (paths are relative to `BU_API = "https://api.browser-use.com/api/v3"` in `admin.py`). Fields: `id`, `name`, `userId`, `lastUsedAt`, `cookieDomains[]`. `list_cloud_profiles()` wraps this.
82
+ - Name → UUID: `profileName=` on `start_remote_daemon` resolves client-side; no API change needed.
83
+ - Need the UUID for an existing profile? `matches = [p["id"] for p in list_cloud_profiles() if p["name"] == "<name>"]` — then verify `len(matches) == 1` before using it. Profile names are not unique; syncs create duplicates unless you pass `cloud_profile_id=`.
84
+ - Lower-level raw calls: `from admin import _browser_use; _browser_use("/profiles/<id>", "DELETE")`. Pass the path *without* the `/api/v3` prefix — it's already on `BU_API`.
85
+
86
+ ## Traps
87
+
88
+ - **Default proxy (`proxyCountryCode="us"`) blocks some destinations** with `ERR_TUNNEL_CONNECTION_FAILED` (e.g. `cloud.browser-use.com` itself). `proxyCountryCode=None` disables the BU proxy; a different country code picks a different exit.
89
+ - **Prefer a dedicated work profile over your personal one.** Especially while testing.
90
+ - **Older than `profile-use` v1.0.5?** Pre-1.0.5 the sync needed the Chrome profile to be closed (exclusive SQLite lock on the `Cookies` DB). v1.0.5+ copies the profile dir to a temp and syncs from the copy — Chrome can stay open.
@@ -1,17 +1,17 @@
1
- # Screenshots
2
-
3
- `capture_screenshot()` writes a PNG of the current viewport. The file is in **device pixels** — on a 2× display a 2296×1143 CSS viewport produces a 4592×2286 PNG.
4
-
5
- That matters for two reasons:
6
-
7
- 1. **Click coordinates are CSS pixels.** Don't read a target off the image and pass it to `click_at_xy()` directly without dividing by `devicePixelRatio`. The simplest workflow is to take the screenshot, look at it in a viewer that shows CSS coordinates, or measure relative positions and use `js("window.devicePixelRatio")` to convert.
8
-
9
- 2. **Some LLMs reject images > 2000 px per side.** Long sessions on 2× displays will eventually hit this. Pass `max_dim=1800` to downscale the file before it gets into the conversation:
10
-
11
- ```python
12
- capture_screenshot("/tmp/shot.png", max_dim=1800)
13
- ```
14
-
15
- The downscale only happens when the image actually exceeds `max_dim`, so it's safe to leave on for every shot.
16
-
17
- Use full-page screenshots (`full=True`) only when you need to see content below the fold — they are much larger and slower than viewport-only.
1
+ # Screenshots
2
+
3
+ `capture_screenshot()` writes a PNG of the current viewport. The file is in **device pixels** — on a 2× display a 2296×1143 CSS viewport produces a 4592×2286 PNG.
4
+
5
+ That matters for two reasons:
6
+
7
+ 1. **Click coordinates are CSS pixels.** Don't read a target off the image and pass it to `click_at_xy()` directly without dividing by `devicePixelRatio`. The simplest workflow is to take the screenshot, look at it in a viewer that shows CSS coordinates, or measure relative positions and use `js("window.devicePixelRatio")` to convert.
8
+
9
+ 2. **Some LLMs reject images > 2000 px per side.** Long sessions on 2× displays will eventually hit this. Pass `max_dim=1800` to downscale the file before it gets into the conversation:
10
+
11
+ ```python
12
+ capture_screenshot("/tmp/shot.png", max_dim=1800)
13
+ ```
14
+
15
+ The downscale only happens when the image actually exceeds `max_dim`, so it's safe to leave on for every shot.
16
+
17
+ Use full-page screenshots (`full=True`) only when you need to see content below the fold — they are much larger and slower than viewport-only.
@@ -1,3 +1,3 @@
1
- # Scrolling
2
-
3
- Separate page scroll, nested containers, virtualized lists, and dropdown menus, and identify which element is actually consuming wheel events before scrolling.
1
+ # Scrolling
2
+
3
+ Separate page scroll, nested containers, virtualized lists, and dropdown menus, and identify which element is actually consuming wheel events before scrolling.
@@ -1,3 +1,3 @@
1
- # Shadow DOM
2
-
3
- Focus on recursive `shadowRoot` traversal, and note when coordinate clicking is simpler than piercing deeply nested component trees.
1
+ # Shadow DOM
2
+
3
+ Focus on recursive `shadowRoot` traversal, and note when coordinate clicking is simpler than piercing deeply nested component trees.
@@ -1,69 +1,69 @@
1
- # Tabs
2
-
3
- Use **CDP for control**, **UI automation for user-visible order**.
4
-
5
- ## Pure CDP (portable: macOS / Linux / Windows)
6
-
7
- ```python
8
- tabs = list_tabs() # includes chrome:// pages too
9
- real_tabs = list_tabs(include_chrome=False)
10
- tid = new_tab("https://example.com") # create + attach
11
- switch_tab(tid) # attach harness to tab
12
- cdp("Target.activateTarget", targetId=tid) # show it in Chrome
13
- print(current_tab())
14
- print(page_info())
15
- ```
16
-
17
- What CDP is good at:
18
- - attach to a tab
19
- - open a tab
20
- - activate a known target
21
- - inspect URL/title/viewport
22
- - capture the attached tab's screenshot even if another tab is visibly frontmost
23
-
24
- What CDP is bad at:
25
- - matching the **left-to-right tab strip order** the user sees
26
- - telling whether the attached target is an omnibox popup / internal page without URL filtering
27
-
28
- ## Visible order (platform UI)
29
-
30
- ### macOS
31
-
32
- ```applescript
33
- tell application "Google Chrome"
34
- set out to {}
35
- set i to 1
36
- repeat with t in every tab of front window
37
- set end of out to {tab_index:i, tab_title:(title of t), tab_url:(URL of t)}
38
- set i to i + 1
39
- end repeat
40
- return out
41
- end tell
42
- ```
43
-
44
- ```applescript
45
- tell application "Google Chrome"
46
- set active tab index of front window to 2
47
- activate
48
- end tell
49
- ```
50
-
51
- ### Linux
52
-
53
- No AppleScript. Same split still applies:
54
- - use CDP for `new_tab`, attach, inspect, activate known targets
55
- - use window-manager / browser UI automation when the user means visible order
56
-
57
- Typical tools:
58
- - `xdotool`
59
- - `wmctrl`
60
- - desktop-environment scripting (`gdbus`, KWin, GNOME Shell extensions, etc.)
61
-
62
- ## Rules that held up in practice
63
-
64
- - `switch_tab()` is **not enough** if the user expects Chrome to visibly change.
65
- - `Target.activateTarget` is the CDP-side "show this tab".
66
- - `list_tabs()` includes `chrome://newtab/` by default; ask for `include_chrome=False` when you want only real pages.
67
- - `chrome://omnibox-popup.top-chrome/` can appear as a fake page target; ignore it for user-facing tab lists.
68
- - If a page has `w=0 h=0`, you may be attached to the wrong target or a non-window surface.
69
- - For dynamic UIs, re-read element rects after opening dropdowns / modals before coordinate-clicking.
1
+ # Tabs
2
+
3
+ Use **CDP for control**, **UI automation for user-visible order**.
4
+
5
+ ## Pure CDP (portable: macOS / Linux / Windows)
6
+
7
+ ```python
8
+ tabs = list_tabs() # includes chrome:// pages too
9
+ real_tabs = list_tabs(include_chrome=False)
10
+ tid = new_tab("https://example.com") # create + attach
11
+ switch_tab(tid) # attach harness to tab
12
+ cdp("Target.activateTarget", targetId=tid) # show it in Chrome
13
+ print(current_tab())
14
+ print(page_info())
15
+ ```
16
+
17
+ What CDP is good at:
18
+ - attach to a tab
19
+ - open a tab
20
+ - activate a known target
21
+ - inspect URL/title/viewport
22
+ - capture the attached tab's screenshot even if another tab is visibly frontmost
23
+
24
+ What CDP is bad at:
25
+ - matching the **left-to-right tab strip order** the user sees
26
+ - telling whether the attached target is an omnibox popup / internal page without URL filtering
27
+
28
+ ## Visible order (platform UI)
29
+
30
+ ### macOS
31
+
32
+ ```applescript
33
+ tell application "Google Chrome"
34
+ set out to {}
35
+ set i to 1
36
+ repeat with t in every tab of front window
37
+ set end of out to {tab_index:i, tab_title:(title of t), tab_url:(URL of t)}
38
+ set i to i + 1
39
+ end repeat
40
+ return out
41
+ end tell
42
+ ```
43
+
44
+ ```applescript
45
+ tell application "Google Chrome"
46
+ set active tab index of front window to 2
47
+ activate
48
+ end tell
49
+ ```
50
+
51
+ ### Linux
52
+
53
+ No AppleScript. Same split still applies:
54
+ - use CDP for `new_tab`, attach, inspect, activate known targets
55
+ - use window-manager / browser UI automation when the user means visible order
56
+
57
+ Typical tools:
58
+ - `xdotool`
59
+ - `wmctrl`
60
+ - desktop-environment scripting (`gdbus`, KWin, GNOME Shell extensions, etc.)
61
+
62
+ ## Rules that held up in practice
63
+
64
+ - `switch_tab()` is **not enough** if the user expects Chrome to visibly change.
65
+ - `Target.activateTarget` is the CDP-side "show this tab".
66
+ - `list_tabs()` includes `chrome://newtab/` by default; ask for `include_chrome=False` when you want only real pages.
67
+ - `chrome://omnibox-popup.top-chrome/` can appear as a fake page target; ignore it for user-facing tab lists.
68
+ - If a page has `w=0 h=0`, you may be attached to the wrong target or a non-window surface.
69
+ - For dynamic UIs, re-read element rects after opening dropdowns / modals before coordinate-clicking.
@@ -1 +1 @@
1
- # Uploads
1
+ # Uploads
@@ -1,3 +1,3 @@
1
- # Viewport
2
-
3
- Cover how viewport size changes affect layout, coordinate clicks, and any workflow that depends on stable geometry.
1
+ # Viewport
2
+
3
+ Cover how viewport size changes affect layout, coordinate clicks, and any workflow that depends on stable geometry.
@@ -1,15 +1,15 @@
1
- # extensions/builtin/browser/src/browser_harness/
2
-
3
- > P2 | Parent: ../../AGENT.md
4
-
5
- Member List
6
- __init__.py: Package marker for the vendored Browser Harness Python module
7
- run.py: CLI-compatible Browser Harness runner, command dispatch, daemon bootstrap, and snippet execution
8
- helpers.py: Core CDP browser helpers for tabs, navigation, screenshots, JS, clicks, uploads, and HTTP
9
- admin.py: Setup, doctor, reload, update, remote browser, and profile management helpers
10
- daemon.py: Long-lived CDP daemon, session relay, event buffering, and browser attachment logic
11
- _ipc.py: Cross-platform IPC plumbing for daemon socket or TCP loopback transport
12
-
13
- Rule: Members complete, one item per line, parent links valid, precise terms first
14
-
15
- [COVENANT]: Update this file header on changes and verify against parent AGENT.md
1
+ # extensions/builtin/browser/src/browser_harness/
2
+
3
+ > P2 | Parent: ../../AGENT.md
4
+
5
+ Member List
6
+ __init__.py: Package marker for the vendored Browser Harness Python module
7
+ run.py: CLI-compatible Browser Harness runner, command dispatch, daemon bootstrap, and snippet execution
8
+ helpers.py: Core CDP browser helpers for tabs, navigation, screenshots, JS, clicks, uploads, and HTTP
9
+ admin.py: Setup, doctor, reload, update, remote browser, and profile management helpers
10
+ daemon.py: Long-lived CDP daemon, session relay, event buffering, and browser attachment logic
11
+ _ipc.py: Cross-platform IPC plumbing for daemon socket or TCP loopback transport
12
+
13
+ Rule: Members complete, one item per line, parent links valid, precise terms first
14
+
15
+ [COVENANT]: Update this file header on changes and verify against parent AGENT.md
@@ -1,8 +1,8 @@
1
- """Browser Harness core package.
2
-
3
- [WHO]: Provides browser_harness package marker for the vendored CDP bridge
4
- [FROM]: Depends on Python import machinery only
5
- [TO]: Consumed by browser_harness.run, browser_harness.daemon, and NanoPencil browser extension subprocesses
6
- [HERE]: extensions/builtin/browser/src/browser_harness/__init__.py within vendored Browser Harness package
7
- """
8
-
1
+ """Browser Harness core package.
2
+
3
+ [WHO]: Provides browser_harness package marker for the vendored CDP bridge
4
+ [FROM]: Depends on Python import machinery only
5
+ [TO]: Consumed by browser_harness.run, browser_harness.daemon, and NanoPencil browser extension subprocesses
6
+ [HERE]: extensions/builtin/browser/src/browser_harness/__init__.py within vendored Browser Harness package
7
+ """
8
+