@openlines/opl-pi-sht 0.1.17 → 0.1.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -11,8 +11,8 @@ A portable collection of various Pi coding agent extensions. Repository director
11
11
  Install a versioned release from npm or GitHub. Both ship identical content; pick one source per machine, because Pi treats the npm and Git entries as separate packages and installing both loads every extension twice.
12
12
 
13
13
  ```bash
14
- pi install npm:@openlines/opl-pi-sht@0.1.17
15
- pi install git:github.com/linellazatin/opl-pi-sht@v0.1.17
14
+ pi install npm:@openlines/opl-pi-sht@0.1.18
15
+ pi install git:github.com/linellazatin/opl-pi-sht@v0.1.18
16
16
  ```
17
17
 
18
18
  > Omitting the version on the npm source tracks the latest published release; Git refs stay pinned, so move them with `pi install ...@v<new>`.
@@ -34,7 +34,7 @@ npx playwright install chromium
34
34
  {
35
35
  "packages": [
36
36
  {
37
- "source": "npm:@openlines/opl-pi-sht@0.1.17",
37
+ "source": "npm:@openlines/opl-pi-sht@0.1.18",
38
38
  "extensions": ["extensions/opl-init/index.ts", "extensions/opl-todo/index.ts"]
39
39
  }
40
40
  ]
@@ -64,7 +64,7 @@ Copy mode overwrites matching destinations. Link mode skips existing destination
64
64
  | [`opl-init`](extensions/opl-init/README.md) | Fingerprinted repository-guide generator. | `/init`; no config. |
65
65
  | [`opl-simplebench`](extensions/opl-simplebench/README.md) | Auditable provider-aware model benchmark with JSON artifacts and metrics. | `/simplebench`, `simplebench`; supports Ollama, OpenAI-compatible providers, and Bedrock; optional `opl-simplebench.json`. |
66
66
  | [`opl-webaccess`](extensions/opl-webaccess/README.md) | Search plus readable URL/PDF retrieval with session recovery. | `web_search`, `fetch_content`, `get_search_content`; `opl-webaccess.json`. |
67
- | [`opl-browser`](extensions/opl-browser/README.md) | Chromium automation via Playwright; single dispatcher tool replacing the chrome-devtools MCP. | `browser` (action-based); `opl-browser.json`. |
67
+ | [`opl-browser`](extensions/opl-browser/README.md) | Chromium automation via Playwright with structured extraction of rendered pages; single dispatcher tool replacing the chrome-devtools MCP. | `browser` (action-based); `opl-browser.json`. |
68
68
  | [`opl-ctxtrim`](extensions/opl-ctxtrim/README.md) | Trims verbose`ctx_*` tool-schema descriptions on outbound provider requests (~67% smaller schema, ~4,700-6,300 tokens/request). Built specifically for the [context-mode](https://github.com/mksglu/context-mode) extension. | No commands/tools; no config. |
69
69
  | [`opl-todo`](extensions/opl-todo/README.md) | Branch-aware task tool, overlay, and task list. | `todo`, `/todos`; `opl-todo.json`. |
70
70
  | [`opl-questionnaire`](extensions/opl-questionnaire/README.md) | Interactive structured-choice tool. | `questionnaire`; no config. |
@@ -103,7 +103,7 @@ Cold prompt-cache write, measured /init session (opl-modes lazy tools + MCP adap
103
103
 
104
104
  - **`opl-init`** writes a fingerprinted `AGENTS.md` so any agent starts already oriented in a repo. It writes a baseline before asking the model to refine the guide, so smaller models cannot leave the update only in chat; it re-runs only when the tree actually changes.
105
105
  ![init](images/ss-init.png)
106
- - **`opl-browser`** gives full Chromium automation (navigate, snapshot, interact, screenshot, console/network capture, evaluate) through a single tool, with handle+preview output for large results.
106
+ - **`opl-browser`** gives full Chromium automation (navigate, snapshot, extract rendered-page markdown, interact, screenshot, console/network capture, evaluate) through a single tool, with handle+preview output for large results.
107
107
  - **`opl-webaccess`** adds provider-backed search plus readable URL and PDF extraction, with session recovery of earlier results.
108
108
  - **`opl-simplebench`** benchmarks models on deterministic closed-answer contracts, instruction-following, and tool-call generation so you pick a model on evidence, not vibes.
109
109
  ![simplebench](images/ss-simplebench.png)
@@ -142,7 +142,7 @@ Installing extensions adds tool schemas (name + description + JSON parameters) t
142
142
 
143
143
  | Extension | Adds to resting prompt | Est. tokens |
144
144
  |---|---|---|
145
- | `opl-browser` | `browser` tool schema | ~554 |
145
+ | `opl-browser` | `browser` tool schema | ~575 |
146
146
  | `opl-questionnaire` | `questionnaire` schema + prompt guidelines | ~532 |
147
147
  | `opl-webaccess` | `web_search`, `fetch_content`, `get_search_content` | ~394 |
148
148
  | `opl-simplebench` | `simplebench` schema | ~230 |
@@ -14,6 +14,8 @@ No slash commands. The extension registers a single tool, `browser`, chosen by a
14
14
  ```ts
15
15
  browser({ action: "navigate", url: "https://example.com" })
16
16
  browser({ action: "snapshot" })
17
+ browser({ action: "extract" }) // rendered-page markdown
18
+ browser({ action: "extract", selector: "#about-gcash" }) // one element, verbatim
17
19
  browser({ action: "click", selector: "button.login" })
18
20
  browser({ action: "fill", selector: "#email", text: "a@b.com" })
19
21
  browser({ action: "evaluate", script: "document.title" })
@@ -25,9 +27,9 @@ browser({ action: "close" })
25
27
  ```
26
28
 
27
29
  Full action set: `navigate` (url, or `back`/`forward`/`reload`), `snapshot`,
28
- `screenshot`, `click`, `fill`, `hover`, `press`, `select`, `evaluate`, `console`,
29
- `network`, `wait_for`, `pages`, `new_page`, `select_page`, `close_page`, `resize`,
30
- `get`, `close`.
30
+ `extract`, `screenshot`, `click`, `fill`, `hover`, `press`, `select`, `evaluate`,
31
+ `console`, `network`, `wait_for`, `pages`, `new_page`, `select_page`, `close_page`,
32
+ `resize`, `get`, `close`.
31
33
 
32
34
  ## Extension features
33
35
 
@@ -37,6 +39,12 @@ Full action set: `navigate` (url, or `back`/`forward`/`reload`), `snapshot`,
37
39
  evaluate output) are kept out of context: the tool returns a truncated preview
38
40
  plus a `responseId`; call `action: "get"` with that id for the full text.
39
41
  Screenshots are written to a file, never inlined as base64.
42
+ - **Structured extraction.** `extract` runs Readability + Turndown over the
43
+ *rendered* (post-JS) DOM — the complement to `opl-webaccess`'s `fetch_content`,
44
+ which only sees raw HTTP HTML. Static pages: `fetch_content`; rendered or
45
+ interacted-with pages: `browser:extract`. With a `selector`, the matched element
46
+ is converted verbatim (no article detection): Readability's candidate scoring is
47
+ a whole-document heuristic and mispicks inside small subtrees.
40
48
  - **Real Chromium via Playwright.** Navigation with `domcontentloaded` waits,
41
49
  CSS-selector interaction, viewport control, multi-page management.
42
50
  - **Per-page capture.** Console messages and network requests are buffered per
@@ -74,6 +82,8 @@ index.ts Pi wiring: registers the single `browser` tool, TTL result store,
74
82
  preview/handle logic, and session_shutdown cleanup.
75
83
  browser.ts Playwright driver: browser/context/page lifecycle, per-page console
76
84
  and network buffers, and the action switch.
85
+ extract.ts Pure rendered-HTML → markdown pipeline (linkedom + Readability +
86
+ turndown), duplicated from opl-webaccess to keep installs independent.
77
87
  config.ts DEFAULT_CONFIG + loadUserConfig (user overrides win via ??).
78
88
  ```
79
89
 
@@ -1,4 +1,5 @@
1
1
  import { chromium, type Browser, type BrowserContext, type Page, type ConsoleMessage } from "playwright";
2
+ import { extractMarkdown } from "./extract.js";
2
3
  import type { BrowserConfig } from "./config.js";
3
4
 
4
5
  // ponytail: single module-level Chromium instance reused across tool calls.
@@ -87,6 +88,14 @@ export async function runAction(p: BrowserParams, cfg: BrowserConfig): Promise<B
87
88
  const tree = await page().locator("body").ariaSnapshot();
88
89
  return { text: tree || "(empty snapshot)" };
89
90
  }
91
+ case "extract": {
92
+ const scoped = Boolean(p.selector);
93
+ const html = scoped
94
+ ? await page().locator(p.selector).evaluate((el: Element) => el.outerHTML)
95
+ : await page().content();
96
+ const { markdown } = extractMarkdown(html, { raw: scoped });
97
+ return { text: markdown || "(empty extraction)" };
98
+ }
90
99
  case "screenshot": {
91
100
  const file = p.path ?? `opl-browser-${Date.now()}.png`;
92
101
  await page().screenshot({ path: file, fullPage: p.fullPage ?? false });
@@ -0,0 +1,36 @@
1
+ import { Readability } from "@mozilla/readability";
2
+ import { parseHTML } from "linkedom";
3
+ import TurndownService from "turndown";
4
+
5
+ // Structured extraction of a rendered HTML string (post-JS DOM from Playwright).
6
+ // Pipeline mirrors opl-webaccess/extract.ts on purpose; duplicated ~40 lines to
7
+ // keep per-extension installs independent (see research/browser-structured-extraction-plan.md).
8
+
9
+ const td = new TurndownService({
10
+ headingStyle: "atx",
11
+ codeBlockStyle: "fenced",
12
+ });
13
+
14
+ export interface ExtractedMarkdown {
15
+ title: string;
16
+ markdown: string;
17
+ }
18
+
19
+ export function extractMarkdown(html: string, opts?: { raw?: boolean }): ExtractedMarkdown {
20
+ // Readability's candidate scoring is a whole-document heuristic; on a small
21
+ // selector-scoped fragment it drops siblings. Pass raw:true for fragments.
22
+ if (opts?.raw) {
23
+ return { title: "", markdown: td.turndown(html) };
24
+ }
25
+ const { document } = parseHTML(html);
26
+ let article = null;
27
+ try {
28
+ article = new Readability(document as unknown as Document).parse();
29
+ } catch {
30
+ // Readability failed — fall back to raw turndown
31
+ }
32
+ if (!article?.content) {
33
+ return { title: "", markdown: td.turndown(html) };
34
+ }
35
+ return { title: article.title ?? "", markdown: td.turndown(article.content) };
36
+ }
@@ -19,7 +19,7 @@ function store(action: string, text: string): string {
19
19
  }
20
20
 
21
21
  const ACTIONS =
22
- "navigate|snapshot|screenshot|click|fill|hover|press|select|evaluate|console|network|wait_for|pages|new_page|select_page|close_page|resize|get|close";
22
+ "navigate|snapshot|extract|screenshot|click|fill|hover|press|select|evaluate|console|network|wait_for|pages|new_page|select_page|close_page|resize|get|close";
23
23
 
24
24
  export default function (pi: ExtensionAPI) {
25
25
  const cfg = loadUserConfig();
@@ -33,14 +33,14 @@ export default function (pi: ExtensionAPI) {
33
33
  label: "Browser",
34
34
  description:
35
35
  `Drive a real Chromium browser (Playwright) for web testing and inspection. One dispatcher tool; pick action: ${ACTIONS}. ` +
36
- "navigate(url|back|forward|reload); snapshot (accessibility tree); screenshot (saved to file, not inlined); " +
36
+ "navigate(url|back|forward|reload); snapshot (accessibility tree); extract (rendered-page markdown, optionally scoped to selector); screenshot (saved to file, not inlined); " +
37
37
  "click/fill/hover/select by CSS selector; press(key); evaluate(script in page); console/network (captured for active page); " +
38
38
  "wait_for(selector|text); pages/new_page/select_page/close_page; resize(width,height); get(responseId) to retrieve a stored large result; close to shut the browser. " +
39
39
  "Large outputs return a preview + responseId; call action:get with that id for the full text.",
40
40
  parameters: Type.Object({
41
41
  action: Type.String({ description: `One of: ${ACTIONS}` }),
42
42
  url: Type.Optional(Type.String({ description: "navigate/new_page: URL, or back|forward|reload for navigate" })),
43
- selector: Type.Optional(Type.String({ description: "CSS selector for click/fill/hover/select/wait_for" })),
43
+ selector: Type.Optional(Type.String({ description: "CSS selector for click/fill/hover/select/wait_for/extract" })),
44
44
  text: Type.Optional(Type.String({ description: "fill: text to type; wait_for: text to await" })),
45
45
  key: Type.Optional(Type.String({ description: "press: key or combo, e.g. Enter, Control+A" })),
46
46
  values: Type.Optional(Type.Array(Type.String(), { description: "select: option values" })),
@@ -1,15 +1,164 @@
1
1
  {
2
2
  "name": "opl-browser",
3
- "version": "1.0.0",
3
+ "version": "1.1.0",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "opl-browser",
9
- "version": "1.0.0",
9
+ "version": "1.1.0",
10
10
  "license": "ISC",
11
11
  "dependencies": {
12
- "playwright": "^1.49.0"
12
+ "@mozilla/readability": "^0.6.0",
13
+ "linkedom": "^0.18.13",
14
+ "playwright": "^1.49.0",
15
+ "turndown": "^7.2.4"
16
+ }
17
+ },
18
+ "node_modules/@mixmark-io/domino": {
19
+ "version": "2.2.0",
20
+ "resolved": "https://registry.npmjs.org/@mixmark-io/domino/-/domino-2.2.0.tgz",
21
+ "integrity": "sha512-Y28PR25bHXUg88kCV7nivXrP2Nj2RueZ3/l/jdx6J9f8J4nsEGcgX0Qe6lt7Pa+J79+kPiJU3LguR6O/6zrLOw==",
22
+ "license": "BSD-2-Clause"
23
+ },
24
+ "node_modules/@mozilla/readability": {
25
+ "version": "0.6.0",
26
+ "resolved": "https://registry.npmjs.org/@mozilla/readability/-/readability-0.6.0.tgz",
27
+ "integrity": "sha512-juG5VWh4qAivzTAeMzvY9xs9HY5rAcr2E4I7tiSSCokRFi7XIZCAu92ZkSTsIj1OPceCifL3cpfteP3pDT9/QQ==",
28
+ "license": "Apache-2.0",
29
+ "engines": {
30
+ "node": ">=14.0.0"
31
+ }
32
+ },
33
+ "node_modules/boolbase": {
34
+ "version": "2.0.0",
35
+ "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-2.0.0.tgz",
36
+ "integrity": "sha512-DkVaaQHymRhpYEYo9x1oo7Q7B0Y6KJUsjm3c9eTyFDby4MHLBTwZ6ZDWBel5zrYxj1WsZgC5oLpiz+93MluXeA==",
37
+ "license": "ISC",
38
+ "engines": {
39
+ "node": ">=20.19.0"
40
+ },
41
+ "funding": {
42
+ "type": "github",
43
+ "url": "https://github.com/sponsors/fb55"
44
+ }
45
+ },
46
+ "node_modules/css-select": {
47
+ "version": "7.0.0",
48
+ "resolved": "https://registry.npmjs.org/css-select/-/css-select-7.0.0.tgz",
49
+ "integrity": "sha512-snmjEVXy+1LnwXdxhYvTMj1d9tOh4HxkA1YmoayVBeeyR2C14Pum7fcxJIm4SswYspVy866eYNwlH6xC3/VH5g==",
50
+ "license": "BSD-2-Clause",
51
+ "dependencies": {
52
+ "boolbase": "^2.0.0",
53
+ "css-what": "^8.0.0",
54
+ "domhandler": "^6.0.1",
55
+ "domutils": "^4.0.2",
56
+ "nth-check": "^3.0.1"
57
+ },
58
+ "engines": {
59
+ "node": ">=20.19.0"
60
+ },
61
+ "funding": {
62
+ "type": "github",
63
+ "url": "https://github.com/sponsors/fb55"
64
+ }
65
+ },
66
+ "node_modules/css-what": {
67
+ "version": "8.0.0",
68
+ "resolved": "https://registry.npmjs.org/css-what/-/css-what-8.0.0.tgz",
69
+ "integrity": "sha512-DH0Bqq3DNp5tdOReuNyAA+Ev4Y2GS5FMbZpeTLP6C4CDi0h5nL0BmUPChXw3o/qbHLDWHl49sbNqQVY7bMSDdw==",
70
+ "license": "BSD-2-Clause",
71
+ "engines": {
72
+ "node": ">=20.19.0"
73
+ },
74
+ "funding": {
75
+ "type": "github",
76
+ "url": "https://github.com/sponsors/fb55"
77
+ }
78
+ },
79
+ "node_modules/cssom": {
80
+ "version": "0.5.0",
81
+ "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.5.0.tgz",
82
+ "integrity": "sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw==",
83
+ "license": "MIT"
84
+ },
85
+ "node_modules/dom-serializer": {
86
+ "version": "3.1.1",
87
+ "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-3.1.1.tgz",
88
+ "integrity": "sha512-4MEa38/QexBob6gFNwu+EGdWvhJ1OKuNwdYY3Y3NyeWDQfnGeDYQUDfIRzWu5B5gsv03so2Uxd28YC6zrsx3Lw==",
89
+ "license": "MIT",
90
+ "dependencies": {
91
+ "domelementtype": "^3.0.0",
92
+ "domhandler": "^6.0.0",
93
+ "entities": "^8.0.0"
94
+ },
95
+ "engines": {
96
+ "node": ">=20.19.0"
97
+ },
98
+ "funding": {
99
+ "type": "github",
100
+ "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1"
101
+ }
102
+ },
103
+ "node_modules/domelementtype": {
104
+ "version": "3.0.0",
105
+ "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-3.0.0.tgz",
106
+ "integrity": "sha512-umCQid3jKbDmVjx8jGaW7uUykm4DEUeyV21hPxNMo2nV955DhUThwqyOIDtreepP31hl84X7G5U9ZfsWvIB3Pg==",
107
+ "funding": [
108
+ {
109
+ "type": "github",
110
+ "url": "https://github.com/sponsors/fb55"
111
+ }
112
+ ],
113
+ "license": "BSD-2-Clause",
114
+ "engines": {
115
+ "node": ">=20.19.0"
116
+ }
117
+ },
118
+ "node_modules/domhandler": {
119
+ "version": "6.0.1",
120
+ "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-6.0.1.tgz",
121
+ "integrity": "sha512-gYzvtM72ZtxQO0T048kd6HWSbbGCNOUwcnfQ01cqIJ4X2IYKFFHZ5mKvrQETcFXxsRObZulDaKmy//R7TPtsBg==",
122
+ "license": "BSD-2-Clause",
123
+ "dependencies": {
124
+ "domelementtype": "^3.0.0"
125
+ },
126
+ "engines": {
127
+ "node": ">=20.19.0"
128
+ },
129
+ "funding": {
130
+ "type": "github",
131
+ "url": "https://github.com/fb55/domhandler?sponsor=1"
132
+ }
133
+ },
134
+ "node_modules/domutils": {
135
+ "version": "4.0.2",
136
+ "resolved": "https://registry.npmjs.org/domutils/-/domutils-4.0.2.tgz",
137
+ "integrity": "sha512-qI4JLRKnSzqFqr7hAlS5xQDusBCjKSEG4t4+7aNrIQMHBcsC2TGEhuyABJdYkgSewL57PNLYEiibY2iPKhKpaA==",
138
+ "license": "BSD-2-Clause",
139
+ "dependencies": {
140
+ "dom-serializer": "^3.0.0",
141
+ "domelementtype": "^3.0.0",
142
+ "domhandler": "^6.0.0"
143
+ },
144
+ "engines": {
145
+ "node": ">=20.19.0"
146
+ },
147
+ "funding": {
148
+ "type": "github",
149
+ "url": "https://github.com/fb55/domutils?sponsor=1"
150
+ }
151
+ },
152
+ "node_modules/entities": {
153
+ "version": "8.1.0",
154
+ "resolved": "https://registry.npmjs.org/entities/-/entities-8.1.0.tgz",
155
+ "integrity": "sha512-kxL7msIffSuh9aaFAMD7rxAIuTRMAHMeBtgHW2yUdWw732ZNh4MehkF2gdjvtdmikkaIP9bFDDJOPlsvm7avrA==",
156
+ "license": "BSD-2-Clause",
157
+ "engines": {
158
+ "node": ">=20.19.0"
159
+ },
160
+ "funding": {
161
+ "url": "https://github.com/fb55/entities?sponsor=1"
13
162
  }
14
163
  },
15
164
  "node_modules/fsevents": {
@@ -26,6 +175,150 @@
26
175
  "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
27
176
  }
28
177
  },
178
+ "node_modules/html-escaper": {
179
+ "version": "3.0.3",
180
+ "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-3.0.3.tgz",
181
+ "integrity": "sha512-RuMffC89BOWQoY0WKGpIhn5gX3iI54O6nRA0yC124NYVtzjmFWBIiFd8M0x+ZdX0P9R4lADg1mgP8C7PxGOWuQ==",
182
+ "license": "MIT"
183
+ },
184
+ "node_modules/htmlparser2": {
185
+ "version": "10.1.0",
186
+ "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-10.1.0.tgz",
187
+ "integrity": "sha512-VTZkM9GWRAtEpveh7MSF6SjjrpNVNNVJfFup7xTY3UpFtm67foy9HDVXneLtFVt4pMz5kZtgNcvCniNFb1hlEQ==",
188
+ "funding": [
189
+ "https://github.com/fb55/htmlparser2?sponsor=1",
190
+ {
191
+ "type": "github",
192
+ "url": "https://github.com/sponsors/fb55"
193
+ }
194
+ ],
195
+ "license": "MIT",
196
+ "dependencies": {
197
+ "domelementtype": "^2.3.0",
198
+ "domhandler": "^5.0.3",
199
+ "domutils": "^3.2.2",
200
+ "entities": "^7.0.1"
201
+ }
202
+ },
203
+ "node_modules/htmlparser2/node_modules/dom-serializer": {
204
+ "version": "2.0.0",
205
+ "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz",
206
+ "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==",
207
+ "license": "MIT",
208
+ "dependencies": {
209
+ "domelementtype": "^2.3.0",
210
+ "domhandler": "^5.0.2",
211
+ "entities": "^4.2.0"
212
+ },
213
+ "funding": {
214
+ "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1"
215
+ }
216
+ },
217
+ "node_modules/htmlparser2/node_modules/dom-serializer/node_modules/entities": {
218
+ "version": "4.5.0",
219
+ "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
220
+ "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==",
221
+ "license": "BSD-2-Clause",
222
+ "engines": {
223
+ "node": ">=0.12"
224
+ },
225
+ "funding": {
226
+ "url": "https://github.com/fb55/entities?sponsor=1"
227
+ }
228
+ },
229
+ "node_modules/htmlparser2/node_modules/domelementtype": {
230
+ "version": "2.3.0",
231
+ "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz",
232
+ "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==",
233
+ "funding": [
234
+ {
235
+ "type": "github",
236
+ "url": "https://github.com/sponsors/fb55"
237
+ }
238
+ ],
239
+ "license": "BSD-2-Clause"
240
+ },
241
+ "node_modules/htmlparser2/node_modules/domhandler": {
242
+ "version": "5.0.3",
243
+ "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz",
244
+ "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==",
245
+ "license": "BSD-2-Clause",
246
+ "dependencies": {
247
+ "domelementtype": "^2.3.0"
248
+ },
249
+ "engines": {
250
+ "node": ">= 4"
251
+ },
252
+ "funding": {
253
+ "url": "https://github.com/fb55/domhandler?sponsor=1"
254
+ }
255
+ },
256
+ "node_modules/htmlparser2/node_modules/domutils": {
257
+ "version": "3.2.2",
258
+ "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz",
259
+ "integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==",
260
+ "license": "BSD-2-Clause",
261
+ "dependencies": {
262
+ "dom-serializer": "^2.0.0",
263
+ "domelementtype": "^2.3.0",
264
+ "domhandler": "^5.0.3"
265
+ },
266
+ "funding": {
267
+ "url": "https://github.com/fb55/domutils?sponsor=1"
268
+ }
269
+ },
270
+ "node_modules/htmlparser2/node_modules/entities": {
271
+ "version": "7.0.1",
272
+ "resolved": "https://registry.npmjs.org/entities/-/entities-7.0.1.tgz",
273
+ "integrity": "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==",
274
+ "license": "BSD-2-Clause",
275
+ "engines": {
276
+ "node": ">=0.12"
277
+ },
278
+ "funding": {
279
+ "url": "https://github.com/fb55/entities?sponsor=1"
280
+ }
281
+ },
282
+ "node_modules/linkedom": {
283
+ "version": "0.18.13",
284
+ "resolved": "https://registry.npmjs.org/linkedom/-/linkedom-0.18.13.tgz",
285
+ "integrity": "sha512-ES/o9qotMpzpN2MHs+Iq/JcVoOj8Fa5wiQYrTdFpvAnwXL0g66XHHUc9WUMk6nAlBtGsFQ24ne+SYnvnaQ2FSw==",
286
+ "license": "ISC",
287
+ "dependencies": {
288
+ "css-select": "^7.0.0",
289
+ "cssom": "^0.5.0",
290
+ "html-escaper": "^3.0.3",
291
+ "htmlparser2": "^10.1.0",
292
+ "uhyphen": "^0.2.0"
293
+ },
294
+ "engines": {
295
+ "node": ">=16"
296
+ },
297
+ "peerDependencies": {
298
+ "canvas": ">= 2"
299
+ },
300
+ "peerDependenciesMeta": {
301
+ "canvas": {
302
+ "optional": true
303
+ }
304
+ }
305
+ },
306
+ "node_modules/nth-check": {
307
+ "version": "3.0.1",
308
+ "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-3.0.1.tgz",
309
+ "integrity": "sha512-GX0gsdbGVCgnRgbeGaubfjpBXyYRWOOCVeYh08bSQvDZqxz5ndXs1OTfAt/h36G1xvI94YIspsI0sVFqAV9+RQ==",
310
+ "license": "BSD-2-Clause",
311
+ "dependencies": {
312
+ "boolbase": "^2.0.0"
313
+ },
314
+ "engines": {
315
+ "node": ">=20.19.0"
316
+ },
317
+ "funding": {
318
+ "type": "github",
319
+ "url": "https://github.com/fb55/nth-check?sponsor=1"
320
+ }
321
+ },
29
322
  "node_modules/playwright": {
30
323
  "version": "1.62.1",
31
324
  "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.62.1.tgz",
@@ -55,6 +348,25 @@
55
348
  "engines": {
56
349
  "node": ">=20"
57
350
  }
351
+ },
352
+ "node_modules/turndown": {
353
+ "version": "7.2.4",
354
+ "resolved": "https://registry.npmjs.org/turndown/-/turndown-7.2.4.tgz",
355
+ "integrity": "sha512-I8yFsfRzmzK0WV1pNNOA4A7y4RDfFxPRxb3t+e3ui14qSGOxGtiSP6GjeX+Y6CHb7HYaFj7ECUD7VE5kQMZWGQ==",
356
+ "license": "MIT",
357
+ "dependencies": {
358
+ "@mixmark-io/domino": "^2.2.0"
359
+ },
360
+ "engines": {
361
+ "node": ">=18",
362
+ "npm": ">=9"
363
+ }
364
+ },
365
+ "node_modules/uhyphen": {
366
+ "version": "0.2.0",
367
+ "resolved": "https://registry.npmjs.org/uhyphen/-/uhyphen-0.2.0.tgz",
368
+ "integrity": "sha512-qz3o9CHXmJJPGBdqzab7qAYuW8kQGKNEuoHFYrBwV6hWIMcpAmxDLXojcHfFr9US1Pe6zUswEIJIbLI610fuqA==",
369
+ "license": "ISC"
58
370
  }
59
371
  }
60
372
  }
@@ -1,10 +1,13 @@
1
1
  {
2
2
  "name": "opl-browser",
3
- "version": "1.0.0",
3
+ "version": "1.1.0",
4
4
  "description": "Chromium browser automation for pi via Playwright. A single dispatcher tool for navigation, snapshots, interaction, console/network capture, and evaluation — replaces the chrome-devtools MCP server.",
5
5
  "main": "index.js",
6
6
  "license": "ISC",
7
7
  "dependencies": {
8
- "playwright": "^1.49.0"
8
+ "@mozilla/readability": "^0.6.0",
9
+ "linkedom": "^0.18.13",
10
+ "playwright": "^1.49.0",
11
+ "turndown": "^7.2.4"
9
12
  }
10
13
  }
@@ -0,0 +1 @@
1
+ declare module "turndown";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openlines/opl-pi-sht",
3
- "version": "0.1.17",
3
+ "version": "0.1.18",
4
4
  "description": "Pi coding-agent extension kit: token-efficient context, safe modes, fingerprinted /init, model benchmarking, web access, and browser automation.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -41,7 +41,7 @@
41
41
  "type": "module",
42
42
  "scripts": {
43
43
  "test": "npm run test:opl-browser && npm run test:opl-footer && npm run test:opl-init && npm run test:opl-input && npm run test:opl-modes && npm run test:opl-questionnaire && npm run test:opl-todo && npm run test:opl-webaccess && npm run test:opl-simplebench && npm run test:opl-ctxtrim",
44
- "test:opl-browser": "OPL_EXTENSION=opl-browser bun test tests/extension-smoke.test.mjs",
44
+ "test:opl-browser": "bun test tests/opl-browser-helpers.test.mjs && OPL_EXTENSION=opl-browser bun test tests/extension-smoke.test.mjs",
45
45
  "test:opl-footer": "bun test tests/opl-footer-helpers.test.mjs && OPL_EXTENSION=opl-footer bun test tests/extension-smoke.test.mjs",
46
46
  "test:opl-init": "bun test tests/opl-init-crawl.test.mjs && OPL_EXTENSION=opl-init bun test tests/extension-smoke.test.mjs",
47
47
  "test:opl-input": "bun test tests/opl-input-style.test.mjs && OPL_EXTENSION=opl-input bun test tests/extension-smoke.test.mjs",